[flutter_plugin_tools] publish-plugin check against pub to determine if a release should happen (#4068)

This PR removes a TODO where we used to check if a release should happen against git tags, instead, we check against pub.

Also, when auto-publish and the package is manually released, the CI will continue to fail without this change.

Fixes flutter/flutter#81047
This commit is contained in:
Chris Yang
2021-06-24 12:46:24 -07:00
committed by GitHub
parent 356d316717
commit 552fceef91
3 changed files with 282 additions and 71 deletions

View File

@ -6,6 +6,7 @@
- `xctest` now supports running macOS tests in addition to iOS - `xctest` now supports running macOS tests in addition to iOS
- **Breaking change**: it now requires an `--ios` and/or `--macos` flag. - **Breaking change**: it now requires an `--ios` and/or `--macos` flag.
- The tooling now runs in strong null-safe mode. - The tooling now runs in strong null-safe mode.
- `publish plugins` check against pub.dev to determine if a release should happen.
- Modified the output format of `pubspec-check` and `xctest` - Modified the output format of `pubspec-check` and `xctest`
## 0.2.0 ## 0.2.0

View File

@ -8,6 +8,7 @@ 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:http/http.dart' as http;
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:pub_semver/pub_semver.dart';
@ -18,6 +19,7 @@ import 'common/core.dart';
import 'common/git_version_finder.dart'; import 'common/git_version_finder.dart';
import 'common/plugin_command.dart'; import 'common/plugin_command.dart';
import 'common/process_runner.dart'; import 'common/process_runner.dart';
import 'common/pub_version_finder.dart';
@immutable @immutable
class _RemoteInfo { class _RemoteInfo {
@ -49,7 +51,10 @@ class PublishPluginCommand extends PluginCommand {
Print print = print, Print print = print,
io.Stdin? stdinput, io.Stdin? stdinput,
GitDir? gitDir, GitDir? gitDir,
}) : _print = print, http.Client? httpClient,
}) : _pubVersionFinder =
PubVersionFinder(httpClient: httpClient ?? http.Client()),
_print = print,
_stdin = stdinput ?? io.stdin, _stdin = stdinput ?? io.stdin,
super(packagesDir, processRunner: processRunner, gitDir: gitDir) { super(packagesDir, processRunner: processRunner, gitDir: gitDir) {
argParser.addOption( argParser.addOption(
@ -131,6 +136,7 @@ class PublishPluginCommand extends PluginCommand {
final Print _print; final Print _print;
final io.Stdin _stdin; final io.Stdin _stdin;
StreamSubscription<String>? _stdinSubscription; StreamSubscription<String>? _stdinSubscription;
final PubVersionFinder _pubVersionFinder;
@override @override
Future<void> run() async { Future<void> run() async {
@ -182,6 +188,8 @@ class PublishPluginCommand extends PluginCommand {
remoteForTagPush: remote, remoteForTagPush: remote,
); );
} }
_pubVersionFinder.httpClient.close();
await _finish(successful); await _finish(successful);
} }
@ -196,6 +204,7 @@ class PublishPluginCommand extends PluginCommand {
_print('No version updates in this commit.'); _print('No version updates in this commit.');
return true; return true;
} }
_print('Getting existing tags...'); _print('Getting existing tags...');
final io.ProcessResult existingTagsResult = final io.ProcessResult existingTagsResult =
await baseGitDir.runCommand(<String>['tag', '--sort=-committerdate']); await baseGitDir.runCommand(<String>['tag', '--sort=-committerdate']);
@ -212,7 +221,6 @@ class PublishPluginCommand extends PluginCommand {
.childFile(pubspecPath); .childFile(pubspecPath);
final _CheckNeedsReleaseResult result = await _checkNeedsRelease( final _CheckNeedsReleaseResult result = await _checkNeedsRelease(
pubspecFile: pubspecFile, pubspecFile: pubspecFile,
gitVersionFinder: gitVersionFinder,
existingTags: existingTags, existingTags: existingTags,
); );
switch (result) { switch (result) {
@ -271,7 +279,6 @@ class PublishPluginCommand extends PluginCommand {
// Returns a [_CheckNeedsReleaseResult] that indicates the result. // Returns a [_CheckNeedsReleaseResult] that indicates the result.
Future<_CheckNeedsReleaseResult> _checkNeedsRelease({ Future<_CheckNeedsReleaseResult> _checkNeedsRelease({
required File pubspecFile, required File pubspecFile,
required GitVersionFinder gitVersionFinder,
required List<String> existingTags, required List<String> existingTags,
}) async { }) async {
if (!pubspecFile.existsSync()) { if (!pubspecFile.existsSync()) {
@ -293,19 +300,24 @@ Safe to ignore if the package is deleted in this commit.
return _CheckNeedsReleaseResult.failure; return _CheckNeedsReleaseResult.failure;
} }
// Get latest tagged version and compare with the current version. // Check if the package named `packageName` with `version` has already published.
// TODO(cyanglaz): Check latest version of the package on pub instead of git final Version version = pubspec.version!;
// https://github.com/flutter/flutter/issues/81047 final PubVersionFinderResponse pubVersionFinderResponse =
await _pubVersionFinder.getPackageVersion(package: pubspec.name);
final String latestTag = existingTags.firstWhere( if (pubVersionFinderResponse.versions.contains(version)) {
(String tag) => tag.split('-v').first == pubspec.name, final String tagsForPackageWithSameVersion = existingTags.firstWhere(
(String tag) =>
tag.split('-v').first == pubspec.name &&
tag.split('-v').last == version.toString(),
orElse: () => ''); orElse: () => '');
if (latestTag.isNotEmpty) {
final String latestTaggedVersion = latestTag.split('-v').last;
final Version latestVersion = Version.parse(latestTaggedVersion);
if (pubspec.version! < latestVersion) {
_print( _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.'); 'The version $version of ${pubspec.name} has already been published');
if (tagsForPackageWithSameVersion.isEmpty) {
_print(
'However, the git release tag for this version (${pubspec.name}-v$version) is not found. Please manually fix the tag then run the command again.');
return _CheckNeedsReleaseResult.failure;
} else {
_print('skip.');
return _CheckNeedsReleaseResult.noRelease; return _CheckNeedsReleaseResult.noRelease;
} }
} }

View File

@ -13,6 +13,8 @@ import 'package:flutter_plugin_tools/src/common/core.dart';
import 'package:flutter_plugin_tools/src/common/process_runner.dart'; import 'package:flutter_plugin_tools/src/common/process_runner.dart';
import 'package:flutter_plugin_tools/src/publish_plugin_command.dart'; import 'package:flutter_plugin_tools/src/publish_plugin_command.dart';
import 'package:git/git.dart'; import 'package:git/git.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
@ -427,6 +429,37 @@ void main() {
}); });
test('can release newly created plugins', () async { test('can release newly created plugins', () async {
const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
'name': 'plugin1',
'versions': <String>[],
};
const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
'name': 'plugin2',
'versions': <String>[],
};
final MockClient mockClient = MockClient((http.Request request) async {
if (request.url.pathSegments.last == 'plugin1.json') {
return http.Response(json.encode(httpResponsePlugin1), 200);
} else if (request.url.pathSegments.last == 'plugin2.json') {
return http.Response(json.encode(httpResponsePlugin2), 200);
}
return http.Response('', 500);
});
final PublishPluginCommand command = PublishPluginCommand(packagesDir,
processRunner: processRunner,
print: (Object? message) => printedMessages.add(message.toString()),
stdinput: mockStdin,
httpClient: mockClient,
gitDir: gitDir);
commandRunner = CommandRunner<void>(
'publish_check_command',
'Test for publish-check command.',
);
commandRunner.addCommand(command);
// Non-federated // Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir);
// federated // federated
@ -446,7 +479,6 @@ void main() {
containsAllInOrder(<String>[ containsAllInOrder(<String>[
'Checking local repo...', 'Checking local repo...',
'Local repo is ready!', 'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n', 'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n', 'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2', 'Packages released: plugin1, plugin2',
@ -463,10 +495,50 @@ void main() {
test('can release newly created plugins, while there are existing plugins', test('can release newly created plugins, while there are existing plugins',
() async { () async {
const Map<String, dynamic> httpResponsePlugin0 = <String, dynamic>{
'name': 'plugin0',
'versions': <String>['0.0.1'],
};
const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
'name': 'plugin1',
'versions': <String>[],
};
const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
'name': 'plugin2',
'versions': <String>[],
};
final MockClient mockClient = MockClient((http.Request request) async {
if (request.url.pathSegments.last == 'plugin0.json') {
return http.Response(json.encode(httpResponsePlugin0), 200);
} else if (request.url.pathSegments.last == 'plugin1.json') {
return http.Response(json.encode(httpResponsePlugin1), 200);
} else if (request.url.pathSegments.last == 'plugin2.json') {
return http.Response(json.encode(httpResponsePlugin2), 200);
}
return http.Response('', 500);
});
final PublishPluginCommand command = PublishPluginCommand(packagesDir,
processRunner: processRunner,
print: (Object? message) => printedMessages.add(message.toString()),
stdinput: mockStdin,
httpClient: mockClient,
gitDir: gitDir);
commandRunner = CommandRunner<void>(
'publish_check_command',
'Test for publish-check command.',
);
commandRunner.addCommand(command);
// Prepare an exiting plugin and tag it // Prepare an exiting plugin and tag it
createFakePlugin('plugin0', packagesDir); createFakePlugin('plugin0', packagesDir);
await gitDir.runCommand(<String>['add', '-A']); await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']); await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
await gitDir.runCommand(<String>['tag', 'plugin0-v0.0.1']);
// Immediately return 0 when running `pub publish`. // Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0; processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y'; mockStdin.readLineOutput = 'y';
@ -489,7 +561,6 @@ void main() {
containsAllInOrder(<String>[ containsAllInOrder(<String>[
'Checking local repo...', 'Checking local repo...',
'Local repo is ready!', 'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n', 'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n', 'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2', 'Packages released: plugin1, plugin2',
@ -505,6 +576,36 @@ void main() {
}); });
test('can release newly created plugins, dry run', () async { test('can release newly created plugins, dry run', () async {
const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
'name': 'plugin1',
'versions': <String>[],
};
const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
'name': 'plugin2',
'versions': <String>[],
};
final MockClient mockClient = MockClient((http.Request request) async {
if (request.url.pathSegments.last == 'plugin1.json') {
return http.Response(json.encode(httpResponsePlugin1), 200);
} else if (request.url.pathSegments.last == 'plugin2.json') {
return http.Response(json.encode(httpResponsePlugin2), 200);
}
return http.Response('', 500);
});
final PublishPluginCommand command = PublishPluginCommand(packagesDir,
processRunner: processRunner,
print: (Object? message) => printedMessages.add(message.toString()),
stdinput: mockStdin,
httpClient: mockClient,
gitDir: gitDir);
commandRunner = CommandRunner<void>(
'publish_check_command',
'Test for publish-check command.',
);
commandRunner.addCommand(command);
// Non-federated // Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir);
// federated // federated
@ -527,7 +628,6 @@ void main() {
'Checking local repo...', 'Checking local repo...',
'Local repo is ready!', 'Local repo is ready!',
'=============== DRY RUN ===============', '=============== DRY RUN ===============',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n', 'Running `pub publish ` in ${pluginDir1.path}...\n',
'Tagging release plugin1-v0.0.1...', 'Tagging release plugin1-v0.0.1...',
'Pushing tag to upstream...', 'Pushing tag to upstream...',
@ -541,6 +641,37 @@ void main() {
}); });
test('version change triggers releases.', () async { test('version change triggers releases.', () async {
const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
'name': 'plugin1',
'versions': <String>[],
};
const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
'name': 'plugin2',
'versions': <String>[],
};
final MockClient mockClient = MockClient((http.Request request) async {
if (request.url.pathSegments.last == 'plugin1.json') {
return http.Response(json.encode(httpResponsePlugin1), 200);
} else if (request.url.pathSegments.last == 'plugin2.json') {
return http.Response(json.encode(httpResponsePlugin2), 200);
}
return http.Response('', 500);
});
final PublishPluginCommand command = PublishPluginCommand(packagesDir,
processRunner: processRunner,
print: (Object? message) => printedMessages.add(message.toString()),
stdinput: mockStdin,
httpClient: mockClient,
gitDir: gitDir);
commandRunner = CommandRunner<void>(
'publish_check_command',
'Test for publish-check command.',
);
commandRunner.addCommand(command);
// Non-federated // Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir);
// federated // federated
@ -558,7 +689,6 @@ void main() {
containsAllInOrder(<String>[ containsAllInOrder(<String>[
'Checking local repo...', 'Checking local repo...',
'Local repo is ready!', 'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n', 'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n', 'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2', 'Packages released: plugin1, plugin2',
@ -600,7 +730,6 @@ void main() {
containsAllInOrder(<String>[ containsAllInOrder(<String>[
'Checking local repo...', 'Checking local repo...',
'Local repo is ready!', 'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n', 'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n', 'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2', 'Packages released: plugin1, plugin2',
@ -619,6 +748,37 @@ void main() {
test( test(
'delete package will not trigger publish but exit the command successfully.', 'delete package will not trigger publish but exit the command successfully.',
() async { () async {
const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
'name': 'plugin1',
'versions': <String>[],
};
const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
'name': 'plugin2',
'versions': <String>[],
};
final MockClient mockClient = MockClient((http.Request request) async {
if (request.url.pathSegments.last == 'plugin1.json') {
return http.Response(json.encode(httpResponsePlugin1), 200);
} else if (request.url.pathSegments.last == 'plugin2.json') {
return http.Response(json.encode(httpResponsePlugin2), 200);
}
return http.Response('', 500);
});
final PublishPluginCommand command = PublishPluginCommand(packagesDir,
processRunner: processRunner,
print: (Object? message) => printedMessages.add(message.toString()),
stdinput: mockStdin,
httpClient: mockClient,
gitDir: gitDir);
commandRunner = CommandRunner<void>(
'publish_check_command',
'Test for publish-check command.',
);
commandRunner.addCommand(command);
// Non-federated // Non-federated
final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir);
// federated // federated
@ -636,7 +796,6 @@ void main() {
containsAllInOrder(<String>[ containsAllInOrder(<String>[
'Checking local repo...', 'Checking local repo...',
'Local repo is ready!', 'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n', 'Running `pub publish ` in ${pluginDir1.path}...\n',
'Running `pub publish ` in ${pluginDir2.path}...\n', 'Running `pub publish ` in ${pluginDir2.path}...\n',
'Packages released: plugin1, plugin2', 'Packages released: plugin1, plugin2',
@ -677,7 +836,6 @@ void main() {
containsAllInOrder(<String>[ containsAllInOrder(<String>[
'Checking local repo...', 'Checking local repo...',
'Local repo is ready!', 'Local repo is ready!',
'Getting existing tags...',
'Running `pub publish ` in ${pluginDir1.path}...\n', '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', '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', 'Packages released: plugin1',
@ -691,18 +849,48 @@ void main() {
expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.2'); expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.2');
}); });
test( test('Exiting versions do not trigger release, also prints out message.',
'versions revert do not trigger releases. Also prints out warning message.',
() async { () async {
const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
'name': 'plugin1',
'versions': <String>['0.0.2'],
};
const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
'name': 'plugin2',
'versions': <String>['0.0.2'],
};
final MockClient mockClient = MockClient((http.Request request) async {
if (request.url.pathSegments.last == 'plugin1.json') {
return http.Response(json.encode(httpResponsePlugin1), 200);
} else if (request.url.pathSegments.last == 'plugin2.json') {
return http.Response(json.encode(httpResponsePlugin2), 200);
}
return http.Response('', 500);
});
final PublishPluginCommand command = PublishPluginCommand(packagesDir,
processRunner: processRunner,
print: (Object? message) => printedMessages.add(message.toString()),
stdinput: mockStdin,
httpClient: mockClient,
gitDir: gitDir);
commandRunner = CommandRunner<void>(
'publish_check_command',
'Test for publish-check command.',
);
commandRunner.addCommand(command);
// Non-federated // Non-federated
final Directory pluginDir1 =
createFakePlugin('plugin1', packagesDir, version: '0.0.2'); createFakePlugin('plugin1', packagesDir, version: '0.0.2');
// federated // federated
final Directory pluginDir2 = createFakePlugin( createFakePlugin('plugin2', packagesDir.childDirectory('plugin2'),
'plugin2', packagesDir.childDirectory('plugin2'),
version: '0.0.2'); version: '0.0.2');
await gitDir.runCommand(<String>['add', '-A']); await gitDir.runCommand(<String>['add', '-A']);
await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']); await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
await gitDir.runCommand(<String>['tag', 'plugin1-v0.0.2']);
await gitDir.runCommand(<String>['tag', 'plugin2-v0.0.2']);
// Immediately return 0 when running `pub publish`. // Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0; processRunner.mockPublishCompleteCode = 0;
mockStdin.readLineOutput = 'y'; mockStdin.readLineOutput = 'y';
@ -713,54 +901,64 @@ void main() {
containsAllInOrder(<String>[ containsAllInOrder(<String>[
'Checking local repo...', 'Checking local repo...',
'Local repo is ready!', 'Local repo is ready!',
'Getting existing tags...', 'The version 0.0.2 of plugin1 has already been published',
'Running `pub publish ` in ${pluginDir1.path}...\n', 'skip.',
'Running `pub publish ` in ${pluginDir2.path}...\n', 'The version 0.0.2 of plugin2 has already been published',
'Packages released: plugin1, plugin2', 'skip.',
'Done!' '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(); expect(processRunner.pushTagsArgs, isEmpty);
printedMessages.clear(); });
final List<String> plugin1Pubspec = test(
pluginDir1.childFile('pubspec.yaml').readAsLinesSync(); 'Exiting versions do not trigger release, but fail if the tags do not exist.',
plugin1Pubspec[plugin1Pubspec.indexWhere( () async {
(String element) => element.contains('version:'))] = 'version: 0.0.1'; const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
pluginDir1 'name': 'plugin1',
.childFile('pubspec.yaml') 'versions': <String>['0.0.2'],
.writeAsStringSync(plugin1Pubspec.join('\n')); };
final List<String> plugin2Pubspec =
pluginDir2.childFile('pubspec.yaml').readAsLinesSync(); const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
plugin2Pubspec[plugin2Pubspec.indexWhere( 'name': 'plugin2',
(String element) => element.contains('version:'))] = 'version: 0.0.1'; 'versions': <String>['0.0.2'],
pluginDir2 };
.childFile('pubspec.yaml')
.writeAsStringSync(plugin2Pubspec.join('\n')); final MockClient mockClient = MockClient((http.Request request) async {
if (request.url.pathSegments.last == 'plugin1.json') {
return http.Response(json.encode(httpResponsePlugin1), 200);
} else if (request.url.pathSegments.last == 'plugin2.json') {
return http.Response(json.encode(httpResponsePlugin2), 200);
}
return http.Response('', 500);
});
final PublishPluginCommand command = PublishPluginCommand(packagesDir,
processRunner: processRunner,
print: (Object? message) => printedMessages.add(message.toString()),
stdinput: mockStdin,
httpClient: mockClient,
gitDir: gitDir);
commandRunner = CommandRunner<void>(
'publish_check_command',
'Test for publish-check command.',
);
commandRunner.addCommand(command);
// Non-federated
createFakePlugin('plugin1', packagesDir, version: '0.0.2');
// federated
createFakePlugin('plugin2', packagesDir.childDirectory('plugin2'),
version: '0.0.2');
await gitDir.runCommand(<String>['add', '-A']); await gitDir.runCommand(<String>['add', '-A']);
await gitDir await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
.runCommand(<String>['commit', '-m', 'Update versions to 0.0.1']); // Immediately return 0 when running `pub publish`.
processRunner.mockPublishCompleteCode = 0;
await commandRunner mockStdin.readLineOutput = 'y';
.run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']); await expectLater(
expect( () => commandRunner.run(
printedMessages, <String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']),
containsAllInOrder(<String>[ throwsA(const TypeMatcher<ToolExit>()));
'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); expect(processRunner.pushTagsArgs, isEmpty);
}); });