The published repo advertised fdroidserver's own demo values (repo.address "https://MyFirstFDroidRepo.org/fdroid/repo", repo.name "My First F-Droid Repo Demo") because fdroid-update.sh wrote config.yml itself but never set repo_url/repo_name/repo_description, so fdroidserver fell back to its template. The "Add to F-Droid" button on the generated index.html is built from repo_url, which is how bit noticed. A publish that succeeds while advertising the wrong URL is silently wrong -- worse than a failed job. Add FDROID_REPO_URL/FDROID_REPO_NAME/FDROID_REPO_DESCRIPTION environment variables (not new positionals -- the script already has five plus pass-through extra args). The first two are now a hard requirement, naming the missing one; FDROID_REPO_DESCRIPTION stays optional. This is a breaking change to a public composite action's interface, acceptable only because weblib-viewer is the sole consumer and is updated in the same change (weblib-viewer#26). Also discovered while wiring this up and worth recording since it contradicts the "no /fdroid/repo suffix" assumption in weblib's working notes: fdroidserver's own common.py unconditionally refuses to run once config.yml sets repo_url at all, unless it ends in the literal suffix "/repo" -- there is no flag to disable that check, and the value is embedded verbatim as repo.address. So FDROID_REPO_URL has to end in /repo, and publish.sh's existing <prefix> argument has to put the published tree under a URL path that actually ends in /repo to match, or fdroid update refuses to run at all (checked positive) / the address points at a path the bucket never serves (checked against the live fdroid-weblib-dev bucket layout). Extends test_fdroid_update.sh: config.yml carries the values, the resulting index-v2.json has the right repo.address/name/description (not fdroidserver's placeholder), a name with an embedded single quote survives the YAML round-trip, and the script fails when FDROID_REPO_URL or FDROID_REPO_NAME is absent or FDROID_REPO_URL lacks the /repo suffix. run_tests.sh: 104 passed / 4 skipped (was 98/4). Filed as fdroid-repo-ci#11. Co-authored-by: bit <bit@das-labor.org>
140 lines
6.1 KiB
Bash
Executable File
140 lines
6.1 KiB
Bash
Executable File
#!/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.
|
|
#
|
|
# FDROID_REPO_URL and FDROID_REPO_NAME (environment variables, not
|
|
# arguments -- they are not secrets, but they are metadata that belongs
|
|
# beside the other config.yml fields this script already writes, not a sixth
|
|
# and seventh positional) are REQUIRED and this script refuses to run
|
|
# without them. Omitting them used to mean fdroidserver silently fell back to
|
|
# its own placeholder template ("MyFirstFDroidRepo.org", "My First F-Droid
|
|
# Repo Demo") -- a repo that publishes successfully while advertising
|
|
# someone else's URL and name, which is worse than failing loudly.
|
|
# FDROID_REPO_DESCRIPTION is optional. This is a breaking change to this
|
|
# script's interface (fdroid-repo-ci#11); acceptable only because
|
|
# weblib-viewer is this action's sole consumer and is updated in the same
|
|
# change.
|
|
#
|
|
# FDROID_REPO_URL MUST end in the literal suffix "/repo" -- fdroidserver
|
|
# enforces this itself (common.py: "repo_url needs to end with /repo"),
|
|
# unconditionally, whenever config.yml sets repo_url at all, and refuses to
|
|
# run otherwise. It is not this script's own rule and there is no way to
|
|
# turn it off, so the caller's publish step has to serve the tree under a
|
|
# URL path actually ending in /repo -- e.g. `publish.sh`'s own <prefix>
|
|
# argument -- or the value embedded here (verbatim, as repo.address in
|
|
# index-v2.json) will not be where the index actually is.
|
|
#
|
|
# 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...]
|
|
|
|
required environment variables: FDROID_REPO_URL, FDROID_REPO_NAME
|
|
optional environment variable: FDROID_REPO_DESCRIPTION
|
|
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}"
|
|
|
|
# Hard-required: an unset repo_url/repo_name used to mean fdroidserver fell
|
|
# back to its own placeholder template, which is a *successful* publish that
|
|
# advertises the wrong repo -- silently wrong, the worst outcome. Failing
|
|
# loudly, naming the variable, is strictly better than defaulting.
|
|
: "${FDROID_REPO_URL:?fdroid-update.sh: environment variable FDROID_REPO_URL (canonical repo URL, no trailing slash) is not set}"
|
|
: "${FDROID_REPO_NAME:?fdroid-update.sh: environment variable FDROID_REPO_NAME (repo display name) is not set}"
|
|
|
|
# YAML single-quoted scalar: doubling an embedded single quote is the only
|
|
# escape it needs, and (unlike double-quoted) it does not interpret
|
|
# backslashes -- so this is safe for an arbitrary URL/name/description
|
|
# without having to know what characters fdroidserver's own parser accepts.
|
|
yaml_squote() {
|
|
local s=$1
|
|
s=${s//\'/\'\'}
|
|
printf "'%s'" "$s"
|
|
}
|
|
|
|
# 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 <<EOF
|
|
keystore: $keystore
|
|
keystorepass:
|
|
env: $ks_pass_env
|
|
keypass:
|
|
env: $key_pass_env
|
|
repo_keyalias: $alias_name
|
|
repo_url: $(yaml_squote "$FDROID_REPO_URL")
|
|
repo_name: $(yaml_squote "$FDROID_REPO_NAME")
|
|
EOF
|
|
if [ -n "${FDROID_REPO_DESCRIPTION:-}" ]; then
|
|
echo "repo_description: $(yaml_squote "$FDROID_REPO_DESCRIPTION")"
|
|
fi
|
|
} > "$root/config.yml"
|
|
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"
|