Build the shared F-Droid publishing tooling: sign, index, publish
Adds sign-apk.sh (align-check + apksigner), fdroid-update.sh (fdroid update over a binary repo, signing the index with its own separate key), publish.sh (S3-compatible upload) and the composite action tying them together, matching weblib-ci's shape. zipalign is not a standalone nixpkgs attribute; resolved by measurement rather than assumption that Gradle/AGP already aligns its output (Android's own docs, corroborated by fdroidserver never shelling out to zipalign) and added check_zip_alignment.py as the safety net, since apksigner itself does not check alignment at all. nixpkgs is pinned via a committed nixpkgs-pin revision, since this repo has no flake.lock of its own for with-nixpkgs.sh to read (fdroid-repo-ci#1, weblib-ci#9) -- a missing or malformed pin fails loudly rather than degrading to an unpinned nix shell. No consumer repo, bucket, hostname or channel name is hard-coded: which repo holds the keys and runs the publish has already changed once during this issue and may again, so every example uses a placeholder. 42 offline/local-only test assertions across five suites (run_tests.sh), including fault injection on the alignment check, the password/argument validation, and the with-nixpkgs.sh fail-loud paths. Co-authored-by: bit <bit@das-labor.org>
This commit is contained in:
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run `fdroid update` over a `repo/` directory of already-signed APKs and
|
||||
# produce the signed index.
|
||||
#
|
||||
# Binary-repo mode (fdroid-repo-ci#1): every APK under repo/ already carries
|
||||
# its own signature -- from sign-apk.sh, or from whatever produced it -- so
|
||||
# this step never touches an app's signing key. It only uses the separate
|
||||
# *repo index* key, a different key entirely, which is what this script
|
||||
# signs the index with.
|
||||
#
|
||||
# bash fdroid-update.sh <repo-root> <index-keystore> <key-alias> \
|
||||
# <keystore-pass-env> [<key-pass-env>]
|
||||
#
|
||||
# <repo-root> is the directory *containing* repo/ -- fdroidserver's own
|
||||
# layout, not repo/ itself. This script writes <repo-root>/config.yml itself
|
||||
# on every run (see below), so an existing one is overwritten; nothing else
|
||||
# under <repo-root> is touched beyond what `fdroid update` writes into repo/.
|
||||
#
|
||||
# <keystore-pass-env>/<key-pass-env> name environment variables holding the
|
||||
# passwords, the same convention as sign-apk.sh and for the same reason: an
|
||||
# argument is visible in the job log and in `ps`, a password must not be. If
|
||||
# <key-pass-env> is omitted, the keystore password is reused as the key
|
||||
# password.
|
||||
#
|
||||
# Needs no network (fdroid-repo-ci#1) -- it only reads repo/ and signs.
|
||||
# Pins fdroidserver, the JDK (fdroidserver shells out to jarsigner/keytool
|
||||
# for the index signature) and apksigner (for the per-entry v1 signature in
|
||||
# newer index formats) via with-nixpkgs.sh/nixpkgs-pin, next to this script.
|
||||
set -euo pipefail
|
||||
|
||||
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
if [ "$#" -lt 4 ]; then
|
||||
cat >&2 <<'USAGE'
|
||||
usage: fdroid-update.sh <repo-root> <index-keystore> <key-alias> \
|
||||
<keystore-pass-env> [<key-pass-env>] [extra fdroid update args...]
|
||||
USAGE
|
||||
exit 2
|
||||
fi
|
||||
|
||||
root=$1
|
||||
keystore=$2
|
||||
alias_name=$3
|
||||
ks_pass_env=$4
|
||||
shift 4
|
||||
|
||||
key_pass_env=$ks_pass_env
|
||||
if [ "$#" -gt 0 ] && [[ $1 != -* ]]; then
|
||||
key_pass_env=$1
|
||||
shift
|
||||
fi
|
||||
|
||||
extra_args=("$@")
|
||||
|
||||
[ -d "$root/repo" ] || { echo "fdroid-update.sh: no repo/ under $root" >&2; exit 2; }
|
||||
[ -f "$keystore" ] || { echo "fdroid-update.sh: no such keystore: $keystore" >&2; exit 2; }
|
||||
: "${!ks_pass_env:?fdroid-update.sh: environment variable $ks_pass_env (keystore password) is not set}"
|
||||
: "${!key_pass_env:?fdroid-update.sh: environment variable $key_pass_env (key password) is not set}"
|
||||
|
||||
# fdroidserver's config.yml supports "{env: VARNAME}" in place of any string
|
||||
# value specifically so a config that names a keystore and its passwords can
|
||||
# still be free of secrets -- fdroidserver's own common.py: "it is a good
|
||||
# idea to manage config.yml (WITHOUT PASSWORDS!) in git". Writing it here
|
||||
# means a caller never has to hand-write fdroidserver's config format, and
|
||||
# this repo -- not every consumer -- is the one place that has to track it if
|
||||
# it changes.
|
||||
cat > "$root/config.yml" <<EOF
|
||||
keystore: $keystore
|
||||
keystorepass:
|
||||
env: $ks_pass_env
|
||||
keypass:
|
||||
env: $key_pass_env
|
||||
repo_keyalias: $alias_name
|
||||
EOF
|
||||
chmod 600 "$root/config.yml"
|
||||
|
||||
echo "fdroid-update.sh: running fdroid update in $root"
|
||||
# fdroidserver locates a JDK by globbing standard system install locations
|
||||
# (or $JAVA_HOME) -- a nix store path matches neither, so JAVA_HOME has to be
|
||||
# set explicitly from whichever jarsigner this nix shell actually put on
|
||||
# PATH, rather than assumed.
|
||||
bash "$here/with-nixpkgs.sh" "fdroidserver jdk21 apksigner" bash -c '
|
||||
set -euo pipefail
|
||||
export JAVA_HOME="$(dirname "$(dirname "$(command -v jarsigner)")")"
|
||||
cd "$1"; shift
|
||||
exec fdroid update "$@"
|
||||
' bash "$root" "${extra_args[@]}"
|
||||
|
||||
echo "fdroid-update.sh: done; index at $root/repo/index-v2.json"
|
||||
Reference in New Issue
Block a user