Skip to content

Testing Changes and Producing Binary Packages

PÁLI Gábor János edited this page Feb 6, 2016 · 2 revisions

Prerequisites

As described below, testing changes or producing binary packages to the ports repository assumes the presence of portshaker, poudriére, and git. For the sake of completeness, we will briefly present their configuration as well. If this is all set, just skip to the "The Basic Routine" section.

Poudriére

Poudriére a is a tool to test the packaging process and build binary packages in bulk for distribution. It can be installed from the Ports Collection, as ports-mgmt/poudriere. There can be also a -devel version found.

# cd /usr/ports/ports-mgmt/poudriere
# make install clean

Poudriére can work with and without zfs(8), and it can also utilize tmpfs(5) for the better performance. Setting up tmpfs(5) is highly recommended as it greatly reduces the time required for bulk builds. Poudriére can also do builds in parallel and for different architectures, so many-core amd64 systems are also certainly useful to have. Once poudriere is installed, remember to configure it to your needs, the details will not be discussed here.

Creating Jails

In general, it is suggested to create jails for all combination of supported architectures and major operating system version. Currently, they are as follows: FreeBSD 8, 9, 10 and i386 and amd64. Note that, for each major branch, the oldest supported minor version has to be picked in order to maintain ABI compatibility for the produced packages. According to this, as the time of writing, the following jails shall be created: FreeBSD 9.3, and FreeBSD 10.1 for both i386 and amd64.

# poudriere jail -c -j 93i386 -a i386 -v 9.3-RELEASE -m ftp
# poudriere jail -c -j 93amd64 -a amd64 -v 9.3-RELEASE -m ftp
# poudriere jail -c -j 101i386 -a i386 -v 10.1-RELEASE -m ftp
# poudriere jail -c -j 101amd64 -a amd64 -v 10.1-RELEASE -m ftp

Creating a Ports Tree

In addition to jails, there is a ports tree needed. Poudriére creates a default ports tree, but perhaps it is safer to maintain a dedicated tree. Note that this tree is going to be empty by default as portshaker will be used for population.

# poudriere ports -c -F -p haskell

Portshaker

Similarly to poudriere, portshaker has to be installed from the Ports Collection. It can be used for maintaining only an overlay for the ports tree, instead of the full tree. Hence portshaker takes two or more trees and merges them into a single one. This way one does not have to track the changes outside of the set of the maintained ports.

# cd /usr/ports/ports-mgmt/portshaker
# make install clean

However, there may be conflicts, when the same port exists in multiple trees. In this case, portshaker always keeps the port of the later version, ignoring the other one. This is the expected behavior, when the overlay has the latest version. But sometimes it does not, so portshaker will emit a warning that it is not going to merge the given port, as the version goes backwards. That is why it is so important to keep the overlay in sync with the Ports Collection.

Configuring a Ports Overlay

The haskell ports tree of poudriere has to be configured in the configuration file of portshaker, which is $LOCALBASE/etc/portshaker.conf. Note that, due to mistakes in the portshaker port, it likes to overwrite this file with the defaults on install/reinstall/upgrade. Make sure to create a backup of this file, once it is done.

The following lines have to be present in the configuration file.

poudriere_ports_mountpoint=$LOCALBASE/poudriere/ports
mirror_base_dir="/var/cache/portshaker"
ports_trees="main haskell"

main_ports_tree="$PORTSDIR"
main_merge_from="ports"
haskell_poudriere_tree="haskell"
haskell_merge_from="ports haskell"

Obviously, both $PORTDIR and $LOCALBASE have to be replaced with their actual values. Do not attempt to use portshaker itself to update the haskell tree — it will be used in a non-standard way, see below. Instead, only a dummy script is created:

# echo > $LOCALBASE/etc/portshaker.d/haskell
# chmod +x $LOCALBASE/etc/portshaker.d/haskell

git (testing only)

Another important component of the procedure is git. It is used because the development overlay is maintained with git, and it is quite convenient to use the same repository but on a different server. This is the server which will be simply referenced as $staging_server. This is the system that is assumed to be used for testing, it shall have poudriere and portshaker installed as well.

That is also where a directory for the staging git repository shall set up. That is what will be called $staging_repository. It is a regular directory where a bare git repository shall be created, which would be used as a target for pushing.

$ mkdir -p $staging_repository
$ cd $staging_repository
$ git init --bare

Once it is ready, the client — which is normally used for everyday development (and which may be, and actually recommended to be, different from the testing machine) — could add this repository as a remote, named stage.

$ git remote add stage ssh://$staging_server/$staging_repository

All right, that is all about the basic setup.

The Basic Routine

By the assumptions described in the section about the prerequisites, the everyday routine would be as follows.

Staging the Changes (testing only)

First, the untested changes, recorded as git commits, should be transferred to the staging server. This is where the use of git and the previously set stage remote come handy.

$ git push stage

Sometimes the push operation has to be forced when it contains rebased commits. This is quite acceptable when the changes have not yet been pushed to any public places, such as GitHub.

$ git push stage --force

Merging

Before launching the build, the poudriere ports tree has to be merged. Since this is the result of merging the Ports Collection with the Haskell ports development overlay, it is recommended to update the FreeBSD ports tree to its latest version first. In case of repeated testing in a short time period, this step could be safely omitted.

Note that the steps from now on will be happening on the staging server.

# portshaker -u ports

Because the git clone may have a different history due to rebases, the whole clone has to be recreated from scratch. The yes(1) command is used to keep portshaker working with the merging as it always stops waiting for user input when it finds a conflicting port. As described above, this is fine until the overlay has the newer version of the port.

# rm -rf /var/cache/portshaker/haskell

(either) for testing:

# git clone $staging_repository /var/cache/portshaker/haskell

(or) for producing binary packages:

# git clone https://github.com/freebsd-haskell/ports /var/cache/portshaker/haskell

Finally:

# yes i | portshaker -v -m haskell

Launching the Build (testing only)

When the merging has completed, poudriere is ready to be launched. However, before we would do so, a list of ports to be tested has to be compiled. Those are the ports that are stored in the development overlay, so find(1) is used create an up-to-date list of them. The lang/ghc port is omitted on purpose, it does not have to be rebuilt if not necessary.

However, note well that all the Haskell ports is listed: this also has a purpose. Haskell ports are often interconnected, that is why all the ports have to be verified for building. A full build indeed takes some time to complete, but it is important to catch such mistakes as soon as possible. Thanks to poudriere, ports are rebuilt only when it is really required, in incremental fashion.

# find /var/cache/portshaker/haskell -depth 2 | cut -d '/' -f6- | grep -v ^\.git | grep -v "lang/ghc" | sort > freebsd-haskell.ports

Use the previously created list of ports and choose a jail for the run. Remember to add the -t flag for poudriere to allow it to test for verifying the packaging lists. Sometimes it is useful to add the -c flag to clean up all the previously built binary packages before starting the build.

# poudriere bulk -f freebsd-haskell.ports -j 101amd64 -t -p haskell

Evaluation

Check the resulting logs, test with other jails. In case of errors, investigate the causes, fix the problem and repeat the whole procedure.

Producing Packages

In case of binary package building, the goal is to provide pkg(8) repositories for the users. This step is recommended to provide packages for wider testing, ideally coupled with a Call for Testing announced on the freebsd-ports mailing list. It is similar to regular testing, but checking for leftovers, that is, the -t flag can be safely omitted. This can be also taken as a final testing to see if every package builds on every possible platform.

However, this time packages for every jails shall be built — the following commands are recommended to be put in a script. This may take a while, depending on the hardware available.

# find /var/cache/portshaker/haskell -depth 2 | cut -d '/' -f6- | grep -v ^\.git | sort > freebsd-haskell.ports
# poudriere bulk -c -f freebsd-haskell.ports -j 93amd64 -p haskell
# poudriere bulk -c -f freebsd-haskell.ports -j 93i386 -p haskell
# poudriere bulk -c -f freebsd-haskell.ports -j 101amd64 -p haskell
# poudriere bulk -c -f freebsd-haskell.ports -j 101i386 -p haskell

Once the packages have been built, a webserver is needed to serve packages, for example www/nginx. Poudriére stores the generated packages under $LOCALBASE/poudriere/data/packages/$jail-$portstree. Those are the directories what should be published. For the directories, a possible layout is as follows. Here, $DOCUMENT_ROOT is the path of the document root, as configured in nginx.

location / {
  root $DOCUMENT_ROOT;
  autoindex on;
}

and the directories in the file system:

$DOCUMENT_ROOT/freebsd:$MAJOR:$ARCH

where $MAJOR is 9, 10 and $ARCH is x86:32 (i386) and x86:64 (amd64), respectively. Under those directories, it is recommended to have the packages created by poudriere in directories marked with dates. Then a symbolic to the latest one could be placed as Latest. An example of this layout can be found on pkg.freebsd.org.

As a result, the following pkg(8) repository configuration should just work out of the box.

Haskell: {
  url: "$pkg_url/${ABI}/latest",
  enabled: yes
}

where $pkg_url is the URL of the server which is publishing the binary packages. Note that the ${ABI} string should not replaced for anything. It is meant to be verbatim, that is how pkg(8) works.