mirror of
https://github.com/flutter/packages.git
synced 2025-06-21 15:20:15 +08:00
[tools] Improves version-check logic (#6354)
Improves the logic used to determine whether to require a version and/or CHANGELOG change: - Removes the requirement that dev-only (e.g., test) changes update the CHANGELOG, since in practice we were essentially always overriding in that case. - Adds file-level analysis of `build.gradle` files to determine whether they are only changing test dependencies. - Improves the "is this a published example file" logic to better match pub.dev's logic, to fix some false positives and false negatives (e.g., `rfw`'s `example/<foo>/lib/main.dart` being considered published). Removes the no-longer-necessary special-case handling of some Dependabot PRs, as well as the PR-description-based system it was built on (and that turned out not to be very useful due to the way `CIRRUS_CHANGE_MESSAGE` actually worked). `build.gradle` analysis should not cover all such cases, and without the need to hard-code them by package name.
This commit is contained in:
@ -1,3 +1,13 @@
|
|||||||
|
## 0.10.0
|
||||||
|
|
||||||
|
* Improves the logic in `version-check` to determine what changes don't require
|
||||||
|
version changes, as well as making any dev-only changes also not require
|
||||||
|
changelog changes since in practice we almost always override the check in
|
||||||
|
that case.
|
||||||
|
* Removes special-case handling of Dependabot PRs, and the (fragile)
|
||||||
|
`--change-description-file` flag was only still used for that case, as
|
||||||
|
the improved diff analysis now handles that case more robustly.
|
||||||
|
|
||||||
## 0.9.3
|
## 0.9.3
|
||||||
|
|
||||||
* Raises minimum `compileSdkVersion` to 32 for the `all-plugins-app` command.
|
* Raises minimum `compileSdkVersion` to 32 for the `all-plugins-app` command.
|
||||||
|
@ -50,6 +50,27 @@ class GitVersionFinder {
|
|||||||
return changedFiles.toList();
|
return changedFiles.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a list of all the changed files.
|
||||||
|
Future<List<String>> getDiffContents({
|
||||||
|
String? targetPath,
|
||||||
|
bool includeUncommitted = false,
|
||||||
|
}) async {
|
||||||
|
final String baseSha = await getBaseSha();
|
||||||
|
final io.ProcessResult diffCommand = await baseGitDir.runCommand(<String>[
|
||||||
|
'diff',
|
||||||
|
baseSha,
|
||||||
|
if (!includeUncommitted) 'HEAD',
|
||||||
|
if (targetPath != null) ...<String>['--', targetPath],
|
||||||
|
]);
|
||||||
|
final String diffStdout = diffCommand.stdout.toString();
|
||||||
|
if (diffStdout.isEmpty) {
|
||||||
|
return <String>[];
|
||||||
|
}
|
||||||
|
final List<String> changedFiles = diffStdout.split('\n')
|
||||||
|
..removeWhere((String element) => element.isEmpty);
|
||||||
|
return changedFiles.toList();
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the package version specified in the pubspec file in `pubspecPath` and
|
/// Get the package version specified in the pubspec file in `pubspecPath` and
|
||||||
/// at the revision of `gitRef` (defaulting to the base if not provided).
|
/// at the revision of `gitRef` (defaulting to the base if not provided).
|
||||||
Future<Version?> getPackageVersion(String pubspecPath,
|
Future<Version?> getPackageVersion(String pubspecPath,
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
import 'package:file/file.dart';
|
||||||
|
import 'package:flutter_plugin_tools/src/common/git_version_finder.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
@ -14,6 +16,7 @@ class PackageChangeState {
|
|||||||
const PackageChangeState({
|
const PackageChangeState({
|
||||||
required this.hasChanges,
|
required this.hasChanges,
|
||||||
required this.hasChangelogChange,
|
required this.hasChangelogChange,
|
||||||
|
required this.needsChangelogChange,
|
||||||
required this.needsVersionChange,
|
required this.needsVersionChange,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -26,6 +29,10 @@ class PackageChangeState {
|
|||||||
/// True if any changes in the package require a version change according
|
/// True if any changes in the package require a version change according
|
||||||
/// to repository policy.
|
/// to repository policy.
|
||||||
final bool needsVersionChange;
|
final bool needsVersionChange;
|
||||||
|
|
||||||
|
/// True if any changes in the package require a CHANGELOG change according
|
||||||
|
/// to repository policy.
|
||||||
|
final bool needsChangelogChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks [package] against [changedPaths] to determine what changes it has
|
/// Checks [package] against [changedPaths] to determine what changes it has
|
||||||
@ -38,11 +45,15 @@ class PackageChangeState {
|
|||||||
/// and `getRelativePosixPath(package.directory, gitDir.path)` respectively;
|
/// and `getRelativePosixPath(package.directory, gitDir.path)` respectively;
|
||||||
/// they are arguments mainly to allow for caching the changed paths for an
|
/// they are arguments mainly to allow for caching the changed paths for an
|
||||||
/// entire command run.
|
/// entire command run.
|
||||||
PackageChangeState checkPackageChangeState(
|
///
|
||||||
|
/// If [git] is provided, [changedPaths] must be repository-relative
|
||||||
|
/// paths, and change type detection can use file diffs in addition to paths.
|
||||||
|
Future<PackageChangeState> checkPackageChangeState(
|
||||||
RepositoryPackage package, {
|
RepositoryPackage package, {
|
||||||
required List<String> changedPaths,
|
required List<String> changedPaths,
|
||||||
required String relativePackagePath,
|
required String relativePackagePath,
|
||||||
}) {
|
GitVersionFinder? git,
|
||||||
|
}) async {
|
||||||
final String packagePrefix = relativePackagePath.endsWith('/')
|
final String packagePrefix = relativePackagePath.endsWith('/')
|
||||||
? relativePackagePath
|
? relativePackagePath
|
||||||
: '$relativePackagePath/';
|
: '$relativePackagePath/';
|
||||||
@ -50,6 +61,7 @@ PackageChangeState checkPackageChangeState(
|
|||||||
bool hasChanges = false;
|
bool hasChanges = false;
|
||||||
bool hasChangelogChange = false;
|
bool hasChangelogChange = false;
|
||||||
bool needsVersionChange = false;
|
bool needsVersionChange = false;
|
||||||
|
bool needsChangelogChange = false;
|
||||||
for (final String path in changedPaths) {
|
for (final String path in changedPaths) {
|
||||||
// Only consider files within the package.
|
// Only consider files within the package.
|
||||||
if (!path.startsWith(packagePrefix)) {
|
if (!path.startsWith(packagePrefix)) {
|
||||||
@ -62,34 +74,131 @@ PackageChangeState checkPackageChangeState(
|
|||||||
if (components.isEmpty) {
|
if (components.isEmpty) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final bool isChangelog = components.first == 'CHANGELOG.md';
|
|
||||||
if (isChangelog) {
|
if (components.first == 'CHANGELOG.md') {
|
||||||
hasChangelogChange = true;
|
hasChangelogChange = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!needsVersionChange &&
|
if (!needsVersionChange) {
|
||||||
!isChangelog &&
|
// Developer-only changes don't need version changes or changelog changes.
|
||||||
// One of a few special files example will be shown on pub.dev, but for
|
if (await _isDevChange(components, git: git, repoPath: path)) {
|
||||||
// anything else in the example publishing has no purpose.
|
continue;
|
||||||
!(components.first == 'example' &&
|
}
|
||||||
!<String>{'main.dart', 'readme.md', 'example.md'}
|
|
||||||
.contains(components.last.toLowerCase())) &&
|
// Some other changes don't need version changes, but might benefit from
|
||||||
// Changes to tests don't need to be published.
|
// changelog changes.
|
||||||
!components.contains('test') &&
|
needsChangelogChange = true;
|
||||||
!components.contains('androidTest') &&
|
if (
|
||||||
!components.contains('RunnerTests') &&
|
// One of a few special files example will be shown on pub.dev, but
|
||||||
!components.contains('RunnerUITests') &&
|
// for anything else in the example publishing has no purpose.
|
||||||
// The top-level "tool" directory is for non-client-facing utility code,
|
!_isUnpublishedExampleChange(components, package)) {
|
||||||
// so doesn't need to be published.
|
needsVersionChange = true;
|
||||||
components.first != 'tool' &&
|
}
|
||||||
// Ignoring lints doesn't affect clients.
|
|
||||||
!components.contains('lint-baseline.xml')) {
|
|
||||||
needsVersionChange = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PackageChangeState(
|
return PackageChangeState(
|
||||||
hasChanges: hasChanges,
|
hasChanges: hasChanges,
|
||||||
hasChangelogChange: hasChangelogChange,
|
hasChangelogChange: hasChangelogChange,
|
||||||
|
needsChangelogChange: needsChangelogChange,
|
||||||
needsVersionChange: needsVersionChange);
|
needsVersionChange: needsVersionChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isTestChange(List<String> pathComponents) {
|
||||||
|
return pathComponents.contains('test') ||
|
||||||
|
pathComponents.contains('androidTest') ||
|
||||||
|
pathComponents.contains('RunnerTests') ||
|
||||||
|
pathComponents.contains('RunnerUITests');
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if the given file is an example file other than the one that will be
|
||||||
|
// published according to https://dart.dev/tools/pub/package-layout#examples.
|
||||||
|
//
|
||||||
|
// This is not exhastive; it currently only handles variations we actually have
|
||||||
|
// in our repositories.
|
||||||
|
bool _isUnpublishedExampleChange(
|
||||||
|
List<String> pathComponents, RepositoryPackage package) {
|
||||||
|
if (pathComponents.first != 'example') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final List<String> exampleComponents = pathComponents.sublist(1);
|
||||||
|
if (exampleComponents.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Directory exampleDirectory =
|
||||||
|
package.directory.childDirectory('example');
|
||||||
|
|
||||||
|
// Check for example.md/EXAMPLE.md first, as that has priority. If it's
|
||||||
|
// present, any other example file is unpublished.
|
||||||
|
final bool hasExampleMd =
|
||||||
|
exampleDirectory.childFile('example.md').existsSync() ||
|
||||||
|
exampleDirectory.childFile('EXAMPLE.md').existsSync();
|
||||||
|
if (hasExampleMd) {
|
||||||
|
return !(exampleComponents.length == 1 &&
|
||||||
|
exampleComponents.first.toLowerCase() == 'example.md');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most packages have an example/lib/main.dart (or occasionally
|
||||||
|
// example/main.dart), so check for that. The other naming variations aren't
|
||||||
|
// currently used.
|
||||||
|
const String mainName = 'main.dart';
|
||||||
|
final bool hasExampleCode =
|
||||||
|
exampleDirectory.childDirectory('lib').childFile(mainName).existsSync() ||
|
||||||
|
exampleDirectory.childFile(mainName).existsSync();
|
||||||
|
if (hasExampleCode) {
|
||||||
|
// If there is an example main, only that example file is published.
|
||||||
|
return !((exampleComponents.length == 1 &&
|
||||||
|
exampleComponents.first == mainName) ||
|
||||||
|
(exampleComponents.length == 2 &&
|
||||||
|
exampleComponents.first == 'lib' &&
|
||||||
|
exampleComponents[1] == mainName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no example code either, the example README.md, if any, is the
|
||||||
|
// file that will be published.
|
||||||
|
return exampleComponents.first.toLowerCase() != 'readme.md';
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if the change is only relevant to people working on the plugin.
|
||||||
|
Future<bool> _isDevChange(List<String> pathComponents,
|
||||||
|
{GitVersionFinder? git, String? repoPath}) async {
|
||||||
|
return _isTestChange(pathComponents) ||
|
||||||
|
// The top-level "tool" directory is for non-client-facing utility
|
||||||
|
// code, such as test scripts.
|
||||||
|
pathComponents.first == 'tool' ||
|
||||||
|
// Ignoring lints doesn't affect clients.
|
||||||
|
pathComponents.contains('lint-baseline.xml') ||
|
||||||
|
await _isGradleTestDependencyChange(pathComponents,
|
||||||
|
git: git, repoPath: repoPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _isGradleTestDependencyChange(List<String> pathComponents,
|
||||||
|
{GitVersionFinder? git, String? repoPath}) async {
|
||||||
|
if (git == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pathComponents.last != 'build.gradle') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final List<String> diff = await git.getDiffContents(targetPath: repoPath);
|
||||||
|
final RegExp changeLine = RegExp(r'[+-] ');
|
||||||
|
final RegExp testDependencyLine =
|
||||||
|
RegExp(r'[+-]\s*(?:androidT|t)estImplementation\s');
|
||||||
|
bool foundTestDependencyChange = false;
|
||||||
|
for (final String line in diff) {
|
||||||
|
if (!changeLine.hasMatch(line) ||
|
||||||
|
line.startsWith('--- ') ||
|
||||||
|
line.startsWith('+++ ')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!testDependencyLine.hasMatch(line)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foundTestDependencyChange = true;
|
||||||
|
}
|
||||||
|
// Only return true if a test dependency change was found, as a failsafe
|
||||||
|
// against having the wrong (e.g., incorrectly empty) diff output.
|
||||||
|
return foundTestDependencyChange;
|
||||||
|
}
|
||||||
|
@ -133,7 +133,7 @@ class UpdateReleaseInfoCommand extends PackageLoopingCommand {
|
|||||||
packagesDir.fileSystem.directory((await gitDir).path);
|
packagesDir.fileSystem.directory((await gitDir).path);
|
||||||
final String relativePackagePath =
|
final String relativePackagePath =
|
||||||
getRelativePosixPath(package.directory, from: gitRoot);
|
getRelativePosixPath(package.directory, from: gitRoot);
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: _changedFiles,
|
changedPaths: _changedFiles,
|
||||||
relativePackagePath: relativePackagePath);
|
relativePackagePath: relativePackagePath);
|
||||||
|
|
||||||
|
@ -18,8 +18,6 @@ import 'common/process_runner.dart';
|
|||||||
import 'common/pub_version_finder.dart';
|
import 'common/pub_version_finder.dart';
|
||||||
import 'common/repository_package.dart';
|
import 'common/repository_package.dart';
|
||||||
|
|
||||||
const int _exitMissingChangeDescriptionFile = 3;
|
|
||||||
|
|
||||||
/// Categories of version change types.
|
/// Categories of version change types.
|
||||||
enum NextVersionType {
|
enum NextVersionType {
|
||||||
/// A breaking change.
|
/// A breaking change.
|
||||||
@ -116,11 +114,6 @@ class VersionCheckCommand extends PackageLoopingCommand {
|
|||||||
'Defaults to false, which means the version check only run against '
|
'Defaults to false, which means the version check only run against '
|
||||||
'the previous version in code.',
|
'the previous version in code.',
|
||||||
);
|
);
|
||||||
argParser.addOption(_changeDescriptionFile,
|
|
||||||
help: 'The path to a file containing the description of the change '
|
|
||||||
'(e.g., PR description or commit message).\n\n'
|
|
||||||
'If supplied, this is used to allow overrides to some version '
|
|
||||||
'checks.');
|
|
||||||
argParser.addOption(_prLabelsArg,
|
argParser.addOption(_prLabelsArg,
|
||||||
help: 'A comma-separated list of labels associated with this PR, '
|
help: 'A comma-separated list of labels associated with this PR, '
|
||||||
'if applicable.\n\n'
|
'if applicable.\n\n'
|
||||||
@ -144,7 +137,6 @@ class VersionCheckCommand extends PackageLoopingCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const String _againstPubFlag = 'against-pub';
|
static const String _againstPubFlag = 'against-pub';
|
||||||
static const String _changeDescriptionFile = 'change-description-file';
|
|
||||||
static const String _prLabelsArg = 'pr-labels';
|
static const String _prLabelsArg = 'pr-labels';
|
||||||
static const String _checkForMissingChanges = 'check-for-missing-changes';
|
static const String _checkForMissingChanges = 'check-for-missing-changes';
|
||||||
static const String _ignorePlatformInterfaceBreaks =
|
static const String _ignorePlatformInterfaceBreaks =
|
||||||
@ -171,7 +163,6 @@ class VersionCheckCommand extends PackageLoopingCommand {
|
|||||||
late final String _mergeBase;
|
late final String _mergeBase;
|
||||||
late final List<String> _changedFiles;
|
late final List<String> _changedFiles;
|
||||||
|
|
||||||
late final String _changeDescription = _loadChangeDescription();
|
|
||||||
late final Set<String> _prLabels = _getPRLabels();
|
late final Set<String> _prLabels = _getPRLabels();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -519,21 +510,6 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog.
|
|||||||
return labels.split(',').map((String label) => label.trim()).toSet();
|
return labels.split(',').map((String label) => label.trim()).toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the contents of the file pointed to by [_changeDescriptionFile],
|
|
||||||
/// or an empty string if that flag is not provided.
|
|
||||||
String _loadChangeDescription() {
|
|
||||||
final String path = getStringArg(_changeDescriptionFile);
|
|
||||||
if (path.isEmpty) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
final File file = packagesDir.fileSystem.file(path);
|
|
||||||
if (!file.existsSync()) {
|
|
||||||
printError('${indentation}No such file: $path');
|
|
||||||
throw ToolExit(_exitMissingChangeDescriptionFile);
|
|
||||||
}
|
|
||||||
return file.readAsStringSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the given version transition should be allowed.
|
/// Returns true if the given version transition should be allowed.
|
||||||
bool _shouldAllowVersionChange(
|
bool _shouldAllowVersionChange(
|
||||||
{required Version oldVersion, required Version newVersion}) {
|
{required Version oldVersion, required Version newVersion}) {
|
||||||
@ -569,8 +545,10 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog.
|
|||||||
final String relativePackagePath =
|
final String relativePackagePath =
|
||||||
getRelativePosixPath(package.directory, from: gitRoot);
|
getRelativePosixPath(package.directory, from: gitRoot);
|
||||||
|
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: _changedFiles, relativePackagePath: relativePackagePath);
|
changedPaths: _changedFiles,
|
||||||
|
relativePackagePath: relativePackagePath,
|
||||||
|
git: await retrieveVersionFinder());
|
||||||
|
|
||||||
if (!state.hasChanges) {
|
if (!state.hasChanges) {
|
||||||
return null;
|
return null;
|
||||||
@ -580,9 +558,6 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog.
|
|||||||
if (_prLabels.contains(_missingVersionChangeOverrideLabel)) {
|
if (_prLabels.contains(_missingVersionChangeOverrideLabel)) {
|
||||||
logWarning('Ignoring lack of version change due to the '
|
logWarning('Ignoring lack of version change due to the '
|
||||||
'"$_missingVersionChangeOverrideLabel" label.');
|
'"$_missingVersionChangeOverrideLabel" label.');
|
||||||
} else if (_isAllowedDependabotChange(package, _changeDescription)) {
|
|
||||||
logWarning('Ignoring lack of version change for Dependabot change to '
|
|
||||||
'a known internal dependency.');
|
|
||||||
} else {
|
} else {
|
||||||
printError(
|
printError(
|
||||||
'No version change found, but the change to this package could '
|
'No version change found, but the change to this package could '
|
||||||
@ -595,76 +570,23 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.hasChangelogChange) {
|
if (!state.hasChangelogChange && state.needsChangelogChange) {
|
||||||
if (_prLabels.contains(_missingChangelogChangeOverrideLabel)) {
|
if (_prLabels.contains(_missingChangelogChangeOverrideLabel)) {
|
||||||
logWarning('Ignoring lack of CHANGELOG update due to the '
|
logWarning('Ignoring lack of CHANGELOG update due to the '
|
||||||
'"$_missingChangelogChangeOverrideLabel" label.');
|
'"$_missingChangelogChangeOverrideLabel" label.');
|
||||||
} else if (_isAllowedDependabotChange(package, _changeDescription)) {
|
|
||||||
logWarning('Ignoring lack of CHANGELOG update for Dependabot change to '
|
|
||||||
'a known internal dependency.');
|
|
||||||
} else {
|
} else {
|
||||||
printError(
|
printError(
|
||||||
'No CHANGELOG change found. If this PR needs an exemption from '
|
'No CHANGELOG change found. If this PR needs an exemption from '
|
||||||
'the standard policy of listing all changes in the CHANGELOG, '
|
'the standard policy of listing all changes in the CHANGELOG, '
|
||||||
'comment in the PR to explain why the PR is exempt, and add (or '
|
'comment in the PR to explain why the PR is exempt, and add (or '
|
||||||
'ask your reviewer to add) the '
|
'ask your reviewer to add) the '
|
||||||
'"$_missingChangelogChangeOverrideLabel" label.');
|
'"$_missingChangelogChangeOverrideLabel" label. Otherwise, '
|
||||||
|
'please add a NEXT entry in the CHANGELOG as described in '
|
||||||
|
'the contributing guide.');
|
||||||
return 'Missing CHANGELOG change';
|
return 'Missing CHANGELOG change';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if [changeDescription] matches a Dependabot change for a
|
|
||||||
/// dependency roll that should bypass the normal version and CHANGELOG change
|
|
||||||
/// checks (for dependencies that are known not to have client impact).
|
|
||||||
///
|
|
||||||
/// Depending on CI, [changeDescription] may either be the PR description, or
|
|
||||||
/// the description of the last commit (see for example discussion in
|
|
||||||
/// https://github.com/cirruslabs/cirrus-ci-docs/issues/1029), so this needs
|
|
||||||
/// to handle both.
|
|
||||||
bool _isAllowedDependabotChange(
|
|
||||||
RepositoryPackage package, String changeDescription) {
|
|
||||||
// Espresso exports some dependencies that are normally just internal test
|
|
||||||
// utils, so always require reviewers to check that.
|
|
||||||
if (package.directory.basename == 'espresso') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A string that is in all Dependabot PRs, but extremely unlikely to be in
|
|
||||||
// any other PR, to identify Dependabot PRs.
|
|
||||||
const String dependabotPRDescriptionMarker =
|
|
||||||
'Dependabot commands and options';
|
|
||||||
// The same thing, but for the Dependabot commit message.
|
|
||||||
const String dependabotCommitMessageMarker =
|
|
||||||
'Signed-off-by: dependabot[bot]';
|
|
||||||
// Expression to extract the name of the dependency being updated.
|
|
||||||
final RegExp dependencyRegex =
|
|
||||||
RegExp(r'Bumps? \[(.*?)\]\(.*?\) from [\d.]+ to [\d.]+');
|
|
||||||
|
|
||||||
// Allowed exact dependency names.
|
|
||||||
const Set<String> allowedDependencies = <String>{
|
|
||||||
'junit',
|
|
||||||
'robolectric',
|
|
||||||
};
|
|
||||||
const Set<String> allowedDependencyPrefixes = <String>{
|
|
||||||
'mockito-' // mockito-core, mockito-inline, etc.
|
|
||||||
};
|
|
||||||
|
|
||||||
if (changeDescription.contains(dependabotPRDescriptionMarker) ||
|
|
||||||
changeDescription.contains(dependabotCommitMessageMarker)) {
|
|
||||||
final Match? match = dependencyRegex.firstMatch(changeDescription);
|
|
||||||
if (match != null) {
|
|
||||||
final String dependency = match.group(1)!;
|
|
||||||
if (allowedDependencies.contains(dependency) ||
|
|
||||||
allowedDependencyPrefixes
|
|
||||||
.any((String prefix) => dependency.startsWith(prefix))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: flutter_plugin_tools
|
name: flutter_plugin_tools
|
||||||
description: Productivity utils for flutter/plugins and flutter/packages
|
description: Productivity utils for flutter/plugins and flutter/packages
|
||||||
repository: https://github.com/flutter/plugins/tree/main/script/tool
|
repository: https://github.com/flutter/plugins/tree/main/script/tool
|
||||||
version: 0.9.3
|
version: 0.10.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
args: ^2.1.0
|
args: ^2.1.0
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
import 'package:file/file.dart';
|
import 'package:file/file.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:flutter_plugin_tools/src/common/git_version_finder.dart';
|
||||||
import 'package:flutter_plugin_tools/src/common/package_state_utils.dart';
|
import 'package:flutter_plugin_tools/src/common/package_state_utils.dart';
|
||||||
|
import 'package:test/fake.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
@ -26,12 +28,13 @@ void main() {
|
|||||||
'packages/a_package/lib/plugin.dart',
|
'packages/a_package/lib/plugin.dart',
|
||||||
];
|
];
|
||||||
|
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: changedFiles,
|
changedPaths: changedFiles,
|
||||||
relativePackagePath: 'packages/a_package');
|
relativePackagePath: 'packages/a_package');
|
||||||
|
|
||||||
expect(state.hasChanges, true);
|
expect(state.hasChanges, true);
|
||||||
expect(state.needsVersionChange, true);
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles trailing slash on package path', () async {
|
test('handles trailing slash on package path', () async {
|
||||||
@ -42,16 +45,18 @@ void main() {
|
|||||||
'packages/a_package/lib/plugin.dart',
|
'packages/a_package/lib/plugin.dart',
|
||||||
];
|
];
|
||||||
|
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: changedFiles,
|
changedPaths: changedFiles,
|
||||||
relativePackagePath: 'packages/a_package/');
|
relativePackagePath: 'packages/a_package/');
|
||||||
|
|
||||||
expect(state.hasChanges, true);
|
expect(state.hasChanges, true);
|
||||||
expect(state.needsVersionChange, true);
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
expect(state.hasChangelogChange, false);
|
expect(state.hasChangelogChange, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not report version change exempt changes', () async {
|
test('does not flag version- and changelog-change-exempt changes',
|
||||||
|
() async {
|
||||||
final RepositoryPackage package =
|
final RepositoryPackage package =
|
||||||
createFakePlugin('a_plugin', packagesDir);
|
createFakePlugin('a_plugin', packagesDir);
|
||||||
|
|
||||||
@ -64,12 +69,13 @@ void main() {
|
|||||||
'packages/a_plugin/CHANGELOG.md',
|
'packages/a_plugin/CHANGELOG.md',
|
||||||
];
|
];
|
||||||
|
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: changedFiles,
|
changedPaths: changedFiles,
|
||||||
relativePackagePath: 'packages/a_plugin/');
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
expect(state.hasChanges, true);
|
expect(state.hasChanges, true);
|
||||||
expect(state.needsVersionChange, false);
|
expect(state.needsVersionChange, false);
|
||||||
|
expect(state.needsChangelogChange, false);
|
||||||
expect(state.hasChangelogChange, true);
|
expect(state.hasChangelogChange, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -81,28 +87,49 @@ void main() {
|
|||||||
'packages/a_plugin/lib/foo/tool/tool_thing.dart',
|
'packages/a_plugin/lib/foo/tool/tool_thing.dart',
|
||||||
];
|
];
|
||||||
|
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: changedFiles,
|
changedPaths: changedFiles,
|
||||||
relativePackagePath: 'packages/a_plugin/');
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
expect(state.hasChanges, true);
|
expect(state.hasChanges, true);
|
||||||
expect(state.needsVersionChange, true);
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('requires a version change for example main', () async {
|
test('requires a version change for example/lib/main.dart', () async {
|
||||||
final RepositoryPackage package =
|
final RepositoryPackage package = createFakePlugin(
|
||||||
createFakePlugin('a_plugin', packagesDir);
|
'a_plugin', packagesDir,
|
||||||
|
extraFiles: <String>['example/lib/main.dart']);
|
||||||
|
|
||||||
const List<String> changedFiles = <String>[
|
const List<String> changedFiles = <String>[
|
||||||
'packages/a_plugin/example/lib/main.dart',
|
'packages/a_plugin/example/lib/main.dart',
|
||||||
];
|
];
|
||||||
|
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: changedFiles,
|
changedPaths: changedFiles,
|
||||||
relativePackagePath: 'packages/a_plugin/');
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
expect(state.hasChanges, true);
|
expect(state.hasChanges, true);
|
||||||
expect(state.needsVersionChange, true);
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('requires a version change for example/main.dart', () async {
|
||||||
|
final RepositoryPackage package = createFakePlugin(
|
||||||
|
'a_plugin', packagesDir,
|
||||||
|
extraFiles: <String>['example/main.dart']);
|
||||||
|
|
||||||
|
const List<String> changedFiles = <String>[
|
||||||
|
'packages/a_plugin/example/main.dart',
|
||||||
|
];
|
||||||
|
|
||||||
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
|
changedPaths: changedFiles,
|
||||||
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
|
expect(state.hasChanges, true);
|
||||||
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('requires a version change for example readme.md', () async {
|
test('requires a version change for example readme.md', () async {
|
||||||
@ -113,28 +140,189 @@ void main() {
|
|||||||
'packages/a_plugin/example/README.md',
|
'packages/a_plugin/example/README.md',
|
||||||
];
|
];
|
||||||
|
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: changedFiles,
|
changedPaths: changedFiles,
|
||||||
relativePackagePath: 'packages/a_plugin/');
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
expect(state.hasChanges, true);
|
expect(state.hasChanges, true);
|
||||||
expect(state.needsVersionChange, true);
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('requires a version change for example example.md', () async {
|
test('requires a version change for example/example.md', () async {
|
||||||
|
final RepositoryPackage package = createFakePlugin(
|
||||||
|
'a_plugin', packagesDir,
|
||||||
|
extraFiles: <String>['example/example.md']);
|
||||||
|
|
||||||
|
const List<String> changedFiles = <String>[
|
||||||
|
'packages/a_plugin/example/example.md',
|
||||||
|
];
|
||||||
|
|
||||||
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
|
changedPaths: changedFiles,
|
||||||
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
|
expect(state.hasChanges, true);
|
||||||
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'requires a changelog change but no version change for '
|
||||||
|
'lower-priority examples when example.md is present', () async {
|
||||||
|
final RepositoryPackage package = createFakePlugin(
|
||||||
|
'a_plugin', packagesDir,
|
||||||
|
extraFiles: <String>['example/example.md']);
|
||||||
|
|
||||||
|
const List<String> changedFiles = <String>[
|
||||||
|
'packages/a_plugin/example/lib/main.dart',
|
||||||
|
'packages/a_plugin/example/main.dart',
|
||||||
|
'packages/a_plugin/example/README.md',
|
||||||
|
];
|
||||||
|
|
||||||
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
|
changedPaths: changedFiles,
|
||||||
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
|
expect(state.hasChanges, true);
|
||||||
|
expect(state.needsVersionChange, false);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'requires a changelog change but no version change for README.md when '
|
||||||
|
'code example is present', () async {
|
||||||
|
final RepositoryPackage package = createFakePlugin(
|
||||||
|
'a_plugin', packagesDir,
|
||||||
|
extraFiles: <String>['example/lib/main.dart']);
|
||||||
|
|
||||||
|
const List<String> changedFiles = <String>[
|
||||||
|
'packages/a_plugin/example/README.md',
|
||||||
|
];
|
||||||
|
|
||||||
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
|
changedPaths: changedFiles,
|
||||||
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
|
expect(state.hasChanges, true);
|
||||||
|
expect(state.needsVersionChange, false);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'does not requires changelog or version change for build.gradle '
|
||||||
|
'test-dependency-only changes', () async {
|
||||||
final RepositoryPackage package =
|
final RepositoryPackage package =
|
||||||
createFakePlugin('a_plugin', packagesDir);
|
createFakePlugin('a_plugin', packagesDir);
|
||||||
|
|
||||||
const List<String> changedFiles = <String>[
|
const List<String> changedFiles = <String>[
|
||||||
'packages/a_plugin/example/lib/example.md',
|
'packages/a_plugin/android/build.gradle',
|
||||||
];
|
];
|
||||||
|
|
||||||
final PackageChangeState state = checkPackageChangeState(package,
|
final GitVersionFinder git = FakeGitVersionFinder(<String, List<String>>{
|
||||||
|
'packages/a_plugin/android/build.gradle': <String>[
|
||||||
|
"- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'",
|
||||||
|
"- testImplementation 'junit:junit:4.10.0'",
|
||||||
|
"+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'",
|
||||||
|
"+ testImplementation 'junit:junit:4.13.2'",
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
|
changedPaths: changedFiles,
|
||||||
|
relativePackagePath: 'packages/a_plugin/',
|
||||||
|
git: git);
|
||||||
|
|
||||||
|
expect(state.hasChanges, true);
|
||||||
|
expect(state.needsVersionChange, false);
|
||||||
|
expect(state.needsChangelogChange, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('requires changelog or version change for other build.gradle changes',
|
||||||
|
() async {
|
||||||
|
final RepositoryPackage package =
|
||||||
|
createFakePlugin('a_plugin', packagesDir);
|
||||||
|
|
||||||
|
const List<String> changedFiles = <String>[
|
||||||
|
'packages/a_plugin/android/build.gradle',
|
||||||
|
];
|
||||||
|
|
||||||
|
final GitVersionFinder git = FakeGitVersionFinder(<String, List<String>>{
|
||||||
|
'packages/a_plugin/android/build.gradle': <String>[
|
||||||
|
"- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'",
|
||||||
|
"- testImplementation 'junit:junit:4.10.0'",
|
||||||
|
"+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'",
|
||||||
|
"+ testImplementation 'junit:junit:4.13.2'",
|
||||||
|
"- implementation 'com.google.android.gms:play-services-maps:18.0.0'",
|
||||||
|
"+ implementation 'com.google.android.gms:play-services-maps:18.0.2'",
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
|
changedPaths: changedFiles,
|
||||||
|
relativePackagePath: 'packages/a_plugin/',
|
||||||
|
git: git);
|
||||||
|
|
||||||
|
expect(state.hasChanges, true);
|
||||||
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'requires changelog or version change if build.gradle diffs cannot '
|
||||||
|
'be checked', () async {
|
||||||
|
final RepositoryPackage package =
|
||||||
|
createFakePlugin('a_plugin', packagesDir);
|
||||||
|
|
||||||
|
const List<String> changedFiles = <String>[
|
||||||
|
'packages/a_plugin/android/build.gradle',
|
||||||
|
];
|
||||||
|
|
||||||
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
changedPaths: changedFiles,
|
changedPaths: changedFiles,
|
||||||
relativePackagePath: 'packages/a_plugin/');
|
relativePackagePath: 'packages/a_plugin/');
|
||||||
|
|
||||||
expect(state.hasChanges, true);
|
expect(state.hasChanges, true);
|
||||||
expect(state.needsVersionChange, true);
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'requires changelog or version change if build.gradle diffs cannot '
|
||||||
|
'be determined', () async {
|
||||||
|
final RepositoryPackage package =
|
||||||
|
createFakePlugin('a_plugin', packagesDir);
|
||||||
|
|
||||||
|
const List<String> changedFiles = <String>[
|
||||||
|
'packages/a_plugin/android/build.gradle',
|
||||||
|
];
|
||||||
|
|
||||||
|
final GitVersionFinder git = FakeGitVersionFinder(<String, List<String>>{
|
||||||
|
'packages/a_plugin/android/build.gradle': <String>[]
|
||||||
|
});
|
||||||
|
|
||||||
|
final PackageChangeState state = await checkPackageChangeState(package,
|
||||||
|
changedPaths: changedFiles,
|
||||||
|
relativePackagePath: 'packages/a_plugin/',
|
||||||
|
git: git);
|
||||||
|
|
||||||
|
expect(state.hasChanges, true);
|
||||||
|
expect(state.needsVersionChange, true);
|
||||||
|
expect(state.needsChangelogChange, true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeGitVersionFinder extends Fake implements GitVersionFinder {
|
||||||
|
FakeGitVersionFinder(this.fileDiffs);
|
||||||
|
|
||||||
|
final Map<String, List<String>> fileDiffs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> getDiffContents({
|
||||||
|
String? targetPath,
|
||||||
|
bool includeUncommitted = false,
|
||||||
|
}) async {
|
||||||
|
return fileDiffs[targetPath]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -40,72 +40,6 @@ void testAllowedVersion(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _generateFakeDependabotPRDescription(String package) {
|
|
||||||
return '''
|
|
||||||
Bumps [$package](https://github.com/foo/$package) from 1.0.0 to 2.0.0.
|
|
||||||
<details>
|
|
||||||
<summary>Release notes</summary>
|
|
||||||
<p><em>Sourced from <a href="https://github.com/foo/$package">$package's releases</a>.</em></p>
|
|
||||||
<blockquote>
|
|
||||||
...
|
|
||||||
</blockquote>
|
|
||||||
</details>
|
|
||||||
<details>
|
|
||||||
<summary>Commits</summary>
|
|
||||||
<ul>
|
|
||||||
<li>...</li>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
|
|
||||||
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
|
|
||||||
|
|
||||||
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
|
|
||||||
|
|
||||||
[//]: # (dependabot-automerge-start)
|
|
||||||
[//]: # (dependabot-automerge-end)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Dependabot commands and options</summary>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
You can trigger Dependabot actions by commenting on this PR:
|
|
||||||
- `@dependabot rebase` will rebase this PR
|
|
||||||
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
|
|
||||||
- `@dependabot merge` will merge this PR after your CI passes on it
|
|
||||||
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
|
|
||||||
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
|
|
||||||
- `@dependabot reopen` will reopen this PR if it is closed
|
|
||||||
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
|
|
||||||
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
|
|
||||||
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
|
|
||||||
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
|
|
||||||
|
|
||||||
|
|
||||||
</details>
|
|
||||||
''';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _generateFakeDependabotCommitMessage(String package) {
|
|
||||||
return '''
|
|
||||||
Bumps [$package](https://github.com/foo/$package) from 1.0.0 to 2.0.0.
|
|
||||||
- [Release notes](https://github.com/foo/$package/releases)
|
|
||||||
- [Commits](foo/$package@v4.3.1...v4.6.1)
|
|
||||||
|
|
||||||
---
|
|
||||||
updated-dependencies:
|
|
||||||
- dependency-name: $package
|
|
||||||
dependency-type: direct:production
|
|
||||||
update-type: version-update:semver-minor
|
|
||||||
...
|
|
||||||
|
|
||||||
Signed-off-by: dependabot[bot] <support@github.com>
|
|
||||||
''';
|
|
||||||
}
|
|
||||||
|
|
||||||
class MockProcessResult extends Mock implements io.ProcessResult {}
|
class MockProcessResult extends Mock implements io.ProcessResult {}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@ -1085,242 +1019,45 @@ packages/plugin/example/lib/foo.dart
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
group('dependabot', () {
|
// This test ensures that Dependabot Gradle changes to test-only files
|
||||||
test('throws if a nonexistent change description file is specified',
|
// aren't flagged by the version check.
|
||||||
() async {
|
test(
|
||||||
final RepositoryPackage plugin =
|
'allows missing CHANGELOG and version change for test-only Gradle changes',
|
||||||
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
() async {
|
||||||
|
final RepositoryPackage plugin =
|
||||||
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
||||||
|
|
||||||
const String changelog = '''
|
const String changelog = '''
|
||||||
## 1.0.0
|
## 1.0.0
|
||||||
* Some changes.
|
* Some changes.
|
||||||
''';
|
''';
|
||||||
plugin.changelogFile.writeAsStringSync(changelog);
|
plugin.changelogFile.writeAsStringSync(changelog);
|
||||||
processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
|
processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
|
||||||
MockProcess(stdout: 'version: 1.0.0'),
|
MockProcess(stdout: 'version: 1.0.0'),
|
||||||
];
|
];
|
||||||
processRunner.mockProcessesForExecutable['git-diff'] = <io.Process>[
|
processRunner.mockProcessesForExecutable['git-diff'] = <io.Process>[
|
||||||
MockProcess(stdout: '''
|
// File list.
|
||||||
|
MockProcess(stdout: '''
|
||||||
packages/plugin/android/build.gradle
|
packages/plugin/android/build.gradle
|
||||||
'''),
|
'''),
|
||||||
];
|
// build.gradle diff
|
||||||
|
MockProcess(stdout: '''
|
||||||
Error? commandError;
|
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
final List<String> output = await _runWithMissingChangeDetection(
|
- testImplementation 'junit:junit:4.10.0'
|
||||||
<String>['--change-description-file=a_missing_file.txt'],
|
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
errorHandler: (Error e) {
|
+ testImplementation 'junit:junit:4.13.2'
|
||||||
commandError = e;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(commandError, isA<ToolExit>());
|
|
||||||
expect(
|
|
||||||
output,
|
|
||||||
containsAllInOrder(<Matcher>[
|
|
||||||
contains('No such file: a_missing_file.txt'),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('allows missing version and CHANGELOG change for mockito',
|
|
||||||
() async {
|
|
||||||
final RepositoryPackage plugin =
|
|
||||||
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
||||||
|
|
||||||
const String changelog = '''
|
|
||||||
## 1.0.0
|
|
||||||
* Some changes.
|
|
||||||
''';
|
|
||||||
plugin.changelogFile.writeAsStringSync(changelog);
|
|
||||||
processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
|
|
||||||
MockProcess(stdout: 'version: 1.0.0'),
|
|
||||||
];
|
|
||||||
processRunner.mockProcessesForExecutable['git-diff'] = <io.Process>[
|
|
||||||
MockProcess(stdout: '''
|
|
||||||
packages/plugin/android/build.gradle
|
|
||||||
'''),
|
'''),
|
||||||
];
|
];
|
||||||
|
|
||||||
final File changeDescriptionFile =
|
final List<String> output =
|
||||||
fileSystem.file('change_description.txt');
|
await _runWithMissingChangeDetection(<String>[]);
|
||||||
changeDescriptionFile.writeAsStringSync(
|
|
||||||
_generateFakeDependabotPRDescription('mockito-core'));
|
|
||||||
|
|
||||||
final List<String> output =
|
expect(
|
||||||
await _runWithMissingChangeDetection(<String>[
|
output,
|
||||||
'--change-description-file=${changeDescriptionFile.path}'
|
containsAllInOrder(<Matcher>[
|
||||||
]);
|
contains('Running for plugin'),
|
||||||
|
]),
|
||||||
expect(
|
);
|
||||||
output,
|
|
||||||
containsAllInOrder(<Matcher>[
|
|
||||||
contains('Ignoring lack of version change for Dependabot '
|
|
||||||
'change to a known internal dependency.'),
|
|
||||||
contains('Ignoring lack of CHANGELOG update for Dependabot '
|
|
||||||
'change to a known internal dependency.'),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('allows missing version and CHANGELOG change for robolectric',
|
|
||||||
() async {
|
|
||||||
final RepositoryPackage plugin =
|
|
||||||
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
||||||
|
|
||||||
const String changelog = '''
|
|
||||||
## 1.0.0
|
|
||||||
* Some changes.
|
|
||||||
''';
|
|
||||||
plugin.changelogFile.writeAsStringSync(changelog);
|
|
||||||
processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
|
|
||||||
MockProcess(stdout: 'version: 1.0.0'),
|
|
||||||
];
|
|
||||||
processRunner.mockProcessesForExecutable['git-diff'] = <io.Process>[
|
|
||||||
MockProcess(stdout: '''
|
|
||||||
packages/plugin/android/build.gradle
|
|
||||||
'''),
|
|
||||||
];
|
|
||||||
|
|
||||||
final File changeDescriptionFile =
|
|
||||||
fileSystem.file('change_description.txt');
|
|
||||||
changeDescriptionFile.writeAsStringSync(
|
|
||||||
_generateFakeDependabotPRDescription('robolectric'));
|
|
||||||
|
|
||||||
final List<String> output =
|
|
||||||
await _runWithMissingChangeDetection(<String>[
|
|
||||||
'--change-description-file=${changeDescriptionFile.path}'
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
output,
|
|
||||||
containsAllInOrder(<Matcher>[
|
|
||||||
contains('Ignoring lack of version change for Dependabot '
|
|
||||||
'change to a known internal dependency.'),
|
|
||||||
contains('Ignoring lack of CHANGELOG update for Dependabot '
|
|
||||||
'change to a known internal dependency.'),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('allows missing version and CHANGELOG change for junit', () async {
|
|
||||||
final RepositoryPackage plugin =
|
|
||||||
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
||||||
|
|
||||||
const String changelog = '''
|
|
||||||
## 1.0.0
|
|
||||||
* Some changes.
|
|
||||||
''';
|
|
||||||
plugin.changelogFile.writeAsStringSync(changelog);
|
|
||||||
processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
|
|
||||||
MockProcess(stdout: 'version: 1.0.0'),
|
|
||||||
];
|
|
||||||
processRunner.mockProcessesForExecutable['git-diff'] = <io.Process>[
|
|
||||||
MockProcess(stdout: '''
|
|
||||||
packages/plugin/android/build.gradle
|
|
||||||
'''),
|
|
||||||
];
|
|
||||||
|
|
||||||
final File changeDescriptionFile =
|
|
||||||
fileSystem.file('change_description.txt');
|
|
||||||
changeDescriptionFile
|
|
||||||
.writeAsStringSync(_generateFakeDependabotPRDescription('junit'));
|
|
||||||
|
|
||||||
final List<String> output =
|
|
||||||
await _runWithMissingChangeDetection(<String>[
|
|
||||||
'--change-description-file=${changeDescriptionFile.path}'
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
output,
|
|
||||||
containsAllInOrder(<Matcher>[
|
|
||||||
contains('Ignoring lack of version change for Dependabot '
|
|
||||||
'change to a known internal dependency.'),
|
|
||||||
contains('Ignoring lack of CHANGELOG update for Dependabot '
|
|
||||||
'change to a known internal dependency.'),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('fails for dependencies that are not explicitly allowed',
|
|
||||||
() async {
|
|
||||||
final RepositoryPackage plugin =
|
|
||||||
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
||||||
|
|
||||||
const String changelog = '''
|
|
||||||
## 1.0.0
|
|
||||||
* Some changes.
|
|
||||||
''';
|
|
||||||
plugin.changelogFile.writeAsStringSync(changelog);
|
|
||||||
processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
|
|
||||||
MockProcess(stdout: 'version: 1.0.0'),
|
|
||||||
];
|
|
||||||
processRunner.mockProcessesForExecutable['git-diff'] = <io.Process>[
|
|
||||||
MockProcess(stdout: '''
|
|
||||||
packages/plugin/android/build.gradle
|
|
||||||
'''),
|
|
||||||
];
|
|
||||||
|
|
||||||
final File changeDescriptionFile =
|
|
||||||
fileSystem.file('change_description.txt');
|
|
||||||
changeDescriptionFile.writeAsStringSync(
|
|
||||||
_generateFakeDependabotPRDescription('somethingelse'));
|
|
||||||
|
|
||||||
Error? commandError;
|
|
||||||
final List<String> output =
|
|
||||||
await _runWithMissingChangeDetection(<String>[
|
|
||||||
'--change-description-file=${changeDescriptionFile.path}'
|
|
||||||
], errorHandler: (Error e) {
|
|
||||||
commandError = e;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(commandError, isA<ToolExit>());
|
|
||||||
expect(
|
|
||||||
output,
|
|
||||||
containsAllInOrder(<Matcher>[
|
|
||||||
contains('No version change found'),
|
|
||||||
contains('plugin:\n'
|
|
||||||
' Missing version change'),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('allow list works for commit messages', () async {
|
|
||||||
final RepositoryPackage plugin =
|
|
||||||
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
||||||
|
|
||||||
const String changelog = '''
|
|
||||||
## 1.0.0
|
|
||||||
* Some changes.
|
|
||||||
''';
|
|
||||||
plugin.changelogFile.writeAsStringSync(changelog);
|
|
||||||
processRunner.mockProcessesForExecutable['git-show'] = <io.Process>[
|
|
||||||
MockProcess(stdout: 'version: 1.0.0'),
|
|
||||||
];
|
|
||||||
processRunner.mockProcessesForExecutable['git-diff'] = <io.Process>[
|
|
||||||
MockProcess(stdout: '''
|
|
||||||
packages/plugin/android/build.gradle
|
|
||||||
'''),
|
|
||||||
];
|
|
||||||
|
|
||||||
final File changeDescriptionFile =
|
|
||||||
fileSystem.file('change_description.txt');
|
|
||||||
changeDescriptionFile.writeAsStringSync(
|
|
||||||
_generateFakeDependabotCommitMessage('mockito-core'));
|
|
||||||
|
|
||||||
final List<String> output =
|
|
||||||
await _runWithMissingChangeDetection(<String>[
|
|
||||||
'--change-description-file=${changeDescriptionFile.path}'
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
output,
|
|
||||||
containsAllInOrder(<Matcher>[
|
|
||||||
contains('Ignoring lack of version change for Dependabot '
|
|
||||||
'change to a known internal dependency.'),
|
|
||||||
contains('Ignoring lack of CHANGELOG update for Dependabot '
|
|
||||||
'change to a known internal dependency.'),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user