Skip to content

Commit d592991

Browse files
committed
PR instead of immediate publishing
Signed-off-by: Polina Simonenko <rabarbrablad@gmail.com>
1 parent c145a28 commit d592991

4 files changed

Lines changed: 238 additions & 112 deletions

File tree

‎.github/workflows/bump.yml‎

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
name: Bump
2+
3+
# Bump one package and open a pull request with the result. Fired by a
4+
# project's release workflow (repository_dispatch: aur-bump), by nvcheck.yml,
5+
# or manually. The payload's `package` is a single package directory (e.g.
6+
# `cnvrt` or `cnvrt-bin`); a release fires one dispatch per variant.
7+
#
8+
# This workflow never touches the AUR. Merging the pull request is the
9+
# approval; push-aur.yml publishes from main afterwards. That also keeps
10+
# AUR_SSH_PRIVATE_KEY out of any dispatch-triggered run.
11+
on:
12+
repository_dispatch:
13+
types: [aur-bump]
14+
workflow_dispatch:
15+
inputs:
16+
package:
17+
description: Package directory (e.g. cnvrt or cnvrt-bin)
18+
required: true
19+
version:
20+
description: Version, with or without a leading v
21+
required: true
22+
23+
# Serialize so concurrent bumps of the same package do not race; different
24+
# packages land on different branches and merge independently.
25+
concurrency:
26+
group: aur-bump-${{ github.event.client_payload.package || inputs.package }}
27+
28+
permissions:
29+
contents: write
30+
pull-requests: write
31+
32+
jobs:
33+
bump:
34+
runs-on: ubuntu-latest
35+
env:
36+
PKG: ${{ github.event.client_payload.package || inputs.package }}
37+
VERSION: ${{ github.event.client_payload.version || inputs.version }}
38+
steps:
39+
- uses: actions/checkout@v4
40+
41+
- name: Validate inputs
42+
env:
43+
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
44+
run: |
45+
# Checked here rather than at the create-pull-request step so a
46+
# missing secret fails in seconds instead of after a container build.
47+
[[ -n "$TAP_GITHUB_TOKEN" ]] || {
48+
echo "TAP_GITHUB_TOKEN is not set; see the one-time setup in README.md"
49+
exit 1
50+
}
51+
[[ "$PKG" =~ ^[a-z0-9][a-z0-9-]*$ ]] || { echo "bad package: $PKG"; exit 1; }
52+
[[ "$VERSION" =~ ^v?[0-9][0-9A-Za-z._-]*$ ]] || { echo "bad version: $VERSION"; exit 1; }
53+
[[ -f "$PKG/PKGBUILD" ]] || { echo "no such package dir: $PKG"; exit 1; }
54+
echo "VERSION=${VERSION#v}" >> "$GITHUB_ENV"
55+
56+
- name: Bump and build-test in an Arch container
57+
run: |
58+
docker run --rm -e PKG -e VERSION -v "$PWD:/aur" archlinux:base-devel bash -c '
59+
set -e
60+
pacman -Syu --noconfirm --needed base-devel namcap pacman-contrib go git >/dev/null
61+
useradd -m builder
62+
chown -R builder:builder /aur
63+
su builder -c "cd /aur && scripts/bump.sh $PKG $VERSION \
64+
&& cd $PKG && namcap PKGBUILD \
65+
&& makepkg -f --noconfirm --nodeps \
66+
&& namcap ./*.pkg.tar.zst"
67+
'
68+
sudo chown -R "$USER" .
69+
# makepkg leaves build output in the package dir; keep it out of the PR.
70+
git clean -fdx -- "$PKG"
71+
72+
# TAP_GITHUB_TOKEN (a PAT) rather than github.token, for two reasons:
73+
# a PR created with the default token never triggers other workflows, so
74+
# ci.yml would not run and the bump would land unverified; and opening
75+
# one at all would need the org-level "Allow GitHub Actions to create and
76+
# approve pull requests" setting, which a PAT does not. No fallback —
77+
# the validate step above already requires the secret.
78+
- name: Open pull request
79+
uses: peter-evans/create-pull-request@v7
80+
with:
81+
token: ${{ secrets.TAP_GITHUB_TOKEN }}
82+
add-paths: |
83+
${{ env.PKG }}/PKGBUILD
84+
${{ env.PKG }}/.SRCINFO
85+
branch: bump-${{ env.PKG }}-${{ env.VERSION }}
86+
commit-message: "${{ env.PKG }}: ${{ env.VERSION }}"
87+
title: "${{ env.PKG }}: ${{ env.VERSION }}"
88+
body: |
89+
Automated bump of `${{ env.PKG }}` to ${{ env.VERSION }}.
90+
91+
Built and linted in an `archlinux:base-devel` container before this
92+
pull request was opened. Merging publishes to the AUR via
93+
`push-aur.yml`.
94+
delete-branch: true

‎.github/workflows/publish.yml‎

Lines changed: 0 additions & 101 deletions
This file was deleted.

‎.github/workflows/push-aur.yml‎

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: Push to AUR
2+
3+
# Publishes to the AUR. main is the source of truth: whichever package
4+
# directories changed in the push are copied to their AUR repos.
5+
#
6+
# The only way a change reaches main is a merged pull request from bump.yml,
7+
# so merging is the approval step. AUR_SSH_PRIVATE_KEY is reachable only from
8+
# here, never from a dispatch-triggered workflow.
9+
on:
10+
push:
11+
branches: [main]
12+
paths:
13+
- '*/PKGBUILD'
14+
- '*/.SRCINFO'
15+
workflow_dispatch:
16+
inputs:
17+
package:
18+
description: Package directory to re-push (e.g. cnvrt or cnvrt-bin)
19+
required: true
20+
21+
# One AUR push at a time; the AUR rejects concurrent pushes to the same repo
22+
# and there is no value in parallelism here.
23+
concurrency:
24+
group: aur-push
25+
26+
permissions:
27+
contents: read
28+
29+
jobs:
30+
push:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
with:
35+
fetch-depth: 0
36+
37+
- name: Collect changed packages
38+
id: changed
39+
env:
40+
MANUAL_PKG: ${{ inputs.package }}
41+
BEFORE: ${{ github.event.before }}
42+
run: |
43+
if [[ -n "$MANUAL_PKG" ]]; then
44+
[[ "$MANUAL_PKG" =~ ^[a-z0-9][a-z0-9-]*$ && -f "$MANUAL_PKG/PKGBUILD" ]] ||
45+
{ echo "no such package dir: $MANUAL_PKG"; exit 1; }
46+
pkgs="$MANUAL_PKG"
47+
else
48+
# A force-push or a first push reports an all-zero base; fall back
49+
# to the previous commit rather than diffing against nothing.
50+
base="$BEFORE"
51+
if [[ -z "$base" || "$base" == 0000000000000000000000000000000000000000 ]] ||
52+
! git cat-file -e "$base^{commit}" 2>/dev/null; then
53+
base="${GITHUB_SHA}^"
54+
fi
55+
# grep exits 1 on a push that touched no package dir; that is the
56+
# empty-result case, not an error.
57+
pkgs=$(git diff --name-only "$base" "$GITHUB_SHA" \
58+
| { grep -E '^[^/]+/(PKGBUILD|\.SRCINFO)$' || true; } \
59+
| cut -d/ -f1 | sort -u)
60+
fi
61+
if [[ -z "$pkgs" ]]; then
62+
echo "no package directories changed"
63+
fi
64+
echo "packages<<EOF" >> "$GITHUB_OUTPUT"
65+
echo "$pkgs" >> "$GITHUB_OUTPUT"
66+
echo "EOF" >> "$GITHUB_OUTPUT"
67+
68+
- name: Configure AUR SSH access
69+
if: steps.changed.outputs.packages != ''
70+
env:
71+
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
72+
run: |
73+
mkdir -p ~/.ssh && chmod 700 ~/.ssh
74+
printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > ~/.ssh/aur
75+
chmod 600 ~/.ssh/aur
76+
cat > ~/.ssh/config <<'EOF'
77+
Host aur.archlinux.org
78+
User aur
79+
IdentityFile ~/.ssh/aur
80+
IdentitiesOnly yes
81+
EOF
82+
# Pin the AUR host key rather than trusting on first use.
83+
ssh-keyscan -t ed25519 aur.archlinux.org > ~/.ssh/known_hosts 2>/dev/null
84+
fp=$(ssh-keygen -lf ~/.ssh/known_hosts | awk '{print $2}')
85+
expected='SHA256:RFzBCUItH9LZS0cKB5UE6ceAYhBD5C8GeOBip8Z11+4'
86+
if [[ "$fp" != "$expected" ]]; then
87+
echo "AUR host key fingerprint mismatch: got $fp"; exit 1
88+
fi
89+
90+
- name: Publish to the AUR
91+
if: steps.changed.outputs.packages != ''
92+
env:
93+
PACKAGES: ${{ steps.changed.outputs.packages }}
94+
run: |
95+
while read -r pkg; do
96+
[[ -n "$pkg" && -f "$pkg/PKGBUILD" ]] || continue
97+
echo "==> $pkg"
98+
version=$(sed -n 's/^pkgver=//p' "$pkg/PKGBUILD")
99+
tmp=$(mktemp -d)
100+
if ! git clone "ssh://aur@aur.archlinux.org/${pkg}.git" "$tmp" 2>/dev/null; then
101+
# Package does not exist yet; first push registers it.
102+
git -C "$tmp" init -q -b master
103+
git -C "$tmp" remote add origin "ssh://aur@aur.archlinux.org/${pkg}.git"
104+
fi
105+
cp "$pkg/PKGBUILD" "$pkg/.SRCINFO" "$tmp/"
106+
git -C "$tmp" add PKGBUILD .SRCINFO
107+
if git -C "$tmp" diff --cached --quiet; then
108+
echo " AUR already up to date"
109+
continue
110+
fi
111+
git -C "$tmp" -c user.name='shellcell-bot' \
112+
-c user.email='rabarbrablad@gmail.com' \
113+
commit -q -m "$version"
114+
git -C "$tmp" push origin HEAD:master
115+
echo " published $version"
116+
done <<< "$PACKAGES"

‎README.md‎

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,30 @@ Both install the binary, man page, and shell completions to the standard paths.
3030
## How it works
3131

3232
`main` is the source of truth. A push that changes a package's `PKGBUILD` is
33-
mirrored to that package's AUR git repo.
33+
mirrored to that package's AUR git repo. Nothing reaches `main` except through
34+
a reviewed pull request, so **merging is the approval to publish**.
3435

3536
```
36-
project release (vX.Y.Z) ──repository_dispatch(aur-bump)──▶ publish.yml
37-
nvcheck.yml (weekly) ──repository_dispatch(aur-bump)──▶ publish.yml
37+
project release (vX.Y.Z) ──repository_dispatch(aur-bump)──▶ bump.yml
38+
nvcheck.yml (weekly) ──repository_dispatch(aur-bump)──▶ bump.yml
3839
│
39-
bump.sh: set pkgver, updpkgsums, regen .SRCINFO │
40-
build-test in an archlinux container (makepkg + namcap) │
41-
commit to main, then push PKGBUILD + .SRCINFO to the AUR ◀┘
40+
bump.sh: set pkgver, updpkgsums, regen .SRCINFO │
41+
build-test in an archlinux container (makepkg + namcap) │
42+
open PR ──ci.yml re-verifies──▶ you merge ─────────────▶│
43+
▼
44+
push-aur.yml
45+
push PKGBUILD + .SRCINFO to the AUR
4246
```
4347

44-
- **`publish.yml`** (dispatch / manual): bumps one package directory,
45-
build-tests it, and publishes. A release fires one `aur-bump` per variant, so
46-
both `<name>` and `<name>-bin` update automatically.
48+
- **`bump.yml`** (dispatch / manual): bumps one package directory, build-tests
49+
it, and opens a pull request. A release fires one `aur-bump` per variant, so
50+
both `<name>` and `<name>-bin` get their own PR. This workflow never touches
51+
the AUR.
52+
- **`push-aur.yml`** (push to `main`): publishes whichever package directories
53+
the push changed. `AUR_SSH_PRIVATE_KEY` is reachable only from here — never
54+
from a dispatch-triggered run — so firing a dispatch cannot publish to the
55+
AUR on its own. `workflow_dispatch` with a package name re-pushes one package
56+
by hand.
4757
- **`nvcheck.yml`** (scheduled): runs nvchecker (`packages.toml`) and, for any
4858
package whose upstream release is newer than the committed `pkgver`, fires the
4959
same `aur-bump` dispatch — a safety net for a release whose dispatch was
@@ -61,7 +71,7 @@ snailrace/ snailrace-bin/
6171
ttysvg/ ttysvg-bin/
6272
packages.toml # nvchecker sources (all 8 packages)
6373
scripts/bump.sh # set pkgver, updpkgsums, regen .SRCINFO
64-
.github/workflows/ # ci.yml, publish.yml, nvcheck.yml
74+
.github/workflows/ # ci.yml, bump.yml, push-aur.yml, nvcheck.yml
6575
```
6676

6777
## Local development
@@ -93,6 +103,13 @@ that regenerate `.SRCINFO` and validate sums on commit. CI regenerates
93103
the AUR — verified free; the first publish registers them.
94104
3. Secrets: `AUR_SSH_PRIVATE_KEY` (this repo); the org `TAP_GITHUB_TOKEN` PAT
95105
extended to cover `shellcell/aur` (used by the release dispatch and by
96-
`nvcheck.yml` to fire dispatches).
106+
`nvcheck.yml` to fire dispatches, and by `bump.yml` to open PRs that CI
107+
will actually run on).
97108
4. `aur-bump` dispatch job added to each project's release workflow (fires one
98109
dispatch per variant).
110+
111+
`bump.yml` requires `TAP_GITHUB_TOKEN` and fails fast without it. The PAT is
112+
not optional: a PR opened by the default `GITHUB_TOKEN` does not trigger
113+
`ci.yml`, so the bump would land unverified — and creating one at all needs
114+
the org-level *Allow GitHub Actions to create and approve pull requests*
115+
setting, which a PAT sidesteps entirely.

0 commit comments

Comments
 (0)