add aur workflow (#19)

This commit is contained in:
Roope Airinen 2026-03-18 15:41:54 +02:00 committed by GitHub
parent 3a4a985573
commit 5f087bcb44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 544 additions and 0 deletions

268
.github/workflows/deploy-aur.yml vendored Normal file
View file

@ -0,0 +1,268 @@
name: Deploy AUR Mirror
on:
workflow_call:
inputs:
tag:
required: true
type: string
version:
required: true
type: string
aur_repo:
required: false
type: string
default: ""
aur_branch:
required: false
type: string
default: "main"
dry_run:
required: false
type: boolean
default: false
secrets:
AUR_REPO_TOKEN:
required: false
workflow_dispatch:
inputs:
version:
description: "Release version (e.g. 0.2.0 or v0.2.0)"
required: true
type: string
tag:
description: "Optional tag override (e.g. v0.2.0). Defaults to v<version>."
required: false
type: string
aur_repo:
description: "Target GitHub repo in OWNER/REPO form (e.g. Auto-Explore/aur-gitcomet). Defaults to AUR_GITHUB_REPO when omitted."
required: false
default: ""
type: string
aur_branch:
description: "Target branch in aur repo. Defaults to AUR_GITHUB_BRANCH when omitted."
required: false
default: "main"
type: string
dry_run:
description: "Validate and print PKGBUILD/.SRCINFO without pushing"
required: true
default: false
type: boolean
permissions:
contents: read
concurrency:
group: deploy-aur-${{ inputs.tag || github.event.inputs.tag || inputs.version || github.event.inputs.version || github.run_id }}
cancel-in-progress: false
jobs:
deploy:
name: Publish PKGBUILD and .SRCINFO to AUR mirror repo
runs-on: ubuntu-latest
timeout-minutes: 30
container:
image: archlinux:base-devel
steps:
- name: Install Arch packaging tooling
run: |
set -euo pipefail
pacman -Sy --noconfirm --needed ca-certificates ca-certificates-utils curl git perl shadow
- uses: actions/checkout@v6
- name: Normalize inputs
id: norm
env:
INPUT_TAG: ${{ inputs.tag }}
DISPATCH_TAG: ${{ github.event.inputs.tag }}
INPUT_VERSION: ${{ inputs.version }}
DISPATCH_VERSION: ${{ github.event.inputs.version }}
INPUT_AUR_REPO: ${{ inputs.aur_repo }}
DISPATCH_AUR_REPO: ${{ github.event.inputs.aur_repo }}
VAR_AUR_REPO: ${{ vars.AUR_GITHUB_REPO }}
INPUT_AUR_BRANCH: ${{ inputs.aur_branch }}
DISPATCH_AUR_BRANCH: ${{ github.event.inputs.aur_branch }}
VAR_AUR_BRANCH: ${{ vars.AUR_GITHUB_BRANCH }}
INPUT_DRY_RUN: ${{ inputs.dry_run }}
DISPATCH_DRY_RUN: ${{ github.event.inputs.dry_run }}
REPO_OWNER: ${{ github.repository_owner }}
run: |
set -euo pipefail
tag="${INPUT_TAG:-${DISPATCH_TAG:-}}"
version="${INPUT_VERSION:-${DISPATCH_VERSION:-}}"
aur_repo="${INPUT_AUR_REPO:-${DISPATCH_AUR_REPO:-${VAR_AUR_REPO:-}}}"
aur_branch="${INPUT_AUR_BRANCH:-${DISPATCH_AUR_BRANCH:-${VAR_AUR_BRANCH:-main}}}"
dry_run="${INPUT_DRY_RUN:-${DISPATCH_DRY_RUN:-false}}"
tag="$(echo "$tag" | tr -d '[:space:]')"
version="$(echo "$version" | tr -d '[:space:]')"
aur_repo="$(echo "$aur_repo" | tr -d '[:space:]')"
aur_branch="$(echo "$aur_branch" | tr -d '[:space:]')"
dry_run="$(echo "$dry_run" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')"
if [ -z "$version" ]; then
echo "::error title=Missing version::Version is required."
exit 1
fi
version="${version#v}"
if [ -z "$tag" ]; then
tag="v${version}"
fi
if [[ "$tag" != v* ]]; then
tag="v${tag}"
fi
if [ "$tag" != "v${version}" ]; then
echo "::error title=Tag/version mismatch::Tag '$tag' does not match version '$version'."
exit 1
fi
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then
echo "::error title=Invalid version::Expected semver like 1.2.3 or 1.2.3-rc.1."
exit 1
fi
if [ -z "$aur_repo" ]; then
aur_repo="${REPO_OWNER}/aur-gitcomet"
fi
if ! [[ "$aur_repo" =~ ^[^/]+/[^/]+$ ]]; then
echo "::error title=Invalid AUR repo::aur_repo must be OWNER/REPO."
exit 1
fi
if [ -z "$aur_branch" ]; then
echo "::error title=Missing AUR branch::aur_branch must not be empty."
exit 1
fi
if [[ "$dry_run" != "true" && "$dry_run" != "false" ]]; then
echo "::error title=Invalid dry_run::dry_run must be true or false."
exit 1
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "aur_repo=$aur_repo" >> "$GITHUB_OUTPUT"
echo "aur_branch=$aur_branch" >> "$GITHUB_OUTPUT"
echo "dry_run=$dry_run" >> "$GITHUB_OUTPUT"
- name: Create non-root packaging user
run: |
set -euo pipefail
id -u builder >/dev/null 2>&1 || useradd -m builder
chown -R builder:builder "$GITHUB_WORKSPACE"
- name: Download release archives referenced by PKGBUILD
env:
TAG: ${{ steps.norm.outputs.tag }}
VERSION: ${{ steps.norm.outputs.version }}
run: |
set -euo pipefail
mkdir -p dist/aur
binary_name="gitcomet-v${VERSION}-linux-x86_64.tar.gz"
source_name="gitcomet-source-v${VERSION}.tar.gz"
curl -fL --retry 3 --retry-all-errors \
"https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${binary_name}" \
-o "dist/aur/${binary_name}"
curl -fL --retry 3 --retry-all-errors \
"https://github.com/${GITHUB_REPOSITORY}/archive/refs/tags/${TAG}.tar.gz" \
-o "dist/aur/${source_name}"
- name: Clone AUR mirror repository
env:
AUR_REPO: ${{ steps.norm.outputs.aur_repo }}
AUR_BRANCH: ${{ steps.norm.outputs.aur_branch }}
DRY_RUN: ${{ steps.norm.outputs.dry_run }}
AUR_TOKEN: ${{ secrets.AUR_REPO_TOKEN }}
run: |
set -euo pipefail
clone_url="https://github.com/${AUR_REPO}.git"
if [ "$DRY_RUN" != "true" ]; then
if [ -z "${AUR_TOKEN:-}" ]; then
echo "::error title=Missing secret::Set AUR_REPO_TOKEN to push to ${AUR_REPO}."
exit 1
fi
clone_url="https://x-access-token:${AUR_TOKEN}@github.com/${AUR_REPO}.git"
fi
rm -rf aur-repo
git clone --depth 1 --branch "$AUR_BRANCH" --single-branch "$clone_url" aur-repo
chown -R builder:builder aur-repo dist
- name: Update PKGBUILD and regenerate .SRCINFO
env:
VERSION: ${{ steps.norm.outputs.version }}
run: |
set -euo pipefail
su builder -c "cd '$GITHUB_WORKSPACE' && scripts/update-aur.sh \
--aur-dir '$GITHUB_WORKSPACE/aur-repo' \
--version '$VERSION' \
--binary-tar '$GITHUB_WORKSPACE/dist/aur/gitcomet-v${VERSION}-linux-x86_64.tar.gz' \
--source-tar '$GITHUB_WORKSPACE/dist/aur/gitcomet-source-v${VERSION}.tar.gz' \
--verify-source"
- name: Emit dry-run summary
if: ${{ steps.norm.outputs.dry_run == 'true' }}
run: |
set -euo pipefail
{
echo "### AUR deployment dry run"
echo ""
echo "- Source release: \`${{ steps.norm.outputs.tag }}\`"
echo "- Target repo: \`${{ steps.norm.outputs.aur_repo }}\`"
echo "- Target branch: \`${{ steps.norm.outputs.aur_branch }}\`"
echo ""
echo "PKGBUILD preview:"
echo '```bash'
cat aur-repo/PKGBUILD
echo '```'
echo ""
echo ".SRCINFO preview:"
echo '```ini'
cat aur-repo/.SRCINFO
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Publish metadata to AUR mirror repo
if: ${{ steps.norm.outputs.dry_run != 'true' }}
env:
AUR_BRANCH: ${{ steps.norm.outputs.aur_branch }}
TAG: ${{ steps.norm.outputs.tag }}
run: |
set -euo pipefail
git config --global --add safe.directory "$GITHUB_WORKSPACE/aur-repo"
pushd aur-repo >/dev/null
git add PKGBUILD .SRCINFO
if git diff --cached --quiet -- PKGBUILD .SRCINFO; then
echo "No AUR metadata changes detected; mirror repo is already up to date."
popd >/dev/null
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -m "gitcomet ${TAG}"
git push origin "HEAD:${AUR_BRANCH}"
popd >/dev/null
- name: Emit deployment summary
run: |
set -euo pipefail
{
echo "### AUR mirror deployment"
echo ""
echo "- Release: \`${{ steps.norm.outputs.tag }}\`"
echo "- Target repo: \`${{ steps.norm.outputs.aur_repo }}\`"
echo "- Target branch: \`${{ steps.norm.outputs.aur_branch }}\`"
echo "- Dry run: \`${{ steps.norm.outputs.dry_run }}\`"
} >> "$GITHUB_STEP_SUMMARY"

View file

@ -5,9 +5,11 @@ on:
branches: ["main"]
paths:
- ".github/workflows/build-release-artifacts.yml"
- ".github/workflows/deploy-aur.yml"
- ".github/workflows/deploy-homebrew-tap.yml"
- ".github/workflows/deploy-apt-repo.yml"
- ".github/workflows/release-manual-main.yml"
- "scripts/update-aur.sh"
- "scripts/package-macos.sh"
- "scripts/generate-homebrew-formula.sh"
- "scripts/generate-homebrew-cask.sh"
@ -16,9 +18,11 @@ on:
branches: ["main"]
paths:
- ".github/workflows/build-release-artifacts.yml"
- ".github/workflows/deploy-aur.yml"
- ".github/workflows/deploy-homebrew-tap.yml"
- ".github/workflows/deploy-apt-repo.yml"
- ".github/workflows/release-manual-main.yml"
- "scripts/update-aur.sh"
- "scripts/package-macos.sh"
- "scripts/generate-homebrew-formula.sh"
- "scripts/generate-homebrew-cask.sh"
@ -41,6 +45,7 @@ jobs:
- name: Validate shell scripts syntax
run: |
bash -n scripts/update-aur.sh
bash -n scripts/package-macos.sh
bash -n scripts/generate-homebrew-formula.sh
bash -n scripts/generate-homebrew-cask.sh
@ -50,6 +55,7 @@ jobs:
run: |
ruby -e 'require "yaml"; %w[
.github/workflows/build-release-artifacts.yml
.github/workflows/deploy-aur.yml
.github/workflows/deploy-homebrew-tap.yml
.github/workflows/deploy-apt-repo.yml
.github/workflows/deployment-ci.yml
@ -59,6 +65,8 @@ jobs:
- name: Validate deployment workflow config keys
run: |
set -euo pipefail
grep -Fq 'vars.AUR_GITHUB_REPO' .github/workflows/release-manual-main.yml
grep -Fq 'vars.AUR_GITHUB_BRANCH' .github/workflows/release-manual-main.yml
grep -Fq 'vars.APT_STORAGE_ACCOUNT' .github/workflows/release-manual-main.yml
grep -Fq 'vars.APT_STORAGE_CONTAINER' .github/workflows/release-manual-main.yml
grep -Fq 'vars.APT_REPO_DISTRIBUTION' .github/workflows/release-manual-main.yml
@ -75,6 +83,9 @@ jobs:
grep -Fq 'vars.APT_REPO_LABEL' .github/workflows/deploy-apt-repo.yml
grep -Fq 'vars.APT_REPO_DESCRIPTION' .github/workflows/deploy-apt-repo.yml
grep -Fq 'vars.APT_STORAGE_PUBLIC_ACCESS' .github/workflows/deploy-apt-repo.yml
grep -Fq 'vars.AUR_GITHUB_REPO' .github/workflows/deploy-aur.yml
grep -Fq 'vars.AUR_GITHUB_BRANCH' .github/workflows/deploy-aur.yml
grep -Fq 'AUR_REPO_TOKEN' .github/workflows/deploy-aur.yml
grep -Fq 'HOMEBREW_TAP_TOKEN' .github/workflows/deploy-homebrew-tap.yml
- name: Generate Homebrew cask and formula from synthetic artifacts
@ -150,6 +161,86 @@ jobs:
fi
popd >/dev/null
aur-metadata-smoke:
name: Validate AUR metadata update script
runs-on: ubuntu-latest
timeout-minutes: 20
container:
image: archlinux:base-devel
steps:
- name: Install Arch packaging tooling
run: |
set -euo pipefail
pacman -Sy --noconfirm --needed perl shadow
- uses: actions/checkout@v6
- name: Create non-root packaging user
run: |
set -euo pipefail
id -u builder >/dev/null 2>&1 || useradd -m builder
chown -R builder:builder "$GITHUB_WORKSPACE"
- name: Prepare synthetic AUR repo and assets
run: |
set -euo pipefail
mkdir -p dist/aur-ci/repo
cat > dist/aur-ci/repo/PKGBUILD <<'EOF'
pkgname=gitcomet-bin
pkgver=0.0.0
pkgrel=1
pkgdesc="Fast, resource-efficient Git GUI written in Rust"
arch=('x86_64')
url="https://gitcomet.dev/"
license=('AGPL-3.0-only')
provides=("${pkgname%-bin}")
conflicts=(
"${pkgname%-bin}"
'gitcomet-git'
)
depends=(
'fontconfig'
'freetype2'
'git'
'libx11'
'libxcb'
'libxkbcommon'
'wayland'
)
source=(
"gitcomet-v$pkgver-linux-x86_64.tar.gz::https://example.invalid/gitcomet-v$pkgver-linux-x86_64.tar.gz"
"gitcomet-source-v$pkgver.tar.gz::https://example.invalid/gitcomet-source-v$pkgver.tar.gz"
)
sha256sums=('oldbinary'
'oldsource')
package() {
install -D -m755 "gitcomet-v$pkgver-linux-x86_64/gitcomet" "$pkgdir/usr/bin/gitcomet"
}
EOF
printf 'synthetic-binary' > dist/aur-ci/gitcomet-v0.0.0-linux-x86_64.tar.gz
printf 'synthetic-source' > dist/aur-ci/gitcomet-source-v0.0.0.tar.gz
chown -R builder:builder dist
- name: Run update-aur.sh
run: |
set -euo pipefail
su builder -c "cd '$GITHUB_WORKSPACE' && scripts/update-aur.sh \
--aur-dir '$GITHUB_WORKSPACE/dist/aur-ci/repo' \
--version 0.0.0 \
--binary-tar '$GITHUB_WORKSPACE/dist/aur-ci/gitcomet-v0.0.0-linux-x86_64.tar.gz' \
--source-tar '$GITHUB_WORKSPACE/dist/aur-ci/gitcomet-source-v0.0.0.tar.gz' \
--verify-source"
- name: Validate generated PKGBUILD and .SRCINFO
run: |
set -euo pipefail
test -f dist/aur-ci/repo/.SRCINFO
grep -q '^pkgver=0.0.0$' dist/aur-ci/repo/PKGBUILD
grep -q 'source = gitcomet-v0.0.0-linux-x86_64.tar.gz::https://example.invalid/gitcomet-v0.0.0-linux-x86_64.tar.gz$' dist/aur-ci/repo/.SRCINFO
grep -q 'source = gitcomet-source-v0.0.0.tar.gz::https://example.invalid/gitcomet-source-v0.0.0.tar.gz$' dist/aur-ci/repo/.SRCINFO
apt-repo-generation-smoke:
name: Validate APT repo generation script
runs-on: ubuntu-latest

View file

@ -187,6 +187,19 @@ jobs:
dry_run: false
secrets: inherit
deploy_aur:
name: Deploy AUR mirror metadata
needs: [validate, build_and_upload, publish_release]
if: ${{ fromJSON(needs.validate.outputs.draft) == false && needs.build_and_upload.result == 'success' }}
uses: ./.github/workflows/deploy-aur.yml
with:
tag: ${{ needs.validate.outputs.tag }}
version: ${{ needs.validate.outputs.version }}
aur_repo: ${{ vars.AUR_GITHUB_REPO }}
aur_branch: ${{ vars.AUR_GITHUB_BRANCH }}
dry_run: false
secrets: inherit
deploy_apt_repo:
name: Deploy Azure APT repository
needs: [validate, build_and_upload, publish_release]

View file

@ -107,3 +107,24 @@ This release flow will:
- call `.github/workflows/deploy-homebrew-tap.yml` to update `Casks/gitcomet.rb` and `Formula/gitcomet-cli.rb` in the tap repo
You can also run `.github/workflows/deploy-homebrew-tap.yml` manually for backfills or dry-runs.
### AUR mirror deployment
To push `PKGBUILD` and `.SRCINFO` into a GitHub-hosted AUR mirror repo automatically on release:
1. Create the target repository (default expected name: `OWNER/aur-gitcomet`).
2. In this repo, configure:
- secret `AUR_REPO_TOKEN`: GitHub token with `contents:write` access to the AUR mirror repository.
- optional variable `AUR_GITHUB_REPO`: target repository in `OWNER/REPO` form.
- optional variable `AUR_GITHUB_BRANCH`: target branch (default `main`).
3. Run `.github/workflows/release-manual-main.yml` with `draft=false`.
This release flow will:
- download the published Linux release tarball and source tarball
- update `PKGBUILD` `pkgver` and `sha256sums`
- regenerate `.SRCINFO`
- validate sources with `makepkg --verifysource`
- push the updated metadata into the configured AUR mirror repository
You can also run `.github/workflows/deploy-aur.yml` manually for backfills or dry-runs.

151
scripts/update-aur.sh Executable file
View file

@ -0,0 +1,151 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'USAGE'
Usage: scripts/update-aur.sh \
--aur-dir PATH \
--version VERSION \
--binary-tar PATH \
--source-tar PATH \
[--verify-source]
Updates PKGBUILD metadata for the GitHub-hosted AUR mirror repo, regenerates
.SRCINFO, and optionally verifies the referenced sources with makepkg.
USAGE
}
aur_dir=""
version=""
binary_tar=""
source_tar=""
verify_source="false"
while [[ $# -gt 0 ]]; do
case "$1" in
--aur-dir)
aur_dir="${2:-}"
shift 2
;;
--version)
version="${2:-}"
shift 2
;;
--binary-tar)
binary_tar="${2:-}"
shift 2
;;
--source-tar)
source_tar="${2:-}"
shift 2
;;
--verify-source)
verify_source="true"
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown arg: $1" >&2
usage
exit 2
;;
esac
done
if [[ -z "$aur_dir" || -z "$version" || -z "$binary_tar" || -z "$source_tar" ]]; then
echo "All required arguments must be provided." >&2
usage
exit 2
fi
version="${version#v}"
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then
echo "Invalid --version '$version'. Expected semver like 1.2.3 or 1.2.3-rc.1." >&2
exit 2
fi
pkgbuild="${aur_dir}/PKGBUILD"
srcinfo="${aur_dir}/.SRCINFO"
expected_binary_name="gitcomet-v${version}-linux-x86_64.tar.gz"
expected_source_name="gitcomet-source-v${version}.tar.gz"
if [[ ! -f "$pkgbuild" ]]; then
echo "PKGBUILD not found: $pkgbuild" >&2
exit 1
fi
if [[ ! -f "$binary_tar" ]]; then
echo "Binary tarball not found: $binary_tar" >&2
exit 1
fi
if [[ ! -f "$source_tar" ]]; then
echo "Source tarball not found: $source_tar" >&2
exit 1
fi
if [[ "$(basename "$binary_tar")" != "$expected_binary_name" ]]; then
echo "Binary tarball must be named $expected_binary_name." >&2
exit 2
fi
if [[ "$(basename "$source_tar")" != "$expected_source_name" ]]; then
echo "Source tarball must be named $expected_source_name." >&2
exit 2
fi
sha256_file() {
local file="$1"
sha256sum "$file" | awk '{print $1}'
}
binary_sha="$(sha256_file "$binary_tar")"
source_sha="$(sha256_file "$source_tar")"
GITCOMET_PKGVER="$version" \
GITCOMET_BIN_SHA="$binary_sha" \
GITCOMET_SRC_SHA="$source_sha" \
perl -0pi -e '
my $pkgver = $ENV{GITCOMET_PKGVER};
my $bin_sha = $ENV{GITCOMET_BIN_SHA};
my $src_sha = $ENV{GITCOMET_SRC_SHA};
s/^pkgver=.*/pkgver=$pkgver/m
or die "Failed to update pkgver\n";
s/^sha256sums=\([^)]+\)/sprintf("sha256sums=(\x27%s\x27\n \x27%s\x27)", $bin_sha, $src_sha)/mse
or die "Failed to update sha256sums\n";
' "$pkgbuild"
pushd "$aur_dir" >/dev/null
makepkg --printsrcinfo > "$srcinfo"
cleanup_binary=""
cleanup_source=""
if [[ "$verify_source" == "true" ]]; then
staged_binary="$PWD/$expected_binary_name"
staged_source="$PWD/$expected_source_name"
if [[ "$binary_tar" != "$staged_binary" ]]; then
cp "$binary_tar" "$staged_binary"
cleanup_binary="$staged_binary"
fi
if [[ "$source_tar" != "$staged_source" ]]; then
cp "$source_tar" "$staged_source"
cleanup_source="$staged_source"
fi
cleanup() {
[[ -n "$cleanup_binary" ]] && rm -f "$cleanup_binary"
[[ -n "$cleanup_source" ]] && rm -f "$cleanup_source"
}
trap cleanup EXIT
makepkg --verifysource
fi
popd >/dev/null
echo "Updated AUR metadata in $aur_dir"