Commit graph halkyon/tool/src
Author SHA1 Message Date
4239a1de24
build(release): what's new drafted from the commits' Whats-New trailers, other apps' commits left out
The drafts came from the commits' subjects, which are written for
developers: each of the releases of 4 Nov 2026 (Todo 0.2.0, Train 0.3.0,
Learn 0.24.0) listed every commit since the last, Meditate's among them
where it touched a shared package, more than twice F-Droid's 500
characters, the German all still to translate, and each had to be written
again whole.

A commit people would notice now says so in its message's trailers,
`Whats-New: <sentence>` once a change and `Whats-New-De: <Satz>` in the
same order, and the draft is those lines alone, the features' first, a
missing translation marked TRANSLATE:. Only where no commit of the
release carries one does it fall back to the subjects. A commit whose
scope names apps (feat(todo,meditate): ...) counts for those alone, any
other for every app it touches. Over a running next.<language>.txt per
app, which parallel branches conflict on at every merge, and over an
assistant drafting at release time (a key, the commits sent to a
provider, a different text each run). ADR 0030, D61.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-05 08:58:26 +01:00
1ba532d6ef
build(check): the images' resolution replayed as the deploy builds them, before any push
All checks were successful
CI / check (push) Successful in 45m37s
CI / deploy (push) Successful in 5m23s
Only the deploy builds the images, on the server, after every check
has passed, so an image that pub refuses cost a push each time (twice
on 4 Nov 2026). A step of tool/check.dart, right after the packages,
replays each Dockerfile's build stage up to its `dart pub get` from
the working tree's files in an empty folder, with Dart alone at the
image's version (downloaded once into ~/.cache/halkyon-check/, its
SHA-256 checked), offline, in an environment holding nothing of the
machine's Dart or Flutter: `dart run` from Flutter sets DART_ROOT,
through which pub found Flutter and accepted what the image refuses.

Checked on both failed deploys' states, which fail it as they did,
and on the fix, which passes. --images-only runs it alone, in
seconds. The list of images, dartImages, moves into
tool/src/images.dart, and a test keeps every Dockerfile building with
Dart on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 13:43:51 +01:00
438ce20dbf
build: Android's built-in Kotlin, the three plugins applying KGP themselves patched in third_party/
AGP 9 compiles Kotlin itself (android.builtInKotlin=true in every app),
and warns that applying the Kotlin Gradle Plugin will soon fail.
flutter_web_auth_2 5.1.0, keyboard_height_plugin 0.3.0 and flutter_tts
4.2.5 still apply it unconditionally, so their pub releases are copied
into third_party/ with only their android/build.gradle changed, and
the root overrides them by path (third_party/README.md says when to
drop each). The servers' images copy third_party/, as pub refuses an
override whose path is missing. third_party/ isn't our own code: left
out of the analysis and the format check.

All four apps' APKs build without the warning. A Kotlin daemon's
session file that slipped into Train's android/ goes, and every app's
android/.gitignore keeps .kotlin/ out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:32:13 +01:00
8565db3d89
fix(release): every snap packed without --output, into its project, then moved
snapcraft builds in LXD with the project mounted at /root/project and refuses
any --output naming a folder of this machine: the root's own build/release/
for Learn's snap (found releasing Learn 0.23.0) as the clone itself for Todo's
and Train's (Todo 0.1.0, both 3 Nov 2026). Every snap now packs into its
project and is moved into the release's folder from there, checked with
snapcraft 9.2.1 at the root. A failure after none of several apps was done
now says "none" rather than nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-04 09:31:54 +01:00
f06fc23023
build(release): several apps released in one run (train:minor learn), their version commits pushed without the checks
All checks were successful
CI / check (push) Successful in 41m7s
CI / deploy (push) Successful in 1m2s
The release tool and tool/with_signing_keys.sh take several apps, each with its version
after a colon or none, and release them one after another, each as alone, stopping at
the first that fails; the key script fetches every named app's key.

The pre-push hook skips tool/check.dart when a push brings release commits alone: the
release tool's chore(release) subject, an app's pubspec changed in its version line and
its texts of what's new, nothing else (tool/release_push.dart). Signatures are checked as
before, and CI still runs the checks. A release no longer costs a run of the checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 12:16:10 +01:00
c8d94d0ceb
fix(release): a snap built in a clone of its own is packed without --output, into the clone
All checks were successful
CI / check (push) Successful in 40m26s
CI / deploy (push) Successful in 1m4s
snapcraft builds in LXD with the project mounted at /root/project and passes --output
on unchanged: naming the clone itself, it took the clone for a file in the folder above,
outside the project, and refused it again. Without --output it packs into the clone
(checked with snapcraft 9.2.1 in Todo's clone), from where the release tool moves it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 10:35:49 +01:00
34001b1aff
fix(release): a snap built in a clone of its own is packed inside it, then moved into the release's folder
All checks were successful
CI / check (push) Successful in 39m47s
CI / deploy (push) Successful in 59s
snapcraft packs nowhere outside the project it builds, so Todo 0.1.0's release stopped
after its snap was built ("Cannot create packages outside of the project tree").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-03 09:21:34 +01:00
e61d3abb4f
build(todo): Todo deployable beside Learn's server, and released as an APK, on F-Droid and as a snap (ADR 0033, step 5)
deploy/compose.todo.yaml adds Todo's server at todo.halkyon.eu, a member
of Learn's register, its database halkyon_todo in the instance's
PostgreSQL, its image built as Train's. redeploy.sh and backup.sh go over
every app server .env switches on (train, todo). Each compose file names
its server to the register in a variable of its own
(INSTANCE_MEMBER_TRAIN, INSTANCE_MEMBER_TODO), which halkyon_base adds to
INSTANCE_MEMBERS, each address once, as a second compose file's default
would have replaced Train's.

apps/todo/release.dart configures the release tool for Todo's APK and
snap: a key of its own (pinned once made; until then nothing is
released), the F-Droid listing (Time, NonFreeNet), and the snap from
apps/todo/snap/, which the tool builds in a clone of the repository at the
tag with that folder in place of snap/, as snapcraft builds one recipe a
folder and the root's is Learn's. What's new for 0.1.0 in English and
German, now shown. Android's backup and transfer are off, as Train's
(D53): the reminders scheduled keep their tasks' titles unsealed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-02 16:04:00 +01:00
d981747728
fix(release): F-Droid's upload resumable alone, by content, and Forgejo asked again
Train's first release (31 Oct 2026) stopped twice after its tag was pushed:

- Forgejo answered 500 (a unique constraint) to the release: it enters a
  pushed tag in its releases itself, a moment after the push. Asked again a
  few seconds later, it makes the release of that entry.
- The F-Droid upload filled the halkyon.app server's disk: rsync compared by
  time, and this laptop's working folder, restored from the repository, had
  every APK anew, so all 1.9 GB went again. It compares by content now.

tool/with_signing_keys.sh fdroid [APK] runs tool/fdroid_repo.sh alone with
the index's key, for an upload that failed after its release was made, and
the script skips an APK the working folder holds already rather than adding
a second file of the same build, which fdroid refuses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 23:28:49 +01:00
7aa8ec7304
feat(release): the keystores' passwords from Proton Pass too, and F-Droid's tools checked first
All checks were successful
CI / check (push) Successful in 38m54s
CI / deploy (push) Successful in 56s
tool/with_signing_keys.sh reads each keystore's password from its item's
hidden field "Password" and hands it to the release tool in its
environment (<keystore variable>_PASSWORD), but for a dry run, which asks
for no secret; the release tool takes a secret from there where it is
set, else asks as before (the Forgejo token from HALKYON_FORGEJO_TOKEN).
Typing them guarded nothing the Pass session didn't already open.

The release tool checks fdroid, rsync, curl and jq with the other tools,
so a dry run says when one is missing, rather than the F-Droid script
after the passwords.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 20:51:21 +01:00
50c0d28194
fix(release): the dry run goes on without snapcraft rather than crashing
All checks were successful
CI / check (push) Successful in 38m27s
CI / deploy (push) Successful in 56s
Where snapcraft is missing, the release is refused already; asking it for
its login then threw, so the dry run stopped before listing the rest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 19:36:35 +01:00
7727317c68
feat(app): what's new after an update, drafted by the release tool (ADR 0030, D61)
halkyon_app's WhatsNew reads what each release changed from the app's
assets/whats_new/ (<version>.<language>.txt, English and German, a change
a line): after an update a sheet lists every release since the one the
device showed last, newest first, once; Settings shows the running
version's again, above About. Nothing on a fresh install.

tool/release.dart drafts the missing texts from the commits that count
after "Go on?" (German marked TRANSLATE:), opens them in the editor until
they fit (no mark left, F-Droid's 500 characters) and commits them with
the version. The English is the Forgejo notes' What's new, each language
F-Droid's changelog of the build, written at publishing and copied in by
tool/fdroid_repo.sh. Learn and Train bundle their texts, Learn's 0.22.0
and Train's 0.1.0 written to start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-31 19:36:20 +01:00
48951c1e1a
build(train): Train is released as Learn is, with a key of its own, beside it in the F-Droid repository (ADR 0030, D58)
apps/train/release.dart configures the release tool for Train's APK on
train.halkyon.eu; its release builds sign with its own key when the tool
hands it over. Its F-Droid listing declares NonFreeDep for Polar's SDK and
NonFreeNet; the repository's script copies each app's icon. The key's
certificate is pinned once the key is made.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-30 17:05:10 +01:00
3def5126a3
build(check): the checks compare the generated code, as CI did alone
On 23 Oct a push went red in CI because an endpoint's changed comment
hadn't been generated into the module's client: only CI compared the
committed generated code with the generator's output. tool/check.dart now
runs every server's generation (Melos' `generate`, about 15 s) after
fetching the packages and fails, naming the files, where the working
tree's generated code changed by it; the files stay written anew, to be
committed. It compares the files that differ from the committed ones
before and after by their content, so uncommitted changes count as they
do everywhere else in the checks.

It refuses a Serverpod CLI at another version than the lock holds
Serverpod at (`dart install serverpod_cli@<version>`), and the root's
tests hold CI's SERVERPOD_VERSION to the lock. CI keeps installing the
CLI at that version and puts it on the job's PATH; its own comparison
step goes, as the checks run it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-23 17:48:31 +02:00
bc499713b8
feat(crypto): SRP-6a, the register's half and the device's
ADR 0017's step 1. Generic over group and hash (SrpGroup); production
takes RFC 5054's 3072-bit group with SHA-256. k = H(N | PAD(g)),
u = H(PAD(A) | PAD(B)), K = H(PAD(S)),
M1 = H(H(N) xor H(g) | H(I) | s | PAD(A) | PAD(B) | K),
M2 = H(PAD(A) | M1 | K). A and B must lie in 1..N-1 and u must not be
0; a and b are 256 bits from Random.secure(); proofs are compared in
constant time. x comes from the caller (halkyon_keys makes it); RFC
5054's own x is there for the vectors only.

- The register's half in halkyon_base_server (base.dart: srpChallenge,
  srpVerify), the device's written by hand in halkyon_base_client
  (srp.dart: SrpClient, srpVerifier). Serverpod's generator cleans and
  writes only a client's lib/src/protocol/ (serverpod_cli 4.0.4's
  _getDirectoriesRequiringCleaning; a generate leaves srp/ untouched).
- Tests: RFC 5054 Appendix B on both halves; the client against the
  register, succeeding and every refusal.
- The module's tests depend on its client, and pub resolves every
  member's dev dependencies, so Learn's server image takes the module's
  client into its workspace too.
- The format check covers the hand-written half.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-22 18:11:08 +02:00
bf20134819
fix(tool): the check limited to changes sees uncommitted ones too
Melos' --diff compares commits alone (<commit>...HEAD), so a check with
--changed-since before committing tested nothing and passed. The check
now works out the packages touched itself, from git diff against the
commit and the files new to git, each file counted in its innermost
package, and hands them to Melos as --scope with --include-dependents;
a changed root pubspec, lock or lint rule means every package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-22 18:11:05 +02:00
e65f61bdd1
build(release): a Dart release tool for every app
tool/release.dart releases one app: `dart run tool/release.dart learn
[major | minor | patch] [--dry-run]`, each app configured in Dart beside
it (apps/learn/release.dart). Every step and check of tool/release.sh
is kept: the repository as pushed, resuming after a release commit and
tag, Flutter as CI pins it, the tools, the key and its certificate pin,
secrets asked for without echo and never written down, CI green on the
newest commit, the signed release commit and tag by a key deploys
accept, the APK built from a checkout of the tag and checked, the snap,
the signed checksums, pushing only after both builds passed, the
release on Forgejo, the F-Droid repository, the Snap Store once a
channel is set.

New: tags per app (learn-v0.19.0, Learn's v… counting as its
history); only commits touching the app decide its version (its folder,
the shared packages it uses by path, the root pubspec and lock, its
release files, where its packages were before the move); --dry-run
reads all of it, lists every step and the notes, and what a release
would refuse, changing nothing. The pure parts are tested in
test/release/. Learn releases build 21 with tool/release.sh, then
switches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-22 18:11:05 +02:00
af1dbee509
build: Melos, a Dart check and every path for the single repository
- Melos 8.9 as the root's dev dependency, with scripts across the
  workspace (analyze, test:dart, test:flutter, generate, check) and its
  filters (--diff, --include-dependents); cli_util overridden to 0.5.2
  for it, since Learn's flutter_launcher_icons asks for 0.4 and uses only
  its logger, unchanged in 0.5.
- tool/check.dart in place of tool/check.sh: packages, formatting of our
  own files, analysis, the tools' tests, then every package's tests
  through Melos (--changed-since=<commit> for only what changed and what
  depends on it). tool/pre-push is the hook: the signature check, then
  the checks. CI generates every server's code and runs the same check.
- The images build from the root with a workspace narrowed to what they
  need (tool/workspace_only.sh); compose, the deploy's migration test,
  the snap, the release script, the editor's launch files and Learn's
  asset tools (run from apps/learn) follow the new layout.
- The project's source is named as project-halkyon/halkyon (source link,
  snap, F-Droid listing, release uploads); the AGPL packages carry the
  licence's own text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-22 18:11:04 +02:00