publish.sh: verify credentials and upload the index last; add next-version-code.sh
Closes #5 Closes #6 An F-Droid repo is a signed index plus the files it names, and a partial upload could previously leave them disagreeing: aws s3 sync walks the tree in its own order, so an interrupted publish could land the index before the APKs it names, breaking the repo for every client until the upload finished or was rolled back. publish.sh now uploads in two passes, unconditionally -- every non-index file first, the index (index-v1.json(.asc), index-v2.json, entry.json, entry.jar) last -- so an interruption anywhere in pass 1 simply leaves the repo describing the old state a little longer, never a mixture. Credentials are also verified with one real authenticated call (head-bucket) before either pass, rather than merely checking the environment variables are non-empty, since an unusable-but-present credential failing halfway through is exactly how a partial upload happens. next-version-code.sh is the one implementation of the versionCode rule (candidate = minutes since 2026-01-01Z, clamped to strictly exceed whatever is already published for that applicationId), shared by CI and manual builds so there is exactly one place this can drift. bit pointed out (appended to #6) that the clamp is monotonic against *observed* state, not monotonic by construction: two jobs starting in the same minute can both read the same published value and race to publish the same versionCode on different APKs. Nothing can prevent that wasted build, so publish.sh now also refuses to overwrite an already-existing APK object before pass 1 -- turning the collision from a silent, permanent wrong state into a loud failure at the last moment the information is reliable. Tested offline (83 assertions: pure-stdlib suites for both scripts, the real CLIs through with-nixpkgs.sh, and a small local HEAD-only S3 stub proving the credential check is a real authenticated call and the APK-collision refusal fires only when it should) and live against fdroid-weblib-dev (7 more: a real publish observed mid-flight via a same-process synchronization hook to prove the index is genuinely absent while the file is already present, both objects reading back byte-identical, and a second publish refused against a real already-published APK with its bytes left unchanged). Co-authored-by: bit <bit@das-labor.org>
This commit is contained in:
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# One monotonic versionCode rule, shared by CI and manual builds
|
||||
# (fdroid-repo-ci#6). Two implementations of "the next version code" is the
|
||||
# whole failure mode this avoids, so both paths call this one script rather
|
||||
# than each computing their own.
|
||||
#
|
||||
# next-version-code.sh <index-v2.json URL or local path> <application-id>
|
||||
#
|
||||
# Prints the integer to stdout and nothing else:
|
||||
#
|
||||
# versionCode=$(bash next-version-code.sh "$INDEX_URL" "$APPLICATION_ID")
|
||||
#
|
||||
# The index source may be an http(s) URL -- what both CI and a manual build
|
||||
# use in practice, and needs no credentials, since this repo and the F-Droid
|
||||
# repo it reads are both public (weblib-viewer#16: buildable when CI is
|
||||
# down) -- or a local file path, which is mainly what the offline test suite
|
||||
# uses but is equally valid for a build that already has the index checked
|
||||
# out.
|
||||
#
|
||||
# Fails loudly (non-zero exit, message on stderr, nothing on stdout) if the
|
||||
# index cannot be fetched or parsed -- see next_version_code.py for why that
|
||||
# is never allowed to fall back to a guess. An index with no entry at all for
|
||||
# this application id is NOT an error: that is a normal first-ever publish
|
||||
# for a channel, and yields the bare candidate.
|
||||
#
|
||||
# The rule itself, and why each part of it is shaped this way, lives in
|
||||
# next_version_code.py's own docstring -- read that, not this file, for the
|
||||
# reasoning. This script only resolves the pinned interpreter and forwards
|
||||
# arguments; everything that can be unit-tested without nix lives in the
|
||||
# stdlib-only Python module next to it.
|
||||
#
|
||||
# Pins python3 (stdlib json/urllib/datetime/argparse does the rest -- no
|
||||
# other package) via with-nixpkgs.sh/nixpkgs-pin, next to this script.
|
||||
set -euo pipefail
|
||||
|
||||
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
cat >&2 <<'USAGE'
|
||||
usage: next-version-code.sh <index-v2.json URL or local path> <application-id>
|
||||
USAGE
|
||||
exit 2
|
||||
fi
|
||||
|
||||
exec bash "$here/with-nixpkgs.sh" python3 python3 "$here/next_version_code.py" "$@"
|
||||
Reference in New Issue
Block a user