mirror of
https://github.com/flutter/packages.git
synced 2025-06-29 06:06:59 +08:00
[tool] Bypass version/changelog checks for some PRs (#6124)
This commit is contained in:
@ -1,3 +1,8 @@
|
|||||||
|
## NEXT
|
||||||
|
|
||||||
|
- Bypasses version and CHANGELOG checks for Dependabot PRs for packages
|
||||||
|
that are known not to be client-affecting.
|
||||||
|
|
||||||
## 0.8.8
|
## 0.8.8
|
||||||
|
|
||||||
- Allows pre-release versions in `version-check`.
|
- Allows pre-release versions in `version-check`.
|
||||||
|
@ -569,11 +569,15 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state.needsVersionChange) {
|
if (state.needsVersionChange) {
|
||||||
if (_getChangeDescription().split('\n').any((String line) =>
|
final String changeDescription = _getChangeDescription();
|
||||||
|
if (changeDescription.split('\n').any((String line) =>
|
||||||
line.startsWith(_missingVersionChangeJustificationMarker))) {
|
line.startsWith(_missingVersionChangeJustificationMarker))) {
|
||||||
logWarning('Ignoring lack of version change due to '
|
logWarning('Ignoring lack of version change due to '
|
||||||
'"$_missingVersionChangeJustificationMarker" in the '
|
'"$_missingVersionChangeJustificationMarker" in the '
|
||||||
'change description.');
|
'change description.');
|
||||||
|
} 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 '
|
||||||
@ -587,11 +591,15 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!state.hasChangelogChange) {
|
if (!state.hasChangelogChange) {
|
||||||
if (_getChangeDescription().split('\n').any((String line) =>
|
final String changeDescription = _getChangeDescription();
|
||||||
|
if (changeDescription.split('\n').any((String line) =>
|
||||||
line.startsWith(_missingChangelogChangeJustificationMarker))) {
|
line.startsWith(_missingChangelogChangeJustificationMarker))) {
|
||||||
logWarning('Ignoring lack of CHANGELOG update due to '
|
logWarning('Ignoring lack of CHANGELOG update due to '
|
||||||
'"$_missingChangelogChangeJustificationMarker" in the '
|
'"$_missingChangelogChangeJustificationMarker" in the '
|
||||||
'change description.');
|
'change description.');
|
||||||
|
} 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 '
|
||||||
@ -605,4 +613,46 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog.
|
|||||||
|
|
||||||
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).
|
||||||
|
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 dependabotMarker = 'Dependabot commands and options';
|
||||||
|
// 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(dependabotMarker)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,55 @@ 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>
|
||||||
|
''';
|
||||||
|
}
|
||||||
|
|
||||||
class MockProcessResult extends Mock implements io.ProcessResult {}
|
class MockProcessResult extends Mock implements io.ProcessResult {}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@ -1078,6 +1127,170 @@ No CHANGELOG change: Code change is only to implementation comments.
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('dependabot', () {
|
||||||
|
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 =
|
||||||
|
fileSystem.file('change_description.txt');
|
||||||
|
changeDescriptionFile.writeAsStringSync(
|
||||||
|
_generateFakeDependabotPRDescription('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.'),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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('allows valid against pub', () async {
|
test('allows valid against pub', () async {
|
||||||
|
Reference in New Issue
Block a user