From 976a2743cd7b0e0cfd43f340d695126cda5dbf78 Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:25:46 +0500 Subject: [PATCH 01/10] fix(scripts): keep cookies on verify downloads --- docs/WORKFLOW.md | 2 +- scripts/lib/sums.sh | 6 ++++-- tests/publish.bats | 9 +++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index 557b5fc..d009a9c 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -868,7 +868,7 @@ A PKGBUILD is a bash script, and every step that reads it executes it: the audit 2. Clone `https://aur.archlinux.org/.git` anonymously with `--no-checkout`. When `HEAD` is not `base`, the AUR repo moved after the update was prepared: exit code 3. The entries at the top of `HEAD` are written to a separate directory with `git ls-tree` and `git cat-file`, so git applies none of the links, attributes or filters of the repo: a regular file with its content, a symlink as a link to nowhere, anything else as an empty directory. The steps below read the AUR copy there. 3. `PKGBUILD` and `.SRCINFO` in the AUR copy must be regular files. The prepared files must hold no NUL byte and pass steps 4 and 5 of publish below, with the prepared `pkgver` as `` and, for a package with `upstream.asset_var`, the value of its line in the prepared `PKGBUILD` as the asset value. From here on, every source URL in the prepared `.SRCINFO` follows from the AUR copy. 4. Each checksum array of the prepared `PKGBUILD` must hold the same values as the same key in the prepared `.SRCINFO`. -5. Every checksum other than `SKIP` must match its source, computed as makepkg does with `sum`. The file is the one makepkg uses. A file in the AUR repo with the name of the source comes first and must be a regular file. Otherwise a remote source is downloaded with curl, retried as makepkg does and limited to `http`, `https` and `ftp`, redirects included. A git source at `#tag=` or `#commit=` is cloned as a mirror over `https` or `http`, and its checksum is that of `git archive --format tar` of the tag or commit, with `export-subst` and `export-ignore` turned off, as makepkg computes it. A checksum for any other source, another VCS for example, cannot be checked and stops the verify. +5. Every checksum other than `SKIP` must match its source, computed as makepkg does with `sum`. The file is the one makepkg uses. A file in the AUR repo with the name of the source comes first and must be a regular file. Otherwise a remote source is downloaded with curl, retried and with the cookie engine on as makepkg does, and limited to `http`, `https` and `ftp`, redirects included. A git source at `#tag=` or `#commit=` is cloned as a mirror over `https` or `http`, and its checksum is that of `git archive --format tar` of the tag or commit, with `export-subst` and `export-ignore` turned off, as makepkg computes it. A checksum for any other source, another VCS for example, cannot be checked and stops the verify. 6. Any mismatch stops the verify with exit code 1. Otherwise the exit code is 0. **Publish**, `publish-pkg.sh /`, never sources the PKGBUILD and never runs `makepkg`. It treats the prepared files as untrusted: diff --git a/scripts/lib/sums.sh b/scripts/lib/sums.sh index 11fc65f..050d788 100644 --- a/scripts/lib/sums.sh +++ b/scripts/lib/sums.sh @@ -37,9 +37,11 @@ _source_file() { return 1 else file="${3}/$(printf '%s' "$url" | sha256sum | cut -d' ' -f1)" - # Retried as makepkg does. Redirects may only lead to the protocols above. + # Retried and with the cookie engine on, as makepkg does, so a cookie set + # on a redirect goes with the next request. Redirects may only lead to + # the protocols above. if [[ ! -f "$file" ]] && - ! curl -qgfsSL --retry 3 --retry-delay 3 --proto '=http,https,ftp' \ + ! curl -qgfsSL -b '' --retry 3 --retry-delay 3 --proto '=http,https,ftp' \ --proto-redir '=http,https,ftp' -o "$file" "$url"; then rm -f "$file" log_err "Could not download $url" diff --git a/tests/publish.bats b/tests/publish.bats index 283f7c8..f7afbee 100644 --- a/tests/publish.bats +++ b/tests/publish.bats @@ -875,6 +875,15 @@ commit_aur_state() { grep -q -- '--proto =http,https,ftp --proto-redir =http,https,ftp .* https://example.com/demo-1.1.0.tar.gz$' "$TEST_TMPDIR/curl.args" } +# makepkg runs curl with -b "", so a cookie set on a redirect goes with the +# next request +@test "verify-pkg: downloads with the cookie engine on, as makepkg does" { + setup_verify + run "$TEST_TMPDIR/scripts/verify-pkg.sh" demo "$PREPARED" + [ "$status" -eq 0 ] + grep -qF -- ' -b --retry 3 ' "$TEST_TMPDIR/curl.args" +} + @test "verify-pkg: refuses a checksum that does not match the download" { setup_verify write_pkgbuild "$PREPARED/PKGBUILD" 1.1.0 1 "sha256sums=('$H3' '$SUM_LOCAL')" From be18cc39defabea33c441ae31fbaaa8c15ffe1b0 Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:27:03 +0500 Subject: [PATCH 02/10] fix(scripts): ignore an origin outside GitHub --- scripts/update-readme.sh | 9 +++++++-- tests/scripts.bats | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/update-readme.sh b/scripts/update-readme.sh index 12e36d1..c5696a5 100755 --- a/scripts/update-readme.sh +++ b/scripts/update-readme.sh @@ -64,8 +64,13 @@ if grep -qxF -- "$BADGES_START" "$README"; then has_badges=true fi -# Detect repo from git origin -repo_url=$(git -C "$AURTOMATOR_DIR" remote get-url origin 2>/dev/null | sed 's|.*github.com[:/]||; s|\.git$||' || true) +# Only a GitHub origin names the repo, so the URL of another remote, or a +# local path, never reaches the README +repo_url="" +origin_url=$(git -C "$AURTOMATOR_DIR" remote get-url origin 2>/dev/null || true) +if [[ "$origin_url" =~ [/@]github\.com[:/]([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+) ]]; then + repo_url="${BASH_REMATCH[1]%.git}" +fi # ============================================================================= # BADGES SECTION diff --git a/tests/scripts.bats b/tests/scripts.bats index 62daa28..c2fe430 100644 --- a/tests/scripts.bats +++ b/tests/scripts.bats @@ -2271,6 +2271,25 @@ current: '1.0.0'" [[ "$badges" != *"Lint & Test"* && "$badges" != *"codecov"* ]] } +@test "update-readme: an origin outside GitHub, or a local path, gives only the static badges" { + create_test_package "pkg-a" "github-release" "upstream: + project: owner/repo +current: '1.0.0'" + git -C "$TEST_TMPDIR" init -q + git -C "$TEST_TMPDIR" remote add origin "https://gitlab.com/someone/fork.git" + local origin + for origin in "https://gitlab.com/someone/fork.git" "https://notgithub.com/someone/fork.git" "$TEST_TMPDIR/clone"; do + git -C "$TEST_TMPDIR" remote set-url origin "$origin" + readme_with_badges + run "$TEST_TMPDIR/scripts/update-readme.sh" + echo "$origin: $output" + [ "$status" -eq 0 ] + [[ "$output" == *"Badges updated (repo: unknown)"* ]] + ! grep -qF -e 'someone/fork' -e "$TEST_TMPDIR" "$TEST_TMPDIR/README.md" + [[ "$(sed -n '3p' "$TEST_TMPDIR/README.md")" != *"Lint & Test"* ]] + done +} + @test "update-readme: an unknown status reads as tracking" { create_test_package "pkg-a" "github-release" "upstream: project: owner/repo From 74942e6b912186c0f60f3f5f8a6dabd5f8d81f6d Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:45:17 +0500 Subject: [PATCH 03/10] fix(scripts): refuse a key made of two known names --- scripts/validate-pkg.sh | 18 +++++++++++++++--- tests/validate-pkg.bats | 11 +++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/scripts/validate-pkg.sh b/scripts/validate-pkg.sh index 0b0979f..b0d9e48 100755 --- a/scripts/validate-pkg.sh +++ b/scripts/validate-pkg.sh @@ -45,6 +45,18 @@ readonly UPSTREAM_KEYS=( "kde-tarball: project path" ) +# Succeeds when $1 is one of the words of $2. A substring match would pass +# a key such as "name strategy". +# Args: $1=word, $2=words separated by spaces +in_words() { + local words word + read -ra words <<<"$2" + for word in "${words[@]}"; do + [[ "$word" != "$1" ]] || return 0 + done + return 1 +} + # Prints the upstream keys of a strategy, or fails when UPSTREAM_KEYS has # no line for it # Args: $1=strategy @@ -66,7 +78,7 @@ upstream_keys() { key_strategies() { local line names=() list i for line in "${UPSTREAM_KEYS[@]}"; do - [[ " ${line#*: } " != *" $1 "* ]] || names+=("${line%%:*}") + ! in_words "$1" "${line#*: }" || names+=("${line%%:*}") done case ${#names[@]} in 0) return 1 ;; @@ -191,7 +203,7 @@ validate_pkg() { local keys key unknown=() list strategy_keys where mapfile -t keys < <(pkg_get "$file" 'keys | .[]') for key in "${keys[@]}"; do - [[ " $PACKAGE_KEYS " == *" $key "* ]] || unknown+=("$key") + in_words "$key" "$PACKAGE_KEYS" || unknown+=("$key") done if ((${#unknown[@]} > 0)); then printf -v list '%s, ' "${unknown[@]}" @@ -210,7 +222,7 @@ validate_pkg() { ((errors++)) elif ((${#keys[@]} > 0)); then for key in "${keys[@]}"; do - [[ " $strategy_keys " != *" $key "* ]] || continue + ! in_words "$key" "$strategy_keys" || continue if where=$(key_strategies "$key"); then log_err "$file: upstream.$key applies to $where only" else diff --git a/tests/validate-pkg.bats b/tests/validate-pkg.bats index 6cead8d..fa88d14 100644 --- a/tests/validate-pkg.bats +++ b/tests/validate-pkg.bats @@ -398,6 +398,17 @@ pkgver_dashes: _" [[ "$output" == *"mypkg.yml has an unknown key: allow_downgrades, pkgver_dashes"* ]] } +@test "validate: a key made of two known names rejected" { + create_test_package "mypkg" "github-release" "upstream: + project: owner/repo + \"project tag_version_regex\": x +\"name strategy\": x" + run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" + [ "$status" -eq 1 ] + [[ "$output" == *"mypkg.yml has an unknown key: name strategy"* ]] + [[ "$output" == *"mypkg.yml: upstream.project tag_version_regex is not a field of any strategy"* ]] +} + # The keys come from the reads of the scripts, so a key that a script # starts to read cannot be refused @test "validate: every top-level key a script reads passes" { From 5bb739f31f0455d6355265634e0c21e8f13645f7 Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:48:55 +0500 Subject: [PATCH 04/10] fix(scripts): ask for quotes around a numeric note --- docs/WORKFLOW.md | 4 ++-- scripts/lib/alerts.sh | 11 ++++++++++- scripts/validate-pkg.sh | 2 +- tests/scripts.bats | 9 +++++++++ tests/validate-pkg.bats | 17 ++++++++++++++++- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index d009a9c..89dbae4 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -909,7 +909,7 @@ alerts: | `when` | one of `when`, `from`, `match` | `major`: the first number of the new version is greater than the first number of `current`, as in `28+25 โ†’ 29+1` | | `from` | one of `when`, `from`, `match` | A version: the new version is at or above it, and `current` is below it | | `match` | one of `when`, `from`, `match` | An ERE: the new version matches it, and `current` does not | -| `note` | yes | One line for the maintainer | +| `note` | yes | One line of text for the maintainer. A number, a boolean or a date goes in quotes, as in `note: '2024'` | | `hold` | no | `true` pauses the update. Default `false` | A rule fires when the new version meets its condition and `current` does not, so it fires once at each crossing. A `from` rule can stay in the file after the release it names, and a `when: major` rule fires again at the next major. The versions are in `pkgver` form, as `current` holds them, after `pkgver_dash` and `pkgver_format`. `vercmp` compares a version with `from` and the first numbers for `when: major`, so `9 โ†’ 10` is a new major. A package without `current` fires no rule. @@ -925,7 +925,7 @@ A skipped downgrade or version jump comes first, see [4.3](#43-downgrade-behavio Every rule that fires writes its note to `.status/_alerts`, and the run opens an issue from it, see [5.6](#56-issues). A held update gets the issue `โœ‹ : update held` with the notes of every rule that fired, because after the hand publish a rule without `hold` no longer fires. The run closes it once the version is in the AUR. A published update gets the issue `๐Ÿ”” : notice` with its notes, and the maintainer closes it once the edit is done. -`validate-pkg.sh` refuses an entry with any other key, without exactly one of `when`, `from` and `match`, with `when` other than `major`, with a `from` that is not a version, a `match` that bash does not accept as an ERE, a `note` that is not one line, or a `hold` that is not a boolean. It refuses `when: major` together with `pkgver_format`, because the fixed text of a format such as `4.%s` holds the first number of every version. `check-all.sh` reads the rules on every run. A rule it cannot evaluate, or a `note` that is not one line, fails the check of the package and never lets a held update through. +`validate-pkg.sh` refuses an entry with any other key, without exactly one of `when`, `from` and `match`, with `when` other than `major`, with a `from` that is not a version, a `match` that bash does not accept as an ERE, a `note` that is not one line of text, or a `hold` that is not a boolean. It refuses `when: major` together with `pkgver_format`, because the fixed text of a format such as `4.%s` holds the first number of every version. `check-all.sh` reads the rules on every run. A rule it cannot evaluate, or a `note` that is not one line of text, fails the check of the package and never lets a held update through. ### 4.6 Values from release asset names diff --git a/scripts/lib/alerts.sh b/scripts/lib/alerts.sh index a7505a5..84ecf9f 100644 --- a/scripts/lib/alerts.sh +++ b/scripts/lib/alerts.sh @@ -40,6 +40,15 @@ alert_note() { printf '%s\n' "$note" } +# Prints the error for a note that alert_note refuses. YAML reads a bare +# number, boolean or date as another type than text. +# Args: $1=package file, $2=index of the alert +alert_note_error() { + local value + value=$(pkg_get "$1" ".alerts[$2].note | select(tag == \"!!int\" or tag == \"!!float\" or tag == \"!!bool\" or tag == \"!!timestamp\")") + printf 'note must be one line of text%s\n' "${value:+: put $value in quotes}" +} + # The alerts of a package that fire on the step from current to the new # version, one per line in file order: hold or notice, a tab and the note. # Fails on a rule it cannot evaluate and on a note that is not one line @@ -60,7 +69,7 @@ fired_alerts() { return 1 fi if ! note=$(alert_note "$file" "$i"); then - log_err "alerts[$i].note must be one line of text" + log_err "alerts[$i].$(alert_note_error "$file" "$i")" return 1 fi rc=0 diff --git a/scripts/validate-pkg.sh b/scripts/validate-pkg.sh index b0d9e48..86c7f81 100755 --- a/scripts/validate-pkg.sh +++ b/scripts/validate-pkg.sh @@ -138,7 +138,7 @@ validate_alert() { fi if ! alert_note "$file" "$i" >/dev/null; then - log_err "$rule.note must be one line of text" + log_err "$rule.$(alert_note_error "$file" "$i")" errors=1 fi diff --git a/tests/scripts.bats b/tests/scripts.bats index c2fe430..e9fe8d2 100644 --- a/tests/scripts.bats +++ b/tests/scripts.bats @@ -1201,6 +1201,15 @@ run_alert_fires() { done } +@test "lib.sh: fired_alerts asks for quotes around a note that YAML reads as a number" { + create_test_package "pkg" "github-release" "alerts: + - from: '0.4.0' + note: 2024" + run bash -c 'source "$0/scripts/lib.sh"; fired_alerts "$1" 0.3 29+1' "$TEST_TMPDIR" "$TEST_TMPDIR/packages/pkg.yml" + [ "$status" -eq 1 ] + [[ "$output" == *"alerts[0].note must be one line of text: put 2024 in quotes"* ]] +} + # === version of aurtomator === @test "lib.sh: aurtomator_version prints VERSION, or nothing without it" { diff --git a/tests/validate-pkg.bats b/tests/validate-pkg.bats index fa88d14..b13d987 100644 --- a/tests/validate-pkg.bats +++ b/tests/validate-pkg.bats @@ -1132,7 +1132,7 @@ alerts: local note for note in "" "note: ''" "note: | First line - Second line" "note: [a, b]" "note: {text: x}" "note: 42"; do + Second line" "note: [a, b]" "note: {text: x}"; do create_test_package "mypkg" "github-release" "upstream: project: owner/repo alerts: @@ -1141,6 +1141,21 @@ alerts: run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" [ "$status" -ne 0 ] [[ "$output" == *"alerts[0].note must be one line of text"* ]] + [[ "$output" != *"in quotes"* ]] + done +} + +@test "validate: an alert note that YAML reads as a number, a boolean or a date asks for quotes" { + local value + for value in 2024 1.5 true 2024-10-30; do + create_test_package "mypkg" "github-release" "upstream: + project: owner/repo +alerts: + - when: major + note: $value" + run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" + [ "$status" -ne 0 ] + [[ "$output" == *"alerts[0].note must be one line of text: put $value in quotes"* ]] done } From d62aa8555f806019298b8e2f7c4448a340363749 Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:51:52 +0500 Subject: [PATCH 05/10] fix(scripts): correct two github-nightly messages --- docs/WORKFLOW.md | 2 +- scripts/validate-pkg.sh | 11 +++++++++-- tests/validate-pkg.bats | 44 ++++++++++++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index 89dbae4..7f6742b 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -302,7 +302,7 @@ upstream: date_format: '%Y%m%d' ``` -`validate-pkg.sh` refuses `date_format` with another `version_source` and a format whose output is not a valid version, such as `%Y-%m-%d`. It refuses `nightly_tag_regex` next to `nightly_tag` or `channel`, and one that bash cannot compile. It refuses `tag_version_regex` with a `version_source` other than `tag`. It refuses a `version_source` other than `tag`, `release_body`, `tag_date` or `published_date`, `release_body` without `version_pattern`, a `version_pattern` that `grep -P` cannot compile, and `version_pattern` with another `version_source`. It refuses `tag_date` with the fixed tag of pattern A: such a tag has no dash and so no `YYYY-MM-DD` date. It refuses `nightly_tag` next to `channel`: `nightly_tag: latest` leaves `channel` unread, and `channel` leaves any other `nightly_tag` unread. +`validate-pkg.sh` refuses `date_format` with another `version_source` and a format whose output is not a valid version, such as `%Y-%m-%d`. It refuses `nightly_tag_regex` next to `nightly_tag` or `channel`, and one that bash cannot compile. It refuses `tag_version_regex` and `tag_version_replace` with a `version_source` other than `tag`. It refuses a `version_source` other than `tag`, `release_body`, `tag_date` or `published_date`, `release_body` without `version_pattern`, a `version_pattern` that `grep -P` cannot compile, and `version_pattern` with another `version_source`. It refuses `tag_date` with the fixed tag of pattern A: such a tag has no dash and so no `YYYY-MM-DD` date. It refuses `nightly_tag` next to `channel`: `nightly_tag: latest` leaves `channel` unread, and `channel` leaves any other `nightly_tag` unread. **Authentication:** same as `github-release`. diff --git a/scripts/validate-pkg.sh b/scripts/validate-pkg.sh index 86c7f81..fd25ea6 100755 --- a/scripts/validate-pkg.sh +++ b/scripts/validate-pkg.sh @@ -365,7 +365,10 @@ validate_pkg() { local tag_version_replace tag_version_replace=$(pkg_get "$file" '.upstream.tag_version_replace // ""') if [[ -n "$tag_version_replace" ]]; then - if [[ -z "$tag_version_regex" ]]; then + if [[ "$strategy" == "github-nightly" && $(pkg_get "$file" '.upstream.version_source // "tag"') != "tag" ]]; then + log_err "$file: upstream.tag_version_replace applies to version_source tag only" + ((errors++)) + elif [[ -z "$tag_version_regex" ]]; then log_err "$file: upstream.tag_version_replace needs upstream.tag_version_regex" ((errors++)) elif [[ ! "$tag_version_replace" =~ ^([A-Za-z0-9._+]|\\[1-9])+$ ]]; then @@ -463,7 +466,11 @@ validate_pkg() { fi # nightly_tag: latest wins over channel, and channel over any other tag if [[ $(pkg_get "$file" '.upstream | (has("nightly_tag") and has("channel"))') == true ]]; then - log_err "$file: upstream.channel takes the place of nightly_tag" + if [[ "$nightly_tag" == latest ]]; then + log_err "$file: upstream.nightly_tag latest takes the place of channel" + else + log_err "$file: upstream.channel takes the place of nightly_tag" + fi ((errors++)) fi fi diff --git a/tests/validate-pkg.bats b/tests/validate-pkg.bats index b13d987..5805fa9 100644 --- a/tests/validate-pkg.bats +++ b/tests/validate-pkg.bats @@ -782,18 +782,21 @@ pkgver_dashes: _" } # nightly_tag: latest wins over channel, and channel over any other tag -@test "validate: nightly_tag next to channel rejected" { - local tag - for tag in latest nightly; do - create_test_package "mypkg" "github-nightly" "upstream: +@test "validate: nightly_tag next to channel rejected with the field that is read" { + create_test_package "mypkg" "github-nightly" "upstream: project: owner/repo - nightly_tag: $tag + nightly_tag: latest channel: Nightly" - run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" - echo "$tag: $output" - [ "$status" -eq 1 ] - [[ "$output" == *"mypkg.yml: upstream.channel takes the place of nightly_tag"* ]] - done + run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" + [ "$status" -eq 1 ] + [[ "$output" == *"mypkg.yml: upstream.nightly_tag latest takes the place of channel"* ]] + create_test_package "mypkg" "github-nightly" "upstream: + project: owner/repo + nightly_tag: nightly + channel: Nightly" + run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" + [ "$status" -eq 1 ] + [[ "$output" == *"mypkg.yml: upstream.channel takes the place of nightly_tag"* ]] } @test "validate: pypi fails without upstream.registry" { @@ -1400,11 +1403,13 @@ pkgver_dash: _" create_test_package "mypkg" "github-release" "upstream: project: owner/repo version_source: release_body - tag_version_regex: '^v(.+)$'" + tag_version_regex: '^v(.+)$' + tag_version_replace: '\\1'" run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" [ "$status" -eq 1 ] [[ "$output" == *"upstream.version_source applies to the github-nightly strategy only"* ]] [[ "$output" != *"tag_version_regex applies to version_source tag only"* ]] + [[ "$output" != *"tag_version_replace applies to version_source tag only"* ]] } @test "validate: tag_version_regex for github-nightly with another version_source rejected" { @@ -1421,6 +1426,23 @@ pkgver_dash: _" done } +# A tag_version_regex there is refused as well, so asking for one would +# send the author in a circle +@test "validate: tag_version_replace for github-nightly with another version_source rejected" { + local source + for source in release_body tag_date published_date; do + create_test_package "mypkg" "github-nightly" "upstream: + project: owner/repo + version_source: $source + version_pattern: 'v\\K\\S+' + tag_version_replace: '\\1'" + run "$TEST_TMPDIR/scripts/validate-pkg.sh" "mypkg" + [ "$status" -eq 1 ] + [[ "$output" == *"upstream.tag_version_replace applies to version_source tag only"* ]] + [[ "$output" != *"needs upstream.tag_version_regex"* ]] + done +} + # === tag_version_replace validation === @test "validate: tag_version_replace with the groups of the regex passes" { From 5153c590831f5c0f9499a28efaf22873d713447b Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:53:48 +0500 Subject: [PATCH 06/10] fix(scripts): name the regex when no tag matches --- scripts/lib/common.sh | 2 +- tests/strategies.bats | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index 4cc8f6c..2ff19e4 100644 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -459,7 +459,7 @@ latest_tag_version() { fi version=$(tag_versions "$regex" "$replace" <<<"$tags" | newest_version) if [[ -z "$version" ]]; then - echo "No matching tags found" >&2 + echo "No tag gives a version with upstream.tag_version_regex '$regex', tags read: $(grep -c . <<<"$1")" >&2 return 1 fi echo "$version" diff --git a/tests/strategies.bats b/tests/strategies.bats index 315eb93..b56baf7 100644 --- a/tests/strategies.bats +++ b/tests/strategies.bats @@ -2126,7 +2126,7 @@ EOF # The mock returns v2.0.0, v1.9.0 and v1.0.0, and none matches ^release- run "$TEST_TMPDIR/strategies/github-tag.sh" "test-filter" [ "$status" -eq 1 ] - [[ "$output" == *"No matching tags found"* ]] + [[ "$output" == *"No tag gives a version with upstream.tag_version_regex '^release-(.+)\$', tags read: 3"* ]] } @test "gitlab-tag: tag_version_regex filters out all tags" { @@ -2137,7 +2137,7 @@ EOF run "$TEST_TMPDIR/strategies/gitlab-tag.sh" "test-filter" [ "$status" -eq 1 ] - [[ "$output" == *"No matching tags found"* ]] + [[ "$output" == *"No tag gives a version with upstream.tag_version_regex '^release-(.+)\$', tags read: 3"* ]] } @test "gitea-tag: tag_version_regex filters out all tags" { @@ -2148,7 +2148,7 @@ EOF run "$TEST_TMPDIR/strategies/gitea-tag.sh" "test-filter" [ "$status" -eq 1 ] - [[ "$output" == *"No matching tags found"* ]] + [[ "$output" == *"No tag gives a version with upstream.tag_version_regex '^release-(.+)\$', tags read: 2"* ]] } # A pin to one release line leaves out v2.0.0 and the v2.1.0 rc tags From 81c129ab901a14e715e7242c04443f5d2a29bf87 Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:54:22 +0500 Subject: [PATCH 07/10] docs: explain an archpkg epoch bump --- docs/WORKFLOW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index 7f6742b..99da184 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -610,7 +610,7 @@ strategy: repology | `upstream.archpkg` | no | Package name in the official repos. Defaults to the package `name`. | | `upstream.testing` | no | `true` also reads `core-testing`, `extra-testing` and `multilib-testing`. Default `false`. | -**Version:** reads the entries of `core`, `extra` and `multilib` and takes the newest by `vercmp`, the epoch included. It prints its `pkgver`, the version without `epoch` and `pkgrel`: ffmpeg `2:9.0.2-2` gives `9.0.2`. A package without an entry in these repositories fails with `Package not found in core, extra, multilib: `. A rebuild in the official repos raises only `pkgrel`, so it gives no new version, and an update of this strategy never raises `pkgrel` alone, see [SUPPORTED-PACKAGES.md](SUPPORTED-PACKAGES.md#packages-needing-a-pkgrel-bump). +**Version:** reads the entries of `core`, `extra` and `multilib` and takes the newest by `vercmp`, the epoch included. It prints its `pkgver`, the version without `epoch` and `pkgrel`: ffmpeg `2:9.0.2-2` gives `9.0.2`. When the official package raises its epoch, as `1:2.0` to `2:1.0`, the strategy gives `1.0` after `2.0`, and the check skips it as a downgrade, see [4.3](#43-downgrade-behavior). aurtomator does not write an `epoch`: the maintainer raises it in the AUR package, publishes `1.0` by hand and sets `current` to it. A package without an entry in these repositories fails with `Package not found in core, extra, multilib: `. A rebuild in the official repos raises only `pkgrel`, so it gives no new version, and an update of this strategy never raises `pkgrel` alone, see [SUPPORTED-PACKAGES.md](SUPPORTED-PACKAGES.md#packages-needing-a-pkgrel-bump). With `testing: true` the strategy also reads `core-testing`, `extra-testing` and `multilib-testing`. It takes the build in testing while that build is newer than the stable one, and the stable build otherwise. A build that moves from testing to the stable repository keeps its version and gives no new update. `gnome-unstable` and `kde-unstable` stay out. `validate-pkg.sh` refuses a `testing` that is not `true` or `false`, and `testing` with another strategy. From e993a16a6a3aa40aec943be5d92b52a7ed0269f7 Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:55:55 +0500 Subject: [PATCH 08/10] fix(scripts): tell how to pick one of two tags --- scripts/check-asset.sh | 2 +- tests/scripts.bats | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/check-asset.sh b/scripts/check-asset.sh index 5cf0d05..904abb9 100755 --- a/scripts/check-asset.sh +++ b/scripts/check-asset.sh @@ -91,7 +91,7 @@ if ((${#found[@]} == 0)); then exit 1 fi if ((${#found[@]} > 1)); then - log_err "Tags ${found[*]} of ${project} all give version ${PKGVER}" + log_err "Tags ${found[*]} of ${project} all give version ${PKGVER}; set upstream.tag_version_regex to match one of them" exit 1 fi tag="${found[0]}" diff --git a/tests/scripts.bats b/tests/scripts.bats index e9fe8d2..453ddca 100644 --- a/tests/scripts.bats +++ b/tests/scripts.bats @@ -3629,7 +3629,7 @@ pkgver_dash: ''" asset_var: _twin" run "$TEST_TMPDIR/scripts/check-asset.sh" twin 1.0.0 [ "$status" -eq 1 ] - [[ "$output" == *"Tags v1.0.0 1.0.0 of asset-owner/twin-repo all give version 1.0.0"* ]] + [[ "$output" == *"Tags v1.0.0 1.0.0 of asset-owner/twin-repo all give version 1.0.0; set upstream.tag_version_regex to match one of them"* ]] } @test "check-asset: fails when the release cannot be read" { From 2c6402d96a2d1d14223906fef763dea177919578 Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:56:52 +0500 Subject: [PATCH 09/10] fix(scripts): expand only ~/ in the SSH key path --- scripts/setup.sh | 4 ++-- tests/setup.bats | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index 78b261d..91dae44 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -165,8 +165,8 @@ setup_ssh() { if [[ -z "$AUR_SSH_KEY" ]]; then AUR_SSH_KEY=$(ask "Path to SSH private key for AUR" "$HOME/.ssh/aur_ed25519") fi - # Expand ~ to home dir - AUR_SSH_KEY="${AUR_SSH_KEY/#\~/$HOME}" + # Expand ~/ to the home dir. ~user/ stays as it is. + AUR_SSH_KEY="${AUR_SSH_KEY/#\~\//$HOME/}" check_ssh_key || return 1 ok "SSH key: $AUR_SSH_KEY" diff --git a/tests/setup.bats b/tests/setup.bats index d78a799..bb84843 100644 --- a/tests/setup.bats +++ b/tests/setup.bats @@ -352,6 +352,15 @@ EOF [[ "$output" == *"SSH key:"* ]] } +# The home directory of another user is not $HOME +@test "setup ssh: leaves a tilde with a user name as it is" { + HOME="$TEST_TMPDIR/fakehome" run "$TEST_TMPDIR/scripts/setup.sh" ssh \ + --ssh-key "~nobody/keys/aur.pem" \ + --non-interactive + [ "$status" -ne 0 ] + [[ "$output" == *"SSH key not found: ~nobody/keys/aur.pem"* ]] +} + @test "setup ssh: nonexistent key rejected" { run "$TEST_TMPDIR/scripts/setup.sh" ssh \ --ssh-key "/nonexistent/path/key.pem" \ From 696eab4f35e9c1d4d9a28e259ec85bca7bca040c Mon Sep 17 00:00:00 2001 From: noctuum <25441068+noctuum@users.noreply.github.com> Date: Fri, 30 Oct 2026 17:59:34 +0500 Subject: [PATCH 10/10] fix(scripts): write ASCII in the generated README --- README.md | 2 +- docs/WORKFLOW.md | 2 +- scripts/update-readme.sh | 12 ++++++------ tests/scripts.bats | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index cdcade5..8f7270c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Automated AUR package updates from a single GitHub repo. - +
PackageVersionStrategyUpdatedStatus
No packages configuredโ€”โ€”โ€”โ€”
No packages configured----
aurtomator 0.7.0 diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index 99da184..5f35f4b 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -1186,7 +1186,7 @@ GitHub starts a `workflow_run` workflow only from its file on the default branch `update-readme.sh` writes the version under the package table, and with `newer:` a link to the release: ```text -aurtomator 0.6.0 ยท [0.7.0 available](https://github.com/staticwire/aurtomator/releases/tag/v0.7.0) +aurtomator 0.6.0 ([0.7.0 available](https://github.com/staticwire/aurtomator/releases/tag/v0.7.0)) ``` The comment marks the line for the release PR of aurtomator, which writes the new version there in the README of the upstream repository. Its check run then finds the README as `update-readme.sh` writes it, and has nothing to commit to a `main` that only takes pull requests. A run that could not read the release writes the line without the link. diff --git a/scripts/update-readme.sh b/scripts/update-readme.sh index c5696a5..3cb5300 100755 --- a/scripts/update-readme.sh +++ b/scripts/update-readme.sh @@ -140,9 +140,9 @@ for pkg_file in "${AURTOMATOR_DIR}"/packages/*.yml; do [[ -f "$pkg_file" ]] || continue name=$(pkg_get "$pkg_file" .name) - current=$(pkg_get "$pkg_file" '.current // "โ€”"') + current=$(pkg_get "$pkg_file" '.current // "-"') strategy=$(pkg_get "$pkg_file" .strategy) - last_updated=$(pkg_get "$pkg_file" '.last_updated // "โ€”"') + last_updated=$(pkg_get "$pkg_file" '.last_updated // "-"') # Determine status from .status/ dir or YAML status_file="${STATUS_DIR}/${name}" @@ -202,9 +202,9 @@ for pkg_file in "${AURTOMATOR_DIR}"/packages/*.yml; do ((pkg_ok++)) || true ;; esac - elif [[ "$current" == "โ€”" || "$current" == "null" ]]; then + elif [[ "$current" == "-" || "$current" == "null" ]]; then status="โœจ new" - current="โ€”" + current="-" ((pkg_ok++)) || true else status="โœ… tracking" @@ -242,7 +242,7 @@ table+=" ${table_header}" if [[ "$pkg_count" -eq 0 ]]; then - table+=$(table_row "No packages configured" โ€” โ€” โ€” โ€”) + table+=$(table_row "No packages configured" - - - -) else table+="$table_rows" fi @@ -258,7 +258,7 @@ if [[ -n "$version" ]]; then [[ ! -f "${STATUS_DIR}/_upstream" ]] || upstream=$(<"${STATUS_DIR}/_upstream") table+=$'\n\n'"aurtomator ${version}" if [[ "$upstream" =~ ^newer:\ ([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then - table+=" ยท [${BASH_REMATCH[1]} available](https://github.com/${AURTOMATOR_UPSTREAM}/releases/tag/v${BASH_REMATCH[1]})" + table+=" ([${BASH_REMATCH[1]} available](https://github.com/${AURTOMATOR_UPSTREAM}/releases/tag/v${BASH_REMATCH[1]}))" fi table+=" " fi diff --git a/tests/scripts.bats b/tests/scripts.bats index 453ddca..d07b6a5 100644 --- a/tests/scripts.bats +++ b/tests/scripts.bats @@ -1314,7 +1314,7 @@ EOF git -C "$TEST_TMPDIR" init -q "$TEST_TMPDIR/scripts/update-readme.sh" 2>/dev/null - grep -qxF 'aurtomator 0.6.0 ยท [0.7.0 available](https://github.com/staticwire/aurtomator/releases/tag/v0.7.0) ' "$TEST_TMPDIR/README.md" + grep -qxF 'aurtomator 0.6.0 ([0.7.0 available](https://github.com/staticwire/aurtomator/releases/tag/v0.7.0)) ' "$TEST_TMPDIR/README.md" } @test "update-readme: links no release for aurtomator at its release or for another _upstream" { @@ -2097,7 +2097,7 @@ EOF "$TEST_TMPDIR/scripts/update-readme.sh" 2>/dev/null [ "$(sed -n '//,$p' "$TEST_TMPDIR/README.md")" = '
- +
PackageVersionStrategyUpdatedStatus
No packages configuredโ€”โ€”โ€”โ€”
No packages configured----
' ] } @@ -2309,7 +2309,7 @@ current: '1.0.0'" run "$TEST_TMPDIR/scripts/update-readme.sh" [ "$status" -eq 0 ] - grep -qF '1.0.0github-releaseโ€”โœ… tracking' "$TEST_TMPDIR/README.md" + grep -qF '1.0.0github-release-โœ… tracking' "$TEST_TMPDIR/README.md" grep -qF 'packages-1%2F1-brightgreen' "$TEST_TMPDIR/README.md" } @@ -2321,7 +2321,7 @@ current: '1.0.0'" run "$TEST_TMPDIR/scripts/update-readme.sh" [ "$status" -eq 0 ] - grep -qF 'pkg-newโ€”github-releaseโ€”โœจ new' "$TEST_TMPDIR/README.md" + grep -qF 'pkg-new-github-release-โœจ new' "$TEST_TMPDIR/README.md" } # A package that has not been checked yet has failed nothing @@ -2371,7 +2371,7 @@ current: '1.0.0'" EOF git -C "$TEST_TMPDIR" init -q "$TEST_TMPDIR/scripts/update-readme.sh" 2>/dev/null - grep -qF 'github-releaseโ€”' "$TEST_TMPDIR/README.md" + grep -qF 'github-release-' "$TEST_TMPDIR/README.md" } # === update-pkg.sh: argument validation ===