Skip to content

Commit 6aa6dff

Browse files
ci: add AUR publish preflight checks (#199)
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 26e53c9 commit 6aa6dff

2 files changed

Lines changed: 134 additions & 1 deletion

File tree

.github/workflows/publish-aur.yml

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,21 @@ on:
1616
permissions:
1717
contents: read
1818

19+
concurrency:
20+
group: publish-aur
21+
cancel-in-progress: false
22+
23+
env:
24+
AUR_PACKAGE: create-awesome-python-app
25+
AUR_RPC_URL: https://aur.archlinux.org/rpc/v5/info?arg=create-awesome-python-app
26+
PYPI_PACKAGE: create-awesome-python-app
27+
1928
jobs:
2029
aur:
2130
name: Update AUR package
2231
runs-on: ubuntu-latest
2332
environment: pypi
33+
timeout-minutes: 20
2434
steps:
2535
- name: Resolve version
2636
id: version
@@ -33,6 +43,10 @@ jobs:
3343
else
3444
VERSION="${TAG_REF#create-awesome-python-app@}"
3545
fi
46+
if ! echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([a-zA-Z0-9.-]+)?$'; then
47+
echo "::error::Invalid AUR package version: $VERSION"
48+
exit 1
49+
fi
3650
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
3751
3852
- name: Checkout aur-package mirror repo
@@ -48,11 +62,27 @@ jobs:
4862
NEW_VERSION: ${{ steps.version.outputs.version }}
4963
run: |
5064
set -euo pipefail
65+
retry() {
66+
local attempts="$1"
67+
local delay="$2"
68+
shift 2
69+
for attempt in $(seq 1 "$attempts"); do
70+
if "$@"; then
71+
return 0
72+
fi
73+
if [ "$attempt" = "$attempts" ]; then
74+
return 1
75+
fi
76+
echo "::warning::Attempt $attempt/$attempts failed: $*; retrying in ${delay}s" >&2
77+
sleep "$delay"
78+
done
79+
}
80+
5181
# Reset pkgver and pkgrel; source URL already interpolates ${pkgver}.
5282
sed -i "s/^pkgver=.*/pkgver=${NEW_VERSION}/" PKGBUILD
5383
sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD
5484
55-
META=$(curl -sfL "https://pypi.org/pypi/create-awesome-python-app/${NEW_VERSION}/json")
85+
META=$(retry 5 10 curl -sfL "https://pypi.org/pypi/${PYPI_PACKAGE}/${NEW_VERSION}/json")
5686
SHA=$(echo "$META" | jq -r '.urls[] | select(.packagetype=="sdist") | .digests.sha256')
5787
if [ -z "$SHA" ] || [ "$SHA" = "null" ]; then
5888
echo "::error::Failed to resolve PyPI sdist sha256 for v${NEW_VERSION}" >&2
@@ -63,6 +93,45 @@ jobs:
6393
echo "----- Updated PKGBUILD -----"
6494
cat PKGBUILD
6595
96+
- name: Preflight AUR availability
97+
env:
98+
AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
99+
run: |
100+
set -euo pipefail
101+
retry() {
102+
local attempts="$1"
103+
local delay="$2"
104+
shift 2
105+
for attempt in $(seq 1 "$attempts"); do
106+
if "$@"; then
107+
return 0
108+
fi
109+
if [ "$attempt" = "$attempts" ]; then
110+
return 1
111+
fi
112+
echo "::warning::Attempt $attempt/$attempts failed: $*; retrying in ${delay}s" >&2
113+
sleep "$delay"
114+
done
115+
}
116+
117+
test -n "$AUR_SSH_PRIVATE_KEY" || {
118+
echo "::error::AUR_SSH_PRIVATE_KEY is empty or unavailable in the pypi environment"
119+
exit 1
120+
}
121+
122+
retry 5 15 curl -fsSL "$AUR_RPC_URL" >/tmp/aur-rpc.json
123+
python3 - <<'PY'
124+
import json
125+
from pathlib import Path
126+
127+
data = json.loads(Path("/tmp/aur-rpc.json").read_text())
128+
print(f"AUR RPC resultcount={data.get('resultcount')}")
129+
PY
130+
131+
mkdir -p ~/.ssh
132+
retry 5 10 ssh-keyscan -T 30 -t rsa,ecdsa,ed25519 aur.archlinux.org >> ~/.ssh/known_hosts
133+
retry 5 15 git ls-remote "https://aur.archlinux.org/${AUR_PACKAGE}.git" >/dev/null
134+
66135
- name: Publish to AUR
67136
# Pushes to aur.archlinux.org via SSH. The action reads the
68137
# updated PKGBUILD, regenerates .SRCINFO, and pushes.
@@ -79,6 +148,50 @@ jobs:
79148
# avoid "Unknown key type" errors during keyscan.
80149
ssh_keyscan_types: "rsa,ecdsa,ed25519"
81150

151+
- name: Verify AUR RPC after publish
152+
env:
153+
EXPECTED_VERSION: ${{ steps.version.outputs.version }}
154+
run: |
155+
set -euo pipefail
156+
retry() {
157+
local attempts="$1"
158+
local delay="$2"
159+
shift 2
160+
for attempt in $(seq 1 "$attempts"); do
161+
if "$@"; then
162+
return 0
163+
fi
164+
if [ "$attempt" = "$attempts" ]; then
165+
return 1
166+
fi
167+
echo "::warning::Attempt $attempt/$attempts failed: $*; retrying in ${delay}s" >&2
168+
sleep "$delay"
169+
done
170+
}
171+
172+
retry 6 20 curl -fsSL "$AUR_RPC_URL" >/tmp/aur-rpc.json
173+
AUR_VERSION=$(
174+
python3 - <<'PY'
175+
import json
176+
from pathlib import Path
177+
178+
data = json.loads(Path("/tmp/aur-rpc.json").read_text())
179+
results = data.get("results") or []
180+
print(results[0]["Version"].split("-", 1)[0] if results else "")
181+
PY
182+
)
183+
echo "AUR version: $AUR_VERSION"
184+
echo "Expected version: $EXPECTED_VERSION"
185+
if [ "$AUR_VERSION" != "$EXPECTED_VERSION" ]; then
186+
echo "::warning::AUR RPC has not reflected ${EXPECTED_VERSION} yet (current: ${AUR_VERSION:-missing})"
187+
fi
188+
{
189+
echo "## AUR publish verification"
190+
echo
191+
echo "- Expected version: \`$EXPECTED_VERSION\`"
192+
echo "- AUR RPC version: \`${AUR_VERSION:-missing}\`"
193+
} >> "$GITHUB_STEP_SUMMARY"
194+
82195
- name: Sync updated PKGBUILD to GitHub mirror
83196
# Keep the aur-package GitHub mirror in sync with what's live
84197
# on AUR. Only PKGBUILD needs to be committed here — .SRCINFO

docs/DISTRIBUTION_SETUP.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,26 @@ Paste the **private** key as repo secret `AUR_SSH_PRIVATE_KEY`.
9292
Fine-grained PAT with **Contents: Read and write** on `Create-Python-App/aur-package` only.
9393
Store as `AUR_REPO_TOKEN`.
9494

95+
### AUR publish runbook
96+
97+
`publish-aur.yml` performs three reliability checks around the publish step:
98+
99+
1. Resolve the PyPI sdist SHA with retry before editing `PKGBUILD`
100+
2. Preflight AUR RPC, `ssh-keyscan`, and `git ls-remote` with retry before pushing
101+
3. Query AUR RPC after publish and write the observed version to the job summary
102+
103+
If the workflow fails before `Publish to AUR`, check the preflight log first:
104+
105+
- Empty `AUR_SSH_PRIVATE_KEY` means the secret is missing from the `pypi`
106+
environment or the workflow did not get environment access.
107+
- AUR RPC / `git ls-remote` failures are usually transient AUR availability
108+
issues; rerun the job after a few minutes.
109+
- PyPI metadata failures usually mean the release tag fired before PyPI finished
110+
indexing the sdist; rerun once PyPI shows the version.
111+
112+
If `Publish to AUR` succeeds but the RPC summary still shows the previous
113+
version, wait for AUR propagation and rerun the distribution smoke workflow.
114+
95115
## Homebrew (`HOMEBREW_TAP_TOKEN`)
96116

97117
**Prereqs**: [`Create-Python-App/homebrew-tap`](https://github.com/Create-Python-App/homebrew-tap)

0 commit comments

Comments
 (0)