Skip to content

feat: add GPG-signed Arch Linux package repository CI/CD pipeline#145

Open
lenucksi wants to merge 4 commits into
archlinuxhardened:masterfrom
lenucksi:feat/signed-repo-ci
Open

feat: add GPG-signed Arch Linux package repository CI/CD pipeline#145
lenucksi wants to merge 4 commits into
archlinuxhardened:masterfrom
lenucksi:feat/signed-repo-ci

Conversation

@lenucksi

@lenucksi lenucksi commented Jun 8, 2026

Copy link
Copy Markdown

feat: add GPG-signed Arch Linux package repository CI/CD pipeline

This PR adds three GitHub Actions workflows that build, GPG-sign, and deploy
Arch Linux SELinux packages to GitHub Pages as a fully signed pacman repository.

Result can be seen here: https://lenucksi.github.io/selinux/

Workflows

1. hello-go.yml — PoC signed repository (229 lines)

A minimal end-to-end signed repository workflow for a single hello-go package.

  • Builds hello-go + hello-go-debug packages
  • Signs packages and DB with GPG
  • Publishes nightly-hello-world prerelease with tarball
  • Demonstrates the full pipeline pattern in a compact workflow

Trigger: push to hello-go/**, workflow_dispatch, or workflow_call

2. main-signed.yml — Full SELinux signed repository (397 lines)

The main production workflow that builds all SELinux packages.

  • Job 1 (build): Builds all 60+ SELinux packages in an Arch Linux container
    using build_and_install_all.sh, with SRCDEST/actions/cache for VCS source
    reuse and --holdver to avoid git refetch on repeated builds
  • Job 2 (sign): Signs each .pkg.tar.zst with GPG on the host runner
  • Job 3 (create-repo): Runs repo-add --include-sigs to create the
    pacman database, adds .db/.files symlinks for compat, uploads to artifact
  • Job 4 (sign-db): GPG-signs the *.db.tar.zst and *.files.tar.zst,
    creates real copies of .db.sig/.files.sig (GitHub Pages doesn't follow
    symlinks), publishes nightly prerelease with tarball
  • Job 5 (prepare-testing): Bootstraps a VM image with the signed repo,
    installs the SELinux system, runs restorecon and sestatus
  • Job 6 (test): Boots the VM under QEMU, SSH in, verifies SELinux status

Trigger: push to master, workflow_dispatch, schedule (weekly),
or workflow_call

3. deploy-pages.yml — GitHub Pages deployment (191 lines)

Triggered by workflow_run on main-signed.yml success (or workflow_dispatch).

  • Downloads nightly (main-signed) and optionally nightly-hello-world releases
  • Extracts tarballs, renames go-hello-worldhello-world
  • Generates index.html for each repo directory with package listing tables
  • Creates landing page at repo root with pacman config instructions
  • Generates .db.sig copies (real files, not symlinks)
  • Uploads to GitHub Pages via upload-pages-artifact

Required Configuration

1. GitHub Pages

Enable GitHub Pages for the repository:

  • Settings → Pages → Source: "GitHub Actions"
  • URLs in index.html templates reference https://<owner>.github.io/<repo>/
    — update if using a different Pages setup

2. GPG Key

The signing key should be an Ed25519 key (small, fast, modern):

gpg --full-generate-key --key-type ed25519 --expire 0
gpg --export-secret-key --armor <KEY-ID> > private.asc

Add to repository secrets:

  • GPG_PRIVATE_KEY: the full ASCII-armored private key
  • GPG_PASSPHRASE: the key's passphrase

The public key is exported automatically during the workflow and placed
in each repo's x86_64/public.asc for user import via pacman-key --add.

3. Workflow Permissions

Settings → Actions → General → Workflow permissions:

  • ✅ Read and write permissions
  • ✅ Allow GitHub Actions to create and approve pull requests

GITHUB_TOKEN is sufficient — no additional tokens needed.

Getting Started

  1. Generate an Ed25519 GPG key pair
  2. Add GPG_PRIVATE_KEY and GPG_PASSPHRASE to repository secrets
  3. Enable GitHub Pages (GitHub Actions source)
  4. Push a commit to master or trigger main-signed.yml via workflow_dispatch
  5. After main-signed.yml completes, deploy-pages.yml will deploy to Pages

The hello-go.yml workflow can be tested independently (triggers on
hello-go/** pushes) — useful for verifying the GPG setup without
building all 60+ packages.

Key Decisions

  • Nightly releases over CI artifacts: gh release create nightly with
    --prerelease + --cleanup-tag. Single tarball per release avoids artifact
    retention limits and makes external consumption possible.
  • Pure gh release CLI: No third-party release actions.
    --repo ${{ github.repository }} used in jobs without git context
    (e.g. workflow_run).
  • SigLevel = Required: DB signing enforced via .db.sig files.
    Users must pacman-key --lsign-key the repo key. Stricter than Arch's
    own DatabaseOptional default but viable for a controlled signing setup.
  • Real copies, not symlinks: GitHub Pages serves symlinks as their text
    content (the target path string), not the target file. .db.sig/.files.sig
    are created as real file copies after GPG signing.
  • Arch containers: Build jobs run in archlinux:base-devel containers.
    GPG signing runs on the host runner where gpg has access to $HOME/.gnupg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant