halkyon/tool/with_signing_keys.sh
Tizian 「ティツィアーン」 90edf2ec84
All checks were successful
CI / check (push) Successful in 40m37s
CI / deploy (push) Successful in 56s
feat(release): the Forgejo token from Proton Pass as well
tool/with_signing_keys.sh reads the token releases publish with from its
login in the vault Infra and hands it over as HALKYON_FORGEJO_TOKEN, but
for a dry run, so a release asks for no secret at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 21:53:08 +01:00

112 lines
4.8 KiB
Shell
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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. Their passwords, each item's hidden
# field "Password", go to it in its environment (<keystore variable>_PASSWORD), so it
# asks for none, nor for the Forgejo token, a login's password in the vault Infra
# (HALKYON_FORGEJO_TOKEN); a dry run asks for no secret, and gets none.
#
# tool/with_signing_keys.sh <app> [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 their IDs,
# their attachments' names and, but for a dry run, the field "Password", which goes
# nowhere but into the release tool's environment.
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 <app> [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'
# The Forgejo token releases publish with (write:repository), a login's password in
# the vault Infra (its folder API Tokens), made for this laptop.
readonly token_vault='Infra'
readonly token_item='Forgejo Token halkyon_learn release signing script (melchior)'
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."
}
# The hidden field "Password" of the item titled $1.
password() {
local item="$1" value
value=$(pass-cli item view --vault-name "$vault" --item-title "$item" --output json |
jq -r '.item.content.extra_fields[]? | select(.name == "Password") | .content.Hidden // empty') ||
fail "Can't read the item \"$item\" in $vault (pass-cli login?)."
[ -n "$value" ] || fail "The item \"$item\" in $vault has no field Password."
printf '%s' "$value"
}
# The Forgejo token: the password of the login $token_item in $token_vault.
forgejo_token() {
local value
value=$(pass-cli item view --vault-name "$token_vault" --item-title "$token_item" --output json |
jq -r '.item.content.content.Login.password // empty') ||
fail "Can't read the item \"$token_item\" in $token_vault (pass-cli login?)."
[ -n "$value" ] || fail "The item \"$token_item\" in $token_vault has no password."
printf '%s' "$value"
}
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"
dry_run=false
for argument in "$@"; do
[ "$argument" = --dry-run ] && dry_run=true
done
if ! $dry_run; then
app_password=$(password "$app_item")
fdroid_password=$(password "$fdroid_item")
export "${app_variable}_PASSWORD=$app_password"
export HALKYON_FDROID_KEYSTORE_PASSWORD="$fdroid_password"
token=$(forgejo_token)
export HALKYON_FORGEJO_TOKEN="$token"
unset app_password fdroid_password token
fi
status=0
dart run tool/release.dart "$app" "$@" || status=$?
exit "$status"