[flutter_plugin_tools] Complete migration to NNBD (#4048)

This commit is contained in:
stuartmorgan
2021-06-12 12:44:04 -07:00
committed by GitHub
parent 038c1796b0
commit ce948ce3cb
5 changed files with 109 additions and 104 deletions

View File

@ -5,6 +5,7 @@
compatibility.
- `xctest` now supports running macOS tests in addition to iOS
- **Breaking change**: it now requires an `--ios` and/or `--macos` flag.
- The tooling now runs in strong null-safe mode.
## 0.2.0

View File

@ -2,6 +2,4 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
export 'package:flutter_plugin_tools/src/main.dart';

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:io' as io;
import 'package:args/command_runner.dart';

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
@ -18,6 +16,17 @@ import 'package:yaml/yaml.dart';
import 'common.dart';
@immutable
class _RemoteInfo {
const _RemoteInfo({required this.name, required this.url});
/// The git name for the remote.
final String name;
/// The remote's URL.
final String url;
}
/// Wraps pub publish with a few niceties used by the flutter/plugin team.
///
/// 1. Checks for any modified files in git and refuses to publish if there's an
@ -35,8 +44,8 @@ class PublishPluginCommand extends PluginCommand {
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
Print print = print,
io.Stdin stdinput,
GitDir gitDir,
io.Stdin? stdinput,
GitDir? gitDir,
}) : _print = print,
_stdin = stdinput ?? io.stdin,
super(packagesDir, processRunner: processRunner, gitDir: gitDir) {
@ -118,7 +127,7 @@ class PublishPluginCommand extends PluginCommand {
final Print _print;
final io.Stdin _stdin;
StreamSubscription<String> _stdinSubscription;
StreamSubscription<String>? _stdinSubscription;
@override
Future<void> run() async {
@ -138,14 +147,20 @@ class PublishPluginCommand extends PluginCommand {
_print('$packagesPath is not a valid Git repository.');
throw ToolExit(1);
}
final GitDir baseGitDir =
final GitDir baseGitDir = gitDir ??
await GitDir.fromExisting(packagesPath, allowSubdirectory: true);
final bool shouldPushTag = getBoolArg(_pushTagsOption);
final String remote = getStringArg(_remoteOption);
String remoteUrl;
_RemoteInfo? remote;
if (shouldPushTag) {
remoteUrl = await _verifyRemote(remote);
final String remoteName = getStringArg(_remoteOption);
final String? remoteUrl = await _verifyRemote(remoteName);
if (remoteUrl == null) {
printError(
'Unable to find URL for remote $remoteName; cannot push tags');
throw ToolExit(1);
}
remote = _RemoteInfo(name: remoteName, url: remoteUrl);
}
_print('Local repo is ready!');
if (getBoolArg(_dryRunFlag)) {
@ -155,27 +170,21 @@ class PublishPluginCommand extends PluginCommand {
bool successful;
if (publishAllChanged) {
successful = await _publishAllChangedPackages(
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag,
baseGitDir: baseGitDir,
remoteForTagPush: remote,
);
} else {
successful = await _publishAndTagPackage(
packageDir: _getPackageDir(package),
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag,
remoteForTagPush: remote,
);
}
await _finish(successful);
}
Future<bool> _publishAllChangedPackages({
String remote,
String remoteUrl,
bool shouldPushTag,
GitDir baseGitDir,
required GitDir baseGitDir,
_RemoteInfo? remoteForTagPush,
}) async {
final GitVersionFinder gitVersionFinder = await retrieveVersionFinder();
final List<String> changedPubspecs =
@ -215,9 +224,7 @@ class PublishPluginCommand extends PluginCommand {
_print('\n');
if (await _publishAndTagPackage(
packageDir: pubspecFile.parent,
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag,
remoteForTagPush: remoteForTagPush,
)) {
packagesReleased.add(pubspecFile.parent.basename);
} else {
@ -237,13 +244,11 @@ class PublishPluginCommand extends PluginCommand {
// Publish the package to pub with `pub publish`.
// If `_tagReleaseOption` is on, git tag the release.
// If `shouldPushTag` is `true`, the tag will be pushed to `remote`.
// Returns `true` if publishing and tag are successful.
// If `remoteForTagPush` is non-null, the tag will be pushed to that remote.
// Returns `true` if publishing and tagging are successful.
Future<bool> _publishAndTagPackage({
@required Directory packageDir,
@required String remote,
@required String remoteUrl,
@required bool shouldPushTag,
required Directory packageDir,
_RemoteInfo? remoteForTagPush,
}) async {
if (!await _publishPlugin(packageDir: packageDir)) {
return false;
@ -251,9 +256,7 @@ class PublishPluginCommand extends PluginCommand {
if (getBoolArg(_tagReleaseOption)) {
if (!await _tagRelease(
packageDir: packageDir,
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag,
remoteForPush: remoteForTagPush,
)) {
return false;
}
@ -264,9 +267,9 @@ class PublishPluginCommand extends PluginCommand {
// Returns a [_CheckNeedsReleaseResult] that indicates the result.
Future<_CheckNeedsReleaseResult> _checkNeedsRelease({
@required File pubspecFile,
@required GitVersionFinder gitVersionFinder,
@required List<String> existingTags,
required File pubspecFile,
required GitVersionFinder gitVersionFinder,
required List<String> existingTags,
}) async {
if (!pubspecFile.existsSync()) {
_print('''
@ -287,10 +290,6 @@ Safe to ignore if the package is deleted in this commit.
return _CheckNeedsReleaseResult.failure;
}
if (pubspec.name == null) {
_print('Fatal: Package name is null.');
return _CheckNeedsReleaseResult.failure;
}
// Get latest tagged version and compare with the current version.
// TODO(cyanglaz): Check latest version of the package on pub instead of git
// https://github.com/flutter/flutter/issues/81047
@ -301,7 +300,7 @@ Safe to ignore if the package is deleted in this commit.
if (latestTag.isNotEmpty) {
final String latestTaggedVersion = latestTag.split('-v').last;
final Version latestVersion = Version.parse(latestTaggedVersion);
if (pubspec.version < latestVersion) {
if (pubspec.version! < latestVersion) {
_print(
'The new version (${pubspec.version}) is lower than the current version ($latestVersion) for ${pubspec.name}.\nThis git commit is a revert, no release is tagged.');
return _CheckNeedsReleaseResult.noRelease;
@ -313,7 +312,7 @@ Safe to ignore if the package is deleted in this commit.
// Publish the plugin.
//
// Returns `true` if successful, `false` otherwise.
Future<bool> _publishPlugin({@required Directory packageDir}) async {
Future<bool> _publishPlugin({required Directory packageDir}) async {
final bool gitStatusOK = await _checkGitStatus(packageDir);
if (!gitStatusOK) {
return false;
@ -326,14 +325,13 @@ Safe to ignore if the package is deleted in this commit.
return true;
}
// Tag the release with <plugin-name>-v<version>
// Tag the release with <plugin-name>-v<version>, and, if [remoteForTagPush]
// is provided, push it to that remote.
//
// Return `true` if successful, `false` otherwise.
Future<bool> _tagRelease({
@required Directory packageDir,
@required String remote,
@required String remoteUrl,
@required bool shouldPushTag,
required Directory packageDir,
_RemoteInfo? remoteForPush,
}) async {
final String tag = _getTag(packageDir);
_print('Tagging release $tag...');
@ -350,23 +348,20 @@ Safe to ignore if the package is deleted in this commit.
}
}
if (!shouldPushTag) {
if (remoteForPush == null) {
return true;
}
_print('Pushing tag to $remote...');
_print('Pushing tag to ${remoteForPush.name}...');
return await _pushTagToRemote(
remote: remote,
tag: tag,
remoteUrl: remoteUrl,
remote: remoteForPush,
);
}
Future<void> _finish(bool successful) async {
if (_stdinSubscription != null) {
await _stdinSubscription.cancel();
await _stdinSubscription?.cancel();
_stdinSubscription = null;
}
if (successful) {
_print('Done!');
} else {
@ -408,15 +403,15 @@ Safe to ignore if the package is deleted in this commit.
return statusOutput.isEmpty;
}
Future<String> _verifyRemote(String remote) async {
final io.ProcessResult remoteInfo = await processRunner.run(
Future<String?> _verifyRemote(String remote) async {
final io.ProcessResult getRemoteUrlResult = await processRunner.run(
'git',
<String>['remote', 'get-url', remote],
workingDir: packagesDir,
exitOnError: true,
logOnError: true,
);
return remoteInfo.stdout as String;
return getRemoteUrlResult.stdout as String?;
}
Future<bool> _publish(Directory packageDir) async {
@ -471,15 +466,14 @@ Safe to ignore if the package is deleted in this commit.
//
// Return `true` if successful, `false` otherwise.
Future<bool> _pushTagToRemote({
@required String remote,
@required String tag,
@required String remoteUrl,
required String tag,
required _RemoteInfo remote,
}) async {
assert(remote != null && tag != null && remoteUrl != null);
assert(remote != null && tag != null);
if (!getBoolArg(_skipConfirmationFlag)) {
_print('Ready to push $tag to $remoteUrl (y/n)?');
final String input = _stdin.readLineSync();
if (input.toLowerCase() != 'y') {
_print('Ready to push $tag to ${remote.url} (y/n)?');
final String? input = _stdin.readLineSync();
if (input?.toLowerCase() != 'y') {
_print('Tag push canceled.');
return false;
}
@ -487,7 +481,7 @@ Safe to ignore if the package is deleted in this commit.
if (!getBoolArg(_dryRunFlag)) {
final io.ProcessResult result = await processRunner.run(
'git',
<String>['push', remote, tag],
<String>['push', remote.name, tag],
workingDir: packagesDir,
exitOnError: false,
logOnError: true,
@ -500,15 +494,16 @@ Safe to ignore if the package is deleted in this commit.
}
void _ensureValidPubCredential() {
final File credentialFile = packagesDir.fileSystem.file(_credentialsPath);
final String credentialsPath = _credentialsPath;
final File credentialFile = packagesDir.fileSystem.file(credentialsPath);
if (credentialFile.existsSync() &&
credentialFile.readAsStringSync().isNotEmpty) {
return;
}
final String credential = io.Platform.environment[_pubCredentialName];
final String? credential = io.Platform.environment[_pubCredentialName];
if (credential == null) {
printError('''
No pub credential available. Please check if `~/.pub-cache/credentials.json` is valid.
No pub credential available. Please check if `$credentialsPath` is valid.
If running this command on CI, you can set the pub credential content in the $_pubCredentialName environment variable.
''');
throw ToolExit(1);
@ -529,17 +524,32 @@ If running this command on CI, you can set the pub credential content in the $_p
final String _credentialsPath = () {
// This follows the same logic as pub:
// https://github.com/dart-lang/pub/blob/d99b0d58f4059d7bb4ac4616fd3d54ec00a2b5d4/lib/src/system_cache.dart#L34-L43
String cacheDir;
final String pubCache = io.Platform.environment['PUB_CACHE'];
String? cacheDir;
final String? pubCache = io.Platform.environment['PUB_CACHE'];
print(pubCache);
if (pubCache != null) {
cacheDir = pubCache;
} else if (io.Platform.isWindows) {
final String appData = io.Platform.environment['APPDATA'];
cacheDir = p.join(appData, 'Pub', 'Cache');
final String? appData = io.Platform.environment['APPDATA'];
if (appData == null) {
printError('"APPDATA" environment variable is not set.');
} else {
cacheDir = p.join(io.Platform.environment['HOME'], '.pub-cache');
cacheDir = p.join(appData, 'Pub', 'Cache');
}
} else {
final String? home = io.Platform.environment['HOME'];
if (home == null) {
printError('"HOME" environment variable is not set.');
} else {
cacheDir = p.join(home, '.pub-cache');
}
}
if (cacheDir == null) {
printError('Unable to determine pub cache location');
throw ToolExit(1);
}
return p.join(cacheDir, 'credentials.json');
}();

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
@ -22,15 +20,15 @@ import 'util.dart';
void main() {
const String testPluginName = 'foo';
List<String> printedMessages;
late List<String> printedMessages;
Directory testRoot;
Directory packagesDir;
Directory pluginDir;
GitDir gitDir;
TestProcessRunner processRunner;
CommandRunner<void> commandRunner;
MockStdin mockStdin;
late Directory testRoot;
late Directory packagesDir;
late Directory pluginDir;
late GitDir gitDir;
late TestProcessRunner processRunner;
late CommandRunner<void> commandRunner;
late MockStdin mockStdin;
// This test uses a local file system instead of an in memory one throughout
// so that git actually works. In setup we initialize a mono repo of plugins
// with one package and commit everything to Git.
@ -65,7 +63,7 @@ void main() {
commandRunner = CommandRunner<void>('tester', '')
..addCommand(PublishPluginCommand(packagesDir,
processRunner: processRunner,
print: (Object message) => printedMessages.add(message.toString()),
print: (Object? message) => printedMessages.add(message.toString()),
stdinput: mockStdin,
gitDir: gitDir));
});
@ -285,9 +283,9 @@ void main() {
'--no-push-tags',
]);
final String tag =
final String? tag =
(await gitDir.runCommand(<String>['show-ref', 'fake_package-v0.0.1']))
.stdout as String;
.stdout as String?;
expect(tag, isNotEmpty);
});
@ -303,10 +301,10 @@ void main() {
throwsA(const TypeMatcher<ToolExit>()));
expect(printedMessages, contains('Publish foo failed.'));
final String tag = (await gitDir.runCommand(
final String? tag = (await gitDir.runCommand(
<String>['show-ref', 'fake_package-v0.0.1'],
throwOnError: false))
.stdout as String;
.stdout as String?;
expect(tag, isEmpty);
});
});
@ -838,20 +836,20 @@ void main() {
class TestProcessRunner extends ProcessRunner {
final List<io.ProcessResult> results = <io.ProcessResult>[];
// Most recent returned publish process.
MockProcess mockPublishProcess;
late MockProcess mockPublishProcess;
final List<String> mockPublishArgs = <String>[];
final MockProcessResult mockPushTagsResult = MockProcessResult();
final List<String> pushTagsArgs = <String>[];
String mockPublishStdout;
String mockPublishStderr;
int mockPublishCompleteCode;
String? mockPublishStdout;
String? mockPublishStderr;
int? mockPublishCompleteCode;
@override
Future<io.ProcessResult> run(
String executable,
List<String> args, {
Directory workingDir,
Directory? workingDir,
bool exitOnError = false,
bool logOnError = false,
Encoding stdoutEncoding = io.systemEncoding,
@ -874,7 +872,7 @@ class TestProcessRunner extends ProcessRunner {
@override
Future<io.Process> start(String executable, List<String> args,
{Directory workingDirectory}) async {
{Directory? workingDirectory}) async {
/// Never actually publish anything. Start is always and only used for this
/// since it returns something we can route stdin through.
assert(executable == 'flutter' &&
@ -884,10 +882,10 @@ class TestProcessRunner extends ProcessRunner {
mockPublishArgs.addAll(args);
mockPublishProcess = MockProcess();
if (mockPublishStdout != null) {
mockPublishProcess.stdoutController.add(utf8.encode(mockPublishStdout));
mockPublishProcess.stdoutController.add(utf8.encode(mockPublishStdout!));
}
if (mockPublishStderr != null) {
mockPublishProcess.stderrController.add(utf8.encode(mockPublishStderr));
mockPublishProcess.stderrController.add(utf8.encode(mockPublishStderr!));
}
if (mockPublishCompleteCode != null) {
mockPublishProcess.exitCodeCompleter.complete(mockPublishCompleteCode);
@ -899,8 +897,8 @@ class TestProcessRunner extends ProcessRunner {
class MockStdin extends Mock implements io.Stdin {
List<List<int>> mockUserInputs = <List<int>>[];
StreamController<List<int>> _controller;
String readLineOutput;
late StreamController<List<int>> _controller;
String? readLineOutput;
@override
Stream<S> transform<S>(StreamTransformer<List<int>, S> streamTransformer) {
@ -915,14 +913,14 @@ class MockStdin extends Mock implements io.Stdin {
}
@override
StreamSubscription<List<int>> listen(void onData(List<int> event),
{Function onError, void onDone(), bool cancelOnError}) {
StreamSubscription<List<int>> listen(void onData(List<int> event)?,
{Function? onError, void onDone()?, bool? cancelOnError}) {
return _controller.stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@override
String readLineSync(
String? readLineSync(
{Encoding encoding = io.systemEncoding,
bool retainNewlines = false}) =>
readLineOutput;