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
4 changes: 2 additions & 2 deletions docs/WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,8 @@ A PKGBUILD is a bash script, and every step that reads it executes it: the audit
1. The prepared directory must not be a symlink, `PKGBUILD`, `.SRCINFO` and `base` must be regular files, and `base` a commit id.
2. When the prepared `pkgver` is not `<version>`, upstream moved after the update was prepared: exit code 3.
3. Clone over SSH. When `HEAD` is not `base`, the AUR repo moved after the update was prepared: exit code 3. `PKGBUILD` and `.SRCINFO` in the clone must be regular files, not symlinks.
4. The prepared `PKGBUILD` may differ from the AUR copy only in `pkgver=`, which must be `<version>`, 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.
5. Each line of the prepared `.SRCINFO` must be the line of the AUR copy with the old version replaced by `<version>`. The same holds for 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 the package needs a manual update. `pkgrel` and the checksums follow the rules of step 4.
4. The prepared `PKGBUILD` may differ from the AUR copy only in `pkgver=`, which must be `<version>`, 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 `<version>`. 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 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 <dir>` 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 `<dir>/prepared/` and copies the statuses to `<dir>/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.
Expand Down
35 changes: 28 additions & 7 deletions scripts/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,8 @@ _has_nul() {
# PKGVER|PKGREL<TAB>val the value of a pkgver= or pkgrel= line
# SUM<TAB>array<TAB>index<TAB>HASH|SKIP one line per checksum
# SUMVAL<TAB>array<TAB>value the same checksum with its value
# BAD<TAB>text anything in a checksum array but a quoted checksum
# BAD<TAB>text anything in a checksum array but a quoted
# checksum, a comment or a line continuation
# Args: $1=pkgbuild path
_pkgbuild_parts() {
awk '
Expand All @@ -377,14 +378,22 @@ _pkgbuild_parts() {
print "SUMVAL\t" name "\t" substr(t, 2, length(t) - 2)
}
}
function body(text, end) {
# As bash does: a # that starts a word comments out the rest of the
# line, and a backslash at the end of a line outside a comment joins
# the next line, which inside the parentheses is only a separator
function body(text, raw, cont, end, rest) {
raw = text
sub(/(^|[ \t])#.*/, "", text)
cont = text == raw && text ~ /(^|[ \t])\\$/
if (cont) text = substr(text, 1, length(text) - 1)
end = index(text, ")")
if (!end) {
tokens(text)
return 1
}
tokens(substr(text, 1, end - 1))
if (substr(text, end + 1) !~ /^[ \t]*$/) print "BAD\t" NR ": " substr(text, end + 1)
rest = substr(text, end + 1) (cont ? "\\" : "")
if (rest !~ /^[ \t]*$/) print "BAD\t" NR ": " rest
return 0
}
in_sums { in_sums = body($0); next }
Expand Down Expand Up @@ -418,7 +427,7 @@ check_prepared_pkgbuild() {
fi

if grep -q $'^BAD\t' <<<"$new_parts"; then
log_err "Prepared PKGBUILD has more than quoted checksums in a checksum array:"
log_err "Prepared PKGBUILD has more than quoted checksums, comments and line continuations in a checksum array:"
grep $'^BAD\t' <<<"$new_parts" | cut -f2- >&2
return 1
fi
Expand Down Expand Up @@ -495,8 +504,12 @@ _bump_version() {
}

# A .SRCINFO is generated, so line N of the prepared file must be line N of
# the AUR file with the old version replaced by the new one, see
# _bump_version. pkgrel and the checksum values are the exceptions.
# the AUR file, with these changes: pkgver is the new version, pkgrel is 1,
# a checksum keeps its key and stays a SKIP or a hash, a source has the old
# version replaced by the new one (see _bump_version), and any other line
# either stays or has the version replaced in the same way. A license, a
# dependency or a pkgdesc can hold the old version without deriving it
# from pkgver.
# Args: $1=AUR .SRCINFO, $2=prepared .SRCINFO, $3=expected version
# Returns: 0 when that holds, 1 otherwise (reason on stderr)
check_prepared_srcinfo() {
Expand Down Expand Up @@ -532,6 +545,8 @@ check_prepared_srcinfo() {
kind="$key"
elif [[ "$key" =~ ^(ck|md5|sha1|sha224|sha256|sha384|sha512|b2)sums(_[A-Za-z0-9_]+)?$ ]]; then
kind=sums
elif [[ "$key" =~ ^source(_[A-Za-z0-9_]+)?$ ]]; then
kind=source
else
kind=other
fi
Expand Down Expand Up @@ -560,8 +575,14 @@ check_prepared_srcinfo() {
return 1
fi
;;
*)
source)
if [[ "$new" != "$(_bump_version "$old" "$old_ver" "$version")" ]]; then
log_err "Prepared .SRCINFO line $((i + 1)) is not the AUR source with the new version: $new"
return 1
fi
;;
*)
if [[ "$new" != "$old" && "$new" != "$(_bump_version "$old" "$old_ver" "$version")" ]]; then
log_err "Prepared .SRCINFO line $((i + 1)) changes more than the version: $new"
return 1
fi
Expand Down
101 changes: 101 additions & 0 deletions tests/publish.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,58 @@ EOF
[ "$status" -eq 0 ]
}

@test "check_prepared_pkgbuild: accepts a checksum array continued with a backslash" {
local sums="sha256sums=('SKIP' \\
'SKIP')"
write_pkgbuild "$OLD" 1.0.0 2 "$sums"
write_pkgbuild "$NEW" 1.1.0 1 "$sums"
run check_prepared_pkgbuild "$OLD" "$NEW" 1.1.0
[ "$status" -eq 0 ]
}

@test "check_prepared_pkgbuild: accepts a comment after a checksum array" {
local sums="sha256sums=('SKIP' 'SKIP') # VCS sources"
write_pkgbuild "$OLD" 1.0.0 2 "$sums"
write_pkgbuild "$NEW" 1.1.0 1 "$sums"
run check_prepared_pkgbuild "$OLD" "$NEW" 1.1.0
[ "$status" -eq 0 ]
}

@test "check_prepared_pkgbuild: accepts comments inside a checksum array" {
local sums="sha256sums=( # git
'SKIP' # patch (local)
'SKIP')"
write_pkgbuild "$OLD" 1.0.0 2 "$sums"
write_pkgbuild "$NEW" 1.1.0 1 "$sums"
run check_prepared_pkgbuild "$OLD" "$NEW" 1.1.0
[ "$status" -eq 0 ]
}

# bash joins the next line to the assignment, and runs it as a command
@test "check_prepared_pkgbuild: rejects a backslash after a checksum array" {
write_pkgbuild "$OLD" 1.0.0 2 "sha256sums=('SKIP' 'SKIP')"
write_pkgbuild "$NEW" 1.1.0 1 "sha256sums=('SKIP' 'SKIP') \\"
run check_prepared_pkgbuild "$OLD" "$NEW" 1.1.0
[ "$status" -eq 1 ]
}

# bash reads an escaped space there, so the # starts a word, not a comment
@test "check_prepared_pkgbuild: rejects a backslash before a hash sign in a checksum array" {
write_pkgbuild "$OLD" 1.0.0 2 "sha256sums=('SKIP' 'SKIP')"
write_pkgbuild "$NEW" 1.1.0 1 "sha256sums=('SKIP' \\ # c
'SKIP')"
run check_prepared_pkgbuild "$OLD" "$NEW" 1.1.0
[ "$status" -eq 1 ]
}

# bash starts no comment right after the closing parenthesis
@test "check_prepared_pkgbuild: rejects a hash sign right after a checksum array" {
write_pkgbuild "$OLD" 1.0.0 2 "sha256sums=('SKIP' 'SKIP')"
write_pkgbuild "$NEW" 1.1.0 1 "sha256sums=('SKIP' 'SKIP')# c"
run check_prepared_pkgbuild "$OLD" "$NEW" 1.1.0
[ "$status" -eq 1 ]
}

@test "check_prepared_pkgbuild: rejects a changed number of checksums" {
write_pkgbuild "$OLD" 1.0.0 2 "sha256sums=('$H1' '$H2')"
write_pkgbuild "$NEW" 1.1.0 1 "sha256sums=('$H3')"
Expand Down Expand Up @@ -1278,6 +1330,55 @@ write_srcinfo_line() {
[ "$status" -eq 1 ]
}

# Helper: .SRCINFO as makepkg --printsrcinfo prints it for a PKGBUILD with
# pkgdesc, license and depends holding 2.0 apart from pkgver
# Usage: write_srcinfo_lookalike <path> <pkgver>
write_srcinfo_lookalike() {
printf '%s\n' \
"pkgbase = demo" \
$'\tpkgdesc = A GPL-2.0 licensed tool' \
$'\tpkgver = '"$2" \
$'\tpkgrel = 1' \
$'\turl = https://example.com' \
$'\tarch = x86_64' \
$'\tlicense = GPL-2.0-or-later' \
$'\tdepends = glibc>=2.0.1' \
$'\tprovides = tool='"$2" \
$'\tsource = https://example.com/releases/'"$2"'/demo-'"$2"'.tar.gz' \
$'\tsha256sums = SKIP' \
"" \
"pkgname = demo" >"$1"
}

@test "check_prepared_srcinfo: keeps text that only looks like the old version" {
write_srcinfo_lookalike "$TEST_TMPDIR/old.SRCINFO" 2.0
write_srcinfo_lookalike "$TEST_TMPDIR/new.SRCINFO" 2.1
run check_prepared_srcinfo "$TEST_TMPDIR/old.SRCINFO" "$TEST_TMPDIR/new.SRCINFO" 2.1
[ "$status" -eq 0 ]
}

@test "check_prepared_srcinfo: rejects a source that keeps the old version" {
write_srcinfo_line "$TEST_TMPDIR/old.SRCINFO" 2.0 "source = https://example.com/demo-2.0.tar.gz"
write_srcinfo_line "$TEST_TMPDIR/new.SRCINFO" 2.1 "source = https://example.com/demo-2.0.tar.gz"
run check_prepared_srcinfo "$TEST_TMPDIR/old.SRCINFO" "$TEST_TMPDIR/new.SRCINFO" 2.1
[ "$status" -eq 1 ]
}

@test "check_prepared_srcinfo: rejects an arch source that keeps the old version" {
write_srcinfo_line "$TEST_TMPDIR/old.SRCINFO" 2.0 "source_x86_64 = https://example.com/demo-2.0-x86_64.tar.gz"
write_srcinfo_line "$TEST_TMPDIR/new.SRCINFO" 2.1 "source_x86_64 = https://example.com/demo-2.0-x86_64.tar.gz"
run check_prepared_srcinfo "$TEST_TMPDIR/old.SRCINFO" "$TEST_TMPDIR/new.SRCINFO" 2.1
[ "$status" -eq 1 ]
}

@test "check_prepared_srcinfo: rejects a line changed beyond the version" {
write_srcinfo_line "$TEST_TMPDIR/old.SRCINFO" 2.0 "depends = glibc>=2.0.1"
write_srcinfo_line "$TEST_TMPDIR/new.SRCINFO" 2.1 "depends = glibc>=2.9"
run check_prepared_srcinfo "$TEST_TMPDIR/old.SRCINFO" "$TEST_TMPDIR/new.SRCINFO" 2.1
[ "$status" -eq 1 ]
[[ "$output" == *"changes more than the version"* ]]
}

@test "check_prepared_srcinfo: rejects an added dependency" {
write_srcinfo "$TEST_TMPDIR/old.SRCINFO" 1.0.0 2 "$H1" "$H2"
write_srcinfo "$TEST_TMPDIR/new.SRCINFO" 1.1.0 1 "$H3" "$H4"
Expand Down
Loading