Skip to content

Commit 48232e8

Browse files
[tool] Consider comment-only changes to be dev-only (#4279)
Updates the state checker to inspect changed Dart files to see if the only changes are implementation (not documentation) comment lines. In particular, this will fix the problem of CI flagging changes that do nothing but add `// ignore:` comments (for federated package changes involving deprecation, framework changes that require temporary ignores in packages to support stable, etc.) as needing version and changelog changes
1 parent 8b6998f commit 48232e8

File tree

3 files changed

+203
-1
lines changed

3 files changed

+203
-1
lines changed

script/tool/lib/src/common/package_state_utils.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ Future<bool> _isDevChange(List<String> pathComponents,
184184
_isExampleBuildFile(pathComponents) ||
185185
// Test-only gradle depenedencies don't affect clients.
186186
await _isGradleTestDependencyChange(pathComponents,
187+
git: git, repoPath: repoPath) ||
188+
// Implementation comments don't affect clients.
189+
// This check is currently Dart-only since that's the only place
190+
// this has come up in practice; it could be generalized to other
191+
// languages if needed.
192+
await _isDartImplementationCommentChange(pathComponents,
187193
git: git, repoPath: repoPath);
188194
}
189195

@@ -231,3 +237,34 @@ Future<bool> _isGradleTestDependencyChange(List<String> pathComponents,
231237
// against having the wrong (e.g., incorrectly empty) diff output.
232238
return foundTestDependencyChange;
233239
}
240+
241+
// Returns true if the given file is a Dart file whose only changes are
242+
// implementation comments (i.e., not doc comments).
243+
Future<bool> _isDartImplementationCommentChange(List<String> pathComponents,
244+
{GitVersionFinder? git, String? repoPath}) async {
245+
if (git == null) {
246+
return false;
247+
}
248+
if (!pathComponents.last.endsWith('.dart')) {
249+
return false;
250+
}
251+
final List<String> diff = await git.getDiffContents(targetPath: repoPath);
252+
final RegExp changeLine = RegExp(r'^[+-] ');
253+
final RegExp whitespaceLine = RegExp(r'^[+-]\s*$');
254+
final RegExp nonDocCommentLine = RegExp(r'^[+-]\s*//\s');
255+
bool foundIgnoredChange = false;
256+
for (final String line in diff) {
257+
if (!changeLine.hasMatch(line) ||
258+
line.startsWith('--- ') ||
259+
line.startsWith('+++ ')) {
260+
continue;
261+
}
262+
if (!nonDocCommentLine.hasMatch(line) && !whitespaceLine.hasMatch(line)) {
263+
return false;
264+
}
265+
foundIgnoredChange = true;
266+
}
267+
// Only return true if an ignored change was found, as a failsafe against
268+
// having the wrong (e.g., incorrectly empty) diff output.
269+
return foundIgnoredChange;
270+
}

script/tool/test/common/package_state_utils_test.dart

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,90 @@ void main() {
289289
expect(state.needsChangelogChange, true);
290290
});
291291

292+
test(
293+
'does not requires changelog or version change for '
294+
'non-doc-comment-only changes', () async {
295+
final RepositoryPackage package =
296+
createFakePlugin('a_plugin', packagesDir);
297+
298+
const List<String> changedFiles = <String>[
299+
'packages/a_plugin/lib/a_plugin.dart',
300+
];
301+
302+
final GitVersionFinder git = FakeGitVersionFinder(<String, List<String>>{
303+
'packages/a_plugin/lib/a_plugin.dart': <String>[
304+
'- // Old comment.',
305+
'+ // New comment.',
306+
'+ ', // Allow whitespace line changes as part of comment changes.
307+
]
308+
});
309+
310+
final PackageChangeState state = await checkPackageChangeState(package,
311+
changedPaths: changedFiles,
312+
relativePackagePath: 'packages/a_plugin/',
313+
git: git);
314+
315+
expect(state.hasChanges, true);
316+
expect(state.needsVersionChange, false);
317+
expect(state.needsChangelogChange, false);
318+
});
319+
320+
test('requires changelog or version change for doc comment changes',
321+
() async {
322+
final RepositoryPackage package =
323+
createFakePlugin('a_plugin', packagesDir);
324+
325+
const List<String> changedFiles = <String>[
326+
'packages/a_plugin/lib/a_plugin.dart',
327+
];
328+
329+
final GitVersionFinder git = FakeGitVersionFinder(<String, List<String>>{
330+
'packages/a_plugin/lib/a_plugin.dart': <String>[
331+
'- /// Old doc comment.',
332+
'+ /// New doc comment.',
333+
]
334+
});
335+
336+
final PackageChangeState state = await checkPackageChangeState(
337+
package,
338+
changedPaths: changedFiles,
339+
relativePackagePath: 'packages/a_plugin/',
340+
git: git,
341+
);
342+
343+
expect(state.hasChanges, true);
344+
expect(state.needsVersionChange, true);
345+
expect(state.needsChangelogChange, true);
346+
});
347+
348+
test('requires changelog or version change for Dart code change', () async {
349+
final RepositoryPackage package =
350+
createFakePlugin('a_plugin', packagesDir);
351+
352+
const List<String> changedFiles = <String>[
353+
'packages/a_plugin/lib/a_plugin.dart',
354+
];
355+
356+
final GitVersionFinder git = FakeGitVersionFinder(<String, List<String>>{
357+
'packages/a_plugin/lib/a_plugin.dart': <String>[
358+
// Include inline comments to ensure the comment check doesn't have
359+
// false positives for lines that include comment changes but aren't
360+
// only comment changes.
361+
'- callOldMethod(); // inline comment',
362+
'+ callNewMethod(); // inline comment',
363+
]
364+
});
365+
366+
final PackageChangeState state = await checkPackageChangeState(package,
367+
changedPaths: changedFiles,
368+
relativePackagePath: 'packages/a_plugin/',
369+
git: git);
370+
371+
expect(state.hasChanges, true);
372+
expect(state.needsVersionChange, true);
373+
expect(state.needsChangelogChange, true);
374+
});
375+
292376
test(
293377
'requires changelog or version change if build.gradle diffs cannot '
294378
'be checked', () async {

script/tool/test/version_check_command_test.dart

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,8 @@ packages/plugin/android/build.gradle
11141114
);
11151115
});
11161116

1117-
test('allows missing CHANGELOG and version change for dev-only changes',
1117+
test(
1118+
'allows missing CHANGELOG and version change for dev-only-file changes',
11181119
() async {
11191120
final RepositoryPackage plugin =
11201121
createFakePlugin('plugin', packagesDir, version: '1.0.0');
@@ -1147,6 +1148,86 @@ packages/plugin/run_tests.sh
11471148
]),
11481149
);
11491150
});
1151+
1152+
test(
1153+
'allows missing CHANGELOG and version change for dev-only line-level '
1154+
'changes in production files', () async {
1155+
final RepositoryPackage plugin =
1156+
createFakePlugin('plugin', packagesDir, version: '1.0.0');
1157+
1158+
const String changelog = '''
1159+
## 1.0.0
1160+
* Some changes.
1161+
''';
1162+
plugin.changelogFile.writeAsStringSync(changelog);
1163+
processRunner.mockProcessesForExecutable['git-show'] =
1164+
<FakeProcessInfo>[
1165+
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
1166+
];
1167+
processRunner.mockProcessesForExecutable['git-diff'] =
1168+
<FakeProcessInfo>[
1169+
// File list.
1170+
FakeProcessInfo(MockProcess(stdout: '''
1171+
packages/plugin/lib/plugin.dart
1172+
''')),
1173+
// Dart file diff.
1174+
FakeProcessInfo(MockProcess(stdout: '''
1175+
+ // TODO(someone): Fix this.
1176+
+ // ignore: some_lint
1177+
'''), <String>['main', 'HEAD', '--', 'packages/plugin/lib/plugin.dart']),
1178+
];
1179+
1180+
final List<String> output =
1181+
await runWithMissingChangeDetection(<String>[]);
1182+
1183+
expect(
1184+
output,
1185+
containsAllInOrder(<Matcher>[
1186+
contains('Running for plugin'),
1187+
]),
1188+
);
1189+
});
1190+
1191+
test('documentation comments are not exempt', () async {
1192+
final RepositoryPackage plugin =
1193+
createFakePlugin('plugin', packagesDir, version: '1.0.0');
1194+
1195+
const String changelog = '''
1196+
## 1.0.0
1197+
* Some changes.
1198+
''';
1199+
plugin.changelogFile.writeAsStringSync(changelog);
1200+
processRunner.mockProcessesForExecutable['git-show'] =
1201+
<FakeProcessInfo>[
1202+
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
1203+
];
1204+
processRunner.mockProcessesForExecutable['git-diff'] =
1205+
<FakeProcessInfo>[
1206+
FakeProcessInfo(MockProcess(stdout: '''
1207+
packages/plugin/lib/plugin.dart
1208+
''')),
1209+
// Dart file diff.
1210+
FakeProcessInfo(MockProcess(stdout: '''
1211+
+ /// Important new information for API clients!
1212+
'''), <String>['main', 'HEAD', '--', 'packages/plugin/lib/plugin.dart']),
1213+
];
1214+
1215+
Error? commandError;
1216+
final List<String> output = await runWithMissingChangeDetection(
1217+
<String>[], errorHandler: (Error e) {
1218+
commandError = e;
1219+
});
1220+
1221+
expect(commandError, isA<ToolExit>());
1222+
expect(
1223+
output,
1224+
containsAllInOrder(<Matcher>[
1225+
contains('No version change found'),
1226+
contains('plugin:\n'
1227+
' Missing version change'),
1228+
]),
1229+
);
1230+
});
11501231
});
11511232

11521233
test('allows valid against pub', () async {

0 commit comments

Comments
 (0)