#!/usr/bin/env bash # Runs a release with its signing keys fetched from Proton Pass, where alone they are # kept (vault "Infra · Signing"): the app's Android keystore and the F-Droid index's, # each a note's attachment, downloaded into a folder in memory ($XDG_RUNTIME_DIR, a # tmpfs only the own user reads), handed to the release tool by the variables it reads # their paths from, and removed however the run ends. The keystores' passwords stay in # Pass: the release tool asks for them, as ever. # # tool/with_signing_keys.sh [release.dart's arguments] # tool/with_signing_keys.sh train minor --dry-run # # Needs pass-cli, logged in (pass-cli login), and jq. Reads of the items take only their # IDs and their attachments' names, never a field. set -euo pipefail cd "$(git rev-parse --show-toplevel)" readonly vault='Infra · Signing' fail() { echo "✗ $*" >&2 exit 1 } [ $# -ge 1 ] || fail 'Usage: tool/with_signing_keys.sh [release arguments]' app="$1" shift # Each app released (tool/src/release/apps.dart): the variable its keystore's path goes # in (its AndroidSigning's keystoreVariable) and the item keeping it. case "$app" in learn) app_variable=HALKYON_ANDROID_KEYSTORE app_item='Halkyon Learn – Android release keystore' ;; train) app_variable=HALKYON_TRAIN_ANDROID_KEYSTORE app_item='Halkyon Train – Android release keystore' ;; *) fail "No signing key known for $app." ;; esac # The F-Droid repository's index key, the same for every app listed there. readonly fdroid_item='Halkyon Learn – F-Droid index keystore' for tool in pass-cli jq; do command -v "$tool" >/dev/null || fail "$tool is missing." done [ -n "${XDG_RUNTIME_DIR:-}" ] && [ -d "$XDG_RUNTIME_DIR" ] || fail 'No $XDG_RUNTIME_DIR to keep the keys in memory.' keys=$(mktemp -d -p "$XDG_RUNTIME_DIR") trap 'rm -rf "$keys"' EXIT trap 'exit 130' INT TERM HUP # Downloads the attachment named $2 of the item titled $1 to $3. fetch() { local item="$1" name="$2" to="$3" ids ids=$(pass-cli item view --vault-name "$vault" --item-title "$item" --output json | jq -r --arg name "$name" \ '[.item.share_id, .item.id, (.attachments[] | select(.content.name == $name) | .id)] | select(length == 3) | @tsv') || fail "Can't read the item \"$item\" in $vault (pass-cli login?)." [ -n "$ids" ] || fail "The item \"$item\" in $vault has no attachment $name." local share item_id attachment IFS=$'\t' read -r share item_id attachment <<<"$ids" pass-cli item attachment download --share-id "$share" --item-id "$item_id" \ --attachment-id "$attachment" --output "$to" >/dev/null [ -s "$to" ] || fail "The attachment $name of \"$item\" came empty." } echo "▸ Fetching the signing keys from $vault" fetch "$app_item" android-release.p12 "$keys/android-release.p12" fetch "$fdroid_item" fdroid-index.p12 "$keys/fdroid-index.p12" export "$app_variable=$keys/android-release.p12" export HALKYON_FDROID_KEYSTORE="$keys/fdroid-index.p12" status=0 dart run tool/release.dart "$app" "$@" || status=$? exit "$status"