feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
#!/usr/bin/env bash
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
# Releases Halkyon Learn. It works out the next version from the commits since the
|
|
|
|
|
# last release, writes it into the app's pubspec in a signed commit with a signed tag,
|
|
|
|
|
# builds the Android APK (one for every phone, signed with the app's key) and the
|
|
|
|
|
# Linux snap, both connecting to learn.halkyon.eu, the project's instance, checks
|
|
|
|
|
# them, signs a list of their checksums with GPG, pushes, and publishes on every
|
|
|
|
|
# channel: a release on code.halkyon.app (APK, snap, checksums), the APK in the
|
|
|
|
|
# F-Droid repository at halkyon.app (tool/fdroid_repo.sh), and, once switched on
|
|
|
|
|
# below, the snap in the Snap Store. Run on a developer's laptop, never in CI: the
|
|
|
|
|
# app's key never leaves it.
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
#
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
# tool/release.sh the version the commits call for
|
|
|
|
|
# tool/release.sh minor or major, or patch: that one instead
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
#
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
# Versions follow semantic versioning, <major>.<minor>.<patch>, and a build number
|
|
|
|
|
# (+N) that rises by one with every release: Android installs only a higher one over
|
|
|
|
|
# an older, and the server tells apps below the build it supports to update. Since
|
|
|
|
|
# the last release, a commit typed feat raises the minor number, fix or perf the
|
|
|
|
|
# patch number, and a breaking one (feat!: or a BREAKING CHANGE: line) the major
|
|
|
|
|
# number from 1.0 on, the minor one before. Other commits (docs, test, chore, ...)
|
|
|
|
|
# release nothing by themselves. The first release is the version the pubspec holds.
|
|
|
|
|
# Raising minimumAppBuild in the server, for changes older apps can't follow, stays a
|
|
|
|
|
# decision of its own.
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
#
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
# The script shows the version and its commits and asks before it changes anything.
|
|
|
|
|
# It needs main clean and as pushed, CI green on its newest commit, and asks for the
|
|
|
|
|
# app key's password, the F-Droid repository key's password and a Forgejo token
|
|
|
|
|
# (scope write:repository) first; then it runs on its own until GPG asks for its
|
|
|
|
|
# passphrase (and ssh for the upload key's, unless an agent holds them). None of
|
|
|
|
|
# them is ever written down. Nothing is pushed or published before both builds have
|
|
|
|
|
# passed their checks; a run that stopped after making the release commit and tag
|
|
|
|
|
# carries on with them when started again.
|
|
|
|
|
#
|
|
|
|
|
# The app's key is a PKCS #12 keystore at $HALKYON_ANDROID_KEYSTORE (default below),
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
# alias $HALKYON_ANDROID_KEY_ALIAS. Its certificate's SHA-256 is committed in
|
|
|
|
|
# tool/release/android-certificate.sha256, and nothing signed otherwise is released.
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
cd "$(git rev-parse --show-toplevel)"
|
|
|
|
|
|
|
|
|
|
readonly server_url='https://learn.halkyon.eu/'
|
|
|
|
|
readonly forgejo='https://code.halkyon.app'
|
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-19 20:39:09 +02:00
|
|
|
readonly repository='project-halkyon/halkyon'
|
|
|
|
|
readonly pubspec='apps/learn/halkyon_learn_flutter/pubspec.yaml'
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
readonly certificate_file='tool/release/android-certificate.sha256'
|
feat: an F-Droid repository at learn.halkyon.app
Releases also publish the APK in the project's own F-Droid repository,
https://learn.halkyon.app/fdroid/repo. tool/fdroid_repo.sh adds the APK,
signs the indexes with the repository's own key and uploads them to
ramiel as the user fdroid, whose key can only write into the folder
Caddy serves (rrsync), the APKs first and the indexes last.
tool/release.sh checks that key and its password before building and
publishes after the Forgejo release.
- tool/release/fdroid/ holds the repository's settings (Halkyon, the
snap's icon) and the app's listing (the snap's text), with the
anti-features TetheredNet (learn.halkyon.eu only, for now) and
NonFreeNet (YouTube, the server's SSPL) declared with their reasons,
in F-Droid's own definitions and with its icons (fdroiddata, AGPL v3).
- The index key's certificate is pinned: a new key would make the F-Droid
app refuse the repository.
- fdroidserver v2.4.5, since Ubuntu's v2.2.1 knows neither TetheredNet
nor reasons.
README and AGENTS.md describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 20:25:02 +02:00
|
|
|
readonly fdroid_certificate_file='tool/release/fdroid-index-certificate.sha256'
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
# The Snap Store channel the snap goes to, e.g. edge; empty: not to the Store yet,
|
|
|
|
|
# only attached to the release on code.halkyon.app.
|
|
|
|
|
readonly snap_store_channel=''
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
keystore="${HALKYON_ANDROID_KEYSTORE:-$HOME/.local/share/halkyon-learn/android-release.p12}"
|
|
|
|
|
readonly key_alias="${HALKYON_ANDROID_KEY_ALIAS:-halkyon-learn}"
|
|
|
|
|
|
|
|
|
|
fail() {
|
|
|
|
|
echo "✗ $*" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
step() { echo "▸ $*"; }
|
|
|
|
|
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
[ $# -le 1 ] || fail 'Usage: tool/release.sh [major | minor | patch]'
|
|
|
|
|
chosen="${1:-}"
|
|
|
|
|
case "$chosen" in
|
|
|
|
|
'' | major | minor | patch) ;;
|
|
|
|
|
*) fail "$chosen is none of major, minor, patch." ;;
|
|
|
|
|
esac
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
step 'Checking the repository'
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
[ -z "$(git status --porcelain)" ] || fail 'The working tree has changes.'
|
|
|
|
|
[ "$(git symbolic-ref --quiet --short HEAD)" = main ] || fail 'Not on main.'
|
|
|
|
|
git fetch --quiet origin main
|
|
|
|
|
declared=$(sed -n 's/^version: *//p' "$pubspec")
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
current="${declared%%+*}"
|
|
|
|
|
current_build="${declared#*+}"
|
|
|
|
|
[[ "$current" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ && "$current_build" =~ ^[0-9]+$ ]] ||
|
|
|
|
|
fail "$pubspec's version isn't <major>.<minor>.<patch>+<build>: $declared"
|
|
|
|
|
|
|
|
|
|
# A run that stopped after its release commit and tag: HEAD is that tag, as pushed
|
|
|
|
|
# or one commit ahead of origin/main.
|
|
|
|
|
resumed=$(git describe --exact-match --tags --match 'v[0-9]*' HEAD 2>/dev/null || true)
|
|
|
|
|
if [ -n "$resumed" ]; then
|
|
|
|
|
git merge-base --is-ancestor origin/main HEAD ||
|
|
|
|
|
fail 'main and origin/main have gone apart.'
|
|
|
|
|
[ "$(git rev-list --count origin/main..HEAD)" -le 1 ] ||
|
|
|
|
|
fail "main is more than the release commit ahead of origin/main."
|
|
|
|
|
[ -z "$chosen" ] || fail "HEAD is $resumed already; carrying on with it takes no argument."
|
|
|
|
|
[ "$resumed" = "v$current" ] || fail "HEAD is $resumed, but $pubspec says $current."
|
|
|
|
|
readonly tag="$resumed" version="$current" build="$current_build"
|
|
|
|
|
previous=$(git describe --tags --abbrev=0 --match 'v[0-9]*' "$tag^" 2>/dev/null || true)
|
|
|
|
|
echo " Carrying on with $tag (build $build)."
|
|
|
|
|
else
|
|
|
|
|
[ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ] ||
|
|
|
|
|
fail 'main differs from origin/main: pull or push first.'
|
|
|
|
|
previous=$(git describe --tags --abbrev=0 --match 'v[0-9]*' 2>/dev/null || true)
|
|
|
|
|
if [ -z "$previous" ]; then
|
|
|
|
|
# The first release: the version the pubspec holds.
|
|
|
|
|
[ -z "$chosen" ] || fail 'The first release is the version the pubspec holds.'
|
|
|
|
|
version="$current"
|
|
|
|
|
build="$current_build"
|
|
|
|
|
kind='the first release'
|
|
|
|
|
else
|
|
|
|
|
[ "$previous" = "v$current" ] ||
|
|
|
|
|
fail "$pubspec says $current, but the last release is $previous."
|
|
|
|
|
subjects=$(git log --format='%s' "$previous..HEAD")
|
|
|
|
|
bodies=$(git log --format='%B' "$previous..HEAD")
|
|
|
|
|
typed='^[a-z]+(\([^)]*\))?'
|
|
|
|
|
if [ -n "$chosen" ]; then
|
|
|
|
|
bump="$chosen"
|
|
|
|
|
elif grep -qE "$typed!:" <<<"$subjects" || grep -q '^BREAKING CHANGE:' <<<"$bodies"; then
|
|
|
|
|
bump='breaking'
|
|
|
|
|
elif grep -qE '^feat(\([^)]*\))?:' <<<"$subjects"; then
|
|
|
|
|
bump='minor'
|
|
|
|
|
elif grep -qE '^(fix|perf)(\([^)]*\))?:' <<<"$subjects"; then
|
|
|
|
|
bump='patch'
|
|
|
|
|
else
|
|
|
|
|
echo "Nothing to release: no feat, fix or perf commit since $previous."
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
IFS=. read -r major minor patch <<<"$current"
|
|
|
|
|
if [ "$bump" = breaking ]; then
|
|
|
|
|
# Before 1.0, a breaking change raises the minor number.
|
|
|
|
|
if [ "$major" -ge 1 ]; then bump='major'; else bump='minor'; fi
|
|
|
|
|
fi
|
|
|
|
|
case "$bump" in
|
|
|
|
|
major) version="$((major + 1)).0.0" ;;
|
|
|
|
|
minor) version="$major.$((minor + 1)).0" ;;
|
|
|
|
|
patch) version="$major.$minor.$((patch + 1))" ;;
|
|
|
|
|
esac
|
|
|
|
|
build=$((current_build + 1))
|
|
|
|
|
kind="a $bump release"
|
|
|
|
|
fi
|
|
|
|
|
readonly tag="v$version" version build
|
|
|
|
|
git rev-parse --quiet --verify "refs/tags/$tag" >/dev/null &&
|
|
|
|
|
fail "The tag $tag exists already."
|
|
|
|
|
|
|
|
|
|
echo " Release $tag (build $build), $kind${previous:+, since $previous}."
|
|
|
|
|
if [ -n "$previous" ]; then
|
|
|
|
|
git log --format=' %s' "$previous..HEAD" | grep -E '^ [a-z]+(\([^)]*\))?!?:' |
|
|
|
|
|
grep -vE '^ chore\(release\):' || true
|
|
|
|
|
fi
|
|
|
|
|
read -rp 'Go on? [y/N] ' answer
|
|
|
|
|
[ "$answer" = y ] || [ "$answer" = Y ] || fail 'Stopped; nothing changed.'
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
pinned=$(sed -n 's/^ *FLUTTER_VERSION: *//p' .forgejo/workflows/ci.yaml)
|
|
|
|
|
running=$(flutter --version --machine | sed -n 's/^ *"frameworkVersion": *"\([^"]*\)".*/\1/p')
|
|
|
|
|
[ "$running" = "$pinned" ] || fail "Flutter is $running here, CI pins $pinned."
|
|
|
|
|
|
|
|
|
|
step 'Checking the tools'
|
|
|
|
|
for tool in snapcraft unsquashfs gpg curl jq sha256sum unzip; do
|
|
|
|
|
command -v "$tool" >/dev/null || fail "$tool is missing."
|
|
|
|
|
done
|
|
|
|
|
sdk="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}}"
|
|
|
|
|
build_tools=$(find "$sdk/build-tools" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort -V | tail -n 1)
|
|
|
|
|
[ -n "$build_tools" ] || fail "No Android build tools in $sdk (ANDROID_HOME)."
|
|
|
|
|
readonly apksigner="$build_tools/apksigner" aapt2="$build_tools/aapt2"
|
|
|
|
|
# keytool, and the java apksigner runs on, come with a JDK: the one on the PATH, else
|
|
|
|
|
# the one Android Studio brings.
|
|
|
|
|
jdk_bin=$(dirname "$(command -v java || echo /snap/android-studio/current/jbr/bin/java)")
|
|
|
|
|
[ -x "$jdk_bin/java" ] && [ -x "$jdk_bin/keytool" ] || fail 'No JDK found (java, keytool).'
|
|
|
|
|
export PATH="$jdk_bin:$PATH"
|
|
|
|
|
readonly keytool="$jdk_bin/keytool"
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
if [ -n "$snap_store_channel" ]; then
|
|
|
|
|
account=$(snapcraft whoami 2>/dev/null | sed -n 's/^username: *//p' || true)
|
|
|
|
|
[ -n "$account" ] || fail 'Not logged in to the Snap Store: snapcraft login'
|
|
|
|
|
fi
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
|
|
|
|
|
[ -f "$certificate_file" ] || fail "$certificate_file is missing."
|
|
|
|
|
certificate=$(grep -v '^#' "$certificate_file" | tr -d ' :\n' | tr 'A-F' 'a-f' || true)
|
|
|
|
|
readonly certificate
|
|
|
|
|
[[ "$certificate" =~ ^[0-9a-f]{64}$ ]] || fail "$certificate_file holds no SHA-256."
|
|
|
|
|
[ -f "$keystore" ] || fail "No keystore at $keystore (HALKYON_ANDROID_KEYSTORE)."
|
|
|
|
|
keystore=$(realpath "$keystore")
|
|
|
|
|
readonly keystore
|
|
|
|
|
|
|
|
|
|
step 'Asking for the secrets'
|
|
|
|
|
read -rsp "Password of $keystore: " password
|
|
|
|
|
echo
|
|
|
|
|
key_certificate=$(HALKYON_ANDROID_KEYSTORE_PASSWORD="$password" "$keytool" -list -v \
|
|
|
|
|
-keystore "$keystore" -alias "$key_alias" \
|
|
|
|
|
-storepass:env HALKYON_ANDROID_KEYSTORE_PASSWORD 2>/dev/null |
|
|
|
|
|
sed -n 's/^.*SHA256: *//p' | head -n 1 | tr -d ':' | tr 'A-F' 'a-f' || true)
|
|
|
|
|
[ -n "$key_certificate" ] || fail "Wrong password, or no key $key_alias in $keystore."
|
|
|
|
|
[ "$key_certificate" = "$certificate" ] ||
|
|
|
|
|
fail "The key $key_alias in $keystore isn't the one in $certificate_file."
|
|
|
|
|
|
feat: an F-Droid repository at learn.halkyon.app
Releases also publish the APK in the project's own F-Droid repository,
https://learn.halkyon.app/fdroid/repo. tool/fdroid_repo.sh adds the APK,
signs the indexes with the repository's own key and uploads them to
ramiel as the user fdroid, whose key can only write into the folder
Caddy serves (rrsync), the APKs first and the indexes last.
tool/release.sh checks that key and its password before building and
publishes after the Forgejo release.
- tool/release/fdroid/ holds the repository's settings (Halkyon, the
snap's icon) and the app's listing (the snap's text), with the
anti-features TetheredNet (learn.halkyon.eu only, for now) and
NonFreeNet (YouTube, the server's SSPL) declared with their reasons,
in F-Droid's own definitions and with its icons (fdroiddata, AGPL v3).
- The index key's certificate is pinned: a new key would make the F-Droid
app refuse the repository.
- fdroidserver v2.4.5, since Ubuntu's v2.2.1 knows neither TetheredNet
nor reasons.
README and AGENTS.md describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 20:25:02 +02:00
|
|
|
read -rsp "Password of the F-Droid repository's key: " HALKYON_FDROID_KEYSTORE_PASSWORD
|
|
|
|
|
echo
|
|
|
|
|
export HALKYON_FDROID_KEYSTORE_PASSWORD
|
|
|
|
|
tool/fdroid_repo.sh --check
|
|
|
|
|
fdroid_fingerprint=$(grep -v '^#' "$fdroid_certificate_file" | tr -d ' :\n' | tr 'a-f' 'A-F')
|
|
|
|
|
readonly fdroid_fingerprint
|
|
|
|
|
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
read -rsp "Forgejo token for $forgejo (write:repository): " token
|
|
|
|
|
echo
|
|
|
|
|
# The token goes to curl in a configuration read from a file descriptor, so it never
|
|
|
|
|
# shows in the list of processes as an argument would.
|
|
|
|
|
forgejo_api() {
|
|
|
|
|
curl --fail-with-body --silent --show-error \
|
|
|
|
|
--config <(printf 'header = "Authorization: token %s"\n' "$token") "$@"
|
|
|
|
|
}
|
2026-10-05 21:47:03 +02:00
|
|
|
# The token's scope, write:repository, covers the repository, not the account.
|
|
|
|
|
forgejo_api --output /dev/null "$forgejo/api/v1/repos/$repository" ||
|
|
|
|
|
fail "$forgejo doesn't take the token for $repository."
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
[ "$(forgejo_api --output /dev/null --write-out '%{http_code}' \
|
|
|
|
|
"$forgejo/api/v1/repos/$repository/releases/tags/$tag" 2>/dev/null || true)" = 404 ] ||
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
fail "A release of $tag exists already on $forgejo. If only publishing it in the" \
|
|
|
|
|
"F-Droid repository failed: tool/fdroid_repo.sh build/release/$tag/halkyon-learn-$version.apk"
|
|
|
|
|
# CI checked the newest commit with code in it: release commits change only the
|
|
|
|
|
# version.
|
|
|
|
|
checked=$(git log -1 --format=%H --invert-grep --grep='^chore(release):' origin/main)
|
|
|
|
|
ci=$(forgejo_api "$forgejo/api/v1/repos/$repository/commits/$checked/status" |
|
|
|
|
|
jq -r .state)
|
|
|
|
|
[ "$ci" = success ] || fail "CI isn't green on ${checked:0:7} ($ci)."
|
|
|
|
|
|
|
|
|
|
if [ -z "$resumed" ]; then
|
|
|
|
|
if [ -n "$previous" ]; then
|
|
|
|
|
step "Writing $version+$build into the pubspec"
|
|
|
|
|
sed -i "s/^version: .*/version: $version+$build/" "$pubspec"
|
|
|
|
|
git commit --quiet -S -m "chore(release): $tag" -m "Build $build." -- "$pubspec"
|
|
|
|
|
fi
|
|
|
|
|
git tag -s "$tag" -m "Halkyon Learn $version"
|
|
|
|
|
fi
|
|
|
|
|
# Signed by a key deploys accept too: the primary key's fingerprint ends GnuPG's
|
|
|
|
|
# VALIDSIG line, as %GP gives it for the commits deploy/redeploy.sh checks.
|
|
|
|
|
validsig=$(git verify-tag --raw "$tag" 2>&1 | grep '^\[GNUPG:\] VALIDSIG ' || true)
|
|
|
|
|
readonly signer="${validsig##* }"
|
|
|
|
|
if [ -z "$validsig" ] || ! grep -qx "$signer" deploy/allowed-signers; then
|
|
|
|
|
fail "$tag isn't signed by a key in deploy/allowed-signers."
|
|
|
|
|
fi
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
|
|
|
|
|
readonly out="build/release/$tag"
|
|
|
|
|
readonly source="$out/source"
|
|
|
|
|
readonly apk="halkyon-learn-$version.apk"
|
|
|
|
|
readonly snap="halkyon-learn_${version}_amd64.snap"
|
|
|
|
|
rm -rf "$out"
|
|
|
|
|
git worktree prune
|
|
|
|
|
mkdir -p "$out"
|
|
|
|
|
cleanup() { git worktree remove --force "$source" 2>/dev/null || true; }
|
|
|
|
|
trap cleanup EXIT
|
|
|
|
|
|
|
|
|
|
# The APK is built from a checkout of the tag, so nothing in the working tree (build
|
|
|
|
|
# caches, files git ignores) can get into it.
|
|
|
|
|
step "Building the APK from $tag"
|
|
|
|
|
git worktree add --quiet --detach "$source" "$tag"
|
|
|
|
|
(
|
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-19 20:39:09 +02:00
|
|
|
cd "$source/apps/learn/halkyon_learn_flutter"
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
flutter pub get --enforce-lockfile
|
|
|
|
|
HALKYON_ANDROID_KEYSTORE="$keystore" \
|
|
|
|
|
HALKYON_ANDROID_KEY_ALIAS="$key_alias" \
|
|
|
|
|
HALKYON_ANDROID_KEYSTORE_PASSWORD="$password" \
|
|
|
|
|
flutter build apk --release --dart-define=SERVER_URL="$server_url"
|
|
|
|
|
# The Gradle daemon would otherwise live on with the password in its environment.
|
|
|
|
|
(cd android && ./gradlew --stop >/dev/null) || true
|
|
|
|
|
)
|
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-19 20:39:09 +02:00
|
|
|
cp "$source/apps/learn/halkyon_learn_flutter/build/app/outputs/flutter-apk/app-release.apk" "$out/$apk"
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
unset password
|
|
|
|
|
|
|
|
|
|
step 'Checking the APK'
|
|
|
|
|
signers=$("$apksigner" verify --print-certs "$out/$apk" |
|
|
|
|
|
sed -n 's/^Signer #[0-9]* certificate SHA-256 digest: //p' || true)
|
|
|
|
|
[ "$signers" = "$certificate" ] ||
|
|
|
|
|
fail "The APK isn't signed by the app's key alone, but by: $signers"
|
|
|
|
|
badging=$("$aapt2" dump badging "$out/$apk")
|
|
|
|
|
grep -q "^package: name='app.halkyon.learn' versionCode='$build' versionName='$version'" \
|
|
|
|
|
<<<"$badging" || fail "The APK isn't app.halkyon.learn $version, build $build."
|
|
|
|
|
! grep -q '^application-debuggable' <<<"$badging" || fail 'The APK is debuggable.'
|
|
|
|
|
# grep's count is kept, not sent to /dev/null: there grep stops at the first match, which
|
|
|
|
|
# ends unzip with SIGPIPE, and pipefail counts that as a failure.
|
|
|
|
|
named=$(unzip -p "$out/$apk" lib/arm64-v8a/libapp.so | grep -caF "$server_url" || true)
|
|
|
|
|
[ "${named:-0}" -gt 0 ] || fail "The APK doesn't name $server_url."
|
|
|
|
|
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
# The snap builds the repository's committed HEAD, which is the tag. Its app part is
|
|
|
|
|
# cleaned so that it is built anew from this commit; the Flutter part stays.
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
step "Building the snap from $tag (in LXD)"
|
|
|
|
|
snapcraft clean halkyon-learn
|
|
|
|
|
snapcraft pack --output "$out"
|
|
|
|
|
[ -f "$out/$snap" ] || fail "snapcraft made no $snap."
|
|
|
|
|
snap_yaml=$(unsquashfs -cat "$out/$snap" meta/snap.yaml)
|
|
|
|
|
grep -qx "version: '\{0,1\}$version'\{0,1\}" <<<"$snap_yaml" ||
|
|
|
|
|
fail "The snap isn't version $version."
|
|
|
|
|
|
|
|
|
|
step 'Signing the checksums'
|
|
|
|
|
(cd "$out" && sha256sum "$apk" "$snap" >SHA256SUMS)
|
|
|
|
|
gpg --local-user "$signer" --armor --detach-sign --output "$out/SHA256SUMS.asc" \
|
|
|
|
|
"$out/SHA256SUMS"
|
|
|
|
|
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
step "Pushing $tag"
|
|
|
|
|
# The release commit goes through the pre-push check like any other; the tag, on the
|
|
|
|
|
# same commit, then needs none.
|
|
|
|
|
if ! git merge-base --is-ancestor HEAD origin/main; then
|
|
|
|
|
git push origin main
|
|
|
|
|
fi
|
|
|
|
|
git push --no-verify origin "$tag"
|
|
|
|
|
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
step "Publishing the release on $forgejo"
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
# What it brings: the commits since the last release, by their type.
|
|
|
|
|
changes() {
|
|
|
|
|
[ -n "$previous" ] || return 0
|
|
|
|
|
git log --reverse --format='%s' "$previous..$tag" |
|
|
|
|
|
sed -nE "s/^($1)(\([^)]*\))?!?: (.*)/- \3/p"
|
|
|
|
|
}
|
|
|
|
|
features=$(changes feat)
|
|
|
|
|
fixes=$(changes 'fix|perf')
|
|
|
|
|
if [ -z "$previous" ]; then
|
|
|
|
|
summary='The first release.'
|
|
|
|
|
elif [ -z "$features$fixes" ]; then
|
|
|
|
|
summary="No features or fixes since $previous."
|
|
|
|
|
else
|
|
|
|
|
summary="${features:+### Features
|
|
|
|
|
|
|
|
|
|
$features
|
|
|
|
|
|
|
|
|
|
}${fixes:+### Fixes
|
|
|
|
|
|
|
|
|
|
$fixes}"
|
|
|
|
|
fi
|
|
|
|
|
if [ -n "$snap_store_channel" ]; then
|
|
|
|
|
linux="\`$snap\`, also in the Snap Store: \`snap install halkyon-learn --$snap_store_channel\`."
|
|
|
|
|
else
|
|
|
|
|
linux="\`$snap\`: \`sudo snap install --dangerous $snap\` (not in the Snap Store yet)."
|
|
|
|
|
fi
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
notes="Halkyon Learn $version for Android and Linux. Both connect to learn.halkyon.eu, \
|
|
|
|
|
the project's instance.
|
|
|
|
|
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
$summary
|
|
|
|
|
|
|
|
|
|
### Installing
|
|
|
|
|
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
- **Android:** \`$apk\`, one APK for every phone, signed with the app's key; the \
|
|
|
|
|
SHA-256 of its certificate is \`$certificate\`.
|
feat: an F-Droid repository at learn.halkyon.app
Releases also publish the APK in the project's own F-Droid repository,
https://learn.halkyon.app/fdroid/repo. tool/fdroid_repo.sh adds the APK,
signs the indexes with the repository's own key and uploads them to
ramiel as the user fdroid, whose key can only write into the folder
Caddy serves (rrsync), the APKs first and the indexes last.
tool/release.sh checks that key and its password before building and
publishes after the Forgejo release.
- tool/release/fdroid/ holds the repository's settings (Halkyon, the
snap's icon) and the app's listing (the snap's text), with the
anti-features TetheredNet (learn.halkyon.eu only, for now) and
NonFreeNet (YouTube, the server's SSPL) declared with their reasons,
in F-Droid's own definitions and with its icons (fdroiddata, AGPL v3).
- The index key's certificate is pinned: a new key would make the F-Droid
app refuse the repository.
- fdroidserver v2.4.5, since Ubuntu's v2.2.1 knows neither TetheredNet
nor reasons.
README and AGENTS.md describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 20:25:02 +02:00
|
|
|
- **F-Droid:** the same APK in the repository \
|
2026-10-05 20:41:54 +02:00
|
|
|
https://halkyon.app/fdroid/repo?fingerprint=$fdroid_fingerprint
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
- **Linux:** $linux
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
|
|
|
|
|
\`SHA256SUMS\` lists the files' checksums, signed with the GPG key \`$signer\` \
|
|
|
|
|
(\`SHA256SUMS.asc\`)."
|
|
|
|
|
release=$(jq -n --arg tag "$tag" --arg name "Halkyon Learn $version" --arg body "$notes" \
|
|
|
|
|
'{tag_name: $tag, name: $name, body: $body}' |
|
|
|
|
|
forgejo_api --header 'Content-Type: application/json' --data @- \
|
|
|
|
|
"$forgejo/api/v1/repos/$repository/releases" | jq -r .id)
|
|
|
|
|
for file in "$apk" "$snap" SHA256SUMS SHA256SUMS.asc; do
|
|
|
|
|
forgejo_api --output /dev/null --form "attachment=@$out/$file" \
|
|
|
|
|
"$forgejo/api/v1/repos/$repository/releases/$release/assets?name=$file"
|
|
|
|
|
done
|
|
|
|
|
echo " $forgejo/$repository/releases/tag/$tag"
|
|
|
|
|
|
feat: an F-Droid repository at learn.halkyon.app
Releases also publish the APK in the project's own F-Droid repository,
https://learn.halkyon.app/fdroid/repo. tool/fdroid_repo.sh adds the APK,
signs the indexes with the repository's own key and uploads them to
ramiel as the user fdroid, whose key can only write into the folder
Caddy serves (rrsync), the APKs first and the indexes last.
tool/release.sh checks that key and its password before building and
publishes after the Forgejo release.
- tool/release/fdroid/ holds the repository's settings (Halkyon, the
snap's icon) and the app's listing (the snap's text), with the
anti-features TetheredNet (learn.halkyon.eu only, for now) and
NonFreeNet (YouTube, the server's SSPL) declared with their reasons,
in F-Droid's own definitions and with its icons (fdroiddata, AGPL v3).
- The index key's certificate is pinned: a new key would make the F-Droid
app refuse the repository.
- fdroidserver v2.4.5, since Ubuntu's v2.2.1 knows neither TetheredNet
nor reasons.
README and AGENTS.md describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 20:25:02 +02:00
|
|
|
step 'Publishing the APK in the F-Droid repository'
|
|
|
|
|
tool/fdroid_repo.sh "$out/$apk"
|
|
|
|
|
unset HALKYON_FDROID_KEYSTORE_PASSWORD
|
|
|
|
|
|
feat: releases work out their version from the commits
tool/release.sh no longer takes a version: it works it out from the
commits since the last release, following semantic versioning (feat
raises the minor number, fix or perf the patch number, a breaking change
the major number from 1.0 on and the minor one before; other types
release nothing), or takes major, minor or patch as chosen. It shows the
version and its commits and asks first, checks that CI is green on the
newest commit with code, writes the version and a raised build number
into the pubspec in a signed release commit with a signed tag, and
pushes only once the APK and the snap passed their checks. The release
notes list the features and fixes. A run that stopped after its release
commit and tag carries on with them.
Channels for now: the release on code.halkyon.app (APK, snap, checksums)
and the F-Droid repository; the Snap Store upload is off until
snap_store_channel names a channel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 21:22:50 +02:00
|
|
|
# Last, as the Store may hold a revision for review; the releases above stand either way.
|
|
|
|
|
if [ -n "$snap_store_channel" ]; then
|
|
|
|
|
step "Uploading the snap to the Snap Store's $snap_store_channel channel (as $account)"
|
|
|
|
|
snapcraft upload --release="$snap_store_channel" "$out/$snap" ||
|
|
|
|
|
fail "The upload failed; once fixed: snapcraft upload --release=$snap_store_channel $out/$snap"
|
|
|
|
|
fi
|
feat: a release script for the APK and the snap
tool/release.sh builds a release on the maintainer's laptop: one APK for
every phone, from a checkout of the signed tag and signed with the app's
own key, and the snap, both connecting to learn.halkyon.eu, the project's
instance; their checksums signed with GPG; the release on code.halkyon.app
through Forgejo's API, and the snap in the Store's edge channel.
Before building it checks that main is clean and pushed, that the tag is
signed by a key deploys accept, the version and a raised build number,
the pinned Flutter, and that the keystore holds the key whose certificate
tool/release/android-certificate.sha256 names. After building it checks
the APK's signer, package, version, that it isn't debuggable and that it
names the server, and the snap's version. The key's password and the
Forgejo token are asked for at each run and never stored.
Release builds take the app's key from the environment and otherwise fall
back to the debug key, so `flutter run --release` works anywhere. The
native libraries go into the APK compressed: an APK for three
architectures was 110 MB with them stored as they are, 48 MB now. The
list of dependencies Gradle adds for Google Play is left out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 12:55:39 +02:00
|
|
|
|
|
|
|
|
echo "✓ Released $tag."
|