Building AUR Packages with Forgejo Actions and Renovate
enThe Arch User Repository (AUR) is a collection of user-contributed source packages for Arch Linux. These packages, which may be submitted by anyone without review, simply consist of package metadata, and instructions for downloading and building software from source. This also means that special care must be taken when using AUR packages; while they can be extremely useful, they may also contain malware as became apparent once more recently.
The "recommended" way to build an AUR package is to checkout the package source repository, review its contents and then build the package using Arch's makepkg package build tool.
Since this can be a rather tedious process, various different AUR build helper tools exist, however usage of them is not recommended, as they often cause various hard-to-troubleshoot issues.
Having been a long-time user of such AUR helpers, and regularly running into the aforementioned problems, I wanted to find a better (and more effortless) solution for building AUR packages.
Forgejo to the Rescue!
The open-source git forge Forgejo, which we also run at our hackerspace, comes with an Arch Linux package registry.
This registry can simply be added to the list of software repositories in an Arch Linux installation's /etc/pacman.conf, and compiled packages can be pushed to it via a REST API endpoint.
To build the AUR packages I need, I set up a Forgejo Actions workflow, which builds these packages at least once a week or when changes are pushed to the repository:
---
# Build on commits to main and once a week
on:
push:
branches:
- main
schedule:
- cron: "0 6 * * 1" # every Monday at 6 AM
jobs:
# One step per package, for other packages just do `packagename: *job`
sslyze: &job
runs-on: docker
container:
# base-devel is implicitly required for package building.
# Any other dependencies must be declared explicitly.
image: docker.io/library/archlinux:base-devel
env:
# Secrets required to push to the package registry
API_REPOSITORY_ARCH: ${{ secrets.API_REPOSITORY_ARCH }}
API_REPOSITORY_ARCH_DEBUG: ${{ secrets.API_REPOSITORY_ARCH_DEBUG }}
API_USERNAME: ${{ secrets.API_USERNAME }}
API_PASSWORD: ${{ secrets.API_PASSWORD }}
steps:
- name: Prepare build pipeline
run: |
# nodejs and git are needed for the checkout action; unlike the default image,
# `docker.io/library/ubuntu`, these are not shipped by default in `archlinux:base-devel`.
pacman --noconfirm -Sy nodejs git
- name: Checkout repository
# This only checks out the project repo, not the submodules
uses: https://code.forgejo.org/actions/checkout@v7
- name: Build and upload package
run: |
# Add AUR repo itself for makedepends from AUR
curl https://git.kabelsalat.ch/api/packages/s3lph/arch/repository.key > /sign.gpg
pacman-key --init
pacman-key --add /sign.gpg
pacman-key --lsign-key 's3lph@noreply.git.kabelsalat.ch'
cat >> /etc/pacman.conf <<EOF
[aur]
SigLevel = Required
Server = https://git.kabelsalat.ch/api/packages/s3lph/arch/aur/\$arch
EOF
pacman -Sy
# Checkout only the submodule of the package to build in this job
git submodule update --init "${FORGEJO_JOB}"
chown nobody: -R "${FORGEJO_JOB}" && cd "${FORGEJO_JOB}"
# Check if the PKGBUILD contains run-time or compile-time dependencies, and install if necessary
(source ./PKGBUILD && [[ -n "${depends[@]}${makedepends[@]}" ]] && pacman --noconfirm -S ${depends[@]} ${makedepends[@]} || true)
# Build the binary package(s) from this source package
sudo -E -u nobody -- makepkg --cleanbuild
# All debug packages are uploaded to the separate aur-debug registry
for file in *-debug-*pkg.tar*; do
curl -X PUT "${API_REPOSITORY_ARCH_DEBUG}" \
--user "${API_USERNAME}:${API_PASSWORD}" \
--header "Content-Type: application/octet-stream" \
--data-binary @"${file}" || true
done
rm -f *-debug-*pkg.tar*
# The regular packages are uploaded to the `aur` registry
for file in *pkg.tar*; do
curl -X PUT "${API_REPOSITORY_ARCH}" \
--user "${API_USERNAME}:${API_PASSWORD}" \
--header "Content-Type: application/octet-stream" \
--data-binary @"${file}"
done
Updating Packages with Renovate
So now I've got a pipeline which builds the AUR packages I need on a regular basis, and a repository where these packages are pushed to. However, I've still got the problem of reviewing all changes to AUR packages to ensure no malicious code is introduced.
To solve this, I added all the AUR package repositories I need to my pipeline repository as git submodules. These submodules are pinned to a fixed git commit id, so I'm not pulling in the latest changes unreviewed, but always build a package for which I know I have reviewed the build instructions.
To receive updates to these submodules, I'm using the dependency management tool Renovate, which runs as another Forgejo Actions workflow in another repository on the same Forgejo server. Once a day, Renovate checks whether there are any kind of dependency updates which would need to be applied to repositories it monitors. Among many other types of dependencies, Renovate also supports checking whether the commit a git submodule points to is still the latest upstream commit. If any such "outdated" dependency is found, Renovate opens a pull request against the repository, and assigns it to the owner for review.
However, these pull requests only consist of a changed submodule commit id, so review of the acutal changes is not as trivial:
--- a/sslyze
+++ b/sslyze
@@ -1 +1 @@
-Subproject commit 848023a5347b8af959de3287d65eb12b0fc341e2
+Subproject commit d78430b2aad33c00ca047f4f498d61ef9a25566e
The last piece of the puzzle is yet another Forgejo Actions workflow, which is triggered by these pull requests:
---
# Run whenever a PR is opened or changed
on:
pull_request:
types:
- opened
- synchronize
- reopened
jobs:
diff:
runs-on: docker
steps:
- name: Checkout repository
uses: https://code.forgejo.org/actions/checkout@v6
with:
submodules: true
fetch-depth: 0
- name: Compute diff and post comment
run: |
apt update; apt install --yes python3-all
# the comment.py script just posts the output of `git diff --submodule=diff`
# as a comment to the PR, or updates an existing comment.
python3 .forgejo/comment.py
This workflow checks out the pipeline repository including all submodules, and computes the PR diff including the submodules. The resulting diff is then posted as a comment to the pull request, which allows me to easily review the changes: