# CI

Continuous integration, testing inside containers. Most of the *btrfs-progs*
functionality is in user space and does not need a virtual machine. The
features supported by the running kernel are detected and tests skipped
eventually.

## Quick start

- install docker, add `"storage-driver": "btrfs"` to */etc/docker/daemon.json*,
  make sure your user is in group *docker*, start the service
- pull all images by `ci/images/images-base-update`, this can take time
- build all images for testing by `ci/images/images-build-all`, or individually
  in the *ci/images/ci-\** directories
- build current branch and test it in the docker image by running `ci/ci-build-musl` from
  the top directory (this will create a fresh tar archive inside the docker sources
  and the image must be rebuilt)
- `ci/ci-build-*` scripts to build on the target image

## Hosted

No active hosted CI is currently set up, we're looking for one.

Reuirements:

* recent kernel, latest stable if possible, 5.10 is minimum
* ability to run commands as root
* ability to create loop devices (losetup)
* ability to mount and unmount filesystems
* integration with github/gitlab repository to watch for updates
* enough granted run time to finish the testsuite
* (optional) run docker images
* (nice to have) web gui with results, email notifications

**Gitlab**

The integration with gitlab.org has been disabled but is possible to revive. We
were experimenting with nested virtualization to run the tests on a current
kernel not some old version provided by the hosted image. The tests took to
long to fit in the free plan quota.

**Travis CI**

Disabled since version v5.12 (05/2021).

## Local

The testsuite can be run directly from the git tree from the built sources, or
from the separate exported testsuite. This depends on the installed system
packages and running kernel.

Another option is to run the tests on a given distribution in a container.
There are several *docker* container images for some distributions. Right now
they're meant for testing development branch *devel*, but can be adapted for
others as well.

**Build tests**

The simplest test is to verify that the project builds on a given distribution.
The backward compatibility of *btrfs-progs* is supposed to cover also old and
long-term support distributions, as well as systems with standard C library
other than GNU glibc. Some features like run-time stack trace dump are not
available but can be disabled at configure time.

**Functional tests**

By default only the build test is run in the container. There's a script to
start the testsuite, although this can be also done manually by running the
appropriate commands (check the script *ci/images/run-tests*)

**Fine-tuned tests**

The build supports additional features like sanitizers, enabled by environment
variables. These can be passed to the container environment, see examples below.

**The container environment**

The tests need to run privileged (to create loop devices and mount/unmount
filesystems) and need to see the block devices (created by device mapper).
Starting the container as *docker run* might not be sufficient without
parameters and additional mounts.

To minimize the image size and installation dependencies, the documentation is
not built by default and lacks the tools to build it, so you need to pass
*--disable-documentation* for the builder scripts or for the raw *configure*
command.

### Examples

Assuming top level directory in the *btrfs-progs* git repository, then moved
to directory with a particular image sources.

**Prepare image**

    cd ci/images/ci-openSUSE-tumbleweed-x86_64
    ./docker-build

Running plain *docker build* may not work as some magic is needed to allow
building either the branch from web repository, or from a local git branch
provided as a tarball. Docker does not allow conditional image contents so this
is pushed to the test build scripts.

**Build**

Neither running the image is just *docker run*, so there's a script for
convenience:

    ./docker-run

You can pass additional docker parameters or a non-default command:

    ./docker-run --env=VAR=text

or

    ./docker-run --env=V=1 -- ./test-build devel --disable-documentation

The *--* is separator for docker and the actual command. The command above will
effectively run the make command with *V=1* ie. raw commands as they're
executed. Other options work as well, see the top level Makefile. Notably, the
sanitizers can be enabled like

    ./docker-run --env=D=asan -- ./test-build devel --disable-documentation

This will just build the sources.

**Build and run tests**

In order to run the whole testsuite one more script needs to be run:

    ./docker-run --env=D=asan -- bash -c "./test-build devel --disable-documentation && \
            ./run-tests /tmp/btrfs-progs-devel"

As docker does not allow to run multiple commands, you can either start the
whole command wrapped in a shell or use the script
*ci/images/docker-run-tests*.

## What else

The current set of build targets covers commonly used systems, in terms of
package versions. There are no significant differences between many
distributions and adding support for each does not bring any benefits. If you
think there's something that can improve build coverage and catch errors during
development, please open an issue or send a pull request adding a new docker
image template or enhancing current support scripts.

To do:

- 32bit coverage -- while this architecture is fading out, it may be useful to
  still have some coverage, however running 32bit docker in 64bit is
  considered experimental and does not work out of the box
- add some kind of templates, there's a lot of repeated stuff in the
  *Dockerfile*s and the scripts need to be inside the directories in order to
  allow copying them to the image

## Administration

To pull all base images from docker hub or to update already downloaded ones run

    ci/images/images-base-update

Before running the CI tests locally the images need to be build from the templates,
for that there's

    ci/images/images-build-all

Which is similar to running the `docker-build` command inside the directories.
