[flutter_plugin_tools] Migrate publish and version checks to NNBD (#4037)

Migrates publish-check and version-check commands to NNBD.

Reworks the version-check flow so that it's more consistent with the other commands: instead of immediately exiting on failure, it checks all plugins, gathers failures, and summarizes all failures at the end. This ensures that we don't have failures in one package temporarily masked by failures in another, so PRs don't need to go through as many check cycles.

Part of https://github.com/flutter/flutter/issues/81912
This commit is contained in:
stuartmorgan
2021-06-11 16:10:41 -07:00
committed by GitHub
parent cb92e5d416
commit 038c1796b0
7 changed files with 135 additions and 119 deletions

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart=2.9
import 'dart:async';
import 'package:file/file.dart';
@ -37,7 +35,7 @@ enum NextVersionType {
/// those have different semver rules.
@visibleForTesting
Map<Version, NextVersionType> getAllowedNextVersions(
Version masterVersion, Version headVersion) {
{required Version masterVersion, required Version headVersion}) {
final Map<Version, NextVersionType> allowedNextVersions =
<Version, NextVersionType>{
masterVersion.nextMajor: NextVersionType.BREAKING_MAJOR,
@ -75,8 +73,8 @@ class VersionCheckCommand extends PluginCommand {
VersionCheckCommand(
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
GitDir gitDir,
this.httpClient,
GitDir? gitDir,
http.Client? httpClient,
}) : _pubVersionFinder =
PubVersionFinder(httpClient: httpClient ?? http.Client()),
super(packagesDir, processRunner: processRunner, gitDir: gitDir) {
@ -100,9 +98,6 @@ class VersionCheckCommand extends PluginCommand {
'Also checks if the latest version in CHANGELOG matches the version in pubspec.\n\n'
'This command requires "pub" and "flutter" to be in your path.';
/// The http client used to query pub server.
final http.Client httpClient;
final PubVersionFinder _pubVersionFinder;
@override
@ -112,6 +107,8 @@ class VersionCheckCommand extends PluginCommand {
final List<String> changedPubspecs =
await gitVersionFinder.getChangedPubSpecs();
final List<String> badVersionChangePubspecs = <String>[];
const String indentation = ' ';
for (final String pubspecPath in changedPubspecs) {
print('Checking versions for $pubspecPath...');
@ -126,15 +123,16 @@ class VersionCheckCommand extends PluginCommand {
continue;
}
final Version headVersion =
final Version? headVersion =
await gitVersionFinder.getPackageVersion(pubspecPath, gitRef: 'HEAD');
if (headVersion == null) {
printErrorAndExit(
errorMessage: '${indentation}No version found. A package that '
'intentionally has no version should be marked '
'"publish_to: none".');
printError('${indentation}No version found. A package that '
'intentionally has no version should be marked '
'"publish_to: none".');
badVersionChangePubspecs.add(pubspecPath);
continue;
}
Version sourceVersion;
Version? sourceVersion;
if (getBoolArg(_againstPubFlag)) {
final String packageName = pubspecFile.parent.basename;
final PubVersionFinderResponse pubVersionFinderResponse =
@ -146,12 +144,13 @@ class VersionCheckCommand extends PluginCommand {
'$indentation$packageName: Current largest version on pub: $sourceVersion');
break;
case PubVersionFinderResult.fail:
printErrorAndExit(errorMessage: '''
printError('''
${indentation}Error fetching version on pub for $packageName.
${indentation}HTTP Status ${pubVersionFinderResponse.httpResponse.statusCode}
${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
''');
break;
badVersionChangePubspecs.add(pubspecPath);
continue;
case PubVersionFinderResult.noPackageFound:
sourceVersion = null;
break;
@ -180,7 +179,8 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
// Check for reverts when doing local validation.
if (!getBoolArg(_againstPubFlag) && headVersion < sourceVersion) {
final Map<Version, NextVersionType> possibleVersionsFromNewVersion =
getAllowedNextVersions(headVersion, sourceVersion);
getAllowedNextVersions(
masterVersion: headVersion, headVersion: sourceVersion);
// 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.
@ -192,14 +192,16 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
}
final Map<Version, NextVersionType> allowedNextVersions =
getAllowedNextVersions(sourceVersion, headVersion);
getAllowedNextVersions(
masterVersion: sourceVersion, headVersion: headVersion);
if (!allowedNextVersions.containsKey(headVersion)) {
final String source = (getBoolArg(_againstPubFlag)) ? 'pub' : 'master';
final String error = '${indentation}Incorrectly updated version.\n'
printError('${indentation}Incorrectly updated version.\n'
'${indentation}HEAD: $headVersion, $source: $sourceVersion.\n'
'${indentation}Allowed versions: $allowedNextVersions';
printErrorAndExit(errorMessage: error);
'${indentation}Allowed versions: $allowedNextVersions');
badVersionChangePubspecs.add(pubspecPath);
continue;
} else {
print('$indentation$headVersion -> $sourceVersion');
}
@ -208,38 +210,65 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
pubspec.name.endsWith('_platform_interface');
if (isPlatformInterface &&
allowedNextVersions[headVersion] == NextVersionType.BREAKING_MAJOR) {
final String error = '$pubspecPath breaking change detected.\n'
'Breaking changes to platform interfaces are strongly discouraged.\n';
printErrorAndExit(errorMessage: error);
printError('$pubspecPath breaking change detected.\n'
'Breaking changes to platform interfaces are strongly discouraged.\n');
badVersionChangePubspecs.add(pubspecPath);
continue;
}
}
_pubVersionFinder.httpClient.close();
// TODO(stuartmorgan): Unify the way iteration works for these checks; the
// two checks shouldn't be operating independently on different lists.
final List<String> mismatchedVersionPlugins = <String>[];
await for (final Directory plugin in getPlugins()) {
if (!(await _checkVersionsMatch(plugin))) {
mismatchedVersionPlugins.add(plugin.basename);
}
}
await for (final Directory plugin in getPlugins()) {
await _checkVersionsMatch(plugin);
bool passed = true;
if (badVersionChangePubspecs.isNotEmpty) {
passed = false;
printError('''
The following pubspecs failed validaton:
$indentation${badVersionChangePubspecs.join('\n$indentation')}
''');
}
if (mismatchedVersionPlugins.isNotEmpty) {
passed = false;
printError('''
The following pubspecs have different versions in pubspec.yaml and CHANGELOG.md:
$indentation${mismatchedVersionPlugins.join('\n$indentation')}
''');
}
if (!passed) {
throw ToolExit(1);
}
_pubVersionFinder.httpClient.close();
print('No version check errors found!');
}
Future<void> _checkVersionsMatch(Directory plugin) async {
/// Returns whether or not the pubspec version and CHANGELOG version for
/// [plugin] match.
Future<bool> _checkVersionsMatch(Directory plugin) async {
// get version from pubspec
final String packageName = plugin.basename;
print('-----------------------------------------');
print(
'Checking the first version listed in CHANGELOG.md matches the version in pubspec.yaml for $packageName.');
final Pubspec pubspec = _tryParsePubspec(plugin);
final Pubspec? pubspec = _tryParsePubspec(plugin);
if (pubspec == null) {
const String error = 'Cannot parse version from pubspec.yaml';
printErrorAndExit(errorMessage: error);
printError('Cannot parse version from pubspec.yaml');
return false;
}
final Version fromPubspec = pubspec.version;
final Version? fromPubspec = pubspec.version;
// get first version from CHANGELOG
final File changelog = plugin.childFile('CHANGELOG.md');
final List<String> lines = changelog.readAsLinesSync();
String firstLineWithText;
String? firstLineWithText;
final Iterator<String> iterator = lines.iterator;
while (iterator.moveNext()) {
if (iterator.current.trim().isNotEmpty) {
@ -248,7 +277,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
}
}
// Remove all leading mark down syntax from the version line.
String versionString = firstLineWithText.split(' ').last;
String? versionString = firstLineWithText?.split(' ').last;
// Skip validation for the special NEXT version that's used to accumulate
// changes that don't warrant publishing on their own.
@ -266,51 +295,48 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
}
}
final Version fromChangeLog = Version.parse(versionString);
final Version? fromChangeLog =
versionString == null ? null : Version.parse(versionString);
if (fromChangeLog == null) {
final String error =
'Cannot find version on the first line of ${plugin.path}/CHANGELOG.md';
printErrorAndExit(errorMessage: error);
printError(
'Cannot find version on the first line of ${plugin.path}/CHANGELOG.md');
return false;
}
if (fromPubspec != fromChangeLog) {
final String error = '''
printError('''
versions for $packageName in CHANGELOG.md and pubspec.yaml do not match.
The version in pubspec.yaml is $fromPubspec.
The first version listed in CHANGELOG.md is $fromChangeLog.
''';
printErrorAndExit(errorMessage: error);
''');
return false;
}
// If NEXT wasn't the first section, it should not exist at all.
if (!hasNextSection) {
final RegExp nextRegex = RegExp(r'^#+\s*NEXT\s*$');
if (lines.any((String line) => nextRegex.hasMatch(line))) {
printErrorAndExit(errorMessage: '''
printError('''
When bumping the version for release, the NEXT section should be incorporated
into the new version's release notes.
''');
return false;
}
}
print('$packageName passed version check');
return true;
}
Pubspec _tryParsePubspec(Directory package) {
Pubspec? _tryParsePubspec(Directory package) {
final File pubspecFile = package.childFile('pubspec.yaml');
try {
final Pubspec pubspec = Pubspec.parse(pubspecFile.readAsStringSync());
if (pubspec == null) {
final String error =
'Failed to parse `pubspec.yaml` at ${pubspecFile.path}';
printErrorAndExit(errorMessage: error);
}
return pubspec;
} on Exception catch (exception) {
final String error =
'Failed to parse `pubspec.yaml` at ${pubspecFile.path}: $exception}';
printErrorAndExit(errorMessage: error);
printError(
'Failed to parse `pubspec.yaml` at ${pubspecFile.path}: $exception}');
}
return null;
}