Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
[tools] Allow pre-release versions (#6061)
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartmorgan committed Jul 1, 2022
1 parent df2dff6 commit eb33778
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 13 deletions.
4 changes: 4 additions & 0 deletions 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.
Expand Down
51 changes: 39 additions & 12 deletions script/tool/lib/src/version_check_command.dart
Expand Up @@ -31,8 +31,8 @@ enum NextVersionType {
/// 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.
Expand All @@ -53,8 +53,8 @@ enum _CurrentVersionState {
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.
Expand All @@ -78,17 +78,17 @@ Map<Version, NextVersionType> getAllowedNextVersions(
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;
}
Expand Down Expand Up @@ -337,12 +337,11 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}

// 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;
Expand All @@ -352,7 +351,8 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
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'
Expand All @@ -361,7 +361,13 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
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 '
Expand Down Expand Up @@ -520,6 +526,27 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog.
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.
Expand Down
2 changes: 1 addition & 1 deletion 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
Expand Down
180 changes: 180 additions & 0 deletions script/tool/test/version_check_command_test.dart
Expand Up @@ -1178,6 +1178,186 @@ ${indentation}HTTP response: null
]),
);
});

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', () {
Expand Down

0 comments on commit eb33778

Please sign in to comment.