Skip to content

Build Pacman Repo

Build Pacman Repo #427

Workflow file for this run

---
name: Build Pacman Repo
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
schedule:
- cron: "0 0 * * *"
# On demand: needed after changing the signing secrets, or to re-attempt a
# package that failed under allow-failure, without waiting for the cron or
# inventing a commit.
workflow_dispatch:
permissions:
contents: write
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
# Only cancel on pull requests, where the branch churns and a superseded run
# is genuinely worthless. On main this build runs for hours, and cancelling it
# throws away every package it had already compiled -- a commit touching a
# workflow this job does not even read was enough to destroy a run 30 minutes
# in. Queue behind the running build instead.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
REPO_NAME: custom
RELEASE_TAG: custom-repo
GH_REPO: ${{ github.repository }}
jobs:
build-pacman-repo:
runs-on: ubuntu-latest
# Bound the run. With 70 members, one package that hangs or grinds would
# otherwise consume the 6 hour default and block the nightly build behind
# it. allow-failure contains a build that fails; it cannot contain one that
# never finishes.
timeout-minutes: 300
environment: ${{ github.ref == 'refs/heads/main' && 'prod' || '' }}
steps:
- name: Free Disk Space (Ubuntu)
uses: BRAINSia/free-disk-space@v2
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
mandb: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
# Keep the runner's 4 GB swapfile. Reclaiming it buys a little disk
# but removes the headroom that large C++ builds rely on, and this
# repo's builds were being OOM-killed rather than running out of disk.
swap-storage: false
- name: Checkout Pacman Repo Builder
uses: actions/checkout@v7
with:
repository: pacman-repo-builder/pacman-repo-builder
path: pacman-repo-builder
- name: Checkout yay
uses: nick-fields/retry@v4
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 10
command: |
git clone https://aur.archlinux.org/yay.git yay
- name: Checkout this repo
uses: actions/checkout@v7
with:
path: repo
submodules: recursive
- name: Seed the repository state from the release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p repo-out
# Stamp the moment before reading the database. Anything uploaded to
# the release after this is not represented in what we are about to
# publish, so the prune must leave it alone -- heavy-build spends
# hours with packages uploaded and not yet in the database.
echo "SEED_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
# Only the database. build-pacman-repo works out what is outdated from
# it, and repo-add keeps the entries of packages that are not present
# locally, so there is no reason to pull down gigabytes of packages
# that are already published and current.
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
for f in "${REPO_NAME}.db.tar.gz" "${REPO_NAME}.files.tar.gz"; do
gh release download "$RELEASE_TAG" --pattern "$f" --dir repo-out --clobber \
|| echo "::warning::no $f published yet"
done
else
echo "Release $RELEASE_TAG does not exist yet; starting from empty."
gh release create "$RELEASE_TAG" --title "custom pacman repo" \
--notes-file repo/.github/release-notes/custom.md
fi
ls -l repo-out/ || true
- name: Run build in Arch Linux container
env:
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
# -e VAR with no value forwards it from the runner environment, so the
# key never appears on the command line where `ps` could read it.
docker run --rm \
-v "${{ github.workspace }}:/workspace" \
-e GITHUB_WORKSPACE=/workspace \
-e GITHUB_OUTPUT=/workspace/.github-output \
-e GPG_SIGNING_KEY \
-e GPG_PASSPHRASE \
-e REPO_NAME \
-w /workspace \
archlinux:base-devel \
bash /workspace/repo/build-inside-container.sh
- name: Re-index anything published while this build ran
if: github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ github.token }}
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
set -euo pipefail
# Compare against the release's assets, not against the published
# database. heavy-build publishes into the same release and indexes
# only when its whole matrix finishes, so anything it added while this
# build ran is missing from the copy about to be written -- and once
# one build-repo run has written a database without it, the published
# copy no longer names it either. At that point only the assets still
# say the package exists.
out=$(bash repo/.github/scripts/reindex-orphans.sh \
repo-out "$REPO_NAME" "$RELEASE_TAG" | tee /dev/stderr) || exit 1
if grep -q '^REINDEXED=1$' <<<"$out"; then
# The database changed after the container signed it, so the old
# signature no longer matches -- and pacman rejects a bad database
# signature far more harshly than a missing one.
echo "Database changed; re-signing."
rm -f repo-out/"${REPO_NAME}".db.sig repo-out/"${REPO_NAME}".files.sig \
repo-out/"${REPO_NAME}".db.tar.gz.sig repo-out/"${REPO_NAME}".files.tar.gz.sig
bash repo/.github/scripts/sign-pacman-repo.sh repo-out "$REPO_NAME"
fi
- name: Publish to the release
if: github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ github.token }}
run: bash repo/.github/scripts/publish-release-repo.sh repo-out "$REPO_NAME" "$RELEASE_TAG"
- name: Summary
if: always()
run: |
{
echo "### [custom]"
echo
echo '```'
ls -lh repo-out/*.pkg.tar.zst 2>/dev/null | awk '{print $9, $5}' || echo "(none built)"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"