build(train): Train on the desktop as a snap, to the Store's edge channel with each release (ADR 0030, D62)

Built as Todo's in a clone of its own, from apps/train/snap/, for train.halkyon.eu; its
listing says the desktop shows, plans and keeps the statistics, and records nothing.
The snap tests now hold every snap built in a clone of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-03 11:48:19 +01:00
commit 551a29445f
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 231 additions and 53 deletions

File diff suppressed because one or more lines are too long

View file

@ -32,4 +32,13 @@ const trainRelease = AppRelease(
indexCertificateFile: 'tool/release/fdroid-index-certificate.sha256',
repositoryUrl: 'https://halkyon.app/fdroid/repo',
),
// The desktop's Train (ADR 0030, D62), built in a clone of its own as
// Todo's (`SnapRelease.projectPath`), the root's snap/ being Learn's; to the
// Store's edge channel, its name registered on 3 Oct 2026.
snap: SnapRelease(
name: 'halkyon-train',
part: 'halkyon-train',
directory: 'apps/train/snap',
storeChannel: 'edge',
),
);

View file

@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=Halkyon Train
Comment=Your workouts, routes and training plans, sealed and synced
Exec=halkyon-train
Icon=${SNAP}/meta/gui/halkyon-train.png
Terminal=false
Categories=Utility;
StartupWMClass=app.halkyon.train

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -0,0 +1,144 @@
# Halkyon Train's Linux app as a Snap (decided 3 Nov 2026, ADR 0030, D62), built in LXD
# by the release tool (`dart run tool/release.dart train`) as Todo's is: snapcraft
# builds one recipe a folder, from its snap/snapcraft.yaml, and the root's snap/ is
# Learn's, so the tool builds this one in a clone of the repository at the release's
# tag with this folder in place of the clone's snap/ (`SnapRelease.projectPath`), but
# for snap/local/, which the recipes share. By hand, the same: in such a clone,
# `snapcraft pack`.
#
# It builds what is committed (the source is the clone's HEAD, the tag), never a
# working tree with its build caches, and always for train.halkyon.eu. Flutter is
# pinned like the local and CI one (FLUTTER_VERSION in .forgejo/workflows/ci.yaml),
# spelled out as in Learn's recipe, which says why.
name: halkyon-train
title: Halkyon Train
base: core24
adopt-info: halkyon-train # the version, from the app's pubspec.yaml
summary: Your workouts, routes and training plans, sealed and synced
description: |
Halkyon Train on the desktop: your runs, walks, hikes, rides and rows with
their maps, splits, heart rate and power, the routes you plan on the map,
turn by turn, your strength plans with the equipment at each place and the
plates to load, and the statistics of your weeks and months.
Recording, the Polar Loop and Health Connect are the phone's (Halkyon Train
for Android); what the phone records shows here, synced.
Everything is sealed on your device with your account's keys before it
reaches the instance, which can't read it. You sign in with SimpleLogin, by
Proton, or with an email address, a password and an authenticator app. It
connects to train.halkyon.eu, the project's instance, or to any other:
everything an instance needs is in the repository, to run your own. The app
is AGPL-3.0 and the server SSPL-1.0, so all of its code is public.
Questions and reports: support@halkyon.app
license: AGPL-3.0-or-later
source-code: https://code.halkyon.app/project-halkyon/halkyon
# The project's, not an instance's: the app works with any instance.
website: https://halkyon.app/train
# No contact or issues, as Learn's (its recipe says why): the address is in the
# description.
# The app's icon with rounded corners, made by the repository's tool/app_icons.py; also
# the desktop file's.
icon: snap/gui/halkyon-train.png
grade: stable
confinement: strict
platforms:
amd64:
apps:
halkyon-train:
command: bin/halkyon_train_flutter
# How the session is kept: see snap/local/secret-backend (Learn's, kept in the
# clone).
command-chain: [bin/secret-backend]
# GTK, WebKitGTK (the sign-in's page) and libsecret come from the GNOME runtime;
# files (GPX, TCX and FIT in and out) and the browser go through the desktop's
# portals.
extensions: [gnome]
plugs:
- network
# SimpleLogin's sign-in comes back on a loopback address the app listens on.
- network-bind
# Never connected by itself; whoever connects it by hand gets the keyring.
- password-manager-service
# As Learn's: the sync's connection ended at once when the network goes;
# not connected until the Store grants it.
- network-manager-observe
parts:
flutter:
plugin: nil
source: https://github.com/flutter/flutter.git
source-tag: '3.47.8'
source-depth: 1
build-packages:
- clang
- cmake
- curl
- git
- libgtk-3-dev
- lld
- llvm-18
- ninja-build
- pkg-config
- unzip
- xz-utils
- zip
override-build: |
mkdir -p "$CRAFT_PART_INSTALL/usr/libexec" "$CRAFT_PART_INSTALL/usr/bin"
cp -r "$CRAFT_PART_SRC" "$CRAFT_PART_INSTALL/usr/libexec/flutter"
ln -s ../libexec/flutter/bin/flutter "$CRAFT_PART_INSTALL/usr/bin/flutter"
ln -s ../libexec/flutter/bin/dart "$CRAFT_PART_INSTALL/usr/bin/dart"
"$CRAFT_PART_INSTALL/usr/bin/flutter" --disable-analytics
"$CRAFT_PART_INSTALL/usr/bin/flutter" precache --linux
# Needed to build, not in the Snap.
override-prime: ''
halkyon-train:
after: [flutter]
plugin: nil
source: .
source-type: git
# sodium's build hook compiles libsodium with its configure script and make
# (ADR 0017), with the Flutter part's clang.
build-packages:
- make
override-build: |
set -eu
export PATH="$CRAFT_STAGE/usr/bin:$PATH"
cd apps/train/halkyon_train_flutter
craftctl set version="$(sed -n 's/^version: *\([^+]*\).*/\1/p' pubspec.yaml)"
flutter pub get --enforce-lockfile
flutter build linux --release \
--dart-define=SERVER_URL=https://train.halkyon.eu/
mkdir -p "$CRAFT_PART_INSTALL/bin"
cp -r build/linux/x64/release/bundle/. "$CRAFT_PART_INSTALL/bin/"
secret-backend:
plugin: dump
source: snap/local
organize:
secret-backend: bin/secret-backend
# As Learn's: every file the GNOME runtime or the base has at the same path
# goes, so that nothing staged hides the runtime's own (Learn's recipe says
# what that broke there). Train stages no package today; the part keeps a
# later one from doing so unnoticed.
cleanup:
after: [halkyon-train, secret-backend]
plugin: nil
build-snaps: [core24, gnome-46-2404]
override-prime: |
set -eu
for snap in core24 gnome-46-2404; do
cd "/snap/$snap/current/usr"
find . \( -type f -o -type l \) -print0 |
while IFS= read -r -d '' path; do
case "$path" in */blas/* | */lapack/*) continue ;; esac
target="$CRAFT_PRIME/usr/$path"
if [ -L "$target" ] || [ -f "$target" ]; then
rm -f "$target"
fi
done
done