feat(release): the Forgejo token from Proton Pass as well
All checks were successful
CI / check (push) Successful in 40m37s
CI / deploy (push) Successful in 56s

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>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-31 21:53:08 +01:00
commit 90edf2ec84
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
3 changed files with 22 additions and 3 deletions

View file

@ -82,7 +82,7 @@ Checklist after doing changes, in this order:
- Check `serverpod` MCP `tail_server_logs` and `tail_flutter_logs` for any issues.
- **Melos** (decided 2026-10-19, ADR 0015) runs scripts across the workspace's packages: a dev dependency of the root (`dart run melos …`, its version in the lock), its scripts under `melos:` in the root `pubspec.yaml`: `analyze`; `test:dart` (the packages without Flutter, one at a time, as each server's tests start a PostgreSQL of their own on the same port); `test:flutter` (`flutter test -x requires-server`, one package at a time); `generate` (Serverpod's code generation in every package with a `config/generator.yaml`, the CLI on the PATH); `check` (`tool/check.dart`). Every script takes Melos' filters: `dart run melos run test:flutter --scope=halkyon_app`, or `--diff=origin/main --include-dependents` for what changed and what depends on it; `dart run melos list` lists the packages. The root overrides `cli_util` to 0.5.2 for it: Learn's `flutter_launcher_icons` (0.14.4, its latest) asks for ^0.4.1 and uses only the logger, which 0.5 keeps; drop the override once that package allows 0.5.
- **Tools are Dart where they hold logic** (decided 2026-10-19): conditions, parsing, parallel work, anything worth a test, as `tool/check.dart` (its file selection in `tool/src/check_files.dart`, tested in the root's `test/`). **Bash for glue**, short and linear (`tool/pre-push`, `tool/check_signatures.sh`, `tool/workspace_only.sh`, which narrows the root pubspec's workspace to the packages an image builds), **and for everything run on the production host**, which has no Dart SDK and gets none (`deploy/redeploy.sh`, `deploy/backup.sh`). **Python only where a library exists only there** (the apps' icons with librsvg and Pillow, `tool/app_icons.py`, its Anki fixtures with Anki itself); Learn's data scripts and `packages/halkyon_app/tool/subdivision_names.py` need nothing but Python's standard library and may move to Dart when they next change. **Releases come from `tool/release.dart`** (decided 2026-10-19, ADR 0015), shared by the apps, each configured in Dart beside it (`apps/<id>/release.dart`, typed and commented, registered in `tool/src/release/apps.dart`): `dart run tool/release.dart learn [major | minor | patch] [--dry-run]`, on a developer's laptop, never in CI, through `tool/with_signing_keys.sh learn …` (Bash glue): the keystores are kept in Proton Pass alone (vault *Infra · Signing*, each a note's attachment, the password its hidden field), and it downloads the app's and the F-Droid index's into a folder in memory (`$XDG_RUNTIME_DIR`), hands their paths and, but for a dry run, their passwords (each item's hidden field *Password*, in the environment as `<keystore variable>_PASSWORD`, so the tool asks for neither) to the release tool and removes the files however the run ends (`test/release/signing_keys_test.dart` holds it to every app released). Tags are per app (`learn-v0.19.0`; an app's earlier tags, Learn's `v…`, count as its history), and only commits touching the app decide its version: its folder, the shared packages its packages use by path (transitively), the root pubspec and lock, its release files (key pins, F-Droid listing, snap recipe) and where its packages were before a move. Each release's *What's new* (Train's ADR 0030, D61), one file a language in the app's `assets/whats_new/` (`<version>.<language>.txt`, English and German), is drafted by it from the commits after *Go on?* and edited in `$VISUAL`/`$EDITOR` until it fits (no `TRANSLATE:` left, F-Droid's 500 characters), committed with the version, and is the app's sheet after the update, the Forgejo notes and F-Droid's changelog. `--dry-run` reads everything a release would (the version, the commits that count and what each touches, the steps, the notes it would publish), changes nothing, asks for no secret, and lists what a release would refuse. Its pure logic is in `tool/src/release/`, tested in the root's `test/release/`. `tool/fdroid_repo.sh` stays Bash (glue for fdroidserver). Build 21 (`v0.18.0`) was the last release of `tool/release.sh`, the Bash script it replaced, removed since.
- **Tools are Dart where they hold logic** (decided 2026-10-19): conditions, parsing, parallel work, anything worth a test, as `tool/check.dart` (its file selection in `tool/src/check_files.dart`, tested in the root's `test/`). **Bash for glue**, short and linear (`tool/pre-push`, `tool/check_signatures.sh`, `tool/workspace_only.sh`, which narrows the root pubspec's workspace to the packages an image builds), **and for everything run on the production host**, which has no Dart SDK and gets none (`deploy/redeploy.sh`, `deploy/backup.sh`). **Python only where a library exists only there** (the apps' icons with librsvg and Pillow, `tool/app_icons.py`, its Anki fixtures with Anki itself); Learn's data scripts and `packages/halkyon_app/tool/subdivision_names.py` need nothing but Python's standard library and may move to Dart when they next change. **Releases come from `tool/release.dart`** (decided 2026-10-19, ADR 0015), shared by the apps, each configured in Dart beside it (`apps/<id>/release.dart`, typed and commented, registered in `tool/src/release/apps.dart`): `dart run tool/release.dart learn [major | minor | patch] [--dry-run]`, on a developer's laptop, never in CI, through `tool/with_signing_keys.sh learn …` (Bash glue): the keystores are kept in Proton Pass alone (vault *Infra · Signing*, each a note's attachment, the password its hidden field), and it downloads the app's and the F-Droid index's into a folder in memory (`$XDG_RUNTIME_DIR`), hands their paths and, but for a dry run, their passwords (each item's hidden field *Password*, in the environment as `<keystore variable>_PASSWORD`, so the tool asks for neither) and the Forgejo token (a login in the vault *Infra*, `HALKYON_FORGEJO_TOKEN`) to the release tool and removes the files however the run ends (`test/release/signing_keys_test.dart` holds it to every app released). Tags are per app (`learn-v0.19.0`; an app's earlier tags, Learn's `v…`, count as its history), and only commits touching the app decide its version: its folder, the shared packages its packages use by path (transitively), the root pubspec and lock, its release files (key pins, F-Droid listing, snap recipe) and where its packages were before a move. Each release's *What's new* (Train's ADR 0030, D61), one file a language in the app's `assets/whats_new/` (`<version>.<language>.txt`, English and German), is drafted by it from the commits after *Go on?* and edited in `$VISUAL`/`$EDITOR` until it fits (no `TRANSLATE:` left, F-Droid's 500 characters), committed with the version, and is the app's sheet after the update, the Forgejo notes and F-Droid's changelog. `--dry-run` reads everything a release would (the version, the commits that count and what each touches, the steps, the notes it would publish), changes nothing, asks for no secret, and lists what a release would refuse. Its pure logic is in `tool/src/release/`, tested in the root's `test/release/`. `tool/fdroid_repo.sh` stays Bash (glue for fdroidserver). Build 21 (`v0.18.0`) was the last release of `tool/release.sh`, the Bash script it replaced, removed since.
## CI and deploys

View file

@ -58,5 +58,7 @@ void main() {
);
expect(releaser, contains("variable: 'HALKYON_FDROID_KEYSTORE_PASSWORD'"));
expect(script, contains(r'if ! $dry_run; then'));
expect(script, contains(r'export HALKYON_FORGEJO_TOKEN="$token"'));
expect(releaser, contains("variable: 'HALKYON_FORGEJO_TOKEN'"));
});
}

View file

@ -5,7 +5,8 @@
# 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; a dry run asks for no secret, and gets none.
# 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
@ -36,6 +37,10 @@ case "$app" in
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."
@ -73,6 +78,16 @@ 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"
@ -88,7 +103,9 @@ if ! $dry_run; then
fdroid_password=$(password "$fdroid_item")
export "${app_variable}_PASSWORD=$app_password"
export HALKYON_FDROID_KEYSTORE_PASSWORD="$fdroid_password"
unset app_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=$?