diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index ee35093..7435f08 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -636,7 +636,7 @@ A PKGBUILD is a bash script, and every step that reads it executes it: the audit 3. Clone over SSH with `--no-checkout`, and write the AUR copy as step 2 of verify does. When `HEAD` is not `base`, the AUR repo moved after the update was prepared: exit code 3. `PKGBUILD` and `.SRCINFO` in the AUR copy must be regular files, not symlinks. 4. The prepared `PKGBUILD` may differ from the AUR copy only in `pkgver=`, which must be ``, in `pkgrel=`, which must be `1`, and in the values of the checksum arrays. The arrays keep their names and lengths, hold only quoted hex checksums or `SKIP`, and have a `SKIP` exactly where the AUR copy has one. Comments and line continuations may stand between the checksums, read as bash reads them: a `#` at the start of a line, right after the opening parenthesis, or after a space or a tab comments out the rest of the line, and a backslash that ends a line without a comment, as a word of its own, continues the array on the next line. A backslash on the line that closes the array stops the publish, because bash joins the next line to the assignment. 5. Each `source` line of the prepared `.SRCINFO`, with or without an arch suffix, must be the line of the AUR copy with the old version replaced by ``. A source that is `SKIP` in every checksum array of its arch may also change the digits in the name it is saved under, the part before `::`, as `neovim-20261014.tar.gz` becomes `neovim-20261016.tar.gz` in a PKGBUILD that names a nightly download by `$(date -u +%Y%m%d)`. Its URL after `::` must still be the AUR URL with the version replaced. Verify never downloads a `SKIP` source, and makepkg takes the sources from the `PKGBUILD`. Any other line must either equal the line of the AUR copy or be that line with the version replaced in the same way, because a license, a dependency or a description can hold the old version without taking it from `pkgver`, as `GPL-2.0` does in a package at version `2.0`. The replacement also covers three forms a PKGBUILD often derives from `pkgver`: dots turned into underscores (`1_86_0`), the part before the first `+` (`0.13.0` of `0.13.0+dev+1853+g6edb38ca98`), and the version without its last component (`6.19` of `6.19.0`, as in `frameworks/${pkgver%.*}/` of KDE Frameworks). The last form counts only while it keeps a dot. Each part of a line is replaced at most once, so `5.1` inside an already replaced `5.10.0` stays. A line built from any other form of the version, a lone major version for example, stops the publish, and so does a line that has the version replaced in one place and kept in another. The package then needs a manual update. `pkgrel` and the checksums follow the rules of step 4. -6. Any other difference, and a NUL byte in either file, stops the publish with exit code 1. Otherwise the index of the clone takes the tree of `HEAD` and only the two prepared files enter its work tree, so the commit keeps every other file of the AUR repo, and steps 10 to 13 of [4.2](#42-step-by-step) follow. When the prepared files equal the AUR copy, only `current` is written to the YAML and the exit code is 2. +6. Any other difference, and a NUL byte in either file, stops the publish with exit code 1. Otherwise the index of the clone takes the tree of `HEAD`, and the two prepared files enter it byte for byte through `git hash-object --no-filters` and `git update-index`, so the commit keeps every other file of the AUR repo. `git add` would pass them through the attributes of the AUR repo, and `working-tree-encoding=UTF-7` there turns `+AAo-` in a comment into a new line that the checks above never saw. Steps 10 to 13 of [4.2](#42-step-by-step) follow. When the prepared files equal the AUR copy, only `current` is written to the YAML and the exit code is 2. `check-all.sh --prepare ` runs no strategy and needs no `GITHUB_TOKEN`. It takes the versions from the statuses that a check run, `check-all.sh` without a mode, wrote to `.status/` before it, and reads them before the first PKGBUILD runs. It prepares every package with `new_version` or `available_downgrade` into `/prepared/` and copies the statuses to `/status/`. A failed preparation becomes `update_failed`, and a package without a status from the check run becomes `check_failed`. The other statuses stay as the check run wrote them. diff --git a/scripts/lib.sh b/scripts/lib.sh index db04d7f..e05943f 100644 --- a/scripts/lib.sh +++ b/scripts/lib.sh @@ -328,8 +328,8 @@ aur_export() { done < <(git -C "$1" ls-tree -z HEAD) } -# Commit PKGBUILD and .SRCINFO, sign when GPG_KEY_ID is set, push with 3 -# attempts. +# Commit what the index holds, sign when GPG_KEY_ID is set, push with 3 +# attempts. 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 @@ -337,7 +337,6 @@ aur_commit_push() { git -C "$dir" config user.name "${GIT_AUTHOR_NAME:-aurtomator}" git -C "$dir" config user.email "${GIT_AUTHOR_EMAIL:-aurtomator@users.noreply.github.com}" git -C "$dir" config commit.gpgsign false - git -C "$dir" add PKGBUILD .SRCINFO git -C "$dir" commit -m "$2 release from $3" || { log_err "git commit failed in $dir" return 1 diff --git a/scripts/publish-pkg.sh b/scripts/publish-pkg.sh index e93e0fd..723b806 100755 --- a/scripts/publish-pkg.sh +++ b/scripts/publish-pkg.sh @@ -62,11 +62,15 @@ aur_files="${tmp_dir}/aur-files" aur_export "$aur_dir" "$aur_files" || exit 1 check_prepared_files "$aur_files" "$PREPARED" "$VERSION" || exit 1 -# The clone has no checkout: the index takes the tree of HEAD, and only the -# two prepared files enter the work tree +# The clone has no checkout: the index takes the tree of HEAD, and the two +# prepared files enter it byte for byte. git add would pass them through +# the attributes of the AUR repo, and an encoding such as UTF-7 turns text +# in a comment into code. git -C "$aur_dir" read-tree HEAD || exit 1 -cp "${PREPARED}/PKGBUILD" "${PREPARED}/.SRCINFO" "$aur_dir/" -git -C "$aur_dir" add PKGBUILD .SRCINFO || exit 1 +for file in PKGBUILD .SRCINFO; do + blob=$(git -C "$aur_dir" hash-object -w --stdin --no-filters <"${PREPARED}/${file}") || exit 1 + git -C "$aur_dir" update-index --add --cacheinfo "100644,${blob},${file}" || exit 1 +done if git -C "$aur_dir" diff --cached --quiet; then log_ok "$PKG_NAME already at $VERSION on AUR, nothing to do" diff --git a/scripts/update-pkg.sh b/scripts/update-pkg.sh index 0bdf2bf..1a60676 100755 --- a/scripts/update-pkg.sh +++ b/scripts/update-pkg.sh @@ -197,6 +197,7 @@ fi # COMMIT & PUSH TO AUR # ============================================================================= +git -C "$aur_dir" add PKGBUILD .SRCINFO || exit 1 aur_commit_push "$aur_dir" "$NEW_VERSION" "$strategy" || exit 1 log_ok "Pushed $PKG_NAME $NEW_VERSION to AUR" diff --git a/tests/publish.bats b/tests/publish.bats index 02114fe..85bf516 100644 --- a/tests/publish.bats +++ b/tests/publish.bats @@ -153,6 +153,22 @@ current: '0.9.0'" [ "$(pkg_get "$TEST_TMPDIR/packages/demo.yml" .current)" = "0.9.0" ] } +@test "update-pkg: commits the updated PKGBUILD and .SRCINFO" { + create_test_package "demo" "github-nightly" "upstream: + project: owner/repo +current: '1.0.0'" + write_pkgbuild "$OLD" 1.0.0 1 "sha256sums=('SKIP' 'SKIP')" + write_srcinfo "$TEST_TMPDIR/old.SRCINFO" 1.0.0 1 SKIP SKIP + make_aur_remote demo "$OLD" "$TEST_TMPDIR/old.SRCINFO" + mock_pkg_tools + + run "$TEST_TMPDIR/scripts/update-pkg.sh" demo 1.1.0 + [ "$status" -eq 0 ] + git -C "$AUR_REMOTE" show HEAD:PKGBUILD | grep -qx 'pkgver=1.1.0' + [ "$(git -C "$AUR_REMOTE" show HEAD:.SRCINFO)" = "pkgbase = demo" ] + [ "$(pkg_get "$TEST_TMPDIR/packages/demo.yml" .current)" = "1.1.0" ] +} + @test "update-pkg: --prepare needs a directory" { create_test_package "demo" "github-nightly" "upstream: project: owner/repo" @@ -200,6 +216,22 @@ current: '1.0.0'" [ "$(git -C "$AUR_REMOTE" ls-tree --name-only HEAD | LC_ALL=C sort | tr '\n' ' ')" = ".SRCINFO PKGBUILD demo.install " ] } +# git add would convert both files on their way into the index: UTF-7 +# turns +AAo- in a comment into a new line, ident collapses $Id: ...$ +@test "publish-pkg: commits the prepared bytes whatever the attributes of the AUR repo say" { + setup_publish + write_pkgbuild "$PREPARED/PKGBUILD" 1.1.0 1 "sha256sums=('$H3' # +AAo-)+AAo-echo PWNED+AAo-x=( + '$H4')" + sed -i 's/^\tpkgdesc = demo$/&, $Id: kept $/' "$TEST_TMPDIR/old.SRCINFO" "$PREPARED/.SRCINFO" + aur_add_file .SRCINFO "$(<"$TEST_TMPDIR/old.SRCINFO")" + aur_add_file .gitattributes $'PKGBUILD working-tree-encoding=UTF-7\n.SRCINFO ident' + + run "$TEST_TMPDIR/scripts/publish-pkg.sh" demo 1.1.0 "$PREPARED" + [ "$status" -eq 0 ] + cmp <(git -C "$AUR_REMOTE" cat-file blob HEAD:PKGBUILD) "$PREPARED/PKGBUILD" + cmp <(git -C "$AUR_REMOTE" cat-file blob HEAD:.SRCINFO) "$PREPARED/.SRCINFO" +} + @test "publish-pkg: refuses a prepared PKGBUILD that changes other lines" { setup_publish sed -i 's|https://example.com/demo|https://evil.example/demo|' "$PREPARED/PKGBUILD"