forked from flutter/plugins
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[flutter_tools] refactor packages_autoroller.dart script (#106580)
- Loading branch information
1 parent
28d271e
commit aac5e95
Showing
5 changed files
with
443 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import 'package:process/process.dart'; | |
import 'git.dart'; | ||
import 'globals.dart'; | ||
import 'repository.dart'; | ||
import 'stdio.dart'; | ||
|
||
/// A service for rolling the SDK's pub packages to latest and open a PR upstream. | ||
class PackageAutoroller { | ||
|
@@ -19,7 +20,10 @@ class PackageAutoroller { | |
required this.framework, | ||
required this.orgName, | ||
required this.processManager, | ||
this.githubUsername = 'fluttergithubbot', | ||
Stdio? stdio, | ||
}) { | ||
this.stdio = stdio ?? VerboseStdio.local(); | ||
if (token.trim().isEmpty) { | ||
throw Exception('empty token!'); | ||
} | ||
|
@@ -31,12 +35,16 @@ class PackageAutoroller { | |
} | ||
} | ||
|
||
late final Stdio stdio; | ||
|
||
final FrameworkRepository framework; | ||
final ProcessManager processManager; | ||
|
||
/// Path to GitHub CLI client. | ||
final String githubClient; | ||
|
||
final String githubUsername; | ||
|
||
/// GitHub API access token. | ||
final String token; | ||
|
||
|
@@ -63,23 +71,46 @@ This PR was generated by `flutter update-packages --force-upgrade`. | |
return name(x); | ||
})(); | ||
|
||
void log(String message) { | ||
stdio.printStatus(_redactToken(message)); | ||
} | ||
|
||
/// Name of the GitHub organization to push the feature branch to. | ||
final String orgName; | ||
|
||
Future<void> roll() async { | ||
await authLogin(); | ||
await updatePackages(); | ||
await pushBranch(); | ||
await createPr( | ||
repository: await framework.checkoutDirectory, | ||
); | ||
await authLogout(); | ||
try { | ||
await authLogin(); | ||
final bool openPrAlready = await hasOpenPrs(); | ||
if (openPrAlready) { | ||
// Don't open multiple roll PRs. | ||
return; | ||
} | ||
final bool didUpdate = await updatePackages(); | ||
if (!didUpdate) { | ||
log('Packages are already at latest.'); | ||
return; | ||
} | ||
await pushBranch(); | ||
await createPr(repository: await framework.checkoutDirectory); | ||
await authLogout(); | ||
} on Exception catch (exception) { | ||
final String message = _redactToken(exception.toString()); | ||
throw Exception('${exception.runtimeType}: $message'); | ||
} | ||
} | ||
|
||
Future<void> updatePackages({ | ||
// Ensure we don't leak the GitHub token in exception messages | ||
String _redactToken(String message) => message.replaceAll(token, '[GitHub TOKEN]'); | ||
|
||
/// Attempt to update all pub packages. | ||
/// | ||
/// Will return whether or not any changes were made. | ||
Future<bool> updatePackages({ | ||
bool verbose = true, | ||
String author = 'flutter-packages-autoroller <[email protected]>' | ||
}) async { | ||
final String author = '$githubUsername <$githubUsername@gmail.com>'; | ||
|
||
await framework.newBranch(await featureBranchName); | ||
final io.Process flutterProcess = await framework.streamFlutter(<String>[ | ||
if (verbose) '--verbose', | ||
|
@@ -90,18 +121,26 @@ This PR was generated by `flutter update-packages --force-upgrade`. | |
if (exitCode != 0) { | ||
throw ConductorException('Failed to update packages with exit code $exitCode'); | ||
} | ||
// If the git checkout is clean, then pub packages are already at latest that cleanly resolve. | ||
if (await framework.gitCheckoutClean()) { | ||
return false; | ||
} | ||
await framework.commit( | ||
'roll packages', | ||
addFirst: true, | ||
author: author, | ||
); | ||
return true; | ||
} | ||
|
||
Future<void> pushBranch() async { | ||
final String projectName = framework.mirrorRemote!.url.split(r'/').last; | ||
// Encode the token into the remote URL for authentication to work | ||
final String remote = 'https://$token@$hostname/$orgName/$projectName'; | ||
await framework.pushRef( | ||
fromRef: await featureBranchName, | ||
toRef: await featureBranchName, | ||
remote: framework.mirrorRemote!.url, | ||
remote: remote, | ||
); | ||
} | ||
|
||
|
@@ -123,7 +162,7 @@ This PR was generated by `flutter update-packages --force-upgrade`. | |
'https', | ||
'--with-token', | ||
], | ||
stdin: token, | ||
stdin: '$token\n', | ||
); | ||
} | ||
|
||
|
@@ -151,6 +190,8 @@ This PR was generated by `flutter update-packages --force-upgrade`. | |
'$orgName:${await featureBranchName}', | ||
'--base', | ||
base, | ||
'--label', | ||
'tool', | ||
if (draft) | ||
'--draft', | ||
], | ||
|
@@ -165,13 +206,16 @@ This PR was generated by `flutter update-packages --force-upgrade`. | |
]); | ||
} | ||
|
||
Future<void> cli( | ||
/// Run a sub-process with the GitHub CLI client. | ||
/// | ||
/// Will return STDOUT of the sub-process. | ||
Future<String> cli( | ||
List<String> args, { | ||
bool allowFailure = false, | ||
String? stdin, | ||
String? workingDirectory, | ||
}) async { | ||
print('Executing "$githubClient ${args.join(' ')}" in $workingDirectory'); | ||
log('Executing "$githubClient ${args.join(' ')}" in $workingDirectory'); | ||
final io.Process process = await processManager.start( | ||
<String>[githubClient, ...args], | ||
workingDirectory: workingDirectory, | ||
|
@@ -203,6 +247,36 @@ This PR was generated by `flutter update-packages --force-upgrade`. | |
args, | ||
); | ||
} | ||
print(stdout); | ||
log(stdout); | ||
return stdout; | ||
} | ||
|
||
Future<bool> hasOpenPrs() async { | ||
// gh pr list --author christopherfujino --repo flutter/flutter --state open --json number | ||
final String openPrString = await cli(<String>[ | ||
'pr', | ||
'list', | ||
'--author', | ||
githubUsername, | ||
'--repo', | ||
'flutter/flutter', | ||
'--state', | ||
'open', | ||
// We are only interested in pub rolls, not devicelab flaky PRs | ||
'--label', | ||
'tool', | ||
// Return structured JSON with the PR numbers of open PRs | ||
'--json', | ||
'number', | ||
]); | ||
|
||
// This will be an array of objects, one for each open PR. | ||
final List<Object?> openPrs = json.decode(openPrString) as List<Object?>; | ||
|
||
if (openPrs.isNotEmpty) { | ||
log('$githubUsername already has open tool PRs:\n$openPrs'); | ||
return true; | ||
} | ||
return false; | ||
} | ||
} |
Oops, something went wrong.