[tool] add all and dry-run flags to publish-plugin command (#3776)

This commit is contained in:
Chris Yang
2021-04-30 15:29:04 -07:00
committed by GitHub
parent 2bd3f401a7
commit 184e9a7023
3 changed files with 891 additions and 100 deletions

View File

@ -4,12 +4,14 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io' as io;
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:git/git.dart'; import 'package:git/git.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:pub_semver/pub_semver.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:yaml/yaml.dart'; import 'package:yaml/yaml.dart';
import 'common.dart'; import 'common.dart';
@ -32,10 +34,12 @@ class PublishPluginCommand extends PluginCommand {
FileSystem fileSystem, { FileSystem fileSystem, {
ProcessRunner processRunner = const ProcessRunner(), ProcessRunner processRunner = const ProcessRunner(),
Print print = print, Print print = print,
Stdin stdinput, io.Stdin stdinput,
GitDir gitDir,
}) : _print = print, }) : _print = print,
_stdin = stdinput ?? stdin, _stdin = stdinput ?? io.stdin,
super(packagesDir, fileSystem, processRunner: processRunner) { super(packagesDir, fileSystem,
processRunner: processRunner, gitDir: gitDir) {
argParser.addOption( argParser.addOption(
_packageOption, _packageOption,
help: 'The package to publish.' help: 'The package to publish.'
@ -64,6 +68,22 @@ class PublishPluginCommand extends PluginCommand {
// Flutter convention is to use "upstream" for the single source of truth, and "origin" for personal forks. // Flutter convention is to use "upstream" for the single source of truth, and "origin" for personal forks.
defaultsTo: 'upstream', defaultsTo: 'upstream',
); );
argParser.addFlag(
_allChangedFlag,
help:
'Release all plugins that contains pubspec changes at the current commit compares to the base-sha.\n'
'The $_packageOption option is ignored if this is on.',
defaultsTo: false,
);
argParser.addFlag(
_dryRunFlag,
help:
'Skips the real `pub publish` and `git tag` commands and assumes both commands are successful.\n'
'This does not run `pub publish --dry-run`.\n'
'If you want to run the command with `pub publish --dry-run`, use `pub-publish-flags=--dry-run`',
defaultsTo: false,
negatable: true,
);
} }
static const String _packageOption = 'package'; static const String _packageOption = 'package';
@ -71,6 +91,8 @@ class PublishPluginCommand extends PluginCommand {
static const String _pushTagsOption = 'push-tags'; static const String _pushTagsOption = 'push-tags';
static const String _pubFlagsOption = 'pub-publish-flags'; static const String _pubFlagsOption = 'pub-publish-flags';
static const String _remoteOption = 'remote'; static const String _remoteOption = 'remote';
static const String _allChangedFlag = 'all-changed';
static const String _dryRunFlag = 'dry-run';
// Version tags should follow <package-name>-v<semantic-version>. For example, // Version tags should follow <package-name>-v<semantic-version>. For example,
// `flutter_plugin_tools-v0.0.24`. // `flutter_plugin_tools-v0.0.24`.
@ -84,14 +106,14 @@ class PublishPluginCommand extends PluginCommand {
'Attempts to publish the given plugin and tag its release on GitHub.'; 'Attempts to publish the given plugin and tag its release on GitHub.';
final Print _print; final Print _print;
final Stdin _stdin; final io.Stdin _stdin;
// The directory of the actual package that we are publishing.
StreamSubscription<String> _stdinSubscription; StreamSubscription<String> _stdinSubscription;
@override @override
Future<void> run() async { Future<void> run() async {
final String package = argResults[_packageOption] as String; final String package = argResults[_packageOption] as String;
if (package == null) { final bool publishAllChanged = argResults[_allChangedFlag] as bool;
if (package == null && !publishAllChanged) {
_print( _print(
'Must specify a package to publish. See `plugin_tools help publish-plugin`.'); 'Must specify a package to publish. See `plugin_tools help publish-plugin`.');
throw ToolExit(1); throw ToolExit(1);
@ -102,6 +124,8 @@ class PublishPluginCommand extends PluginCommand {
_print('$packagesDir is not a valid Git repository.'); _print('$packagesDir is not a valid Git repository.');
throw ToolExit(1); throw ToolExit(1);
} }
final GitDir baseGitDir =
await GitDir.fromExisting(packagesDir.path, allowSubdirectory: true);
final bool shouldPushTag = argResults[_pushTagsOption] == true; final bool shouldPushTag = argResults[_pushTagsOption] == true;
final String remote = argResults[_remoteOption] as String; final String remote = argResults[_remoteOption] as String;
@ -110,50 +134,229 @@ class PublishPluginCommand extends PluginCommand {
remoteUrl = await _verifyRemote(remote); remoteUrl = await _verifyRemote(remote);
} }
_print('Local repo is ready!'); _print('Local repo is ready!');
if (argResults[_dryRunFlag] as bool) {
final Directory packageDir = _getPackageDir(package); _print('=============== DRY RUN ===============');
await _publishPlugin(packageDir: packageDir);
if (argResults[_tagReleaseOption] as bool) {
await _tagRelease(
packageDir: packageDir,
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag);
} }
await _finishSuccesfully();
bool successful;
if (publishAllChanged) {
successful = await _publishAllChangedPackages(
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag,
baseGitDir: baseGitDir,
);
} else {
successful = await _publishAndTagPackage(
packageDir: _getPackageDir(package),
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag,
);
}
await _finish(successful);
} }
Future<void> _publishPlugin({@required Directory packageDir}) async { Future<bool> _publishAllChangedPackages({
await _checkGitStatus(packageDir); String remote,
await _publish(packageDir); String remoteUrl,
bool shouldPushTag,
GitDir baseGitDir,
}) async {
final GitVersionFinder gitVersionFinder = await retrieveVersionFinder();
final List<String> changedPubspecs =
await gitVersionFinder.getChangedPubSpecs();
if (changedPubspecs.isEmpty) {
_print('No version updates in this commit.');
return true;
}
_print('Getting existing tags...');
final io.ProcessResult existingTagsResult =
await baseGitDir.runCommand(<String>['tag', '--sort=-committerdate']);
final List<String> existingTags = (existingTagsResult.stdout as String)
.split('\n')
..removeWhere((String element) => element.isEmpty);
final List<String> packagesReleased = <String>[];
final List<String> packagesFailed = <String>[];
for (final String pubspecPath in changedPubspecs) {
final File pubspecFile =
fileSystem.directory(baseGitDir.path).childFile(pubspecPath);
final _CheckNeedsReleaseResult result = await _checkNeedsRelease(
pubspecFile: pubspecFile,
gitVersionFinder: gitVersionFinder,
existingTags: existingTags,
);
switch (result) {
case _CheckNeedsReleaseResult.release:
break;
case _CheckNeedsReleaseResult.noRelease:
continue;
case _CheckNeedsReleaseResult.failure:
packagesFailed.add(pubspecFile.parent.basename);
continue;
}
_print('\n');
if (await _publishAndTagPackage(
packageDir: pubspecFile.parent,
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag,
)) {
packagesReleased.add(pubspecFile.parent.basename);
} else {
packagesFailed.add(pubspecFile.parent.basename);
}
_print('\n');
}
if (packagesReleased.isNotEmpty) {
_print('Packages released: ${packagesReleased.join(', ')}');
}
if (packagesFailed.isNotEmpty) {
_print(
'Failed to release the following packages: ${packagesFailed.join(', ')}, see above for details.');
}
return packagesFailed.isEmpty;
}
// 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.
Future<bool> _publishAndTagPackage({
@required Directory packageDir,
@required String remote,
@required String remoteUrl,
@required bool shouldPushTag,
}) async {
if (!await _publishPlugin(packageDir: packageDir)) {
return false;
}
if (argResults[_tagReleaseOption] as bool) {
if (!await _tagRelease(
packageDir: packageDir,
remote: remote,
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag,
)) {
return false;
}
}
_print('Released [${packageDir.basename}] successfully.');
return true;
}
// Returns a [_CheckNeedsReleaseResult] that indicates the result.
Future<_CheckNeedsReleaseResult> _checkNeedsRelease({
@required File pubspecFile,
@required GitVersionFinder gitVersionFinder,
@required List<String> existingTags,
}) async {
if (!pubspecFile.existsSync()) {
_print('''
The file at The pubspec file at ${pubspecFile.path} does not exist. Publishing will not happen for ${pubspecFile.parent.basename}.
Safe to ignore if the package is deleted in this commit.
''');
return _CheckNeedsReleaseResult.noRelease;
}
final Pubspec pubspec = Pubspec.parse(pubspecFile.readAsStringSync());
if (pubspec.publishTo == 'none') {
return _CheckNeedsReleaseResult.noRelease;
}
if (pubspec.version == null) {
_print('No version found. A package that intentionally has no version should be marked "publish_to: none"');
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
final String latestTag = existingTags.firstWhere(
(String tag) => tag.split('-v').first == pubspec.name,
orElse: () => '');
if (latestTag.isNotEmpty) {
final String latestTaggedVersion = latestTag.split('-v').last;
final Version latestVersion = Version.parse(latestTaggedVersion);
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;
}
}
return _CheckNeedsReleaseResult.release;
}
// Publish the plugin.
//
// Returns `true` if successful, `false` otherwise.
Future<bool> _publishPlugin({@required Directory packageDir}) async {
final bool gitStatusOK = await _checkGitStatus(packageDir);
if (!gitStatusOK) {
return false;
}
final bool publishOK = await _publish(packageDir);
if (!publishOK) {
return false;
}
_print('Package published!'); _print('Package published!');
return true;
} }
Future<void> _tagRelease( // Tag the release with <plugin-name>-v<version>
{@required Directory packageDir, //
@required String remote, // Return `true` if successful, `false` otherwise.
@required String remoteUrl, Future<bool> _tagRelease({
@required bool shouldPushTag}) async { @required Directory packageDir,
@required String remote,
@required String remoteUrl,
@required bool shouldPushTag,
}) async {
final String tag = _getTag(packageDir); final String tag = _getTag(packageDir);
_print('Tagging release $tag...'); _print('Tagging release $tag...');
await processRunner.run( if (!(argResults[_dryRunFlag] as bool)) {
'git', final io.ProcessResult result = await processRunner.run(
<String>['tag', tag], 'git',
workingDir: packageDir, <String>['tag', tag],
exitOnError: true, workingDir: packageDir,
logOnError: true, exitOnError: false,
); logOnError: true,
);
if (result.exitCode != 0) {
return false;
}
}
if (!shouldPushTag) { if (!shouldPushTag) {
return; return true;
} }
_print('Pushing tag to $remote...'); _print('Pushing tag to $remote...');
await _pushTagToRemote(remote: remote, tag: tag, remoteUrl: remoteUrl); return await _pushTagToRemote(
remote: remote,
tag: tag,
remoteUrl: remoteUrl,
);
} }
Future<void> _finishSuccesfully() async { Future<void> _finish(bool successful) async {
await _stdinSubscription.cancel(); if (_stdinSubscription != null) {
_print('Done!'); await _stdinSubscription.cancel();
_stdinSubscription = null;
}
if (successful) {
_print('Done!');
} else {
_print('Failed, see above for details.');
throw ToolExit(1);
}
} }
// Returns the packageDirectory based on the package name. // Returns the packageDirectory based on the package name.
@ -167,14 +370,17 @@ class PublishPluginCommand extends PluginCommand {
return packageDir; return packageDir;
} }
Future<void> _checkGitStatus(Directory packageDir) async { Future<bool> _checkGitStatus(Directory packageDir) async {
final ProcessResult statusResult = await processRunner.run( final io.ProcessResult statusResult = await processRunner.run(
'git', 'git',
<String>['status', '--porcelain', '--ignored', packageDir.absolute.path], <String>['status', '--porcelain', '--ignored', packageDir.absolute.path],
workingDir: packageDir, workingDir: packageDir,
logOnError: true, logOnError: true,
exitOnError: true, exitOnError: false,
); );
if (statusResult.exitCode != 0) {
return false;
}
final String statusOutput = statusResult.stdout as String; final String statusOutput = statusResult.stdout as String;
if (statusOutput.isNotEmpty) { if (statusOutput.isNotEmpty) {
@ -182,12 +388,12 @@ class PublishPluginCommand extends PluginCommand {
"There are files in the package directory that haven't been saved in git. Refusing to publish these files:\n\n" "There are files in the package directory that haven't been saved in git. Refusing to publish these files:\n\n"
'$statusOutput\n' '$statusOutput\n'
'If the directory should be clean, you can run `git clean -xdf && git reset --hard HEAD` to wipe all local changes.'); 'If the directory should be clean, you can run `git clean -xdf && git reset --hard HEAD` to wipe all local changes.');
throw ToolExit(1);
} }
return statusOutput.isEmpty;
} }
Future<String> _verifyRemote(String remote) async { Future<String> _verifyRemote(String remote) async {
final ProcessResult remoteInfo = await processRunner.run( final io.ProcessResult remoteInfo = await processRunner.run(
'git', 'git',
<String>['remote', 'get-url', remote], <String>['remote', 'get-url', remote],
workingDir: packagesDir, workingDir: packagesDir,
@ -197,28 +403,31 @@ class PublishPluginCommand extends PluginCommand {
return remoteInfo.stdout as String; return remoteInfo.stdout as String;
} }
Future<void> _publish(Directory packageDir) async { Future<bool> _publish(Directory packageDir) async {
final List<String> publishFlags = final List<String> publishFlags =
argResults[_pubFlagsOption] as List<String>; argResults[_pubFlagsOption] as List<String>;
_print( _print(
'Running `pub publish ${publishFlags.join(' ')}` in ${packageDir.absolute.path}...\n'); 'Running `pub publish ${publishFlags.join(' ')}` in ${packageDir.absolute.path}...\n');
final Process publish = await processRunner.start( if (!(argResults[_dryRunFlag] as bool)) {
'flutter', <String>['pub', 'publish'] + publishFlags, final io.Process publish = await processRunner.start(
workingDirectory: packageDir); 'flutter', <String>['pub', 'publish'] + publishFlags,
publish.stdout workingDirectory: packageDir);
.transform(utf8.decoder) publish.stdout
.listen((String data) => _print(data)); .transform(utf8.decoder)
publish.stderr .listen((String data) => _print(data));
.transform(utf8.decoder) publish.stderr
.listen((String data) => _print(data)); .transform(utf8.decoder)
_stdinSubscription = _stdin .listen((String data) => _print(data));
.transform(utf8.decoder) _stdinSubscription ??= _stdin
.listen((String data) => publish.stdin.writeln(data)); .transform(utf8.decoder)
final int result = await publish.exitCode; .listen((String data) => publish.stdin.writeln(data));
if (result != 0) { final int result = await publish.exitCode;
_print('Publish failed. Exiting.'); if (result != 0) {
throw ToolExit(result); _print('Publish ${packageDir.basename} failed.');
return false;
}
} }
return true;
} }
String _getTag(Directory packageDir) { String _getTag(Directory packageDir) {
@ -235,23 +444,44 @@ class PublishPluginCommand extends PluginCommand {
.replaceAll('%VERSION%', version); .replaceAll('%VERSION%', version);
} }
Future<void> _pushTagToRemote( // Pushes the `tag` to `remote`
{@required String remote, //
@required String tag, // Return `true` if successful, `false` otherwise.
@required String remoteUrl}) async { Future<bool> _pushTagToRemote({
@required String remote,
@required String tag,
@required String remoteUrl,
}) async {
assert(remote != null && tag != null && remoteUrl != null); assert(remote != null && tag != null && remoteUrl != null);
_print('Ready to push $tag to $remoteUrl (y/n)?'); _print('Ready to push $tag to $remoteUrl (y/n)?');
final String input = _stdin.readLineSync(); final String input = _stdin.readLineSync();
if (input.toLowerCase() != 'y') { if (input.toLowerCase() != 'y') {
_print('Tag push canceled.'); _print('Tag push canceled.');
throw ToolExit(1); return false;
} }
await processRunner.run( if (!(argResults[_dryRunFlag] as bool)) {
'git', final io.ProcessResult result = await processRunner.run(
<String>['push', remote, tag], 'git',
workingDir: packagesDir, <String>['push', remote, tag],
exitOnError: true, workingDir: packagesDir,
logOnError: true, exitOnError: false,
); logOnError: true,
);
if (result.exitCode != 0) {
return false;
}
}
return true;
} }
} }
enum _CheckNeedsReleaseResult {
// The package needs to be released.
release,
// The package does not need to be released.
noRelease,
// There's an error when trying to determine whether the package needs to be released.
failure,
}

View File

@ -53,7 +53,8 @@ void main() {
mockPackagesDir, mockPackagesDir.fileSystem, mockPackagesDir, mockPackagesDir.fileSystem,
processRunner: processRunner, processRunner: processRunner,
print: (Object message) => printedMessages.add(message.toString()), print: (Object message) => printedMessages.add(message.toString()),
stdinput: mockStdin)); stdinput: mockStdin,
gitDir: await GitDir.fromExisting(mockPackagesDir.path)));
}); });
tearDown(() { tearDown(() {
@ -95,9 +96,11 @@ void main() {
throwsA(const TypeMatcher<ToolExit>())); throwsA(const TypeMatcher<ToolExit>()));
expect( expect(
printedMessages.last, printedMessages,
contains( containsAllInOrder(<String>[
"There are files in the package directory that haven't been saved in git.")); 'There are files in the package directory that haven\'t been saved in git. Refusing to publish these files:\n\n?? foo/tmp\n\nIf the directory should be clean, you can run `git clean -xdf && git reset --hard HEAD` to wipe all local changes.',
'Failed, see above for details.',
]));
}); });
test('fails immediately if the remote doesn\'t exist', () async { test('fails immediately if the remote doesn\'t exist', () async {
@ -110,7 +113,7 @@ void main() {
test("doesn't validate the remote if it's not pushing tags", () async { test("doesn't validate the remote if it's not pushing tags", () async {
// Immediately return 0 when running `pub publish`. // Immediately return 0 when running `pub publish`.
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
await commandRunner.run(<String>[ await commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
@ -131,7 +134,7 @@ void main() {
await gitDir.runCommand(<String>['add', '-A']); await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Initial commit']); await gitDir.runCommand(<String>['commit', '-m', 'Initial commit']);
// Immediately return 0 when running `pub publish`. // Immediately return 0 when running `pub publish`.
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
await commandRunner.run(<String>[ await commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
'--package', '--package',
@ -152,9 +155,10 @@ void main() {
'--no-push-tags', '--no-push-tags',
'--no-tag-release' '--no-tag-release'
]); ]);
processRunner.mockPublishProcess.stdoutController.add(utf8.encode('Foo'));
processRunner.mockPublishProcess.stderrController.add(utf8.encode('Bar')); processRunner.mockPublishStdout = 'Foo';
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishStderr = 'Bar';
processRunner.mockPublishCompleteCode = 0;
await publishCommand; await publishCommand;
@ -170,8 +174,8 @@ void main() {
'--no-push-tags', '--no-push-tags',
'--no-tag-release' '--no-tag-release'
]); ]);
mockStdin.controller.add(utf8.encode('user input')); mockStdin.mockUserInputs.add(utf8.encode('user input'));
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
await publishCommand; await publishCommand;
@ -180,7 +184,7 @@ void main() {
}); });
test('forwards --pub-publish-flags to pub publish', () async { test('forwards --pub-publish-flags to pub publish', () async {
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
await commandRunner.run(<String>[ await commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
'--package', '--package',
@ -199,7 +203,7 @@ void main() {
}); });
test('throws if pub publish fails', () async { test('throws if pub publish fails', () async {
processRunner.mockPublishProcess.exitCodeCompleter.complete(128); processRunner.mockPublishCompleteCode = 128;
await expectLater( await expectLater(
() => commandRunner.run(<String>[ () => commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
@ -210,13 +214,35 @@ void main() {
]), ]),
throwsA(const TypeMatcher<ToolExit>())); throwsA(const TypeMatcher<ToolExit>()));
expect(printedMessages, contains('Publish failed. Exiting.')); expect(printedMessages, contains('Publish foo failed.'));
});
test('publish, dry run', () async {
// Immediately return 1 when running `pub publish`. If dry-run does not work, test should throw.
processRunner.mockPublishCompleteCode = 1;
await commandRunner.run(<String>[
'publish-plugin',
'--package',
testPluginName,
'--dry-run',
'--no-push-tags',
'--no-tag-release',
]);
expect(processRunner.pushTagsArgs, isEmpty);
expect(
printedMessages,
containsAllInOrder(<String>[
'=============== DRY RUN ===============',
'Running `pub publish ` in ${pluginDir.path}...\n',
'Done!'
]));
}); });
}); });
group('Tags release', () { group('Tags release', () {
test('with the version and name from the pubspec.yaml', () async { test('with the version and name from the pubspec.yaml', () async {
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
await commandRunner.run(<String>[ await commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
'--package', '--package',
@ -231,7 +257,7 @@ void main() {
}); });
test('only if publishing succeeded', () async { test('only if publishing succeeded', () async {
processRunner.mockPublishProcess.exitCodeCompleter.complete(128); processRunner.mockPublishCompleteCode = 128;
await expectLater( await expectLater(
() => commandRunner.run(<String>[ () => commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
@ -241,7 +267,7 @@ void main() {
]), ]),
throwsA(const TypeMatcher<ToolExit>())); throwsA(const TypeMatcher<ToolExit>()));
expect(printedMessages, contains('Publish failed. Exiting.')); 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'], <String>['show-ref', 'fake_package-v0.0.1'],
throwOnError: false)) throwOnError: false))
@ -257,7 +283,7 @@ void main() {
}); });
test('requires user confirmation', () async { test('requires user confirmation', () async {
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'help'; mockStdin.readLineOutput = 'help';
await expectLater( await expectLater(
() => commandRunner.run(<String>[ () => commandRunner.run(<String>[
@ -272,7 +298,7 @@ void main() {
test('to upstream by default', () async { test('to upstream by default', () async {
await gitDir.runCommand(<String>['tag', 'garbage']); await gitDir.runCommand(<String>['tag', 'garbage']);
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y'; mockStdin.readLineOutput = 'y';
await commandRunner.run(<String>[ await commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
@ -286,10 +312,30 @@ void main() {
expect(printedMessages.last, 'Done!'); expect(printedMessages.last, 'Done!');
}); });
test('to upstream by default, dry run', () async {
await gitDir.runCommand(<String>['tag', 'garbage']);
// Immediately return 1 when running `pub publish`. If dry-run does not work, test should throw.
processRunner.mockPublishCompleteCode = 1;
mockStdin.readLineOutput = 'y';
await commandRunner.run(
<String>['publish-plugin', '--package', testPluginName, '--dry-run']);
expect(processRunner.pushTagsArgs, isEmpty);
expect(
printedMessages,
containsAllInOrder(<String>[
'=============== DRY RUN ===============',
'Running `pub publish ` in ${pluginDir.path}...\n',
'Tagging release fake_package-v0.0.1...',
'Pushing tag to upstream...',
'Done!'
]));
});
test('to different remotes based on a flag', () async { test('to different remotes based on a flag', () async {
await gitDir.runCommand( await gitDir.runCommand(
<String>['remote', 'add', 'origin', 'http://localhost:8001']); <String>['remote', 'add', 'origin', 'http://localhost:8001']);
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y'; mockStdin.readLineOutput = 'y';
await commandRunner.run(<String>[ await commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
@ -306,7 +352,7 @@ void main() {
}); });
test('only if tagging and pushing to remotes are both enabled', () async { test('only if tagging and pushing to remotes are both enabled', () async {
processRunner.mockPublishProcess.exitCodeCompleter.complete(0); processRunner.mockPublishCompleteCode = 0;
await commandRunner.run(<String>[ await commandRunner.run(<String>[
'publish-plugin', 'publish-plugin',
'--package', '--package',
@ -318,15 +364,495 @@ void main() {
expect(printedMessages.last, 'Done!'); expect(printedMessages.last, 'Done!');
}); });
}); });
group('Auto release (all-changed flag)', () {
setUp(() async {
io.Process.runSync('git', <String>['init'],
workingDirectory: mockPackagesDir.path);
gitDir = await GitDir.fromExisting(mockPackagesDir.path);
await gitDir.runCommand(
<String>['remote', 'add', 'upstream', 'http://localhost:8000']);
});
test('can release newly created plugins', () async {
// Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1',
withSingleExample: true, packagesDirectory: mockPackagesDir);
// federated
final Directory pluginDir2 = createFakePlugin('plugin2',
withSingleExample: true,
parentDirectoryName: 'plugin2',
packagesDirectory: mockPackagesDir);
createFakePubspec(pluginDir1,
name: 'plugin1',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
createFakePubspec(pluginDir2,
name: 'plugin2',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
// Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y';
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2',
'Done!'
]));
expect(processRunner.pushTagsArgs, isNotEmpty);
expect(processRunner.pushTagsArgs[0], 'push');
expect(processRunner.pushTagsArgs[1], 'upstream');
expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.1');
expect(processRunner.pushTagsArgs[3], 'push');
expect(processRunner.pushTagsArgs[4], 'upstream');
expect(processRunner.pushTagsArgs[5], 'plugin2-v0.0.1');
});
test('can release newly created plugins, while there are existing plugins',
() async {
// Prepare an exiting plugin and tag it
final Directory pluginDir0 = createFakePlugin('plugin0',
withSingleExample: true, packagesDirectory: mockPackagesDir);
createFakePubspec(pluginDir0,
name: 'plugin0',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
// Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y';
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
processRunner.pushTagsArgs.clear();
// Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1',
withSingleExample: true, packagesDirectory: mockPackagesDir);
// federated
final Directory pluginDir2 = createFakePlugin('plugin2',
withSingleExample: true,
parentDirectoryName: 'plugin2',
packagesDirectory: mockPackagesDir);
createFakePubspec(pluginDir1,
name: 'plugin1',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
createFakePubspec(pluginDir2,
name: 'plugin2',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
// Immediately return 0 when running `pub publish`.
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2',
'Done!'
]));
expect(processRunner.pushTagsArgs, isNotEmpty);
expect(processRunner.pushTagsArgs[0], 'push');
expect(processRunner.pushTagsArgs[1], 'upstream');
expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.1');
expect(processRunner.pushTagsArgs[3], 'push');
expect(processRunner.pushTagsArgs[4], 'upstream');
expect(processRunner.pushTagsArgs[5], 'plugin2-v0.0.1');
});
test('can release newly created plugins, dry run', () async {
// Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1',
withSingleExample: true, packagesDirectory: mockPackagesDir);
// federated
final Directory pluginDir2 = createFakePlugin('plugin2',
withSingleExample: true,
parentDirectoryName: 'plugin2',
packagesDirectory: mockPackagesDir);
createFakePubspec(pluginDir1,
name: 'plugin1',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
createFakePubspec(pluginDir2,
name: 'plugin2',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
// Immediately return 1 when running `pub publish`. If dry-run does not work, test should throw.
processRunner.mockPublishCompleteCode = 1;
mockStdin.readLineOutput = 'y';
await commandRunner.run(<String>[
'publish-plugin',
'--all-changed',
'--base-sha=HEAD~',
'--dry-run'
]);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'=============== DRY RUN ===============',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n',
'Tagging release plugin1-v0.0.1...',
'Pushing tag to upstream...',
'Running `pub publish ` in ${pluginDir2.path}...\n',
'Tagging release plugin2-v0.0.1...',
'Pushing tag to upstream...',
'Packages released: plugin1, plugin2',
'Done!'
]));
expect(processRunner.pushTagsArgs, isEmpty);
});
test('version change triggers releases.', () async {
// Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1',
withSingleExample: true, packagesDirectory: mockPackagesDir);
// federated
final Directory pluginDir2 = createFakePlugin('plugin2',
withSingleExample: true,
parentDirectoryName: 'plugin2',
packagesDirectory: mockPackagesDir);
createFakePubspec(pluginDir1,
name: 'plugin1',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
createFakePubspec(pluginDir2,
name: 'plugin2',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
// Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y';
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2',
'Done!'
]));
expect(processRunner.pushTagsArgs, isNotEmpty);
expect(processRunner.pushTagsArgs[0], 'push');
expect(processRunner.pushTagsArgs[1], 'upstream');
expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.1');
expect(processRunner.pushTagsArgs[3], 'push');
expect(processRunner.pushTagsArgs[4], 'upstream');
expect(processRunner.pushTagsArgs[5], 'plugin2-v0.0.1');
processRunner.pushTagsArgs.clear();
printedMessages.clear();
final List<String> plugin1Pubspec =
pluginDir1.childFile('pubspec.yaml').readAsLinesSync();
plugin1Pubspec[plugin1Pubspec.indexWhere(
(String element) => element.contains('version:'))] = 'version: 0.0.2';
pluginDir1
.childFile('pubspec.yaml')
.writeAsStringSync(plugin1Pubspec.join('\n'));
final List<String> plugin2Pubspec =
pluginDir2.childFile('pubspec.yaml').readAsLinesSync();
plugin2Pubspec[plugin2Pubspec.indexWhere(
(String element) => element.contains('version:'))] = 'version: 0.0.2';
pluginDir2
.childFile('pubspec.yaml')
.writeAsStringSync(plugin2Pubspec.join('\n'));
await gitDir.runCommand(<String>['add', '-A']);
await gitDir
.runCommand(<String>['commit', '-m', 'Update versions to 0.0.2']);
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2',
'Done!'
]));
expect(processRunner.pushTagsArgs, isNotEmpty);
expect(processRunner.pushTagsArgs[0], 'push');
expect(processRunner.pushTagsArgs[1], 'upstream');
expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.2');
expect(processRunner.pushTagsArgs[3], 'push');
expect(processRunner.pushTagsArgs[4], 'upstream');
expect(processRunner.pushTagsArgs[5], 'plugin2-v0.0.2');
});
test(
'delete package will not trigger publish but exit the command successfully.',
() async {
// Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1',
withSingleExample: true, packagesDirectory: mockPackagesDir);
// federated
final Directory pluginDir2 = createFakePlugin('plugin2',
withSingleExample: true,
parentDirectoryName: 'plugin2',
packagesDirectory: mockPackagesDir);
createFakePubspec(pluginDir1,
name: 'plugin1',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
createFakePubspec(pluginDir2,
name: 'plugin2',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
// Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y';
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2',
'Done!'
]));
expect(processRunner.pushTagsArgs, isNotEmpty);
expect(processRunner.pushTagsArgs[0], 'push');
expect(processRunner.pushTagsArgs[1], 'upstream');
expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.1');
expect(processRunner.pushTagsArgs[3], 'push');
expect(processRunner.pushTagsArgs[4], 'upstream');
expect(processRunner.pushTagsArgs[5], 'plugin2-v0.0.1');
processRunner.pushTagsArgs.clear();
printedMessages.clear();
final List<String> plugin1Pubspec =
pluginDir1.childFile('pubspec.yaml').readAsLinesSync();
plugin1Pubspec[plugin1Pubspec.indexWhere(
(String element) => element.contains('version:'))] = 'version: 0.0.2';
pluginDir1
.childFile('pubspec.yaml')
.writeAsStringSync(plugin1Pubspec.join('\n'));
pluginDir2.deleteSync(recursive: true);
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>[
'commit',
'-m',
'Update plugin1 versions to 0.0.2, delete plugin2'
]);
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n',
'The file at The pubspec file at ${pluginDir2.childFile('pubspec.yaml').path} does not exist. Publishing will not happen for plugin2.\nSafe to ignore if the package is deleted in this commit.\n',
'Packages released: plugin1',
'Done!'
]));
expect(processRunner.pushTagsArgs, isNotEmpty);
expect(processRunner.pushTagsArgs.length, 3);
expect(processRunner.pushTagsArgs[0], 'push');
expect(processRunner.pushTagsArgs[1], 'upstream');
expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.2');
});
test(
'versions revert do not trigger releases. Also prints out warning message.',
() async {
// Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1',
withSingleExample: true, packagesDirectory: mockPackagesDir);
// federated
final Directory pluginDir2 = createFakePlugin('plugin2',
withSingleExample: true,
parentDirectoryName: 'plugin2',
packagesDirectory: mockPackagesDir);
createFakePubspec(pluginDir1,
name: 'plugin1',
includeVersion: true,
isFlutter: false,
version: '0.0.2');
createFakePubspec(pluginDir2,
name: 'plugin2',
includeVersion: true,
isFlutter: false,
version: '0.0.2');
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
// Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y';
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2',
'Done!'
]));
expect(processRunner.pushTagsArgs, isNotEmpty);
expect(processRunner.pushTagsArgs[0], 'push');
expect(processRunner.pushTagsArgs[1], 'upstream');
expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.2');
expect(processRunner.pushTagsArgs[3], 'push');
expect(processRunner.pushTagsArgs[4], 'upstream');
expect(processRunner.pushTagsArgs[5], 'plugin2-v0.0.2');
processRunner.pushTagsArgs.clear();
printedMessages.clear();
final List<String> plugin1Pubspec =
pluginDir1.childFile('pubspec.yaml').readAsLinesSync();
plugin1Pubspec[plugin1Pubspec.indexWhere(
(String element) => element.contains('version:'))] = 'version: 0.0.1';
pluginDir1
.childFile('pubspec.yaml')
.writeAsStringSync(plugin1Pubspec.join('\n'));
final List<String> plugin2Pubspec =
pluginDir2.childFile('pubspec.yaml').readAsLinesSync();
plugin2Pubspec[plugin2Pubspec.indexWhere(
(String element) => element.contains('version:'))] = 'version: 0.0.1';
pluginDir2
.childFile('pubspec.yaml')
.writeAsStringSync(plugin2Pubspec.join('\n'));
await gitDir.runCommand(<String>['add', '-A']);
await gitDir
.runCommand(<String>['commit', '-m', 'Update versions to 0.0.1']);
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'Getting existing tags...',
'The new version (0.0.1) is lower than the current version (0.0.2) for plugin1.\nThis git commit is a revert, no release is tagged.',
'The new version (0.0.1) is lower than the current version (0.0.2) for plugin2.\nThis git commit is a revert, no release is tagged.',
'Done!'
]));
expect(processRunner.pushTagsArgs, isEmpty);
});
test('No version change does not release any plugins', () async {
// Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1',
withSingleExample: true, packagesDirectory: mockPackagesDir);
// federated
final Directory pluginDir2 = createFakePlugin('plugin2',
withSingleExample: true,
parentDirectoryName: 'plugin2',
packagesDirectory: mockPackagesDir);
createFakePubspec(pluginDir1,
name: 'plugin1',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
createFakePubspec(pluginDir2,
name: 'plugin2',
includeVersion: true,
isFlutter: false,
version: '0.0.1');
io.Process.runSync('git', <String>['init'],
workingDirectory: mockPackagesDir.path);
gitDir = await GitDir.fromExisting(mockPackagesDir.path);
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
pluginDir1.childFile('plugin1.dart').createSync();
pluginDir2.childFile('plugin2.dart').createSync();
await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add dart files']);
// Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y';
await commandRunner
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
expect(
printedMessages,
containsAllInOrder(<String>[
'Checking local repo...',
'Local repo is ready!',
'No version updates in this commit.',
'Done!'
]));
expect(processRunner.pushTagsArgs, isEmpty);
});
});
} }
class TestProcessRunner extends ProcessRunner { class TestProcessRunner extends ProcessRunner {
final List<io.ProcessResult> results = <io.ProcessResult>[]; final List<io.ProcessResult> results = <io.ProcessResult>[];
final MockProcess mockPublishProcess = MockProcess(); // Most recent returned publish process.
MockProcess mockPublishProcess;
final List<String> mockPublishArgs = <String>[]; final List<String> mockPublishArgs = <String>[];
final MockProcessResult mockPushTagsResult = MockProcessResult(); final MockProcessResult mockPushTagsResult = MockProcessResult();
final List<String> pushTagsArgs = <String>[]; final List<String> pushTagsArgs = <String>[];
String mockPublishStdout;
String mockPublishStderr;
int mockPublishCompleteCode;
@override @override
Future<io.ProcessResult> run( Future<io.ProcessResult> run(
String executable, String executable,
@ -362,23 +888,42 @@ class TestProcessRunner extends ProcessRunner {
args[0] == 'pub' && args[0] == 'pub' &&
args[1] == 'publish'); args[1] == 'publish');
mockPublishArgs.addAll(args); mockPublishArgs.addAll(args);
mockPublishProcess = MockProcess();
if (mockPublishStdout != null) {
mockPublishProcess.stdoutController.add(utf8.encode(mockPublishStdout));
}
if (mockPublishStderr != null) {
mockPublishProcess.stderrController.add(utf8.encode(mockPublishStderr));
}
if (mockPublishCompleteCode != null) {
mockPublishProcess.exitCodeCompleter.complete(mockPublishCompleteCode);
}
return mockPublishProcess; return mockPublishProcess;
} }
} }
class MockStdin extends Mock implements io.Stdin { class MockStdin extends Mock implements io.Stdin {
final StreamController<List<int>> controller = StreamController<List<int>>(); List<List<int>> mockUserInputs = <List<int>>[];
StreamController<List<int>> _controller;
String readLineOutput; String readLineOutput;
@override @override
Stream<S> transform<S>(StreamTransformer<List<int>, S> streamTransformer) { Stream<S> transform<S>(StreamTransformer<List<int>, S> streamTransformer) {
return controller.stream.transform(streamTransformer); // In the test context, only one `PublishPluginCommand` object is created for a single test case.
// However, sometimes, we need to run multiple commands in a single test case.
// In such situation, this `MockStdin`'s StreamController might be listened to more than once, which is not allowed.
//
// Create a new controller every time so this Stdin could be listened to multiple times.
_controller = StreamController<List<int>>();
mockUserInputs.forEach(_addUserInputsToSteam);
return _controller.stream.transform(streamTransformer);
} }
@override @override
StreamSubscription<List<int>> listen(void onData(List<int> event), StreamSubscription<List<int>> listen(void onData(List<int> event),
{Function onError, void onDone(), bool cancelOnError}) { {Function onError, void onDone(), bool cancelOnError}) {
return controller.stream.listen(onData, return _controller.stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError); onError: onError, onDone: onDone, cancelOnError: cancelOnError);
} }
@ -387,6 +932,15 @@ class MockStdin extends Mock implements io.Stdin {
{Encoding encoding = io.systemEncoding, {Encoding encoding = io.systemEncoding,
bool retainNewlines = false}) => bool retainNewlines = false}) =>
readLineOutput; readLineOutput;
void _addUserInputsToSteam(List<int> input) => _controller.add(input);
} }
class MockProcessResult extends Mock implements io.ProcessResult {} class MockProcessResult extends Mock implements io.ProcessResult {
MockProcessResult({int exitCode = 0}) : _exitCode = exitCode;
final int _exitCode;
@override
int get exitCode => _exitCode;
}

View File

@ -49,6 +49,7 @@ Directory createFakePlugin(
bool isWindowsPlugin = false, bool isWindowsPlugin = false,
bool includeChangeLog = false, bool includeChangeLog = false,
bool includeVersion = false, bool includeVersion = false,
String version = '0.0.1',
String parentDirectoryName = '', String parentDirectoryName = '',
Directory packagesDirectory, Directory packagesDirectory,
}) { }) {
@ -73,6 +74,7 @@ Directory createFakePlugin(
isMacOsPlugin: isMacOsPlugin, isMacOsPlugin: isMacOsPlugin,
isWindowsPlugin: isWindowsPlugin, isWindowsPlugin: isWindowsPlugin,
includeVersion: includeVersion, includeVersion: includeVersion,
version: version
); );
if (includeChangeLog) { if (includeChangeLog) {
createFakeCHANGELOG(pluginDirectory, ''' createFakeCHANGELOG(pluginDirectory, '''
@ -85,14 +87,14 @@ Directory createFakePlugin(
final Directory exampleDir = pluginDirectory.childDirectory('example') final Directory exampleDir = pluginDirectory.childDirectory('example')
..createSync(); ..createSync();
createFakePubspec(exampleDir, createFakePubspec(exampleDir,
name: '${name}_example', isFlutter: isFlutter); name: '${name}_example', isFlutter: isFlutter, includeVersion: false, publishTo: 'none');
} else if (withExamples.isNotEmpty) { } else if (withExamples.isNotEmpty) {
final Directory exampleDir = pluginDirectory.childDirectory('example') final Directory exampleDir = pluginDirectory.childDirectory('example')
..createSync(); ..createSync();
for (final String example in withExamples) { for (final String example in withExamples) {
final Directory currentExample = exampleDir.childDirectory(example) final Directory currentExample = exampleDir.childDirectory(example)
..createSync(); ..createSync();
createFakePubspec(currentExample, name: example, isFlutter: isFlutter); createFakePubspec(currentExample, name: example, isFlutter: isFlutter, includeVersion: false, publishTo: 'none');
} }
} }
@ -123,6 +125,7 @@ void createFakePubspec(
bool isLinuxPlugin = false, bool isLinuxPlugin = false,
bool isMacOsPlugin = false, bool isMacOsPlugin = false,
bool isWindowsPlugin = false, bool isWindowsPlugin = false,
String publishTo = 'http://no_pub_server.com',
String version = '0.0.1', String version = '0.0.1',
}) { }) {
parent.childFile('pubspec.yaml').createSync(); parent.childFile('pubspec.yaml').createSync();
@ -180,7 +183,11 @@ dependencies:
if (includeVersion) { if (includeVersion) {
yaml += ''' yaml += '''
version: $version version: $version
publish_to: http://no_pub_server.com # Hardcoded safeguard to prevent this from somehow being published by a broken test. ''';
}
if (publishTo.isNotEmpty) {
yaml += '''
publish_to: $publishTo # Hardcoded safeguard to prevent this from somehow being published by a broken test.
'''; ''';
} }
parent.childFile('pubspec.yaml').writeAsStringSync(yaml); parent.childFile('pubspec.yaml').writeAsStringSync(yaml);