Skip to content

Commit 260102b

Browse files
[tool] Provide better CI feedback for combo PRs (flutter#6865)
Currently if a PR follows the recommended combo PR process for a federated plugin, the main PR will have CI errors that say the PR isn't allowed to do what it is doing, which is confusing, especially to new contributors or reviewers. This updates the tooling to detect the temporary overrides created by the tooling, and uses that to trigger a different error message that explains that the error is expected, and exists only to prevent accidental landing. Fixes flutter#129303
1 parent 8c24fd4 commit 260102b

File tree

4 files changed

+109
-1
lines changed

4 files changed

+109
-1
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ const String platformWindows = 'windows';
3333
/// Key for enable experiment.
3434
const String kEnableExperiment = 'enable-experiment';
3535

36+
/// A String to add to comments on temporarily-added changes that should not
37+
/// land (e.g., dependency overrides in federated plugin combination PRs).
38+
const String kDoNotLandWarning = 'DO NOT MERGE';
39+
3640
/// Target platforms supported by Flutter.
3741
// ignore: public_member_api_docs
3842
enum FlutterPlatform { android, ios, linux, macos, web, windows }

script/tool/lib/src/federation_safety_check_command.dart

+21
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:file/file.dart';
66
import 'package:path/path.dart' as p;
77
import 'package:pub_semver/pub_semver.dart';
88

9+
import 'common/core.dart';
910
import 'common/file_utils.dart';
1011
import 'common/git_version_finder.dart';
1112
import 'common/output_utils.dart';
@@ -112,6 +113,21 @@ class FederationSafetyCheckCommand extends PackageLoopingCommand {
112113
'Platform interface changes are not validated.');
113114
}
114115

116+
// Special-case combination PRs that are following repo process, so that
117+
// they don't get an error that makes it sound like something is wrong with
118+
// the PR (but is still an error so that the PR can't land without following
119+
// the resolution process).
120+
if (package.getExamples().any(_hasTemporaryDependencyOverrides)) {
121+
printError('"$kDoNotLandWarning" found in pubspec.yaml, so this is '
122+
'assumed to be the initial combination PR for a federated change, '
123+
'following the standard repository procedure. This failure is '
124+
'expected, in order to prevent accidentally landing the temporary '
125+
'overrides, and will automatically be resolved when the temporary '
126+
'overrides are replaced by dependency version bumps later in the '
127+
'process.');
128+
return PackageResult.fail(<String>['Unresolved combo PR.']);
129+
}
130+
115131
// Uses basename to match _changedPackageFiles.
116132
final String basePackageName = package.directory.parent.basename;
117133
final String platformInterfacePackageName =
@@ -216,4 +232,9 @@ class FederationSafetyCheckCommand extends PackageLoopingCommand {
216232
// against having the wrong (e.g., incorrectly empty) diff output.
217233
return foundComment;
218234
}
235+
236+
bool _hasTemporaryDependencyOverrides(RepositoryPackage package) {
237+
final String pubspecContents = package.pubspecFile.readAsStringSync();
238+
return pubspecContents.contains(kDoNotLandWarning);
239+
}
219240
}

script/tool/lib/src/make_deps_path_based_command.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class MakeDepsPathBasedCommand extends PackageCommand {
5757
// Includes a reference to the docs so that reviewers who aren't familiar with
5858
// the federated plugin change process don't think it's a mistake.
5959
static const String _dependencyOverrideWarningComment =
60-
'# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.\n'
60+
'# FOR TESTING AND INITIAL REVIEW ONLY. $kDoNotLandWarning.\n'
6161
'# See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins';
6262

6363
@override

script/tool/test/federation_safety_check_command_test.dart

+83
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,89 @@ index abc123..def456 100644
262262
);
263263
});
264264

265+
test('fails with specific text for combo PRs using the recommended tooling',
266+
() async {
267+
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
268+
final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir);
269+
final RepositoryPackage implementation =
270+
createFakePlugin('foo_bar', pluginGroupDir);
271+
final RepositoryPackage platformInterface =
272+
createFakePlugin('foo_platform_interface', pluginGroupDir);
273+
274+
void addFakeTempPubspecOverrides(RepositoryPackage package) {
275+
final String contents = package.pubspecFile.readAsStringSync();
276+
package.pubspecFile.writeAsStringSync('''
277+
$contents
278+
279+
# FOR TESTING AND INITIAL REVIEW ONLY. $kDoNotLandWarning.
280+
dependency_overrides:
281+
foo_platform_interface:
282+
path: ../../../foo/foo_platform_interface
283+
''');
284+
}
285+
286+
addFakeTempPubspecOverrides(appFacing.getExamples().first);
287+
addFakeTempPubspecOverrides(implementation.getExamples().first);
288+
289+
const String appFacingChanges = '''
290+
diff --git a/packages/foo/foo/lib/foo.dart b/packages/foo/foo/lib/foo.dart
291+
index abc123..def456 100644
292+
--- a/packages/foo/foo/lib/foo.dart
293+
+++ b/packages/foo/foo/lib/foo.dart
294+
@@ -51,6 +51,9 @@ Future<bool> launchUrl(
295+
return true;
296+
}
297+
298+
+// This is a new method
299+
+bool foo() => true;
300+
+
301+
// This in an existing method
302+
void aMethod() {
303+
// Do things.
304+
''';
305+
306+
final String changedFileOutput = <File>[
307+
appFacing.libDirectory.childFile('foo.dart'),
308+
implementation.libDirectory.childFile('foo.dart'),
309+
platformInterface.pubspecFile,
310+
platformInterface.libDirectory.childFile('foo.dart'),
311+
].map((File file) => file.path).join('\n');
312+
processRunner.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
313+
FakeProcessInfo(MockProcess(stdout: changedFileOutput)),
314+
FakeProcessInfo(MockProcess(stdout: appFacingChanges),
315+
<String>['', 'HEAD', '--', '/packages/foo/foo/lib/foo.dart']),
316+
// The others diffs don't need to be specified, since empty diff is also
317+
// treated as a non-comment change.
318+
];
319+
320+
Error? commandError;
321+
final List<String> output = await runCapturingPrint(
322+
runner, <String>['federation-safety-check'], errorHandler: (Error e) {
323+
commandError = e;
324+
});
325+
326+
expect(commandError, isA<ToolExit>());
327+
expect(
328+
output,
329+
containsAllInOrder(<Matcher>[
330+
contains('Running for foo/foo...'),
331+
contains('"DO NOT MERGE" found in pubspec.yaml, so this is assumed to '
332+
'be the initial combination PR for a federated change, following '
333+
'the standard repository procedure. This failure is expected, in '
334+
'order to prevent accidentally landing the temporary overrides, '
335+
'and will automatically be resolved when the temporary overrides '
336+
'are replaced by dependency version bumps later in the process.'),
337+
contains('Running for foo_bar...'),
338+
contains('"DO NOT MERGE" found in pubspec.yaml'),
339+
contains('The following packages had errors:'),
340+
contains('foo/foo:\n'
341+
' Unresolved combo PR.'),
342+
contains('foo_bar:\n'
343+
' Unresolved combo PR.'),
344+
]),
345+
);
346+
});
347+
265348
test('ignores test-only changes to interface packages', () async {
266349
final Directory pluginGroupDir = packagesDir.childDirectory('foo');
267350
final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir);

0 commit comments

Comments
 (0)