Files
Triple-C/.gitea/workflows/publish-aur-package.yml
T
shadow-testandClaude Sonnet 5 e025a7441a
Secret Scan / scan (push) Successful in 27s
Secret Scan / scan (pull_request) Successful in 10s
Add a native Arch/CachyOS package via its own AUR publish workflow
Part of triple-c#34's third ask ("I would like to also have an
Arch/CachyOS native version as well"), addressed separately from the
Wayland crash fix (fix/wayland-webkit-egl-crash) since it's an unrelated
feature, not a bug.

packaging/arch/PKGBUILD is a "-bin" AUR package repackaging the same .deb
build-app.yml already produces — no Rust/Node toolchain needed to install
it, and the user gets exactly the binary the project ships and tests.
Verified end to end against a real release (v0.4.14) rather than going by
Tauri's generic docs: downloaded the actual .deb, ldd'd the actual binary
to ground-truth `depends` (dropped `pango` and `libayatana-appindicator`
from an earlier draft — the first is already pulled in transitively by
gtk3, the second was never linked at all since this app has no tray icon
or menu), and ran a real makepkg/namcap/pacman -U cycle. namcap caught a
real issue this way (missing license file under
/usr/share/licenses/triple-c-bin/), now fixed by fetching LICENSE
alongside the .deb.

.gitea/workflows/publish-aur-package.yml does the actual publishing:
given a version (or "latest"), it finds that release's real Linux asset
on GitHub, downloads it, computes real checksums, renders the PKGBUILD
template, validates the result with makepkg and namcap inside a real
Arch container, and pushes to AUR. workflow_dispatch only, deliberately —
the same reasoning that killed sync-release.yml in triple-c#32 (releases
are assembled by build-app.yml across three separate platform jobs, so
there's no single automatic event that fires only once the Linux .deb
this needs actually exists) applies here too.

Requires a repo secret this workflow cannot set up itself:
AUR_SSH_PRIVATE_KEY, from an AUR account that has already created (or
been given co-maintainer access to) triple-c-bin — both one-time manual
steps on aur.archlinux.org. Until that secret exists, the workflow fails
loudly at the push step rather than silently doing nothing. See
packaging/arch/README.md for the full maintenance flow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FGjXq6fqtAFHdbhk4f3PfZ
2026-08-27 11:10:55 -07:00

217 lines
9.6 KiB
YAML

name: Publish AUR Package
# Builds and pushes the `triple-c-bin` AUR package (packaging/arch/PKGBUILD)
# for a given release, or the latest one if none is given. Manual dispatch
# only — deliberately not triggered by `release` or `push`, for the same
# reason sync-release.yml (removed in triple-c#32) never worked safely as an
# automatic trigger: this repo's releases are assembled by build-app.yml
# across three separate platform jobs, and there is no single automatic event
# that fires only once everything (including the Linux .deb this workflow
# needs) is actually uploaded. A human deciding "this release is ready, go
# package it" is the correct trigger, the same reasoning
# backfill-releases.yml already uses for its own manual-only GitHub sync.
#
# ## What this does and does not do
#
# It renders `packaging/arch/PKGBUILD` for one specific version (real
# download URL, real sha256sums — never guessed; see the resolve-asset step)
# and pushes the rendered PKGBUILD plus a regenerated `.SRCINFO` to AUR. It
# does NOT commit anything back to this repo — `packaging/arch/PKGBUILD` stays
# a hand-maintained template with a placeholder version, and every real,
# published version lives only in AUR's own git history, which is where a
# PKGBUILD's revision history is expected to live.
#
# ## Required secret
#
# `AUR_SSH_PRIVATE_KEY` — an SSH private key registered against an AUR
# account that has already created (or been given co-maintainer access to)
# the `triple-c-bin` package. This workflow cannot create that AUR account or
# register the key for you — both are manual, one-time steps on
# https://aur.archlinux.org. Until this secret exists, every run fails at the
# "Push to AUR" step with a clear error rather than silently doing nothing.
on:
workflow_dispatch:
inputs:
version:
description: >-
Release version to package, without a leading "v" (e.g. "0.4.14").
Leave empty to use the latest published GitHub release.
required: false
env:
GITHUB_REPO: shadowdao/triple-c
AUR_REPO: ssh://aur@aur.archlinux.org/triple-c-bin.git
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Resolve version and find the Linux asset
id: resolve
run: |
set -euo pipefail
VERSION="${{ inputs.version }}"
if [ -z "$VERSION" ]; then
echo "No version given — resolving the latest GitHub release"
RELEASE_JSON=$(curl -fsS "https://api.github.com/repos/${GITHUB_REPO}/releases/latest")
else
echo "Using requested version ${VERSION}"
RELEASE_JSON=$(curl -fsS "https://api.github.com/repos/${GITHUB_REPO}/releases/tags/v${VERSION}")
fi
TAG=$(echo "$RELEASE_JSON" | jq -r '.tag_name')
VERSION="${TAG#v}"
echo "Resolved to ${TAG}"
# Discovered from the real release, not assumed: Tauri names the
# asset after `productName` verbatim ("Triple-C"), not the
# lowercase Cargo binary name, and asset naming is exactly the kind
# of thing that silently drifts if a future Tauri upgrade changes
# bundler defaults — a hardcoded pattern here would then 404
# forever until someone noticed.
DEB_URL=$(echo "$RELEASE_JSON" | jq -r '.assets[] | select(.name | endswith("_amd64.deb")) | .browser_download_url')
DEB_NAME=$(echo "$RELEASE_JSON" | jq -r '.assets[] | select(.name | endswith("_amd64.deb")) | .name')
if [ -z "$DEB_URL" ] || [ "$DEB_URL" = "null" ]; then
echo "No *_amd64.deb asset found on release ${TAG}" >&2
exit 1
fi
echo "Found asset: ${DEB_NAME}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "deb_url=${DEB_URL}" >> "$GITHUB_OUTPUT"
echo "deb_name=${DEB_NAME}" >> "$GITHUB_OUTPUT"
- name: Download the release asset and compute real checksums
id: checksums
env:
DEB_URL: ${{ steps.resolve.outputs.deb_url }}
DEB_NAME: ${{ steps.resolve.outputs.deb_name }}
TAG: ${{ steps.resolve.outputs.tag }}
run: |
set -euo pipefail
curl -fsSL -o "${DEB_NAME}" "${DEB_URL}"
curl -fsSL -o LICENSE "https://raw.githubusercontent.com/${GITHUB_REPO}/${TAG}/LICENSE"
echo "deb_sha256=$(sha256sum "${DEB_NAME}" | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
echo "license_sha256=$(sha256sum LICENSE | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Render PKGBUILD
id: render
env:
VERSION: ${{ steps.resolve.outputs.version }}
DEB_NAME: ${{ steps.resolve.outputs.deb_name }}
DEB_SHA256: ${{ steps.checksums.outputs.deb_sha256 }}
LICENSE_SHA256: ${{ steps.checksums.outputs.license_sha256 }}
run: |
set -euo pipefail
mkdir -p rendered
cp packaging/arch/PKGBUILD rendered/PKGBUILD
cd rendered
# Plain string replacement throughout, not sed — the source URL
# contains slashes and the repo name does too, and getting a sed
# delimiter choice AND its escaping right for that is exactly the
# kind of thing that looks correct, passes review, and breaks the
# next time someone touches it. `re.sub` with `count=1` and an
# exact `.format`-free literal match is boring and that's the
# point: every substitution below fails loudly (KeyError / the
# assertions after) rather than silently no-op'ing if the
# template's shape ever drifts from what this expects.
#
# pkgrel resets to 1 for a new pkgver — a packaging-only fix to the
# same upstream version (a dependency bump, say) is what pkgrel is
# for, and this workflow always republishes the current PKGBUILD
# verbatim rather than incrementing anything, so 1 is always
# correct here.
python3 - "$VERSION" "$DEB_NAME" "$DEB_SHA256" "$LICENSE_SHA256" <<'PY'
import re, sys
version, deb_name, deb_sha, license_sha = sys.argv[1:5]
github_repo = "shadowdao/triple-c"
with open("PKGBUILD") as f:
text = f.read()
text, n = re.subn(r"(?m)^pkgver=.*$", f"pkgver={version}", text, count=1)
assert n == 1, "pkgver=... line not found"
text, n = re.subn(r"(?m)^pkgrel=.*$", "pkgrel=1", text, count=1)
assert n == 1, "pkgrel=... line not found"
old_source = (
f'source=("Triple-C_${{pkgver}}_amd64.deb::'
f'https://github.com/{github_repo}/releases/download/v${{pkgver}}/'
f'Triple-C_${{pkgver}}_amd64.deb"'
)
new_source = (
f'source=("{deb_name}::'
f'https://github.com/{github_repo}/releases/download/v{version}/{deb_name}"'
)
assert old_source in text, "source=() line does not match the expected template shape"
text = text.replace(old_source, new_source, 1)
old_sums = "sha256sums=('SKIP'\n 'SKIP')"
assert old_sums in text, "sha256sums=() placeholders not found"
text = text.replace(old_sums, f"sha256sums=('{deb_sha}'\n '{license_sha}')", 1)
with open("PKGBUILD", "w") as f:
f.write(text)
PY
grep -q "pkgver=${VERSION}$" PKGBUILD
! grep -q "SKIP" PKGBUILD
- name: Validate with makepkg and namcap
run: |
set -euo pipefail
docker run --rm -v "$PWD/rendered:/work" -w /work archlinux:latest bash -c '
set -euo pipefail
pacman -Sy --noconfirm base-devel namcap sudo git openssh >/dev/null
useradd -m builder
chown -R builder:builder /work
echo "builder ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/builder
sudo -u builder bash -c "cd /work && makepkg --printsrcinfo > .SRCINFO"
sudo -u builder bash -c "cd /work && makepkg -s --noconfirm"
echo "--- namcap ---"
NAMCAP_OUT=$(sudo -u builder bash -c "cd /work && namcap PKGBUILD *.pkg.tar.*" || true)
echo "$NAMCAP_OUT"
if echo "$NAMCAP_OUT" | grep -q "^[a-zA-Z0-9_-]*bin E:"; then
echo "namcap reported an error — see above" >&2
exit 1
fi
'
- name: Push to AUR
env:
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
VERSION: ${{ steps.resolve.outputs.version }}
run: |
set -euo pipefail
if [ -z "${AUR_SSH_PRIVATE_KEY}" ]; then
echo "AUR_SSH_PRIVATE_KEY is not set — see this workflow file's header comment for" >&2
echo "the one-time AUR account setup this needs before it can publish anything." >&2
exit 1
fi
mkdir -p ~/.ssh
echo "${AUR_SSH_PRIVATE_KEY}" > ~/.ssh/aur
chmod 600 ~/.ssh/aur
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null
export GIT_SSH_COMMAND="ssh -i ~/.ssh/aur -o UserKnownHostsFile=~/.ssh/known_hosts"
git clone "${AUR_REPO}" aur-repo
cp rendered/PKGBUILD rendered/.SRCINFO aur-repo/
cd aur-repo
git config user.name "Triple-C CI"
git config user.email "noreply@triple-c.invalid"
git add PKGBUILD .SRCINFO
if git diff --cached --quiet; then
echo "No change from what's already published on AUR for ${VERSION}"
exit 0
fi
git commit -m "triple-c-bin: update to ${VERSION}"
git push origin master