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-19 21:12:31 +02:00
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
|
|
|
|
|
|
import '../../apps/learn/release.dart';
|
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 15:35:40 +01:00
|
|
|
import '../../apps/todo/release.dart';
|
2026-10-30 16:40:12 +01:00
|
|
|
import '../../apps/train/release.dart';
|
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-19 21:12:31 +02:00
|
|
|
import '../../tool/src/release/apps.dart';
|
|
|
|
|
import '../../tool/src/release/version.dart';
|
|
|
|
|
import 'fixtures.dart';
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
test("an app's release names its files and tags by its id", () {
|
|
|
|
|
expect(testApp.tagPrefix, 'notes-v');
|
|
|
|
|
expect(testApp.pubspec, 'apps/notes/halkyon_notes_flutter/pubspec.yaml');
|
|
|
|
|
expect(testApp.instance, 'notes.halkyon.eu');
|
|
|
|
|
expect(
|
|
|
|
|
testApp.apkFile(const Version(0, 19, 0)),
|
|
|
|
|
'halkyon-notes-0.19.0.apk',
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
testApp.snapFile(const Version(0, 19, 0)),
|
|
|
|
|
'halkyon-notes_0.19.0_amd64.snap',
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('a configuration that would release something wrong is refused', () {
|
|
|
|
|
expect(testApp.problems(), isEmpty);
|
|
|
|
|
expect(
|
|
|
|
|
testApp.copyWith(id: 'Notes App').problems(),
|
|
|
|
|
contains('The id "Notes App" is not lower-case letters and digits.'),
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
testApp.copyWith(serverUrl: 'http://notes.halkyon.eu/').problems(),
|
|
|
|
|
contains("The server URL http://notes.halkyon.eu/ isn't HTTPS."),
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
testApp.copyWith(serverUrl: 'https://notes.halkyon.eu').problems(),
|
|
|
|
|
contains(
|
|
|
|
|
"The server URL https://notes.halkyon.eu doesn't end with a slash, "
|
|
|
|
|
'as the app is built with it.',
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
testApp
|
|
|
|
|
.copyWith(flutterPackage: 'apps/todo/halkyon_todo_flutter')
|
|
|
|
|
.problems(),
|
|
|
|
|
contains(
|
|
|
|
|
"The Flutter package apps/todo/halkyon_todo_flutter isn't in "
|
|
|
|
|
'apps/notes.',
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
testApp.copyWith(applicationId: 'notes').problems(),
|
|
|
|
|
contains("The application ID notes isn't one Android takes."),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("Learn's release is registered, sound, and names files that exist", () {
|
|
|
|
|
expect(releasedApps['learn'], same(learnRelease));
|
|
|
|
|
expect(learnRelease.problems(), isEmpty);
|
|
|
|
|
// The test runs in the repository's root, as `dart test` does.
|
|
|
|
|
for (final path in [
|
|
|
|
|
learnRelease.pubspec,
|
|
|
|
|
learnRelease.android.certificateFile,
|
|
|
|
|
...learnRelease.releaseFiles,
|
|
|
|
|
]) {
|
|
|
|
|
expect(
|
|
|
|
|
FileSystemEntity.typeSync(path),
|
|
|
|
|
isNot(FileSystemEntityType.notFound),
|
|
|
|
|
reason: path,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
expect(
|
|
|
|
|
File('${learnRelease.snap!.directory}/snapcraft.yaml').readAsStringSync(),
|
|
|
|
|
contains('name: ${learnRelease.snap!.name}\n'),
|
|
|
|
|
);
|
|
|
|
|
});
|
2026-10-30 16:40:12 +01:00
|
|
|
|
|
|
|
|
test("Train's release is registered, sound, names files that exist, and "
|
|
|
|
|
'has a key of its own', () {
|
|
|
|
|
expect(releasedApps['train'], same(trainRelease));
|
|
|
|
|
expect(trainRelease.problems(), isEmpty);
|
|
|
|
|
for (final path in [
|
|
|
|
|
trainRelease.pubspec,
|
|
|
|
|
trainRelease.android.certificateFile,
|
|
|
|
|
...trainRelease.releaseFiles,
|
|
|
|
|
]) {
|
|
|
|
|
expect(
|
|
|
|
|
FileSystemEntity.typeSync(path),
|
|
|
|
|
isNot(FileSystemEntityType.notFound),
|
|
|
|
|
reason: path,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
expect(
|
|
|
|
|
trainRelease.android.certificateFile,
|
|
|
|
|
isNot(learnRelease.android.certificateFile),
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
trainRelease.android.defaultKeystore,
|
|
|
|
|
isNot(learnRelease.android.defaultKeystore),
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
File(
|
|
|
|
|
'${trainRelease.fdroid!.listing}/metadata/'
|
|
|
|
|
'${trainRelease.applicationId}.yml',
|
|
|
|
|
).readAsStringSync(),
|
|
|
|
|
contains('NonFreeDep'),
|
|
|
|
|
);
|
|
|
|
|
});
|
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 15:35:40 +01:00
|
|
|
|
|
|
|
|
test("Todo's release is registered, sound, names files that exist, and "
|
|
|
|
|
'has a key of its own', () {
|
|
|
|
|
expect(releasedApps['todo'], same(todoRelease));
|
|
|
|
|
expect(todoRelease.problems(), isEmpty);
|
|
|
|
|
for (final path in [
|
|
|
|
|
todoRelease.pubspec,
|
|
|
|
|
todoRelease.android.certificateFile,
|
|
|
|
|
...todoRelease.releaseFiles,
|
|
|
|
|
]) {
|
|
|
|
|
expect(
|
|
|
|
|
FileSystemEntity.typeSync(path),
|
|
|
|
|
isNot(FileSystemEntityType.notFound),
|
|
|
|
|
reason: path,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
for (final other in [learnRelease, trainRelease]) {
|
|
|
|
|
expect(
|
|
|
|
|
todoRelease.android.certificateFile,
|
|
|
|
|
isNot(other.android.certificateFile),
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
todoRelease.android.defaultKeystore,
|
|
|
|
|
isNot(other.android.defaultKeystore),
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
todoRelease.android.keystoreVariable,
|
|
|
|
|
isNot(other.android.keystoreVariable),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
final listing = File(
|
|
|
|
|
'${todoRelease.fdroid!.listing}/metadata/'
|
|
|
|
|
'${todoRelease.applicationId}.yml',
|
|
|
|
|
).readAsStringSync();
|
|
|
|
|
expect(listing, contains('NonFreeNet'));
|
|
|
|
|
expect(
|
|
|
|
|
File('${todoRelease.fdroid!.listing}/config/categories.yml')
|
|
|
|
|
.readAsStringSync(),
|
|
|
|
|
contains(
|
|
|
|
|
'\n${RegExp(r'Categories:\n - (.+)\n').firstMatch(listing)![1]}:\n',
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2026-11-03 18:59:40 +01:00
|
|
|
test("Learn's snap is the root's; Todo's and Train's are built in clones of "
|
|
|
|
|
'their own, outside the repository', () {
|
|
|
|
|
expect(learnRelease.snap!.atRoot, isTrue);
|
|
|
|
|
for (final (app, name) in [
|
|
|
|
|
(todoRelease, 'halkyon-todo'),
|
|
|
|
|
(trainRelease, 'halkyon-train'),
|
|
|
|
|
]) {
|
|
|
|
|
final snap = app.snap!;
|
|
|
|
|
expect(snap.atRoot, isFalse, reason: app.id);
|
2026-11-03 09:20:42 +01:00
|
|
|
expect(
|
2026-11-03 18:59:40 +01:00
|
|
|
snap.projectPath('/home/someone'),
|
|
|
|
|
'/home/someone/.cache/halkyon-release/snap/$name',
|
2026-11-03 09:20:42 +01:00
|
|
|
);
|
2026-11-03 18:59:40 +01:00
|
|
|
expect(snap.storeChannel, 'edge', reason: app.id);
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-11-03 09:20:42 +01:00
|
|
|
|
2026-11-03 11:48:19 +01:00
|
|
|
for (final app in [todoRelease, trainRelease]) {
|
|
|
|
|
group("${app.name}'s snap, built in a clone of its own with its folder in "
|
|
|
|
|
"place of the root's snap/", () {
|
|
|
|
|
final snap = app.snap!;
|
|
|
|
|
final recipe = File('${snap.directory}/snapcraft.yaml')
|
|
|
|
|
.readAsStringSync();
|
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 15:35:40 +01:00
|
|
|
|
2026-11-03 11:48:19 +01:00
|
|
|
test('names the snap and the part the release tool takes', () {
|
|
|
|
|
expect(recipe, contains('name: ${snap.name}\n'));
|
|
|
|
|
expect(recipe, contains('\n ${snap.part}:\n after: [flutter]\n'));
|
|
|
|
|
expect(recipe, contains('adopt-info: ${snap.part} '));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('builds the app for its instance', () {
|
|
|
|
|
expect(recipe, contains('cd ${app.flutterPackage}\n'));
|
|
|
|
|
expect(recipe, contains('--dart-define=SERVER_URL=${app.serverUrl}\n'));
|
|
|
|
|
});
|
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 15:35:40 +01:00
|
|
|
|
2026-11-03 11:48:19 +01:00
|
|
|
test('has the files it names, as they will stand in the clone', () {
|
|
|
|
|
// In the clone, the folder is snap/, the root's snap/local/ beside
|
|
|
|
|
// it.
|
|
|
|
|
for (final match in RegExp(
|
|
|
|
|
r'(?<![\w/$])snap/([\w./-]+)',
|
|
|
|
|
).allMatches(recipe)) {
|
|
|
|
|
final inClone = match[1]!;
|
|
|
|
|
final path = inClone.startsWith('local')
|
|
|
|
|
? 'snap/$inClone'
|
|
|
|
|
: '${snap.directory}/$inClone';
|
|
|
|
|
if (inClone == 'snapcraft.yaml') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
expect(
|
|
|
|
|
FileSystemEntity.typeSync(path),
|
|
|
|
|
isNot(FileSystemEntityType.notFound),
|
|
|
|
|
reason: match[0],
|
|
|
|
|
);
|
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 15:35:40 +01:00
|
|
|
}
|
|
|
|
|
expect(
|
2026-11-03 11:48:19 +01:00
|
|
|
File('${snap.directory}/gui/${snap.name}.desktop').readAsStringSync(),
|
|
|
|
|
contains('Exec=${snap.name}\n'),
|
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 15:35:40 +01:00
|
|
|
);
|
2026-11-03 11:48:19 +01:00
|
|
|
});
|
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 15:35:40 +01:00
|
|
|
});
|
2026-11-03 11:48:19 +01:00
|
|
|
}
|
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 15:35:40 +01:00
|
|
|
|
|
|
|
|
test("every snap's recipe builds with the Flutter CI pins", () {
|
|
|
|
|
final pinned = RegExp(
|
|
|
|
|
r'^\s*FLUTTER_VERSION:\s*(\S+)',
|
|
|
|
|
multiLine: true,
|
|
|
|
|
).firstMatch(File('.forgejo/workflows/ci.yaml').readAsStringSync())![1];
|
|
|
|
|
for (final app in releasedApps.values) {
|
|
|
|
|
if (app.snap case final snap?) {
|
|
|
|
|
expect(
|
|
|
|
|
File('${snap.directory}/snapcraft.yaml').readAsStringSync(),
|
|
|
|
|
contains("source-tag: '$pinned'"),
|
|
|
|
|
reason: app.id,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
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-19 21:12:31 +02:00
|
|
|
}
|