Add --force to roll_dev.dart (#56501)
This commit is contained in:
committed by
GitHub
parent
7ba5078f85
commit
d56af3ca5e
@@ -10,6 +10,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
const String kIncrement = 'increment';
|
||||
const String kX = 'x';
|
||||
@@ -20,12 +21,119 @@ const String kOrigin = 'origin';
|
||||
const String kJustPrint = 'just-print';
|
||||
const String kYes = 'yes';
|
||||
const String kHelp = 'help';
|
||||
const String kForce = 'force';
|
||||
|
||||
const String kUpstreamRemote = 'git@github.com:flutter/flutter.git';
|
||||
|
||||
void main(List<String> args) {
|
||||
final ArgParser argParser = ArgParser(allowTrailingOptions: false);
|
||||
|
||||
ArgResults argResults;
|
||||
try {
|
||||
argResults = parseArguments(argParser, args);
|
||||
} on ArgParserException catch (error) {
|
||||
print(error.message);
|
||||
print(argParser.usage);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
run(
|
||||
usage: argParser.usage,
|
||||
argResults: argResults,
|
||||
git: const Git(),
|
||||
);
|
||||
} on Exception catch (e) {
|
||||
print(e.toString());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Main script execution.
|
||||
///
|
||||
/// Returns true if publishing was successful, else false.
|
||||
bool run({
|
||||
@required String usage,
|
||||
@required ArgResults argResults,
|
||||
@required Git git,
|
||||
}) {
|
||||
final String level = argResults[kIncrement] as String;
|
||||
final String commit = argResults[kCommit] as String;
|
||||
final String origin = argResults[kOrigin] as String;
|
||||
final bool justPrint = argResults[kJustPrint] as bool;
|
||||
final bool autoApprove = argResults[kYes] as bool;
|
||||
final bool help = argResults[kHelp] as bool;
|
||||
final bool force = argResults[kForce] as bool;
|
||||
|
||||
if (help || level == null || commit == null) {
|
||||
print(
|
||||
'roll_dev.dart --increment=level --commit=hash • update the version tags '
|
||||
'and roll a new dev build.\n$usage'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
final String remote = git.getOutput(
|
||||
'remote get-url $origin',
|
||||
'check whether this is a flutter checkout',
|
||||
);
|
||||
if (remote != kUpstreamRemote) {
|
||||
throw Exception(
|
||||
'The current directory is not a Flutter repository checkout with a '
|
||||
'correctly configured upstream remote.\nFor more details see: '
|
||||
'https://github.com/flutter/flutter/wiki/Release-process'
|
||||
);
|
||||
}
|
||||
|
||||
if (git.getOutput('status --porcelain', 'check status of your local checkout') != '') {
|
||||
throw Exception(
|
||||
'Your git repository is not clean. Try running "git clean -fd". Warning, '
|
||||
'this will delete files! Run with -n to find out which ones.'
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(fujino): move this after `justPrint`
|
||||
git.run('fetch $origin', 'fetch $origin');
|
||||
git.run('reset $commit --hard', 'reset to the release commit');
|
||||
|
||||
String version = getFullTag(git);
|
||||
|
||||
version = incrementLevel(version, level);
|
||||
|
||||
if (justPrint) {
|
||||
print(version);
|
||||
return false;
|
||||
}
|
||||
|
||||
final String hash = git.getOutput('rev-parse HEAD', 'Get git hash for $commit');
|
||||
|
||||
git.run('tag $version', 'tag the commit with the version label');
|
||||
|
||||
// PROMPT
|
||||
|
||||
if (autoApprove) {
|
||||
print('Publishing Flutter $version (${hash.substring(0, 10)}) to the "dev" channel.');
|
||||
} else {
|
||||
print('Your tree is ready to publish Flutter $version (${hash.substring(0, 10)}) '
|
||||
'to the "dev" channel.');
|
||||
stdout.write('Are you? [yes/no] ');
|
||||
if (stdin.readLineSync() != 'yes') {
|
||||
git.run('tag -d $version', 'remove the tag you did not want to publish');
|
||||
print('The dev roll has been aborted.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
git.run('push $origin $version', 'publish the version');
|
||||
git.run(
|
||||
'push ${force ? "--force " : ""}$origin HEAD:dev',
|
||||
'land the new version on the "dev" branch',
|
||||
);
|
||||
print('Flutter version $version has been rolled to the "dev" channel!');
|
||||
return true;
|
||||
}
|
||||
|
||||
ArgResults parseArguments(ArgParser argParser, List<String> args) {
|
||||
argParser.addOption(
|
||||
kIncrement,
|
||||
help: 'Specifies which part of the x.y.z version number to increment. Required.',
|
||||
@@ -49,6 +157,12 @@ void main(List<String> args) {
|
||||
valueHelp: 'repository',
|
||||
defaultsTo: 'upstream',
|
||||
);
|
||||
argParser.addFlag(
|
||||
kForce,
|
||||
abbr: 'f',
|
||||
help: 'Force push. Necessary when the previous release had cherry-picks.',
|
||||
negatable: false,
|
||||
);
|
||||
argParser.addFlag(
|
||||
kJustPrint,
|
||||
negatable: false,
|
||||
@@ -59,59 +173,92 @@ void main(List<String> args) {
|
||||
argParser.addFlag(kYes, negatable: false, abbr: 'y', help: 'Skip the confirmation prompt.');
|
||||
argParser.addFlag(kHelp, negatable: false, help: 'Show this help message.', hide: true);
|
||||
|
||||
ArgResults argResults;
|
||||
try {
|
||||
argResults = argParser.parse(args);
|
||||
} on ArgParserException catch (error) {
|
||||
print(error.message);
|
||||
print(argParser.usage);
|
||||
return argParser.parse(args);
|
||||
}
|
||||
|
||||
/// Obtain the version tag of the previous dev release.
|
||||
String getFullTag(Git git) {
|
||||
const String glob = '*.*.*-*.*.pre';
|
||||
// describe the latest dev release
|
||||
const String ref = 'refs/heads/dev';
|
||||
return git.getOutput(
|
||||
'describe --match $glob --exact-match --tags $ref',
|
||||
'obtain last released version number',
|
||||
);
|
||||
}
|
||||
|
||||
Match parseFullTag(String version) {
|
||||
// of the form: x.y.z-m.n.pre
|
||||
final RegExp versionPattern = RegExp(
|
||||
r'^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.pre$');
|
||||
return versionPattern.matchAsPrefix(version);
|
||||
}
|
||||
|
||||
String getVersionFromParts(List<int> parts) {
|
||||
// where parts correspond to [x, y, z, m, n] from tag
|
||||
assert(parts.length == 5);
|
||||
final StringBuffer buf = StringBuffer()
|
||||
// take x, y, and z
|
||||
..write(parts.take(3).join('.'))
|
||||
..write('-')
|
||||
// skip x, y, and z, take m and n
|
||||
..write(parts.skip(3).take(2).join('.'))
|
||||
..write('.pre');
|
||||
// return a string that looks like: '1.2.3-4.5.pre'
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
class Git {
|
||||
const Git();
|
||||
|
||||
String getOutput(String command, String explanation) {
|
||||
final ProcessResult result = _run(command);
|
||||
if ((result.stderr as String).isEmpty && result.exitCode == 0)
|
||||
return (result.stdout as String).trim();
|
||||
_reportFailureAndExit(result, explanation);
|
||||
return null; // for the analyzer's sake
|
||||
}
|
||||
|
||||
void run(String command, String explanation) {
|
||||
final ProcessResult result = _run(command);
|
||||
if (result.exitCode != 0)
|
||||
_reportFailureAndExit(result, explanation);
|
||||
}
|
||||
|
||||
ProcessResult _run(String command) {
|
||||
return Process.runSync('git', command.split(' '));
|
||||
}
|
||||
|
||||
void _reportFailureAndExit(ProcessResult result, String explanation) {
|
||||
if (result.exitCode != 0) {
|
||||
print('Failed to $explanation. Git exited with error code ${result.exitCode}.');
|
||||
} else {
|
||||
print('Failed to $explanation.');
|
||||
}
|
||||
if ((result.stdout as String).isNotEmpty)
|
||||
print('stdout from git:\n${result.stdout}\n');
|
||||
if ((result.stderr as String).isNotEmpty)
|
||||
print('stderr from git:\n${result.stderr}\n');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
final String level = argResults[kIncrement] as String;
|
||||
final String commit = argResults[kCommit] as String;
|
||||
final String origin = argResults[kOrigin] as String;
|
||||
final bool justPrint = argResults[kJustPrint] as bool;
|
||||
final bool autoApprove = argResults[kYes] as bool;
|
||||
final bool help = argResults[kHelp] as bool;
|
||||
|
||||
if (help || level == null || commit == null) {
|
||||
print('roll_dev.dart --increment=level --commit=hash • update the version tags and roll a new dev build.\n');
|
||||
print(argParser.usage);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (getGitOutput('remote get-url $origin', 'check whether this is a flutter checkout') != kUpstreamRemote) {
|
||||
print('The current directory is not a Flutter repository checkout with a correctly configured upstream remote.');
|
||||
print('For more details see: https://github.com/flutter/flutter/wiki/Release-process');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (getGitOutput('status --porcelain', 'check status of your local checkout') != '') {
|
||||
print('Your git repository is not clean. Try running "git clean -fd". Warning, this ');
|
||||
print('will delete files! Run with -n to find out which ones.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
runGit('fetch $origin', 'fetch $origin');
|
||||
runGit('reset $commit --hard', 'reset to the release commit');
|
||||
|
||||
String version = getFullTag();
|
||||
/// Return a copy of the [version] with [level] incremented by one.
|
||||
String incrementLevel(String version, String level) {
|
||||
final Match match = parseFullTag(version);
|
||||
if (match == null) {
|
||||
print('Could not determine the version for this build.');
|
||||
if (version.isNotEmpty)
|
||||
print('Git reported the latest version as "$version", which does not fit the expected pattern.');
|
||||
exit(1);
|
||||
String errorMessage;
|
||||
if (version.isEmpty) {
|
||||
errorMessage = 'Could not determine the version for this build.';
|
||||
} else {
|
||||
errorMessage = 'Git reported the latest version as "$version", which '
|
||||
'does not fit the expected pattern.';
|
||||
}
|
||||
throw Exception(errorMessage);
|
||||
}
|
||||
|
||||
final List<int> parts = match.groups(<int>[1, 2, 3, 4, 5]).map<int>(int.parse).toList();
|
||||
|
||||
if (match.group(6) == '0') {
|
||||
print('This commit has already been released, as version ${getVersionFromParts(parts)}.');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
switch (level) {
|
||||
case kX:
|
||||
parts[0] += 1;
|
||||
@@ -132,96 +279,7 @@ void main(List<String> args) {
|
||||
parts[4] = 0;
|
||||
break;
|
||||
default:
|
||||
print('Unknown increment level. The valid values are "$kX", "$kY", and "$kZ".');
|
||||
exit(1);
|
||||
throw Exception('Unknown increment level. The valid values are "$kX", "$kY", and "$kZ".');
|
||||
}
|
||||
version = getVersionFromParts(parts);
|
||||
|
||||
if (justPrint) {
|
||||
print(version);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
final String hash = getGitOutput('rev-parse HEAD', 'Get git hash for $commit');
|
||||
|
||||
runGit('tag $version', 'tag the commit with the version label');
|
||||
|
||||
// PROMPT
|
||||
|
||||
if (autoApprove) {
|
||||
print('Publishing Flutter $version (${hash.substring(0, 10)}) to the "dev" channel.');
|
||||
} else {
|
||||
print('Your tree is ready to publish Flutter $version (${hash.substring(0, 10)}) '
|
||||
'to the "dev" channel.');
|
||||
stdout.write('Are you? [yes/no] ');
|
||||
if (stdin.readLineSync() != 'yes') {
|
||||
runGit('tag -d $version', 'remove the tag you did not want to publish');
|
||||
print('The dev roll has been aborted.');
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
runGit('push $origin $version', 'publish the version');
|
||||
runGit('push $origin HEAD:dev', 'land the new version on the "dev" branch');
|
||||
print('Flutter version $version has been rolled to the "dev" channel!');
|
||||
}
|
||||
|
||||
String getFullTag() {
|
||||
const String glob = '*.*.*-*.*.pre';
|
||||
return getGitOutput(
|
||||
'describe --match $glob --first-parent --long --tags',
|
||||
'obtain last released version number',
|
||||
);
|
||||
}
|
||||
|
||||
Match parseFullTag(String version) {
|
||||
// of the form: x.y.z-m.n.pre-c-g<revision>
|
||||
final RegExp versionPattern = RegExp(
|
||||
r'^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.pre-(\d+)-g([a-f0-9]+)$');
|
||||
return versionPattern.matchAsPrefix(version);
|
||||
}
|
||||
|
||||
String getVersionFromParts(List<int> parts) {
|
||||
// where parts correspond to [x, y, z, m, n] from tag
|
||||
assert(parts.length == 5);
|
||||
final StringBuffer buf = StringBuffer()
|
||||
// take x, y, and z
|
||||
..write(parts.take(3).join('.'))
|
||||
..write('-')
|
||||
// skip x, y, and z, take m and n
|
||||
..write(parts.skip(3).take(2).join('.'))
|
||||
..write('.pre');
|
||||
// return a string that looks like: '1.2.3-4.5.pre'
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
String getGitOutput(String command, String explanation) {
|
||||
final ProcessResult result = _runGit(command);
|
||||
if ((result.stderr as String).isEmpty && result.exitCode == 0)
|
||||
return (result.stdout as String).trim();
|
||||
_reportGitFailureAndExit(result, explanation);
|
||||
return null; // for the analyzer's sake
|
||||
}
|
||||
|
||||
void runGit(String command, String explanation) {
|
||||
final ProcessResult result = _runGit(command);
|
||||
if (result.exitCode != 0)
|
||||
_reportGitFailureAndExit(result, explanation);
|
||||
}
|
||||
|
||||
ProcessResult _runGit(String command) {
|
||||
return Process.runSync('git', command.split(' '));
|
||||
}
|
||||
|
||||
void _reportGitFailureAndExit(ProcessResult result, String explanation) {
|
||||
if (result.exitCode != 0) {
|
||||
print('Failed to $explanation. Git exited with error code ${result.exitCode}.');
|
||||
} else {
|
||||
print('Failed to $explanation.');
|
||||
}
|
||||
if ((result.stdout as String).isNotEmpty)
|
||||
print('stdout from git:\n${result.stdout}\n');
|
||||
if ((result.stderr as String).isNotEmpty)
|
||||
print('stderr from git:\n${result.stderr}\n');
|
||||
exit(1);
|
||||
return getVersionFromParts(parts);
|
||||
}
|
||||
|
||||
@@ -2,20 +2,227 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:dev_tools/roll_dev.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import './common.dart';
|
||||
|
||||
void main() {
|
||||
group('run()', () {
|
||||
const String usage = 'usage info...';
|
||||
const String level = 'z';
|
||||
const String commit = 'abcde012345';
|
||||
const String origin = 'upstream';
|
||||
FakeArgResults fakeArgResults;
|
||||
MockGit mockGit;
|
||||
|
||||
setUp(() {
|
||||
mockGit = MockGit();
|
||||
});
|
||||
|
||||
test('returns false if help requested', () {
|
||||
fakeArgResults = FakeArgResults(
|
||||
level: level,
|
||||
commit: commit,
|
||||
origin: origin,
|
||||
justPrint: false,
|
||||
autoApprove: true,
|
||||
help: true,
|
||||
);
|
||||
expect(
|
||||
run(
|
||||
usage: usage,
|
||||
argResults: fakeArgResults,
|
||||
git: mockGit,
|
||||
),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('returns false if level not provided', () {
|
||||
fakeArgResults = FakeArgResults(
|
||||
level: level,
|
||||
commit: commit,
|
||||
origin: origin,
|
||||
justPrint: false,
|
||||
autoApprove: true,
|
||||
help: true,
|
||||
);
|
||||
expect(
|
||||
run(
|
||||
usage: usage,
|
||||
argResults: fakeArgResults,
|
||||
git: mockGit,
|
||||
),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('returns false if commit not provided', () {
|
||||
fakeArgResults = FakeArgResults(
|
||||
level: level,
|
||||
commit: commit,
|
||||
origin: origin,
|
||||
justPrint: false,
|
||||
autoApprove: true,
|
||||
help: true,
|
||||
);
|
||||
expect(
|
||||
run(
|
||||
usage: usage,
|
||||
argResults: fakeArgResults,
|
||||
git: mockGit,
|
||||
),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('throws exception if upstream remote wrong', () {
|
||||
when(mockGit.getOutput('remote get-url $origin', any)).thenReturn('wrong-remote');
|
||||
fakeArgResults = FakeArgResults(
|
||||
level: level,
|
||||
commit: commit,
|
||||
origin: origin,
|
||||
justPrint: false,
|
||||
autoApprove: true,
|
||||
help: false,
|
||||
);
|
||||
Exception exception;
|
||||
try {
|
||||
run(
|
||||
usage: usage,
|
||||
argResults: fakeArgResults,
|
||||
git: mockGit,
|
||||
);
|
||||
} on Exception catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
const String pattern = r'The current directory is not a Flutter '
|
||||
'repository checkout with a correctly configured upstream remote.';
|
||||
expect(exception?.toString(), contains(pattern));
|
||||
});
|
||||
|
||||
test('throws exception if git checkout not clean', () {
|
||||
when(mockGit.getOutput('remote get-url $origin', any)).thenReturn(kUpstreamRemote);
|
||||
when(mockGit.getOutput('status --porcelain', any)).thenReturn(
|
||||
' M dev/tools/test/roll_dev_test.dart',
|
||||
);
|
||||
fakeArgResults = FakeArgResults(
|
||||
level: level,
|
||||
commit: commit,
|
||||
origin: origin,
|
||||
justPrint: false,
|
||||
autoApprove: true,
|
||||
help: false,
|
||||
);
|
||||
Exception exception;
|
||||
try {
|
||||
run(
|
||||
usage: usage,
|
||||
argResults: fakeArgResults,
|
||||
git: mockGit,
|
||||
);
|
||||
} on Exception catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
const String pattern = r'Your git repository is not clean. Try running '
|
||||
'"git clean -fd". Warning, this will delete files! Run with -n to find '
|
||||
'out which ones.';
|
||||
expect(exception?.toString(), contains(pattern));
|
||||
});
|
||||
|
||||
test('does not tag if --just-print is specified', () {
|
||||
when(mockGit.getOutput('remote get-url $origin', any)).thenReturn(kUpstreamRemote);
|
||||
when(mockGit.getOutput('status --porcelain', any)).thenReturn('');
|
||||
when(mockGit.getOutput(
|
||||
'describe --match *.*.*-*.*.pre --exact-match --tags refs/heads/dev',
|
||||
any,
|
||||
)).thenReturn('1.2.3-0.0.pre');
|
||||
fakeArgResults = FakeArgResults(
|
||||
level: level,
|
||||
commit: commit,
|
||||
origin: origin,
|
||||
justPrint: true,
|
||||
autoApprove: true,
|
||||
help: false,
|
||||
);
|
||||
expect(run(
|
||||
usage: usage,
|
||||
argResults: fakeArgResults,
|
||||
git: mockGit,
|
||||
), false);
|
||||
verify(mockGit.run('fetch $origin', any));
|
||||
verify(mockGit.run('reset $commit --hard', any));
|
||||
verifyNever(mockGit.getOutput('rev-parse HEAD', any));
|
||||
});
|
||||
|
||||
test('successfully tags and publishes release', () {
|
||||
when(mockGit.getOutput('remote get-url $origin', any)).thenReturn(kUpstreamRemote);
|
||||
when(mockGit.getOutput('status --porcelain', any)).thenReturn('');
|
||||
when(mockGit.getOutput(
|
||||
'describe --match *.*.*-*.*.pre --exact-match --tags refs/heads/dev',
|
||||
any,
|
||||
)).thenReturn('1.2.3-0.0.pre');
|
||||
when(mockGit.getOutput('rev-parse HEAD', any)).thenReturn(commit);
|
||||
fakeArgResults = FakeArgResults(
|
||||
level: level,
|
||||
commit: commit,
|
||||
origin: origin,
|
||||
justPrint: false,
|
||||
autoApprove: true,
|
||||
help: false,
|
||||
);
|
||||
expect(run(
|
||||
usage: usage,
|
||||
argResults: fakeArgResults,
|
||||
git: mockGit,
|
||||
), true);
|
||||
verify(mockGit.run('fetch $origin', any));
|
||||
verify(mockGit.run('reset $commit --hard', any));
|
||||
verify(mockGit.run('tag 1.2.0-1.0.pre', any));
|
||||
verify(mockGit.run('push $origin HEAD:dev', any));
|
||||
});
|
||||
|
||||
test('successfully publishes release with --force', () {
|
||||
when(mockGit.getOutput('remote get-url $origin', any)).thenReturn(kUpstreamRemote);
|
||||
when(mockGit.getOutput('status --porcelain', any)).thenReturn('');
|
||||
when(mockGit.getOutput(
|
||||
'describe --match *.*.*-*.*.pre --exact-match --tags refs/heads/dev',
|
||||
any,
|
||||
)).thenReturn('1.2.3-0.0.pre');
|
||||
when(mockGit.getOutput('rev-parse HEAD', any)).thenReturn(commit);
|
||||
fakeArgResults = FakeArgResults(
|
||||
level: level,
|
||||
commit: commit,
|
||||
origin: origin,
|
||||
justPrint: false,
|
||||
autoApprove: true,
|
||||
help: false,
|
||||
force: true,
|
||||
);
|
||||
expect(run(
|
||||
usage: usage,
|
||||
argResults: fakeArgResults,
|
||||
git: mockGit,
|
||||
), true);
|
||||
verify(mockGit.run('fetch $origin', any));
|
||||
verify(mockGit.run('reset $commit --hard', any));
|
||||
verify(mockGit.run('tag 1.2.0-1.0.pre', any));
|
||||
verify(mockGit.run('push --force $origin HEAD:dev', any));
|
||||
});
|
||||
});
|
||||
|
||||
group('parseFullTag', () {
|
||||
test('returns match on valid version input', () {
|
||||
final List<String> validTags = <String>[
|
||||
'1.2.3-1.2.pre-3-gabc123',
|
||||
'10.2.30-12.22.pre-45-gabc123',
|
||||
'1.18.0-0.0.pre-0-gf0adb240a',
|
||||
'2.0.0-1.99.pre-45-gf0adb240a',
|
||||
'12.34.56-78.90.pre-12-g9db2703a2',
|
||||
'0.0.1-0.0.pre-1-g07601eb95ff82f01e870566586340ed2e87b9cbb',
|
||||
'958.80.144-6.224.pre-7803-g06e90',
|
||||
'1.2.3-1.2.pre',
|
||||
'10.2.30-12.22.pre',
|
||||
'1.18.0-0.0.pre',
|
||||
'2.0.0-1.99.pre',
|
||||
'12.34.56-78.90.pre',
|
||||
'0.0.1-0.0.pre',
|
||||
'958.80.144-6.224.pre',
|
||||
];
|
||||
for (final String validTag in validTags) {
|
||||
final Match match = parseFullTag(validTag);
|
||||
@@ -25,15 +232,15 @@ void main() {
|
||||
|
||||
test('returns null on invalid version input', () {
|
||||
final List<String> invalidTags = <String>[
|
||||
'1.2.3-dev.1.2-3-gabc123',
|
||||
'1.2.3-1.2-3-gabc123',
|
||||
'1.2.3-1.2.pre-3-gabc123',
|
||||
'1.2.3-1.2.3.pre',
|
||||
'1.2.3.1.2.pre',
|
||||
'1.2.3-dev.1.2',
|
||||
'1.2.3-1.2-3',
|
||||
'v1.2.3',
|
||||
'2.0.0',
|
||||
'v1.2.3-1.2.pre-3-gabc123',
|
||||
'10.0.1-0.0.pre-gf0adb240a',
|
||||
'10.0.1-0.0.pre-3-gggggggggg',
|
||||
'1.2.3-1.2.pre-3-abc123',
|
||||
'1.2.3-1.2.pre-3-gabc123_',
|
||||
'v1.2.3-1.2.pre',
|
||||
'1.2.3-1.2.pre_',
|
||||
];
|
||||
for (final String invalidTag in invalidTags) {
|
||||
final Match match = parseFullTag(invalidTag);
|
||||
@@ -51,4 +258,124 @@ void main() {
|
||||
expect(getVersionFromParts(parts), '11.2.33-1.0.pre');
|
||||
});
|
||||
});
|
||||
|
||||
group('incrementLevel()', () {
|
||||
const String hash = 'abc123';
|
||||
|
||||
test('throws exception if hash is not valid release candidate', () {
|
||||
String level = 'z';
|
||||
|
||||
String version = '1.0.0-0.0.pre-1-g$hash';
|
||||
expect(
|
||||
() => incrementLevel(version, level),
|
||||
throwsException,
|
||||
reason: 'should throw because $version should be an exact tag',
|
||||
);
|
||||
|
||||
version = '1.2.3';
|
||||
expect(
|
||||
() => incrementLevel(version, level),
|
||||
throwsException,
|
||||
reason: 'should throw because $version should be a dev tag, not stable.'
|
||||
);
|
||||
|
||||
version = '1.0.0-0.0.pre-1-g$hash';
|
||||
level = 'q';
|
||||
expect(
|
||||
() => incrementLevel(version, level),
|
||||
throwsException,
|
||||
reason: 'should throw because $level is unsupported',
|
||||
);
|
||||
});
|
||||
|
||||
test('successfully increments x', () {
|
||||
const String level = 'x';
|
||||
|
||||
String version = '1.0.0-0.0.pre';
|
||||
expect(incrementLevel(version, level), '2.0.0-0.0.pre');
|
||||
|
||||
version = '10.20.0-40.50.pre';
|
||||
expect(incrementLevel(version, level), '11.0.0-0.0.pre');
|
||||
|
||||
version = '1.18.0-3.0.pre';
|
||||
expect(incrementLevel(version, level), '2.0.0-0.0.pre');
|
||||
});
|
||||
|
||||
test('successfully increments y', () {
|
||||
const String level = 'y';
|
||||
|
||||
String version = '1.0.0-0.0.pre';
|
||||
expect(incrementLevel(version, level), '1.1.0-0.0.pre');
|
||||
|
||||
version = '10.20.0-40.50.pre';
|
||||
expect(incrementLevel(version, level), '10.21.0-0.0.pre');
|
||||
|
||||
version = '1.18.0-3.0.pre';
|
||||
expect(incrementLevel(version, level), '1.19.0-0.0.pre');
|
||||
});
|
||||
|
||||
test('successfully increments z', () {
|
||||
const String level = 'z';
|
||||
|
||||
String version = '1.0.0-0.0.pre';
|
||||
expect(incrementLevel(version, level), '1.0.0-1.0.pre');
|
||||
|
||||
version = '10.20.0-40.50.pre';
|
||||
expect(incrementLevel(version, level), '10.20.0-41.0.pre');
|
||||
|
||||
version = '1.18.0-3.0.pre';
|
||||
expect(incrementLevel(version, level), '1.18.0-4.0.pre');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakeArgResults implements ArgResults {
|
||||
FakeArgResults({
|
||||
String level,
|
||||
String commit,
|
||||
String origin,
|
||||
bool justPrint,
|
||||
bool autoApprove,
|
||||
bool help,
|
||||
bool force = false,
|
||||
}) : _parsedArgs = <String, dynamic>{
|
||||
'increment': level,
|
||||
'commit': commit,
|
||||
'origin': origin,
|
||||
'just-print': justPrint,
|
||||
'yes': autoApprove,
|
||||
'help': help,
|
||||
'force': force,
|
||||
};
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
||||
@override
|
||||
ArgResults command;
|
||||
|
||||
@override
|
||||
final List<String> rest = <String>[];
|
||||
|
||||
@override
|
||||
List<String> arguments;
|
||||
|
||||
final Map<String, dynamic> _parsedArgs;
|
||||
|
||||
@override
|
||||
Iterable<String> get options {
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic operator [](String name) {
|
||||
return _parsedArgs[name];
|
||||
}
|
||||
|
||||
@override
|
||||
bool wasParsed(String name) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class MockGit extends Mock implements Git {}
|
||||
|
||||
Reference in New Issue
Block a user