[tools] Allow pre-release versions (#6061)
diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md
index 9606eed..da14eae 100644
--- a/script/tool/CHANGELOG.md
+++ b/script/tool/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.8
+
+- Allows pre-release versions in `version-check`.
+
## 0.8.7
- Supports empty custom analysis allow list files.
diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart
index 62abdb2..246382d 100644
--- a/script/tool/lib/src/version_check_command.dart
+++ b/script/tool/lib/src/version_check_command.dart
@@ -31,8 +31,8 @@
/// A bugfix change.
PATCH,
- /// The release of an existing prerelease version.
- RELEASE,
+ /// The release of an existing pre-1.0 version.
+ V1_RELEASE,
}
/// The state of a package's version relative to the comparison base.
@@ -53,8 +53,8 @@
unknown,
}
-/// Returns the set of allowed next versions, with their change type, for
-/// [version].
+/// Returns the set of allowed next non-prerelease versions, with their change
+/// type, for [version].
///
/// [newVersion] is used to check whether this is a pre-1.0 version bump, as
/// those have different semver rules.
@@ -78,17 +78,17 @@
final int currentBuildNumber = version.build.first as int;
nextBuildNumber = currentBuildNumber + 1;
}
- final Version preReleaseVersion = Version(
+ final Version nextBuildVersion = Version(
version.major,
version.minor,
version.patch,
build: nextBuildNumber.toString(),
);
allowedNextVersions.clear();
- allowedNextVersions[version.nextMajor] = NextVersionType.RELEASE;
+ allowedNextVersions[version.nextMajor] = NextVersionType.V1_RELEASE;
allowedNextVersions[version.nextMinor] = NextVersionType.BREAKING_MAJOR;
allowedNextVersions[version.nextPatch] = NextVersionType.MINOR;
- allowedNextVersions[preReleaseVersion] = NextVersionType.PATCH;
+ allowedNextVersions[nextBuildVersion] = NextVersionType.PATCH;
}
return allowedNextVersions;
}
@@ -337,12 +337,11 @@
// Check for reverts when doing local validation.
if (!getBoolArg(_againstPubFlag) && currentVersion < previousVersion) {
- final Map<Version, NextVersionType> possibleVersionsFromNewVersion =
- getAllowedNextVersions(currentVersion, newVersion: previousVersion);
// Since this skips validation, try to ensure that it really is likely
// to be a revert rather than a typo by checking that the transition
// from the lower version to the new version would have been valid.
- if (possibleVersionsFromNewVersion.containsKey(previousVersion)) {
+ if (_shouldAllowVersionChange(
+ oldVersion: currentVersion, newVersion: previousVersion)) {
logWarning('${indentation}New version is lower than previous version. '
'This is assumed to be a revert.');
return _CurrentVersionState.validRevert;
@@ -352,7 +351,8 @@
final Map<Version, NextVersionType> allowedNextVersions =
getAllowedNextVersions(previousVersion, newVersion: currentVersion);
- if (allowedNextVersions.containsKey(currentVersion)) {
+ if (_shouldAllowVersionChange(
+ oldVersion: previousVersion, newVersion: currentVersion)) {
print('$indentation$previousVersion -> $currentVersion');
} else {
printError('${indentation}Incorrectly updated version.\n'
@@ -361,7 +361,13 @@
return _CurrentVersionState.invalidChange;
}
- if (allowedNextVersions[currentVersion] == NextVersionType.BREAKING_MAJOR &&
+ // Check whether the version (or for a pre-release, the version that
+ // pre-release would eventually be released as) is a breaking change, and
+ // if so, validate it.
+ final Version targetReleaseVersion =
+ currentVersion.isPreRelease ? currentVersion.nextPatch : currentVersion;
+ if (allowedNextVersions[targetReleaseVersion] ==
+ NextVersionType.BREAKING_MAJOR &&
!_validateBreakingChange(package)) {
printError('${indentation}Breaking change detected.\n'
'${indentation}Breaking changes to platform interfaces are not '
@@ -520,6 +526,27 @@
return file.readAsStringSync();
}
+ /// Returns true if the given version transition should be allowed.
+ bool _shouldAllowVersionChange(
+ {required Version oldVersion, required Version newVersion}) {
+ // Get the non-pre-release next version mapping.
+ final Map<Version, NextVersionType> allowedNextVersions =
+ getAllowedNextVersions(oldVersion, newVersion: newVersion);
+
+ if (allowedNextVersions.containsKey(newVersion)) {
+ return true;
+ }
+ // Allow a pre-release version of a version that would be a valid
+ // transition.
+ if (newVersion.isPreRelease) {
+ final Version targetReleaseVersion = newVersion.nextPatch;
+ if (allowedNextVersions.containsKey(targetReleaseVersion)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/// Returns an error string if the changes to this package should have
/// resulted in a version change, or shoud have resulted in a CHANGELOG change
/// but didn't.
diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml
index 1e631b1..b8233de 100644
--- a/script/tool/pubspec.yaml
+++ b/script/tool/pubspec.yaml
@@ -1,7 +1,7 @@
name: flutter_plugin_tools
description: Productivity utils for flutter/plugins and flutter/packages
repository: https://github.com/flutter/plugins/tree/main/script/tool
-version: 0.8.7
+version: 0.8.8
dependencies:
args: ^2.1.0
diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart
index a310f0f..8f8d510 100644
--- a/script/tool/test/version_check_command_test.dart
+++ b/script/tool/test/version_check_command_test.dart
@@ -1178,6 +1178,186 @@
]),
);
});
+
+ group('prelease versions', () {
+ test(
+ 'allow an otherwise-valid transition that also adds a pre-release component',
+ () async {
+ createFakePlugin('plugin', packagesDir, version: '2.0.0-dev');
+ processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
+ MockProcess(stdout: 'version: 1.0.0'),
+ ];
+ final List<String> output = await runCapturingPrint(
+ runner, <String>['version-check', '--base-sha=main']);
+
+ expect(
+ output,
+ containsAllInOrder(<Matcher>[
+ contains('Running for plugin'),
+ contains('1.0.0 -> 2.0.0-dev'),
+ ]),
+ );
+ expect(
+ processRunner.recordedCalls,
+ containsAllInOrder(const <ProcessCall>[
+ ProcessCall('git-show',
+ <String>['main:packages/plugin/pubspec.yaml'], null)
+ ]));
+ });
+
+ test('allow releasing a pre-release', () async {
+ createFakePlugin('plugin', packagesDir, version: '1.2.0');
+ processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
+ MockProcess(stdout: 'version: 1.2.0-dev'),
+ ];
+ final List<String> output = await runCapturingPrint(
+ runner, <String>['version-check', '--base-sha=main']);
+
+ expect(
+ output,
+ containsAllInOrder(<Matcher>[
+ contains('Running for plugin'),
+ contains('1.2.0-dev -> 1.2.0'),
+ ]),
+ );
+ expect(
+ processRunner.recordedCalls,
+ containsAllInOrder(const <ProcessCall>[
+ ProcessCall('git-show',
+ <String>['main:packages/plugin/pubspec.yaml'], null)
+ ]));
+ });
+
+ // Allow abandoning a pre-release version in favor of a different version
+ // change type.
+ test(
+ 'allow an otherwise-valid transition that also removes a pre-release component',
+ () async {
+ createFakePlugin('plugin', packagesDir, version: '2.0.0');
+ processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
+ MockProcess(stdout: 'version: 1.2.0-dev'),
+ ];
+ final List<String> output = await runCapturingPrint(
+ runner, <String>['version-check', '--base-sha=main']);
+
+ expect(
+ output,
+ containsAllInOrder(<Matcher>[
+ contains('Running for plugin'),
+ contains('1.2.0-dev -> 2.0.0'),
+ ]),
+ );
+ expect(
+ processRunner.recordedCalls,
+ containsAllInOrder(const <ProcessCall>[
+ ProcessCall('git-show',
+ <String>['main:packages/plugin/pubspec.yaml'], null)
+ ]));
+ });
+
+ test('allow changing only the pre-release version', () async {
+ createFakePlugin('plugin', packagesDir, version: '1.2.0-dev.2');
+ processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
+ MockProcess(stdout: 'version: 1.2.0-dev.1'),
+ ];
+ final List<String> output = await runCapturingPrint(
+ runner, <String>['version-check', '--base-sha=main']);
+
+ expect(
+ output,
+ containsAllInOrder(<Matcher>[
+ contains('Running for plugin'),
+ contains('1.2.0-dev.1 -> 1.2.0-dev.2'),
+ ]),
+ );
+ expect(
+ processRunner.recordedCalls,
+ containsAllInOrder(const <ProcessCall>[
+ ProcessCall('git-show',
+ <String>['main:packages/plugin/pubspec.yaml'], null)
+ ]));
+ });
+
+ test('denies invalid version change that also adds a pre-release',
+ () async {
+ createFakePlugin('plugin', packagesDir, version: '0.2.0-dev');
+ processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
+ MockProcess(stdout: 'version: 0.0.1'),
+ ];
+ Error? commandError;
+ final List<String> output = await runCapturingPrint(
+ runner, <String>['version-check', '--base-sha=main'],
+ errorHandler: (Error e) {
+ commandError = e;
+ });
+
+ expect(commandError, isA<ToolExit>());
+ expect(
+ output,
+ containsAllInOrder(<Matcher>[
+ contains('Incorrectly updated version.'),
+ ]));
+ expect(
+ processRunner.recordedCalls,
+ containsAllInOrder(const <ProcessCall>[
+ ProcessCall('git-show',
+ <String>['main:packages/plugin/pubspec.yaml'], null)
+ ]));
+ });
+
+ test('denies invalid version change that also removes a pre-release',
+ () async {
+ createFakePlugin('plugin', packagesDir, version: '0.2.0');
+ processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
+ MockProcess(stdout: 'version: 0.0.1-dev'),
+ ];
+ Error? commandError;
+ final List<String> output = await runCapturingPrint(
+ runner, <String>['version-check', '--base-sha=main'],
+ errorHandler: (Error e) {
+ commandError = e;
+ });
+
+ expect(commandError, isA<ToolExit>());
+ expect(
+ output,
+ containsAllInOrder(<Matcher>[
+ contains('Incorrectly updated version.'),
+ ]));
+ expect(
+ processRunner.recordedCalls,
+ containsAllInOrder(const <ProcessCall>[
+ ProcessCall('git-show',
+ <String>['main:packages/plugin/pubspec.yaml'], null)
+ ]));
+ });
+
+ test('denies invalid version change between pre-releases', () async {
+ createFakePlugin('plugin', packagesDir, version: '0.2.0-dev');
+ processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
+ MockProcess(stdout: 'version: 0.0.1-dev'),
+ ];
+ Error? commandError;
+ final List<String> output = await runCapturingPrint(
+ runner, <String>['version-check', '--base-sha=main'],
+ errorHandler: (Error e) {
+ commandError = e;
+ });
+
+ expect(commandError, isA<ToolExit>());
+ expect(
+ output,
+ containsAllInOrder(<Matcher>[
+ contains('Incorrectly updated version.'),
+ ]));
+ expect(
+ processRunner.recordedCalls,
+ containsAllInOrder(const <ProcessCall>[
+ ProcessCall('git-show',
+ <String>['main:packages/plugin/pubspec.yaml'], null)
+ ]));
+ });
+ });
});
group('Pre 1.0', () {