Changelog and What's new: CHANGELOG.md cut into each release, shown in the update band, a What's new card and once after updating
CHANGELOG.md keeps an Unreleased section (user-facing changes add a line in the same commit); tool/release.sh cuts it into the version, the manifest gets a structured "changes" field next to the one-line "notes" older apps read, and the Gitea release text is that section. The app's update band shows the top items, What's new opens a card (Added / Changed / Fixed), and the first start after an update shows it once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ec0849eb01
commit
638a30e16c
22 files changed
+1637
-58
No files matched your search
+28
-13
@@ -3,7 +3,8 @@
|
||||
// The Linux and Windows CI jobs both run this, possibly at the same time,
|
||||
// so a lost create race falls back to the release the other job made.
|
||||
// The manifest is written by tool/release.sh at release time (predictable
|
||||
// asset URLs), so this never touches git history beyond reading notes.
|
||||
// asset URLs). The release page's text is the version's CHANGELOG.md
|
||||
// section (tool/changelog.dart).
|
||||
// Usage: dart tool/release_publish.dart vX.Y.Z <archive>
|
||||
// (run after tool/package_linux.sh or tool/package_windows.ps1)
|
||||
// dart tool/release_publish.dart dev <archive> <manifest.json>
|
||||
@@ -13,6 +14,8 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'changelog.dart';
|
||||
|
||||
Future<void> main(List<String> args) async {
|
||||
final dev = args.isNotEmpty && args[0] == 'dev';
|
||||
if (args.length != (dev ? 3 : 2)) {
|
||||
@@ -42,17 +45,7 @@ Future<void> main(List<String> args) async {
|
||||
final token = _token();
|
||||
final api = _apiBase(root);
|
||||
|
||||
final prev = _git(root, ['describe', '--tags', '--abbrev=0', '$tag^'],
|
||||
allowFail: true);
|
||||
final notes = prev.isEmpty
|
||||
? 'Starsync $ver.'
|
||||
: _git(root, [
|
||||
'log',
|
||||
'$prev..$tag',
|
||||
'--format=- %s',
|
||||
'--invert-grep',
|
||||
'--grep=^Release v'
|
||||
]).split('\n').take(20).join('\n');
|
||||
final body = _changelog(root)?.section(ver)?.releaseBody(ver) ?? 'Starsync $ver.';
|
||||
|
||||
final http = HttpClient();
|
||||
try {
|
||||
@@ -63,7 +56,7 @@ Future<void> main(List<String> args) async {
|
||||
rel = await c.post('$api/releases', {
|
||||
'tag_name': tag,
|
||||
'name': tag,
|
||||
'body': 'Starsync $ver\n\n$notes',
|
||||
'body': body,
|
||||
'draft': false,
|
||||
'prerelease': false,
|
||||
});
|
||||
@@ -130,6 +123,12 @@ Future<void> _publishDev(String root, List<File> files) async {
|
||||
}) ??
|
||||
_die('could not create or find release dev');
|
||||
final id = rel['id'];
|
||||
// The page says what the build has: Unreleased.
|
||||
final ver = jsonDecode(files.last.readAsStringSync())['version'];
|
||||
final next = _changelog(root)?.section(unreleased);
|
||||
if (next != null && !next.isEmpty) {
|
||||
await c.patch('$api/releases/$id', {'body': 'Latest test build; dev builds update from here.\n\n${next.releaseBody('$ver')}'});
|
||||
}
|
||||
final old = [for (final a in (rel['assets'] as List? ?? const [])) if (a is Map) a];
|
||||
Future<void> upload(File f) {
|
||||
final name = f.uri.pathSegments.last;
|
||||
@@ -153,6 +152,16 @@ Future<void> _publishDev(String root, List<File> files) async {
|
||||
}
|
||||
}
|
||||
|
||||
Changelog? _changelog(String root) {
|
||||
final f = File('$root/CHANGELOG.md');
|
||||
try {
|
||||
return Changelog.parse(f.readAsStringSync());
|
||||
} on Object catch (e) {
|
||||
stderr.writeln('warning: CHANGELOG.md not used: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Never _die(String msg) {
|
||||
stderr.writeln('error: $msg');
|
||||
exit(1);
|
||||
@@ -203,6 +212,12 @@ class _Api {
|
||||
return jsonDecode(text) as Map<String, Object?>;
|
||||
}
|
||||
|
||||
Future<void> patch(String url, Object json) async {
|
||||
final (code, text) = await _send('PATCH', url,
|
||||
body: utf8.encode(jsonEncode(json)), type: 'application/json');
|
||||
_check('PATCH', url, code, text);
|
||||
}
|
||||
|
||||
Future<void> delete(String url) async {
|
||||
final (code, text) = await _send('DELETE', url);
|
||||
_check('DELETE', url, code, text);
|
||||
|
||||
Reference in new issue
Block a user