mirror of
https://github.com/flutter/packages.git
synced 2025-06-17 10:58:42 +08:00
[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:
@ -165,11 +165,10 @@ bool isLinuxPlugin(FileSystemEntity entity) {
|
|||||||
return pluginSupportsPlatform(kPlatformFlagLinux, entity);
|
return pluginSupportsPlatform(kPlatformFlagLinux, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Throws a [ToolExit] with `exitCode` and log the `errorMessage` in red.
|
/// Prints `errorMessage` in red.
|
||||||
void printErrorAndExit({required String errorMessage, int exitCode = 1}) {
|
void printError(String errorMessage) {
|
||||||
final Colorize redError = Colorize(errorMessage)..red();
|
final Colorize redError = Colorize(errorMessage)..red();
|
||||||
print(redError);
|
print(redError);
|
||||||
throw ToolExit(exitCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error thrown when a command needs to exit with a non-zero exit code.
|
/// Error thrown when a command needs to exit with a non-zero exit code.
|
||||||
@ -456,9 +455,10 @@ abstract class PluginCommand extends Command<void> {
|
|||||||
GitDir? baseGitDir = gitDir;
|
GitDir? baseGitDir = gitDir;
|
||||||
if (baseGitDir == null) {
|
if (baseGitDir == null) {
|
||||||
if (!await GitDir.isGitDir(rootDir)) {
|
if (!await GitDir.isGitDir(rootDir)) {
|
||||||
printErrorAndExit(
|
printError(
|
||||||
errorMessage: '$rootDir is not a valid Git repository.',
|
'$rootDir is not a valid Git repository.',
|
||||||
exitCode: 2);
|
);
|
||||||
|
throw ToolExit(2);
|
||||||
}
|
}
|
||||||
baseGitDir = await GitDir.fromExisting(rootDir);
|
baseGitDir = await GitDir.fromExisting(rootDir);
|
||||||
}
|
}
|
||||||
@ -599,7 +599,7 @@ class ProcessRunner {
|
|||||||
/// passing [workingDir].
|
/// passing [workingDir].
|
||||||
///
|
///
|
||||||
/// Returns the started [io.Process].
|
/// Returns the started [io.Process].
|
||||||
Future<io.Process?> start(String executable, List<String> args,
|
Future<io.Process> start(String executable, List<String> args,
|
||||||
{Directory? workingDirectory}) async {
|
{Directory? workingDirectory}) async {
|
||||||
final io.Process process = await io.Process.start(executable, args,
|
final io.Process process = await io.Process.start(executable, args,
|
||||||
workingDirectory: workingDirectory?.path);
|
workingDirectory: workingDirectory?.path);
|
||||||
@ -641,12 +641,12 @@ class PubVersionFinder {
|
|||||||
|
|
||||||
if (response.statusCode == 404) {
|
if (response.statusCode == 404) {
|
||||||
return PubVersionFinderResponse(
|
return PubVersionFinderResponse(
|
||||||
versions: null,
|
versions: <Version>[],
|
||||||
result: PubVersionFinderResult.noPackageFound,
|
result: PubVersionFinderResult.noPackageFound,
|
||||||
httpResponse: response);
|
httpResponse: response);
|
||||||
} else if (response.statusCode != 200) {
|
} else if (response.statusCode != 200) {
|
||||||
return PubVersionFinderResponse(
|
return PubVersionFinderResponse(
|
||||||
versions: null,
|
versions: <Version>[],
|
||||||
result: PubVersionFinderResult.fail,
|
result: PubVersionFinderResult.fail,
|
||||||
httpResponse: response);
|
httpResponse: response);
|
||||||
}
|
}
|
||||||
@ -666,9 +666,12 @@ class PubVersionFinder {
|
|||||||
/// Represents a response for [PubVersionFinder].
|
/// Represents a response for [PubVersionFinder].
|
||||||
class PubVersionFinderResponse {
|
class PubVersionFinderResponse {
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
PubVersionFinderResponse({this.versions, this.result, this.httpResponse}) {
|
PubVersionFinderResponse(
|
||||||
if (versions != null && versions!.isNotEmpty) {
|
{required this.versions,
|
||||||
versions!.sort((Version a, Version b) {
|
required this.result,
|
||||||
|
required this.httpResponse}) {
|
||||||
|
if (versions.isNotEmpty) {
|
||||||
|
versions.sort((Version a, Version b) {
|
||||||
// TODO(cyanglaz): Think about how to handle pre-release version with [Version.prioritize].
|
// TODO(cyanglaz): Think about how to handle pre-release version with [Version.prioritize].
|
||||||
// https://github.com/flutter/flutter/issues/82222
|
// https://github.com/flutter/flutter/issues/82222
|
||||||
return b.compareTo(a);
|
return b.compareTo(a);
|
||||||
@ -680,13 +683,13 @@ class PubVersionFinderResponse {
|
|||||||
///
|
///
|
||||||
/// This is sorted by largest to smallest, so the first element in the list is the largest version.
|
/// This is sorted by largest to smallest, so the first element in the list is the largest version.
|
||||||
/// Might be `null` if the [result] is not [PubVersionFinderResult.success].
|
/// Might be `null` if the [result] is not [PubVersionFinderResult.success].
|
||||||
final List<Version>? versions;
|
final List<Version> versions;
|
||||||
|
|
||||||
/// The result of the version finder.
|
/// The result of the version finder.
|
||||||
final PubVersionFinderResult? result;
|
final PubVersionFinderResult result;
|
||||||
|
|
||||||
/// The response object of the http request.
|
/// The response object of the http request.
|
||||||
final http.Response? httpResponse;
|
final http.Response httpResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An enum representing the result of [PubVersionFinder].
|
/// An enum representing the result of [PubVersionFinder].
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart=2.9
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io' as io;
|
import 'dart:io' as io;
|
||||||
@ -11,7 +9,6 @@ import 'dart:io' as io;
|
|||||||
import 'package:colorize/colorize.dart';
|
import 'package:colorize/colorize.dart';
|
||||||
import 'package:file/file.dart';
|
import 'package:file/file.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
import 'package:pub_semver/pub_semver.dart';
|
import 'package:pub_semver/pub_semver.dart';
|
||||||
import 'package:pubspec_parse/pubspec_parse.dart';
|
import 'package:pubspec_parse/pubspec_parse.dart';
|
||||||
|
|
||||||
@ -23,7 +20,7 @@ class PublishCheckCommand extends PluginCommand {
|
|||||||
PublishCheckCommand(
|
PublishCheckCommand(
|
||||||
Directory packagesDir, {
|
Directory packagesDir, {
|
||||||
ProcessRunner processRunner = const ProcessRunner(),
|
ProcessRunner processRunner = const ProcessRunner(),
|
||||||
this.httpClient,
|
http.Client? httpClient,
|
||||||
}) : _pubVersionFinder =
|
}) : _pubVersionFinder =
|
||||||
PubVersionFinder(httpClient: httpClient ?? http.Client()),
|
PubVersionFinder(httpClient: httpClient ?? http.Client()),
|
||||||
super(packagesDir, processRunner: processRunner) {
|
super(packagesDir, processRunner: processRunner) {
|
||||||
@ -65,9 +62,6 @@ class PublishCheckCommand extends PluginCommand {
|
|||||||
final String description =
|
final String description =
|
||||||
'Checks to make sure that a plugin *could* be published.';
|
'Checks to make sure that a plugin *could* be published.';
|
||||||
|
|
||||||
/// The custom http client used to query versions on pub.
|
|
||||||
final http.Client httpClient;
|
|
||||||
|
|
||||||
final PubVersionFinder _pubVersionFinder;
|
final PubVersionFinder _pubVersionFinder;
|
||||||
|
|
||||||
// The output JSON when the _machineFlag is on.
|
// The output JSON when the _machineFlag is on.
|
||||||
@ -135,7 +129,7 @@ class PublishCheckCommand extends PluginCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pubspec _tryParsePubspec(Directory package) {
|
Pubspec? _tryParsePubspec(Directory package) {
|
||||||
final File pubspecFile = package.childFile('pubspec.yaml');
|
final File pubspecFile = package.childFile('pubspec.yaml');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -205,7 +199,7 @@ class PublishCheckCommand extends PluginCommand {
|
|||||||
final String packageName = package.basename;
|
final String packageName = package.basename;
|
||||||
print('Checking that $packageName can be published.');
|
print('Checking that $packageName can be published.');
|
||||||
|
|
||||||
final Pubspec pubspec = _tryParsePubspec(package);
|
final Pubspec? pubspec = _tryParsePubspec(package);
|
||||||
if (pubspec == null) {
|
if (pubspec == null) {
|
||||||
print('no pubspec');
|
print('no pubspec');
|
||||||
return _PublishCheckResult._error;
|
return _PublishCheckResult._error;
|
||||||
@ -214,7 +208,7 @@ class PublishCheckCommand extends PluginCommand {
|
|||||||
return _PublishCheckResult._published;
|
return _PublishCheckResult._published;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Version version = pubspec.version;
|
final Version? version = pubspec.version;
|
||||||
final _PublishCheckResult alreadyPublishedResult =
|
final _PublishCheckResult alreadyPublishedResult =
|
||||||
await _checkIfAlreadyPublished(
|
await _checkIfAlreadyPublished(
|
||||||
packageName: packageName, version: version);
|
packageName: packageName, version: version);
|
||||||
@ -238,29 +232,24 @@ class PublishCheckCommand extends PluginCommand {
|
|||||||
|
|
||||||
// Check if `packageName` already has `version` published on pub.
|
// Check if `packageName` already has `version` published on pub.
|
||||||
Future<_PublishCheckResult> _checkIfAlreadyPublished(
|
Future<_PublishCheckResult> _checkIfAlreadyPublished(
|
||||||
{String packageName, Version version}) async {
|
{required String packageName, required Version? version}) async {
|
||||||
final PubVersionFinderResponse pubVersionFinderResponse =
|
final PubVersionFinderResponse pubVersionFinderResponse =
|
||||||
await _pubVersionFinder.getPackageVersion(package: packageName);
|
await _pubVersionFinder.getPackageVersion(package: packageName);
|
||||||
_PublishCheckResult result;
|
|
||||||
switch (pubVersionFinderResponse.result) {
|
switch (pubVersionFinderResponse.result) {
|
||||||
case PubVersionFinderResult.success:
|
case PubVersionFinderResult.success:
|
||||||
result = pubVersionFinderResponse.versions.contains(version)
|
return pubVersionFinderResponse.versions.contains(version)
|
||||||
? _PublishCheckResult._published
|
? _PublishCheckResult._published
|
||||||
: _PublishCheckResult._notPublished;
|
: _PublishCheckResult._notPublished;
|
||||||
break;
|
|
||||||
case PubVersionFinderResult.fail:
|
case PubVersionFinderResult.fail:
|
||||||
print('''
|
print('''
|
||||||
Error fetching version on pub for $packageName.
|
Error fetching version on pub for $packageName.
|
||||||
HTTP Status ${pubVersionFinderResponse.httpResponse.statusCode}
|
HTTP Status ${pubVersionFinderResponse.httpResponse.statusCode}
|
||||||
HTTP response: ${pubVersionFinderResponse.httpResponse.body}
|
HTTP response: ${pubVersionFinderResponse.httpResponse.body}
|
||||||
''');
|
''');
|
||||||
result = _PublishCheckResult._error;
|
return _PublishCheckResult._error;
|
||||||
break;
|
|
||||||
case PubVersionFinderResult.noPackageFound:
|
case PubVersionFinderResult.noPackageFound:
|
||||||
result = _PublishCheckResult._notPublished;
|
return _PublishCheckResult._notPublished;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setStatus(String status) {
|
void _setStatus(String status) {
|
||||||
@ -272,7 +261,7 @@ HTTP response: ${pubVersionFinderResponse.httpResponse.body}
|
|||||||
return const JsonEncoder.withIndent(' ').convert(_machineOutput);
|
return const JsonEncoder.withIndent(' ').convert(_machineOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _printImportantStatusMessage(String message, {@required bool isError}) {
|
void _printImportantStatusMessage(String message, {required bool isError}) {
|
||||||
final String statusMessage = '${isError ? 'ERROR' : 'SUCCESS'}: $message';
|
final String statusMessage = '${isError ? 'ERROR' : 'SUCCESS'}: $message';
|
||||||
if (getBoolArg(_machineFlag)) {
|
if (getBoolArg(_machineFlag)) {
|
||||||
print(statusMessage);
|
print(statusMessage);
|
||||||
|
@ -507,10 +507,11 @@ Safe to ignore if the package is deleted in this commit.
|
|||||||
}
|
}
|
||||||
final String credential = io.Platform.environment[_pubCredentialName];
|
final String credential = io.Platform.environment[_pubCredentialName];
|
||||||
if (credential == null) {
|
if (credential == null) {
|
||||||
printErrorAndExit(errorMessage: '''
|
printError('''
|
||||||
No pub credential available. Please check if `~/.pub-cache/credentials.json` is valid.
|
No pub credential available. Please check if `~/.pub-cache/credentials.json` is valid.
|
||||||
If running this command on CI, you can set the pub credential content in the $_pubCredentialName environment variable.
|
If running this command on CI, you can set the pub credential content in the $_pubCredentialName environment variable.
|
||||||
''');
|
''');
|
||||||
|
throw ToolExit(1);
|
||||||
}
|
}
|
||||||
credentialFile.openSync(mode: FileMode.writeOnlyAppend)
|
credentialFile.openSync(mode: FileMode.writeOnlyAppend)
|
||||||
..writeStringSync(credential)
|
..writeStringSync(credential)
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart=2.9
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:file/file.dart';
|
import 'package:file/file.dart';
|
||||||
@ -37,7 +35,7 @@ enum NextVersionType {
|
|||||||
/// those have different semver rules.
|
/// those have different semver rules.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
Map<Version, NextVersionType> getAllowedNextVersions(
|
Map<Version, NextVersionType> getAllowedNextVersions(
|
||||||
Version masterVersion, Version headVersion) {
|
{required Version masterVersion, required Version headVersion}) {
|
||||||
final Map<Version, NextVersionType> allowedNextVersions =
|
final Map<Version, NextVersionType> allowedNextVersions =
|
||||||
<Version, NextVersionType>{
|
<Version, NextVersionType>{
|
||||||
masterVersion.nextMajor: NextVersionType.BREAKING_MAJOR,
|
masterVersion.nextMajor: NextVersionType.BREAKING_MAJOR,
|
||||||
@ -75,8 +73,8 @@ class VersionCheckCommand extends PluginCommand {
|
|||||||
VersionCheckCommand(
|
VersionCheckCommand(
|
||||||
Directory packagesDir, {
|
Directory packagesDir, {
|
||||||
ProcessRunner processRunner = const ProcessRunner(),
|
ProcessRunner processRunner = const ProcessRunner(),
|
||||||
GitDir gitDir,
|
GitDir? gitDir,
|
||||||
this.httpClient,
|
http.Client? httpClient,
|
||||||
}) : _pubVersionFinder =
|
}) : _pubVersionFinder =
|
||||||
PubVersionFinder(httpClient: httpClient ?? http.Client()),
|
PubVersionFinder(httpClient: httpClient ?? http.Client()),
|
||||||
super(packagesDir, processRunner: processRunner, gitDir: gitDir) {
|
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'
|
'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.';
|
'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;
|
final PubVersionFinder _pubVersionFinder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -112,6 +107,8 @@ class VersionCheckCommand extends PluginCommand {
|
|||||||
final List<String> changedPubspecs =
|
final List<String> changedPubspecs =
|
||||||
await gitVersionFinder.getChangedPubSpecs();
|
await gitVersionFinder.getChangedPubSpecs();
|
||||||
|
|
||||||
|
final List<String> badVersionChangePubspecs = <String>[];
|
||||||
|
|
||||||
const String indentation = ' ';
|
const String indentation = ' ';
|
||||||
for (final String pubspecPath in changedPubspecs) {
|
for (final String pubspecPath in changedPubspecs) {
|
||||||
print('Checking versions for $pubspecPath...');
|
print('Checking versions for $pubspecPath...');
|
||||||
@ -126,15 +123,16 @@ class VersionCheckCommand extends PluginCommand {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Version headVersion =
|
final Version? headVersion =
|
||||||
await gitVersionFinder.getPackageVersion(pubspecPath, gitRef: 'HEAD');
|
await gitVersionFinder.getPackageVersion(pubspecPath, gitRef: 'HEAD');
|
||||||
if (headVersion == null) {
|
if (headVersion == null) {
|
||||||
printErrorAndExit(
|
printError('${indentation}No version found. A package that '
|
||||||
errorMessage: '${indentation}No version found. A package that '
|
'intentionally has no version should be marked '
|
||||||
'intentionally has no version should be marked '
|
'"publish_to: none".');
|
||||||
'"publish_to: none".');
|
badVersionChangePubspecs.add(pubspecPath);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
Version sourceVersion;
|
Version? sourceVersion;
|
||||||
if (getBoolArg(_againstPubFlag)) {
|
if (getBoolArg(_againstPubFlag)) {
|
||||||
final String packageName = pubspecFile.parent.basename;
|
final String packageName = pubspecFile.parent.basename;
|
||||||
final PubVersionFinderResponse pubVersionFinderResponse =
|
final PubVersionFinderResponse pubVersionFinderResponse =
|
||||||
@ -146,12 +144,13 @@ class VersionCheckCommand extends PluginCommand {
|
|||||||
'$indentation$packageName: Current largest version on pub: $sourceVersion');
|
'$indentation$packageName: Current largest version on pub: $sourceVersion');
|
||||||
break;
|
break;
|
||||||
case PubVersionFinderResult.fail:
|
case PubVersionFinderResult.fail:
|
||||||
printErrorAndExit(errorMessage: '''
|
printError('''
|
||||||
${indentation}Error fetching version on pub for $packageName.
|
${indentation}Error fetching version on pub for $packageName.
|
||||||
${indentation}HTTP Status ${pubVersionFinderResponse.httpResponse.statusCode}
|
${indentation}HTTP Status ${pubVersionFinderResponse.httpResponse.statusCode}
|
||||||
${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
|
${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
|
||||||
''');
|
''');
|
||||||
break;
|
badVersionChangePubspecs.add(pubspecPath);
|
||||||
|
continue;
|
||||||
case PubVersionFinderResult.noPackageFound:
|
case PubVersionFinderResult.noPackageFound:
|
||||||
sourceVersion = null;
|
sourceVersion = null;
|
||||||
break;
|
break;
|
||||||
@ -180,7 +179,8 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
|
|||||||
// Check for reverts when doing local validation.
|
// Check for reverts when doing local validation.
|
||||||
if (!getBoolArg(_againstPubFlag) && headVersion < sourceVersion) {
|
if (!getBoolArg(_againstPubFlag) && headVersion < sourceVersion) {
|
||||||
final Map<Version, NextVersionType> possibleVersionsFromNewVersion =
|
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
|
// 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
|
// 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.
|
// 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 =
|
final Map<Version, NextVersionType> allowedNextVersions =
|
||||||
getAllowedNextVersions(sourceVersion, headVersion);
|
getAllowedNextVersions(
|
||||||
|
masterVersion: sourceVersion, headVersion: headVersion);
|
||||||
|
|
||||||
if (!allowedNextVersions.containsKey(headVersion)) {
|
if (!allowedNextVersions.containsKey(headVersion)) {
|
||||||
final String source = (getBoolArg(_againstPubFlag)) ? 'pub' : 'master';
|
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}HEAD: $headVersion, $source: $sourceVersion.\n'
|
||||||
'${indentation}Allowed versions: $allowedNextVersions';
|
'${indentation}Allowed versions: $allowedNextVersions');
|
||||||
printErrorAndExit(errorMessage: error);
|
badVersionChangePubspecs.add(pubspecPath);
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
print('$indentation$headVersion -> $sourceVersion');
|
print('$indentation$headVersion -> $sourceVersion');
|
||||||
}
|
}
|
||||||
@ -208,38 +210,65 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body}
|
|||||||
pubspec.name.endsWith('_platform_interface');
|
pubspec.name.endsWith('_platform_interface');
|
||||||
if (isPlatformInterface &&
|
if (isPlatformInterface &&
|
||||||
allowedNextVersions[headVersion] == NextVersionType.BREAKING_MAJOR) {
|
allowedNextVersions[headVersion] == NextVersionType.BREAKING_MAJOR) {
|
||||||
final String error = '$pubspecPath breaking change detected.\n'
|
printError('$pubspecPath breaking change detected.\n'
|
||||||
'Breaking changes to platform interfaces are strongly discouraged.\n';
|
'Breaking changes to platform interfaces are strongly discouraged.\n');
|
||||||
printErrorAndExit(errorMessage: error);
|
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()) {
|
bool passed = true;
|
||||||
await _checkVersionsMatch(plugin);
|
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!');
|
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
|
// get version from pubspec
|
||||||
final String packageName = plugin.basename;
|
final String packageName = plugin.basename;
|
||||||
print('-----------------------------------------');
|
print('-----------------------------------------');
|
||||||
print(
|
print(
|
||||||
'Checking the first version listed in CHANGELOG.md matches the version in pubspec.yaml for $packageName.');
|
'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) {
|
if (pubspec == null) {
|
||||||
const String error = 'Cannot parse version from pubspec.yaml';
|
printError('Cannot parse version from pubspec.yaml');
|
||||||
printErrorAndExit(errorMessage: error);
|
return false;
|
||||||
}
|
}
|
||||||
final Version fromPubspec = pubspec.version;
|
final Version? fromPubspec = pubspec.version;
|
||||||
|
|
||||||
// get first version from CHANGELOG
|
// get first version from CHANGELOG
|
||||||
final File changelog = plugin.childFile('CHANGELOG.md');
|
final File changelog = plugin.childFile('CHANGELOG.md');
|
||||||
final List<String> lines = changelog.readAsLinesSync();
|
final List<String> lines = changelog.readAsLinesSync();
|
||||||
String firstLineWithText;
|
String? firstLineWithText;
|
||||||
final Iterator<String> iterator = lines.iterator;
|
final Iterator<String> iterator = lines.iterator;
|
||||||
while (iterator.moveNext()) {
|
while (iterator.moveNext()) {
|
||||||
if (iterator.current.trim().isNotEmpty) {
|
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.
|
// 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
|
// Skip validation for the special NEXT version that's used to accumulate
|
||||||
// changes that don't warrant publishing on their own.
|
// 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) {
|
if (fromChangeLog == null) {
|
||||||
final String error =
|
printError(
|
||||||
'Cannot find version on the first line of ${plugin.path}/CHANGELOG.md';
|
'Cannot find version on the first line of ${plugin.path}/CHANGELOG.md');
|
||||||
printErrorAndExit(errorMessage: error);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromPubspec != fromChangeLog) {
|
if (fromPubspec != fromChangeLog) {
|
||||||
final String error = '''
|
printError('''
|
||||||
versions for $packageName in CHANGELOG.md and pubspec.yaml do not match.
|
versions for $packageName in CHANGELOG.md and pubspec.yaml do not match.
|
||||||
The version in pubspec.yaml is $fromPubspec.
|
The version in pubspec.yaml is $fromPubspec.
|
||||||
The first version listed in CHANGELOG.md is $fromChangeLog.
|
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 NEXT wasn't the first section, it should not exist at all.
|
||||||
if (!hasNextSection) {
|
if (!hasNextSection) {
|
||||||
final RegExp nextRegex = RegExp(r'^#+\s*NEXT\s*$');
|
final RegExp nextRegex = RegExp(r'^#+\s*NEXT\s*$');
|
||||||
if (lines.any((String line) => nextRegex.hasMatch(line))) {
|
if (lines.any((String line) => nextRegex.hasMatch(line))) {
|
||||||
printErrorAndExit(errorMessage: '''
|
printError('''
|
||||||
When bumping the version for release, the NEXT section should be incorporated
|
When bumping the version for release, the NEXT section should be incorporated
|
||||||
into the new version's release notes.
|
into the new version's release notes.
|
||||||
''');
|
''');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print('$packageName passed version check');
|
print('$packageName passed version check');
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pubspec _tryParsePubspec(Directory package) {
|
Pubspec? _tryParsePubspec(Directory package) {
|
||||||
final File pubspecFile = package.childFile('pubspec.yaml');
|
final File pubspecFile = package.childFile('pubspec.yaml');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Pubspec pubspec = Pubspec.parse(pubspecFile.readAsStringSync());
|
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;
|
return pubspec;
|
||||||
} on Exception catch (exception) {
|
} on Exception catch (exception) {
|
||||||
final String error =
|
printError(
|
||||||
'Failed to parse `pubspec.yaml` at ${pubspecFile.path}: $exception}';
|
'Failed to parse `pubspec.yaml` at ${pubspecFile.path}: $exception}');
|
||||||
printErrorAndExit(errorMessage: error);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -457,10 +457,10 @@ file2/file2.cc
|
|||||||
final PubVersionFinderResponse response =
|
final PubVersionFinderResponse response =
|
||||||
await finder.getPackageVersion(package: 'some_package');
|
await finder.getPackageVersion(package: 'some_package');
|
||||||
|
|
||||||
expect(response.versions, isNull);
|
expect(response.versions, isEmpty);
|
||||||
expect(response.result, PubVersionFinderResult.noPackageFound);
|
expect(response.result, PubVersionFinderResult.noPackageFound);
|
||||||
expect(response.httpResponse!.statusCode, 404);
|
expect(response.httpResponse.statusCode, 404);
|
||||||
expect(response.httpResponse!.body, '');
|
expect(response.httpResponse.body, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('HTTP error when getting versions from pub', () async {
|
test('HTTP error when getting versions from pub', () async {
|
||||||
@ -471,10 +471,10 @@ file2/file2.cc
|
|||||||
final PubVersionFinderResponse response =
|
final PubVersionFinderResponse response =
|
||||||
await finder.getPackageVersion(package: 'some_package');
|
await finder.getPackageVersion(package: 'some_package');
|
||||||
|
|
||||||
expect(response.versions, isNull);
|
expect(response.versions, isEmpty);
|
||||||
expect(response.result, PubVersionFinderResult.fail);
|
expect(response.result, PubVersionFinderResult.fail);
|
||||||
expect(response.httpResponse!.statusCode, 400);
|
expect(response.httpResponse.statusCode, 400);
|
||||||
expect(response.httpResponse!.body, '');
|
expect(response.httpResponse.body, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Get a correct list of versions when http response is OK.', () async {
|
test('Get a correct list of versions when http response is OK.', () async {
|
||||||
@ -517,8 +517,8 @@ file2/file2.cc
|
|||||||
Version.parse('0.0.1'),
|
Version.parse('0.0.1'),
|
||||||
]);
|
]);
|
||||||
expect(response.result, PubVersionFinderResult.success);
|
expect(response.result, PubVersionFinderResult.success);
|
||||||
expect(response.httpResponse!.statusCode, 200);
|
expect(response.httpResponse.statusCode, 200);
|
||||||
expect(response.httpResponse!.body, json.encode(httpResponse));
|
expect(response.httpResponse.body, json.encode(httpResponse));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart=2.9
|
|
||||||
|
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io' as io;
|
import 'dart:io' as io;
|
||||||
@ -23,9 +21,9 @@ import 'util.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
group('$PublishCheckProcessRunner tests', () {
|
group('$PublishCheckProcessRunner tests', () {
|
||||||
FileSystem fileSystem;
|
FileSystem fileSystem;
|
||||||
Directory packagesDir;
|
late Directory packagesDir;
|
||||||
PublishCheckProcessRunner processRunner;
|
late PublishCheckProcessRunner processRunner;
|
||||||
CommandRunner<void> runner;
|
late CommandRunner<void> runner;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
fileSystem = MemoryFileSystem();
|
fileSystem = MemoryFileSystem();
|
||||||
@ -177,7 +175,7 @@ void main() {
|
|||||||
} else if (request.url.pathSegments.last == 'no_publish_b.json') {
|
} else if (request.url.pathSegments.last == 'no_publish_b.json') {
|
||||||
return http.Response(json.encode(httpResponseB), 200);
|
return http.Response(json.encode(httpResponseB), 200);
|
||||||
}
|
}
|
||||||
return null;
|
return http.Response('', 500);
|
||||||
});
|
});
|
||||||
final PublishCheckCommand command = PublishCheckCommand(packagesDir,
|
final PublishCheckCommand command = PublishCheckCommand(packagesDir,
|
||||||
processRunner: processRunner, httpClient: mockClient);
|
processRunner: processRunner, httpClient: mockClient);
|
||||||
@ -241,7 +239,7 @@ void main() {
|
|||||||
} else if (request.url.pathSegments.last == 'no_publish_b.json') {
|
} else if (request.url.pathSegments.last == 'no_publish_b.json') {
|
||||||
return http.Response(json.encode(httpResponseB), 200);
|
return http.Response(json.encode(httpResponseB), 200);
|
||||||
}
|
}
|
||||||
return null;
|
return http.Response('', 500);
|
||||||
});
|
});
|
||||||
final PublishCheckCommand command = PublishCheckCommand(packagesDir,
|
final PublishCheckCommand command = PublishCheckCommand(packagesDir,
|
||||||
processRunner: processRunner, httpClient: mockClient);
|
processRunner: processRunner, httpClient: mockClient);
|
||||||
@ -308,7 +306,7 @@ void main() {
|
|||||||
} else if (request.url.pathSegments.last == 'no_publish_b.json') {
|
} else if (request.url.pathSegments.last == 'no_publish_b.json') {
|
||||||
return http.Response(json.encode(httpResponseB), 200);
|
return http.Response(json.encode(httpResponseB), 200);
|
||||||
}
|
}
|
||||||
return null;
|
return http.Response('', 500);
|
||||||
});
|
});
|
||||||
final PublishCheckCommand command = PublishCheckCommand(packagesDir,
|
final PublishCheckCommand command = PublishCheckCommand(packagesDir,
|
||||||
processRunner: processRunner, httpClient: mockClient);
|
processRunner: processRunner, httpClient: mockClient);
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart=2.9
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io' as io;
|
import 'dart:io' as io;
|
||||||
@ -13,24 +11,25 @@ import 'package:file/file.dart';
|
|||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_plugin_tools/src/common.dart';
|
import 'package:flutter_plugin_tools/src/common.dart';
|
||||||
import 'package:flutter_plugin_tools/src/version_check_command.dart';
|
import 'package:flutter_plugin_tools/src/version_check_command.dart';
|
||||||
import 'package:git/git.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:http/testing.dart';
|
import 'package:http/testing.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:pub_semver/pub_semver.dart';
|
import 'package:pub_semver/pub_semver.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import 'common_test.mocks.dart';
|
||||||
import 'util.dart';
|
import 'util.dart';
|
||||||
|
|
||||||
void testAllowedVersion(
|
void testAllowedVersion(
|
||||||
String masterVersion,
|
String masterVersion,
|
||||||
String headVersion, {
|
String headVersion, {
|
||||||
bool allowed = true,
|
bool allowed = true,
|
||||||
NextVersionType nextVersionType,
|
NextVersionType? nextVersionType,
|
||||||
}) {
|
}) {
|
||||||
final Version master = Version.parse(masterVersion);
|
final Version master = Version.parse(masterVersion);
|
||||||
final Version head = Version.parse(headVersion);
|
final Version head = Version.parse(headVersion);
|
||||||
final Map<Version, NextVersionType> allowedVersions =
|
final Map<Version, NextVersionType> allowedVersions =
|
||||||
getAllowedNextVersions(master, head);
|
getAllowedNextVersions(masterVersion: master, headVersion: head);
|
||||||
if (allowed) {
|
if (allowed) {
|
||||||
expect(allowedVersions, contains(head));
|
expect(allowedVersions, contains(head));
|
||||||
if (nextVersionType != null) {
|
if (nextVersionType != null) {
|
||||||
@ -41,8 +40,6 @@ void testAllowedVersion(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockGitDir extends Mock implements GitDir {}
|
|
||||||
|
|
||||||
class MockProcessResult extends Mock implements io.ProcessResult {}
|
class MockProcessResult extends Mock implements io.ProcessResult {}
|
||||||
|
|
||||||
const String _redColorMessagePrefix = '\x1B[31m';
|
const String _redColorMessagePrefix = '\x1B[31m';
|
||||||
@ -57,13 +54,13 @@ void main() {
|
|||||||
const String indentation = ' ';
|
const String indentation = ' ';
|
||||||
group('$VersionCheckCommand', () {
|
group('$VersionCheckCommand', () {
|
||||||
FileSystem fileSystem;
|
FileSystem fileSystem;
|
||||||
Directory packagesDir;
|
late Directory packagesDir;
|
||||||
CommandRunner<void> runner;
|
late CommandRunner<void> runner;
|
||||||
RecordingProcessRunner processRunner;
|
late RecordingProcessRunner processRunner;
|
||||||
List<List<String>> gitDirCommands;
|
late List<List<String>> gitDirCommands;
|
||||||
String gitDiffResponse;
|
String gitDiffResponse;
|
||||||
Map<String, String> gitShowResponses;
|
Map<String, String> gitShowResponses;
|
||||||
MockGitDir gitDir;
|
late MockGitDir gitDir;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
fileSystem = MemoryFileSystem();
|
fileSystem = MemoryFileSystem();
|
||||||
@ -77,17 +74,19 @@ void main() {
|
|||||||
gitDirCommands.add(invocation.positionalArguments[0] as List<String>);
|
gitDirCommands.add(invocation.positionalArguments[0] as List<String>);
|
||||||
final MockProcessResult mockProcessResult = MockProcessResult();
|
final MockProcessResult mockProcessResult = MockProcessResult();
|
||||||
if (invocation.positionalArguments[0][0] == 'diff') {
|
if (invocation.positionalArguments[0][0] == 'diff') {
|
||||||
when<String>(mockProcessResult.stdout as String)
|
when<String?>(mockProcessResult.stdout as String?)
|
||||||
.thenReturn(gitDiffResponse);
|
.thenReturn(gitDiffResponse);
|
||||||
} else if (invocation.positionalArguments[0][0] == 'show') {
|
} else if (invocation.positionalArguments[0][0] == 'show') {
|
||||||
final String response =
|
final String? response =
|
||||||
gitShowResponses[invocation.positionalArguments[0][1]];
|
gitShowResponses[invocation.positionalArguments[0][1]];
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
throw const io.ProcessException('git', <String>['show']);
|
throw const io.ProcessException('git', <String>['show']);
|
||||||
}
|
}
|
||||||
when<String>(mockProcessResult.stdout as String).thenReturn(response);
|
when<String?>(mockProcessResult.stdout as String?)
|
||||||
|
.thenReturn(response);
|
||||||
} else if (invocation.positionalArguments[0][0] == 'merge-base') {
|
} else if (invocation.positionalArguments[0][0] == 'merge-base') {
|
||||||
when<String>(mockProcessResult.stdout as String).thenReturn('abc123');
|
when<String?>(mockProcessResult.stdout as String?)
|
||||||
|
.thenReturn('abc123');
|
||||||
}
|
}
|
||||||
return Future<io.ProcessResult>.value(mockProcessResult);
|
return Future<io.ProcessResult>.value(mockProcessResult);
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user