Unpacking Pakku
Tags: rakulang
Published: 2023-05-07 17:43:55

In this post I'm going to explain how I use [Pakku](https://github.com/hythm7/Pakku) (a package manager for Raku) to manage Raku distributions. On a clean Rakudo installation, I will start with installing Pakku, then use some of the features like installing a distribution, search the ecosystem for a module and other features like configuring a local recommendation manager. and in the process I will explain how Pakku works.


##Bootstrapping is easy
The first thing I do on a new Rakudo installation is I install Pakku. all I need to do is clone the repo and ask Pakku to install Pakku!
<pre>
> git clone https://github.com/hythm7/Pakku.git
> cd Pakku
> raku -I. bin/pakku add .
🧚 PRC: ｢.｣
🦋 STG: ｢Pakku:ver&lt;caria>:auth&lt;github:hythm7&gt;:api&lt;0&gt;｣
🦋 TST: ｢Pakku:ver&lt;caria>:auth&lt;github:hythm7&gt;:api&lt;0&gt;｣
🦋 TST: ｢cache.rakutest｣
🦋 TST: ｢cmd.rakutest｣
🦋 TST: ｢config.rakutest｣
🦋 TST: ｢core.rakutest｣
🦋 TST: ｢log.rakutest｣
🦋 TST: ｢meta.rakutest｣
🦋 TST: ｢spec.rakutest｣
🧚 TST: ｢｢Pakku:ver&lt;caria>:auth&lt;github:hythm7&gt;:api&lt;0&gt;｣
🧚 BIN: ｢pakku｣
🧚 -Ofun
</pre>

The output shows that Pakku distribution was staged, then test files were run, and also a binary `pakku` was added. We will go through these different stages in "Installing a distribution" section later in this post.

But for now looks like Pakku can install it self, thats nice. however, if one looked at the installation code will realize that the actual installation logic lives in Rakudo [here](https://github.com/rakudo/rakudo/blob/main/src/core.c/CompUnit/Repository/Installation.pm6#L220), and that what made bootstrapping Pakku easy.


##Searching for a module
To search the ecosystems for a module, for instance a `DB` module:

<pre>
> pakku search db
｢DB:ver&lt;0.5>:auth&lt;github:CurtTilmes>:api&lt;1>｣
｢DB:ver&lt;0.4>:auth&lt;>:api&lt;>｣
｢DB:ver&lt;0.3>:auth&lt;>:api&lt;>｣
｢DB:ver&lt;0.2>:auth&lt;>:api&lt;>｣
｢DB:ver&lt;0.1>:auth&lt;>:api&lt;>｣
｢DB::Migration::Declare:ver&lt;0.1.3>:auth&lt;zef:jnthn>:api&lt;>｣
｢DB::Migration::Declare:ver&lt;0.1.2>:auth&lt;zef:jnthn>:api&lt;>｣
｢DB::Migration::Declare:ver&lt;0.1.1>:auth&lt;zef:jnthn>:api&lt;>｣
｢DB::Migration::Declare:ver&lt;0.1>:auth&lt;zef:jnthn>:api&lt;>｣
｢DB::Migration::Simple:ver&lt;1.0>:auth&lt;>:api&lt;>｣
｢DB::Model::Easy:ver&lt;*>:auth&lt;>:api&lt;>｣
｢DB::MySQL:ver&lt;0.5>:auth&lt;>:api&lt;>｣
...
</pre>

The search is relaxed by default, but if exact match is needed, one can provide `norelaxed` option:

<pre>
> pakku search norelaxed db
｢DB:ver&lt;0.5>:auth&lt;github:CurtTilmes>:api&lt;1>｣
｢DB:ver&lt;0.4>:auth&lt;>:api&lt;>｣
｢DB:ver&lt;0.3>:auth&lt;>:api&lt;>｣
｢DB:ver&lt;0.2>:auth&lt;>:api&lt;>｣
｢DB:ver&lt;0.1>:auth&lt;>:api&lt;>｣
🧚 -Ofun

</pre>

One can check the details of a distribution, such as description, dependencies and what it provides. for example:

<pre>
> pakku search details 'DB::Migration::Declare:ver&lt;0.1.3>'
MTA: ｢DB::Migration::Declare:ver&lt;0.1.3>:auth&lt;zef:jnthn>:api&lt;>｣
DEP: ｢Digest::SHA1::Native｣
DEP: ｢Test::ContainerizedService｣
PRV: ｢DB::Migration::Declare｣
PRV: ｢DB::Migration::Declare::Applicator｣
PRV: ｢DB::Migration::Declare::ColumnType｣
PRV: ｢DB::Migration::Declare::Database｣
PRV: ｢DB::Migration::Declare::Database::Postgres｣
PRV: ｢DB::Migration::Declare::MigrationDirection｣
PRV: ｢DB::Migration::Declare::MigrationHistory｣
PRV: ｢DB::Migration::Declare::Model｣
PRV: ｢DB::Migration::Declare::Model::MigrationStep｣
PRV: ｢DB::Migration::Declare::Problem｣
PRV: ｢DB::Migration::Declare::SQLLiteral｣
PRV: ｢DB::Migration::Declare::Schema｣
PRV: ｢DB::Migration::Declare::Test｣
URL: ｢https://github.com/jnthn/db-migration-declare.git｣
DES: ｢Specify database migrations using a Raku DSL. Currently Postgres only.｣
🧚 -Ofun
</pre>


##Installing a distribution

In this section I will install  `Inline::Perl5` in debug mode enabled. because `Inline::Perl5` has multiple dependencies (including bin dependency), and a build stage, I think it's a good candidate for this section.

since running in debug mode, I will truncate the output as possible for easy readability, then explain the steps Pakku goes through for installing a distribution.

<pre>
> pakku verbose debug add Inline::Perl5
1 - 🧚 PRC: ｢Inline::Perl5｣
2 - 🐛 SPC: ｢Inline::Perl5｣
3 - 🐛 CAC: ｢Inline::Perl5｣ recommending...
4 - 🐛 REC: ｢pakku｣ ‹Inline::Perl5› recommending...
5 - 🦋 MTA: ｢Inline::Perl5:ver&lt;0.60>:auth&lt;github:niner>:api&lt;>｣
</pre>

1 - Started processing `Inline::Perl5`.

2 - Recieved `Inline::Perl5` specification.

3 - Asking the cache for a recommendation for `Inline::Perl5` spec.

4 - No recommendation from cache, so asking the default recommendation manager (named pakku).

5 - `Inline::Perl5:ver<0.60>:auth<github:niner>:api<>` meta info recommended from the recommendation manager.


<pre>
🐛 SPC: ｢Distribution::Builder::MakeFromJSON:ver&lt;0.6+>｣
🐛 CAC: ｢Distribution::Builder::MakeFromJSON:ver&lt;0.6+>｣ recommending...
🐛 REC: ｢pakku｣ ‹Distribution::Builder::MakeFromJSON:ver&lt;0.6+>› recommending...
🦋 MTA: ｢Distribution::Builder::MakeFromJSON:ver&lt;0.6>:auth&lt;github:niner>:api&lt;>｣
...
</pre>

The meta info is checked for dependencies, and run the same steps for found dependencies.

<pre>
🐛 SPC: ｢perl:from&lt;bin>｣ satisfied!
🐛 SPC: ｢JSON::Fast｣ satisfied!
🐛 SPC: ｢Test｣ satisfied!
...
</pre>

Nothing will be done for any of these dependencies specs because they are already satisfied. Confirmed that `perl` binary exists in the system. `JSON::Fast` is already installed. `Test` is already installed from rakudo core distribution.

<pre>
🦋 FTC: ｢Distribution::Builder::MakeFromJSON:ver&lt;0.6>:auth&lt;github:niner>:api&lt;>｣
🐛 CAC: ｢Distribution::Builder::MakeFromJSON:ver&lt;0.6>:auth&lt;github:niner>:api&lt;>｣ looking...
🐛 FTC: ｢http://truncated...｣
🐛 FTC: ｢/home/hythm/.pakku/.tmp/truncated...｣
🐛 CAC: ｢Distribution::Builder::MakeFromJSON:ver&lt;0.6>:auth&lt;github:niner>:api&lt;>｣ caching...
🐛 CAC: ｢Distribution::Builder::MakeFromJSON:ver&lt;0.6>:auth&lt;github:niner>:api&lt;>｣ ‹/home/hythm/.pakku/.cache/truncated...›
...
</pre>

Now as all dependencies have been resolved, it is time to fetch the distributions. so first, check if the distribution is in the cache, if not then fetch the archive, extract it to a temp directory, and cache it so it can be found in cache next time.


<pre>
...
1 - 🦋 BLD: ｢Inline::Perl5:ver&lt;0.60>:auth&lt;github:niner>:api&lt;>｣
2 - 🐛 BLD: ｢/home/hythm/.rakubrew/versions/moar-blead/install/bin/rakudo truncated...
3 - 🐛 BLD: gcc -Wall p5helper.c -Wl,-E  -fstack-protector-strong -L/usr/ truncated...
4 - 🧚 BLD: ｢Inline::Perl5:ver&lt;0.60>:auth&lt;github:niner>:api&lt;>｣
5 - 🦋 STG: ｢Inline::Perl5:ver&lt;0.60>:auth&lt;github:niner>:api&lt;>｣
6 - 🦋 TST: ｢Inline::Perl5:ver&lt;0.60>:auth&lt;github:niner>:api&lt;>｣
7 - ...
8 - 🧚 TST: ｢Inline::Perl5:ver&lt;0.60>:auth&lt;github:niner>:api&lt;>｣
🧚 -Ofun
</pre>

Now as all distributions have been fetched, what is remaining is to build, stage, test and install them.

1 - Start building

2 - The running build command

3 - The build process output

4 - Build succeeded 

5 - Staging the distribution - installing the dist to a staging repository

6 - Start testing the distribution using the staging repository.

7 - The truncated output shows the files being tested and their output

8 - Test succeeded

After all dists have been tested successfully, the staging repository content is moved to the actuall installation repo finishing the installation process.

##List installed dists

To list all installed dists, one can run:

<pre>
> pakku list
｢Libarchive:ver&lt;0.1>:auth&lt;github:CurtTilmes>:api&lt;>｣
｢Number::Bytes::Human:ver&lt;0.0.3>:auth&lt;>:api&lt;>｣
｢JSON::OptIn:ver&lt;0.0.2>:auth&lt;zef:jonathanstowe>:api&lt;>｣
｢Cro::Core:ver&lt;0.8.9>:auth&lt;zef:cro>:api&lt;>｣
...
</pre>
To list installed dists in a specific repo, eg. `core`:

<pre>
> pakku list repo core
｢rakudo:ver&lt;2023.04.45.g.5965.d.7.fb.0>:auth&lt;Yet Another Society>:api&lt;>｣
🧚 -Ofun
</pre>

To check if a dist is installed:

<pre>
> pakku list JSON::Fast
｢JSON::Fast:ver&lt;0.17>:auth&lt;cpan:TIMOTIMO>:api&lt;>｣
🧚 -Ofun

</pre>

##Recommendation managers

Pakku currently supports two types of recommendation managers:


1- HTTP service recommendation manager

Pakku ships configured with a default HTTP recman, that has all available Raku distriutions in [Raku Ecosystem Archive](https://github.com/Raku/REA)

Can check the configured recommendation managers by running:

<pre>
> pakku config recman
🦋 CNF: ｢recman｣
[
  {
    "active": true,
    "location": "http://recman.pakku.org",
    "name": "pakku",
    "priority": 1
  }
]
🧚 -Ofun
</pre>

2- Local recommendation manager

Used for making a recommendation manager from a local directory containing a collection of Raku dists, and these dists can be seen by Pakku and become available for installation. 

To configure a local recman one can run:

<pre>
> pakku config recman name-of-local-recman set location /home/hythm/dev/my-collection-of-raku-dists
🦋 CNF: ｢recman｣
🦋 REC: ｢name-of-local-recman｣
🦋 CNF: ｢{"location":"/home/hythm/dev/my-collection-of-raku-dists"}｣
🧚 -Ofun
</pre>

A local recman is a quick and easy way to turn a local collection of dists into a recman, but if one needs more managable way (eg manage dists internally in a company or an organization) it's advised to use an HTTP service recamn. in this case the HTTP service recman expects a collection of dist archives, and it will process them, build a database and can serve and recommend the dists. this can be acheived using [recman-simple](https://github.com/hythm7/recman-simple) distribution.

When multiple recmans are configured, it's advised to set the priority of each one as needed, because Pakku searches recmans based on priority, if nothing recommended from first recman, then search the next one.

To set recman priority:

```
> pakku config recman name-of-local-recman set priority 2
🦋 CNF: ｢recman｣
🦋 REC: ｢name-of-local-recman｣
🦋 CNF: ｢{"priority":"2"}｣
🧚 -Ofun

```