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>
128 lines
3.9 KiB
Dart
128 lines
3.9 KiB
Dart
import 'package:test/test.dart';
|
|
|
|
import '../../tool/src/release/relevance.dart';
|
|
import '../../tool/src/release/version.dart';
|
|
import 'fixtures.dart';
|
|
|
|
void main() {
|
|
const rootPubspec = '''
|
|
name: _
|
|
workspace:
|
|
- apps/notes/halkyon_notes_client
|
|
- apps/notes/halkyon_notes_flutter
|
|
- apps/notes/halkyon_notes_server
|
|
- apps/todo/halkyon_todo_flutter
|
|
- packages/halkyon_app
|
|
- packages/halkyon_base_client
|
|
- packages/halkyon_base_server
|
|
- packages/halkyon_core
|
|
- packages/halkyon_ui
|
|
- packages/halkyon_ui/example
|
|
''';
|
|
const pubspecs = {
|
|
'apps/notes/halkyon_notes_client': '''
|
|
name: halkyon_notes_client
|
|
dependencies:
|
|
halkyon_base_client:
|
|
path: ../../../packages/halkyon_base_client
|
|
''',
|
|
'apps/notes/halkyon_notes_flutter': '''
|
|
name: halkyon_notes_flutter
|
|
dependencies:
|
|
halkyon_app:
|
|
path: ../../../packages/halkyon_app
|
|
halkyon_notes_client:
|
|
path: ../halkyon_notes_client
|
|
intl: ^0.20.2
|
|
dev_dependencies:
|
|
halkyon_ui_example:
|
|
path: ../../../packages/halkyon_ui/example
|
|
''',
|
|
'apps/notes/halkyon_notes_server': '''
|
|
name: halkyon_notes_server
|
|
dependencies:
|
|
halkyon_base_server:
|
|
path: ../../../packages/halkyon_base_server
|
|
''',
|
|
'apps/todo/halkyon_todo_flutter': '''
|
|
name: halkyon_todo_flutter
|
|
dependencies:
|
|
halkyon_core:
|
|
path: ../../../packages/halkyon_core
|
|
''',
|
|
'packages/halkyon_app': '''
|
|
name: halkyon_app
|
|
dependencies:
|
|
halkyon_ui:
|
|
path: ../halkyon_ui
|
|
halkyon_base_client:
|
|
path: ../halkyon_base_client
|
|
''',
|
|
'packages/halkyon_base_client': 'name: halkyon_base_client\n',
|
|
'packages/halkyon_base_server': 'name: halkyon_base_server\n',
|
|
'packages/halkyon_core': 'name: halkyon_core\n',
|
|
'packages/halkyon_ui': 'name: halkyon_ui\n',
|
|
'packages/halkyon_ui/example': 'name: halkyon_ui_example\n',
|
|
};
|
|
|
|
final paths = relevantPaths(
|
|
testApp,
|
|
rootPubspec: rootPubspec,
|
|
pubspecs: pubspecs,
|
|
);
|
|
|
|
test("an app's changes are its folder's, those of the shared packages it "
|
|
'uses, the workspace and its release files', () {
|
|
expect(paths, [
|
|
'apps/notes',
|
|
'packages/halkyon_app',
|
|
'packages/halkyon_base_client',
|
|
'packages/halkyon_base_server',
|
|
// Through halkyon_app, by path from there.
|
|
'packages/halkyon_ui',
|
|
'pubspec.yaml',
|
|
'pubspec.lock',
|
|
'tool/release/notes-certificate.sha256',
|
|
'tool/release/fdroid',
|
|
'tool/release/fdroid-index-certificate.sha256',
|
|
'snap',
|
|
// Where its packages were before it moved into its folder.
|
|
'halkyon_notes_flutter',
|
|
'halkyon_notes_server',
|
|
]);
|
|
// Neither another app's packages, nor one only the app's tests use.
|
|
expect(paths, isNot(contains('packages/halkyon_core')));
|
|
expect(paths, isNot(contains('packages/halkyon_ui/example')));
|
|
});
|
|
|
|
test('a commit counts when it changes one of them, and says which', () {
|
|
const ours = Commit(
|
|
hash: 'a',
|
|
subject: 'feat(app): x',
|
|
files: [
|
|
'packages/halkyon_ui/lib/src/theme/brand.dart',
|
|
'apps/notes/halkyon_notes_flutter/lib/main.dart',
|
|
'apps/todo/halkyon_todo_flutter/lib/main.dart',
|
|
],
|
|
);
|
|
expect(touchedPaths(ours.files, paths), [
|
|
'apps/notes',
|
|
'packages/halkyon_ui',
|
|
]);
|
|
|
|
// A package's name starting like another's isn't that one.
|
|
expect(
|
|
touchedPaths(['packages/halkyon_ui_extras/lib/a.dart'], paths),
|
|
isEmpty,
|
|
);
|
|
expect(touchedPaths(['apps/notes-web/index.html'], paths), isEmpty);
|
|
expect(touchedPaths(['packages/halkyon_core/lib/a.dart'], paths), isEmpty);
|
|
expect(touchedPaths(['pubspec.lock'], paths), ['pubspec.lock']);
|
|
expect(touchedPaths(['snap/snapcraft.yaml'], paths), ['snap']);
|
|
expect(touchedPaths(['README.md', 'docs/adr/0015.md'], paths), isEmpty);
|
|
// A commit from before the move counts by where the app was then.
|
|
expect(touchedPaths(['halkyon_notes_server/lib/server.dart'], paths), [
|
|
'halkyon_notes_server',
|
|
]);
|
|
});
|
|
}
|