ci(release): publish project-maintained pacman repo to R2 (#6334)#6352
Merged
Conversation
AUR account registration is closed with no reopening date, which blocks the AUR automation in #6341. Ship the same release-pinned binary packages directly through a project-maintained pacman repository so Arch users can install with `pacman -Syu`. A new `publish_arch_repo` release job builds, GPG-signs, and folds librefang-bin / librefang-desktop-bin / librefang-docker into one shared pacman database, then syncs it to Cloudflare R2 (packages.librefang.ai). It reuses the committed PKGBUILDs under packaging/aur/ as the single source of truth, deriving only the per-release values (pkgver, sha256sums, desktop bundle version, pinned Docker tag). Single job, not three, because all packages share one db; a fixed concurrency group serialises the read-modify-write across releases. Degrades to a no-op when the GPG key or R2 secrets are absent, so it is safe to merge before the secrets are configured. pacman verifies GPG/PGP only and cannot use the cosign keyless signatures the rest of the release uses, so this introduces the project's first long-lived signing key — documented as an offline primary key with a passphrase-less CI subkey.
repo-add --sign writes the short-name librefang.db.sig / librefang.files.sig as symlinks to their .tar.gz.sig targets, alongside the .db / .files symlinks. The publish script only materialised the latter two, so rclone (which skips symlinks) silently dropped the two .sig files. The repo would then serve librefang.db with no fetchable librefang.db.sig, breaking pacman's signed-db verification under the default SigLevel. Materialise all four symlinks as real objects before upload. Verified by a local dry-run against R2 (amd64 container): the prior `Can't follow symlink` rclone notices for the two .sig files are gone, the incremental repo-add replaces the same-version entries pulled from R2, and the upload completes clean.
This was referenced Jun 29, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements the project-maintained pacman repository proposed in #6334 — the path that can actually ship today.
AUR account registration is closed with no reopening date, which blocks the AUR automation in #6341 (those jobs no-op forever without an AUR account).
This publishes the same release-pinned binary packages directly, so Arch users install with
pacman -Syuand track every release through normal system updates.The two are complementary, not competing: when AUR registration reopens, #6341 serves
yayusers; this servespacmanusers. Nothing here changes or depends on #6341.How
A new
publish_arch_repojob inrelease.ymlruns on everyv*tag (and on achannel=currentre-publish), modeled on the existingsync_homebrew*jobs. It runs a composite action that executespackaging/arch-repo/publish-arch-repo.shinsidearchlinux:base-devel, which:packaging/aur/<package>/as the single source of truth, deriving only the per-release values (pkgver,sha256sums, the desktop bundle version off the actual.debasset name, the pinnedghcr.io/...tag) — the same derivation the AUR publisher in ci(release): publish AUR packages on release (#6334) #6341 does.makepkg --sign(no Rust compile — these repackage the prebuilt release artifacts).librefang-bin/librefang-desktop-bin/librefang-dockerinto one shared, signed pacman database withrepo-add --sign, pulling the existing db from R2 first so the update is incremental.packages.librefang.ai).Single job, not three (unlike #6341): all packages share one pacman db, so they must be
repo-add'd together. A fixedconcurrency: { group: arch-pacman-repo }serialises the read-modify-write on that db across releases. A failed build of one package is skipped with a::warning::and the others still publish.Design decisions I made (please confirm)
These were the open questions from the issue thread; I picked defaults and want a maintainer's sign-off:
deploy-worker.yml,CLOUDFLARE_ACCOUNT_ID); R2 has no GH-Pages 1GB/100GB soft caps, free egress, andlibrefang.aiDNS is presumably already there.CLOUDFLARE_ACCOUNT_IDis reused; only the R2 S3 keys are new.sign_release_artifactsproduces. So this is the project's first long-lived signing key. Documented as an offline primary key + passphrase-less CI subkey so a runner compromise revokes the subkey without rotating the identity users imported.arch=('x86_64'), the desktop amd64-only bundle, and the Homebrew sync cadence). aarch64 and a separate[librefang-beta]channel are natural later extensions.Maintainer setup required (the job no-ops until then)
Safe to merge first — the job degrades to a
::notice::no-op while any secret is unset, so forks and the unconfigured repo are unaffected. To activate (full commands + rotation policy in.github/SECRETS.md):packages.librefang.ai.ARCH_REPO_GPG_PRIVATE_KEY,ARCH_REPO_GPG_KEY_ID,R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY.workflow_dispatchonrelease.yml(channel=current,tag=<existing release>) — the first run cold-starts the db.Verification
bash -npasses onpublish-arch-repo.sh.yaml.safe_loadparsesaction.ymlandrelease.yml.run:block contains no${{ }}interpolation (no shell-injection surface); all values pass throughenv:.needs:targets (cli_linux,desktop,docker-manifest) all exist; the new job id is unique (32 jobs total)..debversion parse, the Docker image-tag re-pin, the symlink-materialisation for object storage, and the per-pkgname retention grouping against the real PKGBUILDs and pacman file-naming.Live end-to-end (real GPG sign + R2 push + a real Arch container against real release assets) can only run the first time a maintainer dispatches a release with the secrets configured — it cannot run in CI without the key, exactly like the mobile/desktop signing jobs.
Out-of-scope follow-up
docs/src/app/...integrations/cli) integration of the install steps is deferred because it touches the Next.js site + itszhi18n mirror — a different domain than this CI/packaging change. The full install instructions live inpackaging/arch-repo/README.mdin the meantime; happy to do the site page in a follow-up if wanted.Refs #6334