François' Blog

Using Forgejo CI

Published on 2024-03-09

It took me quite a while to understand why it was so difficult to get the CI runner for Forgejo "Actions", forgejo-runner working to my satisfaction.

Sunset at Werbellinsee, Germany

Earlier I wrote documentation on how to setup forgejo-runner to work under a standard user account using systemd user services, and podman which turns out to work super smooth!

But then, how do you actually write these "workflow" files in a way that makes sense to how our current CI works on SourceHut?

Of course, it is a given that I will have strong opinions on how I want my CI to work, which I'll detail below. It actually matches exactly with how SourceHut builds work. Perhaps I should also quickly explain what I mean with CI in this context: On git push I want the unit tests to run in the CI system on the exact platform the code will be deployed on in production. We cheat a little bit here, as our OS package builders (Debian/Ubuntu, Fedora/EL) take care of the exact same thing during package build, but we do not create packages on every commit. It would be nice to run at least on one of the supported platforms, let's say Debian 12 so we make sure most is at least somewhat in order.

Searching for documentation regarding Forgejo, Gitea or GitHub "Actions" is not really fruitful as all same to want to use either ubuntu-latest, whatever that is. It turns out to be a 60 GB, no really, image used by GitHub. Why?!

So, it turns out our best bet is to use "plain" Docker images, for example debian:latest, prepare it and run our unit tests on it. It took quite a bit of trial and error, it doesn't seem that what I want is what others want, so it requires some fiddling with users accounts in the Docker image, but after powering through, finally I got something that works more or less the way I want. Without further ado, here the .forgejo/workflows/tests.yml of one of my PHP projects:

# ---
    BUILD_DEPENDENCIES: git composer unzip php-curl php-date php-dom php-intl
# ---
on: [push]
        runs-on: docker
            image: debian:latest
            - name: Install OS Dependencies
              run: |
                apt-get update && apt-get install --yes $BUILD_DEPENDENCIES
            - name: Add CI User
              run: |
                useradd -m ci-user
            - name: Clone Repository
              run: |
                mkdir app
                chown ci-user:ci-user app
                su -c "git clone --depth 1 -b ${{ github.ref_name }} ${{ github.server_url }}/${{ github.repository }} app" ci-user
            - name: Install Dependencies
              run: |
                cd app
                su -c "composer update" ci-user
            - name: Run Unit Tests
              run: |
                cd app
                su -c "unshare -c -n vendor/bin/phpunit" ci-user

It should be rather self explanatory, but some small notes:

So, it would be much nicer if there was a mechanism to only install OS dependencies as root, and run the rest as user without the boilerplate. These are things I hope to find out at some point how to do!