Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ check-all.sh [--update | --prepare <dir> | --verify <dir> | --publish <dir>] [--
### 4.2 Step-by-step

1. Load `.aurtomator.conf` when it exists. In CI, `GIT_AUTHOR_NAME`, `GIT_AUTHOR_EMAIL` and `GPG_KEY_ID` come from the workflow environment.
2. Clone `ssh://aur@aur.archlinux.org/<package-name>.git` into a temporary directory, with up to 3 attempts. A clone without a PKGBUILD stops the update.
2. Wait 2 seconds, then clone `ssh://aur@aur.archlinux.org/<package-name>.git` into a temporary directory, with up to 3 attempts 5 seconds apart. A clone without a PKGBUILD stops the update.
3. When the clone lands on a detached `HEAD`, reset `master` to `origin/master`, so the push is a fast-forward.
4. Warn when the PKGBUILD has no `# Maintainer:` line.
5. Audit the PKGBUILD. Split packages, `pkgver()` with the wrong strategy and mixed checksums block the update. Patches only warn. See [SUPPORTED-PACKAGES.md](SUPPORTED-PACKAGES.md).
Expand All @@ -565,9 +565,11 @@ check-all.sh [--update | --prepare <dir> | --verify <dir> | --publish <dir>] [--
9. Regenerate `.SRCINFO` with `makepkg --printsrcinfo`.
10. With `--dry-run`, print the PKGBUILD diff and stop.
11. Commit `PKGBUILD` and `.SRCINFO` as `<new-version> release from <strategy>`, authored by `GIT_AUTHOR_NAME` and `GIT_AUTHOR_EMAIL` (default `aurtomator`). With `GPG_KEY_ID` set, the commit is amended with a signature. When signing fails, the commit is pushed unsigned.
12. Push, with up to 3 attempts.
12. Push, with up to 3 attempts 5 seconds apart.
13. Write `current` and `last_updated` to the package YAML.

`AURTOMATOR_CLONE_DELAY` and `AURTOMATOR_RETRY_DELAY` change the 2 and the 5 seconds. The tests set both to 0.

### 4.3 Downgrade behavior

aurtomator follows upstream when it publishes a version older than the tracked one, and does **not** block it. Upstream is the source of truth: a packager that refuses a re-published older release only drifts out of sync. A downgrade is logged and shown, and it does not count as a failure.
Expand Down
20 changes: 11 additions & 9 deletions scripts/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,13 @@ validate_source_urls() {
# AUR GIT
# =============================================================================

# Clone an AUR repo, with 3 attempts
# Clone an AUR repo, with 3 attempts. AURTOMATOR_CLONE_DELAY and
# AURTOMATOR_RETRY_DELAY set the pauses, in seconds.
# Args: $1=url, $2=destination, $3...=options for git clone
aur_clone() {
local attempt
local attempt delay="${AURTOMATOR_RETRY_DELAY:-5}"
# Brief delay to reduce AUR SSH rate-limit pressure from GitHub Actions IP pool
sleep 2
sleep "${AURTOMATOR_CLONE_DELAY:-2}"
for attempt in 1 2 3; do
if git clone "${@:3}" "$1" "$2" 2>&1; then
return 0
Expand All @@ -289,9 +290,9 @@ aur_clone() {
log_err "Failed to clone AUR repo $1 after 3 attempts"
return 1
fi
log_warn "Clone attempt $attempt failed, retrying in 5s..."
log_warn "Clone attempt $attempt failed, retrying in ${delay}s..."
rm -rf "$2"
sleep 5
sleep "$delay"
done
}

Expand Down Expand Up @@ -340,10 +341,11 @@ aur_export() {
}

# Commit what the index holds, sign when GPG_KEY_ID is set, push with 3
# attempts. The caller stages PKGBUILD and .SRCINFO.
# attempts, AURTOMATOR_RETRY_DELAY seconds apart. The caller stages
# PKGBUILD and .SRCINFO.
# Args: $1=clone directory, $2=version, $3=strategy (for the commit message)
aur_commit_push() {
local dir="$1" attempt
local dir="$1" attempt delay="${AURTOMATOR_RETRY_DELAY:-5}"
log_info "Pushing to AUR"
git -C "$dir" config user.name "${GIT_AUTHOR_NAME:-aurtomator}"
git -C "$dir" config user.email "${GIT_AUTHOR_EMAIL:-aurtomator@users.noreply.github.com}"
Expand All @@ -369,8 +371,8 @@ aur_commit_push() {
log_err "git push to AUR failed after 3 attempts"
return 1
fi
log_warn "Push attempt $attempt failed, retrying in 5s..."
sleep 5
log_warn "Push attempt $attempt failed, retrying in ${delay}s..."
sleep "$delay"
done
}

Expand Down
20 changes: 15 additions & 5 deletions tests/gpg-flow.bats
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@

load helpers/setup.sh

setup() {
setup_test_env

# Isolated GPG keyring for all tests
export GNUPGHOME="$TEST_TMPDIR/gnupg"
# The keys are generated once per file, and every test gets a copy of the
# keyring
setup_file() {
export GNUPGHOME="$BATS_FILE_TMPDIR/gnupg"
mkdir -m 0700 "$GNUPGHOME"
echo "allow-loopback-pinentry" >"$GNUPGHOME/gpg-agent.conf"

Expand Down Expand Up @@ -49,6 +48,17 @@ GPGEOF
withpass_fpr=$(gpg --list-secret-keys --keyid-format long 2>/dev/null | grep -B2 "Test Withpass" | grep "^sec" | head -1 | awk '{print $2}' | cut -d'/' -f2)
WITHPASS_MASTER_ID="$withpass_fpr"
WITHPASS_SUBKEY_ID=$(gpg --list-secret-keys --keyid-format long 2>/dev/null | grep -A8 "Test Withpass" | grep "^ssb" | head -1 | awk '{print $2}' | cut -d'/' -f2)
export NOPASS_SUBKEY_ID NOPASS_MASTER_ID WITHPASS_MASTER_ID WITHPASS_SUBKEY_ID

gpgconf --kill gpg-agent
}

setup() {
setup_test_env

# Isolated GPG keyring for each test
export GNUPGHOME="$TEST_TMPDIR/gnupg"
cp -a "$BATS_FILE_TMPDIR/gnupg" "$GNUPGHOME"

# Git repo for commit signing tests
AUR_DIR="$TEST_TMPDIR/aur-test-pkg"
Expand Down
3 changes: 3 additions & 0 deletions tests/helpers/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ setup_test_env() {

# Override AURTOMATOR_DIR so all scripts use our temp workspace
export AURTOMATOR_DIR="$TEST_TMPDIR"

# No pause before an AUR clone or between attempts
export AURTOMATOR_CLONE_DELAY=0 AURTOMATOR_RETRY_DELAY=0
}

teardown_test_env() {
Expand Down
45 changes: 45 additions & 0 deletions tests/publish.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,51 @@ make_export_clone() {
[ ! -e "$clone/PKGBUILD" ]
}

# === aur_clone and aur_commit_push ===

# Helper: a sleep on PATH that logs its argument to $TEST_TMPDIR/sleeps, and
# a git whose clone always fails
mock_sleep_and_clone() {
printf '#!/usr/bin/env bash\necho "$1" >>"%s/sleeps"\n' "$TEST_TMPDIR" >"$MOCK_DIR/sleep"
printf '#!/usr/bin/env bash\n[[ "$1" == clone ]] && exit 128\nexec /usr/bin/git "$@"\n' >"$MOCK_DIR/git"
chmod +x "$MOCK_DIR/sleep" "$MOCK_DIR/git"
}

# Helper: a repo without a remote, with a staged PKGBUILD, at $TEST_TMPDIR/clone
make_unpushable_repo() {
git init -q -b master "$TEST_TMPDIR/clone"
echo "pkgver=1.0.0" >"$TEST_TMPDIR/clone/PKGBUILD"
git -C "$TEST_TMPDIR/clone" add PKGBUILD
}

@test "aur_clone: waits the pauses the environment sets" {
mock_sleep_and_clone
AURTOMATOR_CLONE_DELAY=3 AURTOMATOR_RETRY_DELAY=7 run aur_clone https://example.invalid/demo.git "$TEST_TMPDIR/clone"
[ "$status" -eq 1 ]
[ "$(cat "$TEST_TMPDIR/sleeps")" = $'3\n7\n7' ]
[[ "$output" == *"Clone attempt 2 failed, retrying in 7s"* ]]
}

@test "aur_commit_push: waits the pause the environment sets" {
mock_sleep_and_clone
make_unpushable_repo
AURTOMATOR_RETRY_DELAY=7 run aur_commit_push "$TEST_TMPDIR/clone" 1.0.0 test
[ "$status" -eq 1 ]
[ "$(cat "$TEST_TMPDIR/sleeps")" = $'7\n7' ]
[[ "$output" == *"Push attempt 2 failed, retrying in 7s"* ]]
}

@test "aur_clone and aur_commit_push: wait 2 s before a clone and 5 s between attempts by default" {
mock_sleep_and_clone
make_unpushable_repo
unset AURTOMATOR_CLONE_DELAY AURTOMATOR_RETRY_DELAY
run aur_clone https://example.invalid/demo.git "$TEST_TMPDIR/other"
[ "$status" -eq 1 ]
run aur_commit_push "$TEST_TMPDIR/clone" 1.0.0 test
[ "$status" -eq 1 ]
[ "$(cat "$TEST_TMPDIR/sleeps")" = $'2\n5\n5\n5\n5' ]
}

# === check_prepared_pkgbuild ===

@test "check_prepared_pkgbuild: accepts new pkgver, pkgrel and checksums" {
Expand Down
Loading