Skip to content

Commit bb6a59c

Browse files
committed
feat: 添加文档差异生成脚本并移除旧的验证脚本和工作流
1 parent c6ac2c0 commit bb6a59c

5 files changed

Lines changed: 303 additions & 249 deletions

File tree

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
#!/usr/bin/env dart
2+
/// 生成 PR 文档 diff 评论(develop 已提交 vs PR tools 重新生成)。
3+
///
4+
/// dart run .github/scripts/build_doc_diff_comment.dart \
5+
/// --out doc-diff-comment.md \
6+
/// --preview-url https://preview-pr-1-xxx.surge.sh \
7+
/// --compare /tmp/api-baseline path/to/api "API 文档" "*_api.md" \
8+
/// --compare /tmp/site-baseline path/to/src "站点文档" "README.md"
9+
import 'dart:io';
10+
11+
const _maxLinesPerFile = 150;
12+
const _maxBytes = 55 * 1024;
13+
14+
void main(List<String> args) async {
15+
final config = _Config.parse(args);
16+
if (config == null) {
17+
stderr.writeln(_usage);
18+
exit(1);
19+
}
20+
21+
final slugs = <String>{};
22+
final sections = StringBuffer();
23+
var omitted = 0;
24+
25+
for (final pair in config.pairs) {
26+
final changed = await _diffPair(pair);
27+
for (final c in changed) {
28+
final slug = _componentSlug(c.path);
29+
if (slug != null) slugs.add(slug);
30+
}
31+
32+
sections.writeln('### ${pair.title}');
33+
sections.writeln();
34+
if (changed.isEmpty) {
35+
sections.writeln('_无变更_');
36+
sections.writeln();
37+
continue;
38+
}
39+
40+
for (final file in changed) {
41+
final block = _detailsBlock(file);
42+
if (sections.length + block.length > _maxBytes) {
43+
omitted++;
44+
continue;
45+
}
46+
sections.write(block);
47+
}
48+
}
49+
50+
final out = StringBuffer('''
51+
## 文档变更预览
52+
53+
> 对比基准:[Tencent/tdesign-flutter](https://github.com/Tencent/tdesign-flutter) @ develop 已提交文档 vs 本 PR tools 重新生成
54+
55+
''');
56+
57+
if (slugs.isNotEmpty && config.previewUrl != null) {
58+
final url = config.previewUrl!.replaceAll(RegExp(r'/+$'), '');
59+
out.writeln('**有变更的组件预览**:');
60+
for (final s in slugs.toList()..sort()) {
61+
out.writeln('- [$s]($url/flutter/components/$s)');
62+
}
63+
out.writeln();
64+
}
65+
66+
out.write(sections);
67+
if (omitted > 0) {
68+
out.writeln('> 还有 **$omitted** 个文件未展示(GitHub 评论长度限制)。\n');
69+
}
70+
71+
File(config.outPath).writeAsStringSync(out.toString());
72+
stdout.writeln('Wrote ${config.outPath} (${out.length} bytes)');
73+
}
74+
75+
class _Pair {
76+
_Pair(this.baseline, this.generated, this.title, this.namePattern);
77+
final String baseline;
78+
final String generated;
79+
final String title;
80+
final String namePattern;
81+
}
82+
83+
class _Config {
84+
_Config({required this.outPath, required this.pairs, this.previewUrl});
85+
final String outPath;
86+
final List<_Pair> pairs;
87+
final String? previewUrl;
88+
89+
static _Config? parse(List<String> args) {
90+
String? out;
91+
String? previewUrl;
92+
final pairs = <_Pair>[];
93+
94+
for (var i = 0; i < args.length; i++) {
95+
switch (args[i]) {
96+
case '--out':
97+
out = args[++i];
98+
case '--preview-url':
99+
previewUrl = args[++i];
100+
case '--compare':
101+
if (i + 4 >= args.length) return null;
102+
pairs.add(_Pair(args[++i], args[++i], args[++i], args[++i]));
103+
}
104+
}
105+
if (out == null || pairs.isEmpty) return null;
106+
return _Config(outPath: out, pairs: pairs, previewUrl: previewUrl);
107+
}
108+
}
109+
110+
const _usage = '''
111+
用法:
112+
dart run .github/scripts/build_doc_diff_comment.dart \\
113+
--out doc-diff-comment.md \\
114+
[--preview-url <url>] \\
115+
--compare <baseline> <generated> <标题> <文件名模式> ...
116+
''';
117+
118+
class _Changed {
119+
_Changed(this.path, this.diff, this.added, this.removed);
120+
final String path;
121+
final String diff;
122+
final int added;
123+
final int removed;
124+
}
125+
126+
Future<List<_Changed>> _diffPair(_Pair pair) async {
127+
final baseDir = Directory(pair.baseline);
128+
final genDir = Directory(pair.generated);
129+
if (!baseDir.existsSync() || !genDir.existsSync()) {
130+
stderr.writeln('ERROR: 目录不存在');
131+
exit(1);
132+
}
133+
134+
final paths = <String>{};
135+
for (final root in [pair.baseline, pair.generated]) {
136+
final prefix = '${Directory(root).absolute.path}${Platform.pathSeparator}';
137+
await for (final entity in Directory(root).list(recursive: true)) {
138+
if (entity is! File) continue;
139+
final name = entity.path.split(Platform.pathSeparator).last;
140+
if (!_matchName(name, pair.namePattern)) continue;
141+
paths.add(entity.path.substring(prefix.length));
142+
}
143+
}
144+
145+
final result = <_Changed>[];
146+
for (final rel in paths.toList()..sort()) {
147+
final a = File('${pair.baseline}${Platform.pathSeparator}$rel');
148+
final b = File('${pair.generated}${Platform.pathSeparator}$rel');
149+
if (a.existsSync() && b.existsSync() && await a.readAsString() == await b.readAsString()) {
150+
continue;
151+
}
152+
153+
final proc = await Process.run('diff', [
154+
'-u', '-U0',
155+
'--label', 'develop/$rel',
156+
'--label', 'pr/$rel',
157+
a.existsSync() ? a.path : '/dev/null',
158+
b.existsSync() ? b.path : '/dev/null',
159+
]);
160+
final text = '${proc.stdout}${proc.stderr}'.trimRight();
161+
var added = 0, removed = 0;
162+
for (final line in text.split('\n')) {
163+
if (line.startsWith('+++') || line.startsWith('---') || line.startsWith('@@')) continue;
164+
if (line.startsWith('+')) added++;
165+
if (line.startsWith('-')) removed++;
166+
}
167+
result.add(_Changed(rel, text.isEmpty ? '(无 diff 输出)' : text, added, removed));
168+
}
169+
return result;
170+
}
171+
172+
bool _matchName(String filename, String pattern) {
173+
if (pattern.startsWith('*')) return filename.endsWith(pattern.substring(1));
174+
return filename == pattern;
175+
}
176+
177+
String _detailsBlock(_Changed f) {
178+
var body = f.diff;
179+
final lines = body.split('\n');
180+
if (lines.length > _maxLinesPerFile) {
181+
body = '${lines.take(_maxLinesPerFile).join('\n')}\n\n... 已截断(共 ${lines.length} 行)';
182+
}
183+
return '''
184+
<details>
185+
<summary>${f.path} (+${f.added} −${f.removed})</summary>
186+
187+
```diff
188+
$body
189+
```
190+
191+
</details>
192+
193+
''';
194+
}
195+
196+
String? _componentSlug(String rel) {
197+
final name = rel.split(Platform.pathSeparator).last;
198+
if (name.endsWith('_api.md')) {
199+
return name.substring(0, name.length - '_api.md'.length);
200+
}
201+
if (rel.endsWith('${Platform.pathSeparator}README.md')) {
202+
return rel.substring(0, rel.length - '${Platform.pathSeparator}README.md'.length);
203+
}
204+
return null;
205+
}

.github/scripts/cross_platform_verify.dart

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)