#!/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 \ # [] # # is the directory *containing* repo/ -- fdroidserver's own # layout, not repo/ itself. This script writes /config.yml itself # on every run (see below), so an existing one is overwritten; nothing else # under is touched beyond what `fdroid update` writes into repo/. # # / 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 # 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 # 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 \ [] [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 < "$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"