From 0394fd955be2598d1485fcfeacc6b8b4e460ecc5 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Tue, 24 Mar 2026 13:31:27 +0800 Subject: [PATCH] chore(docs): update release docs tooling --- docs/planning/xworkmate-ui-feature-matrix.md | 2 +- docs/planning/xworkmate-ui-feature-roadmap.md | 2 +- tool/render_release_docs.dart | 116 +++++++++++++++++- 3 files changed, 112 insertions(+), 8 deletions(-) diff --git a/docs/planning/xworkmate-ui-feature-matrix.md b/docs/planning/xworkmate-ui-feature-matrix.md index 11132c68..e4b4d17e 100644 --- a/docs/planning/xworkmate-ui-feature-matrix.md +++ b/docs/planning/xworkmate-ui-feature-matrix.md @@ -2,7 +2,7 @@ > Generated by `tool/render_release_docs.dart` > Source manifest: [`config/feature_flags.yaml`](../../config/feature_flags.yaml) -> Generated at: `2026-03-24T00:32:25.947047` +> Generated at: `2026-03-24T09:36:52.598713` ## Release Policy diff --git a/docs/planning/xworkmate-ui-feature-roadmap.md b/docs/planning/xworkmate-ui-feature-roadmap.md index 03ae7db8..617bb447 100644 --- a/docs/planning/xworkmate-ui-feature-roadmap.md +++ b/docs/planning/xworkmate-ui-feature-roadmap.md @@ -2,7 +2,7 @@ > Generated by `tool/render_release_docs.dart` > Source manifest: [`config/feature_flags.yaml`](../../config/feature_flags.yaml) -> Generated at: `2026-03-24T00:32:25.947047` +> Generated at: `2026-03-24T09:36:52.598713` ## 规划规则 diff --git a/tool/render_release_docs.dart b/tool/render_release_docs.dart index 5699c60e..94ee593a 100644 --- a/tool/render_release_docs.dart +++ b/tool/render_release_docs.dart @@ -391,14 +391,17 @@ String _renderChangelog(GitSnapshot git) { '| Comparison Range | `${_escapeMarkdown(git.comparisonRangeLabel)}` |', ) ..writeln() - ..writeln('## Recent Tags') + ..writeln('## Recent Releases') ..writeln() - ..writeln('| Tag | Date |') - ..writeln('| --- | --- |'); + ..writeln('| Version | Date | Branch | Tag |') + ..writeln('| --- | --- | --- | --- |'); - for (final tag in git.recentTags) { + for (final release in git.recentReleases) { buffer.writeln( - '| `${_escapeMarkdown(tag.name)}` | `${_escapeMarkdown(tag.date)}` |', + '| `${_escapeMarkdown(release.version)}` | ' + '`${_escapeMarkdown(release.date)}` | ' + '`${_escapeMarkdown(release.branch)}` | ' + '`${_escapeMarkdown(release.tag)}` |', ); } @@ -633,6 +636,7 @@ class GitSnapshot { required this.generatedAt, required this.commits, required this.recentTags, + required this.recentReleases, }); factory GitSnapshot.capture() { @@ -643,7 +647,11 @@ class GitSnapshot { final headShort = _git(['rev-parse', '--short', 'HEAD']); final headLong = _git(['rev-parse', 'HEAD']); final headTags = _gitLines(['tag', '--points-at', 'HEAD']); - final recentTags = _gitTagRefs().take(5).toList(growable: false); + final allTags = _gitTagRefs(); + final recentTags = allTags.take(5).toList(growable: false); + final recentReleases = _gitReleaseRefs( + allTags, + ).take(8).toList(growable: false); final latestTag = recentTags.isEmpty ? null : recentTags.first.name; String? previousTag; @@ -683,6 +691,7 @@ class GitSnapshot { generatedAt: DateTime.now().toIso8601String(), commits: commits, recentTags: recentTags, + recentReleases: recentReleases, ); } @@ -696,6 +705,7 @@ class GitSnapshot { final String generatedAt; final List commits; final List recentTags; + final List recentReleases; } class GitCommit { @@ -719,6 +729,20 @@ class GitTagRef { final String date; } +class GitReleaseRef { + const GitReleaseRef({ + required this.version, + required this.date, + required this.branch, + required this.tag, + }); + + final String version; + final String date; + final String branch; + final String tag; +} + List _gitCommitLog(String? comparisonRange) { final args = [ 'log', @@ -762,6 +786,86 @@ List _gitTagRefs() { .toList(growable: false); } +List _gitReleaseRefs(List tags) { + final releases = {}; + + for (final tag in tags) { + final version = _normalizeReleaseVersion(tag.name); + if (version == null) { + continue; + } + releases[version] = GitReleaseRef( + version: version, + date: tag.date, + branch: _releaseBranchName(version), + tag: tag.name, + ); + } + + final branchLines = _gitLines([ + 'for-each-ref', + '--sort=-committerdate', + '--format=%(refname:short)%09%(committerdate:short)', + 'refs/heads/release', + ], allowFailure: true); + + for (final line in branchLines) { + final parts = line.split('\t'); + if (parts.length < 2) { + continue; + } + final branch = parts[0]; + final date = parts[1]; + final version = _normalizeReleaseVersion(branch); + if (version == null) { + continue; + } + releases[version] = GitReleaseRef( + version: version, + date: releases[version]?.date ?? date, + branch: branch, + tag: releases[version]?.tag ?? '-', + ); + } + + final values = releases.values.toList(growable: false); + values.sort( + (left, right) => _compareReleaseVersions(right.version, left.version), + ); + return values; +} + +String? _normalizeReleaseVersion(String refName) { + final match = RegExp(r'^(?:release/)?(v\d+(?:\.\d+)*)$').firstMatch(refName); + return match?.group(1); +} + +String _releaseBranchName(String version) => 'release/$version'; + +int _compareReleaseVersions(String left, String right) { + final leftParts = _releaseVersionParts(left); + final rightParts = _releaseVersionParts(right); + final maxLength = leftParts.length > rightParts.length + ? leftParts.length + : rightParts.length; + for (var index = 0; index < maxLength; index += 1) { + final leftPart = index < leftParts.length ? leftParts[index] : 0; + final rightPart = index < rightParts.length ? rightParts[index] : 0; + if (leftPart != rightPart) { + return leftPart.compareTo(rightPart); + } + } + return 0; +} + +List _releaseVersionParts(String version) { + return version + .replaceFirst('v', '') + .split('.') + .map(int.parse) + .toList(growable: false); +} + String _git(List args, {bool allowFailure = false}) { final result = Process.runSync('git', args); if (result.exitCode != 0) {