mirror of
https://github.com/flutter/packages.git
synced 2026-03-13 10:40:37 +08:00
This fixes main branch part of https://github.com/flutter/flutter/issues/176433 The work left is adjusting cocoon to also run this check in release branch The high level view of this pr. 1. Move ci_config.yaml and pending changelog parsing into thir own files and the parsed results are exposed as a getter method in RepositoryPackage so that it can be shared between branch_for_release, repo_package_info_check, and version_check 2. The format error of ci_config.yaml and changelog files are reported in repo_package_info_check. The other 2 assume they are correct formatted and throw directly if they are not. 3. update the version_check to be able to handle batch release package ## Pre-Review Checklist **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
2412 lines
70 KiB
Dart
2412 lines
70 KiB
Dart
// Copyright 2013 The Flutter Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:args/command_runner.dart';
|
|
import 'package:file/file.dart';
|
|
import 'package:flutter_plugin_tools/src/common/core.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/testing.dart';
|
|
import 'package:pub_semver/pub_semver.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import 'mocks.dart';
|
|
import 'util.dart';
|
|
|
|
void testAllowedVersion(
|
|
String mainVersion,
|
|
String headVersion, {
|
|
bool allowed = true,
|
|
NextVersionType? nextVersionType,
|
|
}) {
|
|
final main = Version.parse(mainVersion);
|
|
final head = Version.parse(headVersion);
|
|
final Map<Version, NextVersionType> allowedVersions = getAllowedNextVersions(
|
|
main,
|
|
newVersion: head,
|
|
);
|
|
if (allowed) {
|
|
expect(allowedVersions, contains(head));
|
|
if (nextVersionType != null) {
|
|
expect(allowedVersions[head], equals(nextVersionType));
|
|
}
|
|
} else {
|
|
expect(allowedVersions, isNot(contains(head)));
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
const indentation = ' ';
|
|
group('VersionCheckCommand', () {
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
late CommandRunner<void> runner;
|
|
late RecordingProcessRunner gitProcessRunner;
|
|
// Ignored if mockHttpResponse is set.
|
|
int mockHttpStatus;
|
|
Map<String, dynamic>? mockHttpResponse;
|
|
|
|
setUp(() {
|
|
mockPlatform = MockPlatform();
|
|
final RecordingProcessRunner processRunner;
|
|
final GitDir gitDir;
|
|
(:packagesDir, :processRunner, :gitProcessRunner, :gitDir) =
|
|
configureBaseCommandMocks(platform: mockPlatform);
|
|
|
|
// Default to simulating the plugin never having been published.
|
|
mockHttpStatus = 404;
|
|
mockHttpResponse = null;
|
|
final mockClient = MockClient((http.Request request) async {
|
|
return http.Response(
|
|
json.encode(mockHttpResponse),
|
|
mockHttpResponse == null ? mockHttpStatus : 200,
|
|
);
|
|
});
|
|
|
|
final command = VersionCheckCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
httpClient: mockClient,
|
|
);
|
|
|
|
runner = CommandRunner<void>(
|
|
'version_check_command',
|
|
'Test for $VersionCheckCommand',
|
|
);
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
test('allows valid version', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.0.0 -> 2.0.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('denies invalid version', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.2.0');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Incorrectly updated version.')]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('uses merge-base without explicit base-sha', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
gitProcessRunner.mockProcessesForExecutable['git-merge-base'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'abc123')),
|
|
FakeProcessInfo(MockProcess(stdout: 'abc123')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.0.0 -> 2.0.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-merge-base', <String>[
|
|
'--fork-point',
|
|
'main',
|
|
'HEAD',
|
|
], null),
|
|
ProcessCall('git-show', <String>[
|
|
'abc123:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows valid version for new package.', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('Unable to find previous version at git base.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows likely reverts.', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.6.1');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.6.2')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'New version is lower than previous version. '
|
|
'This is assumed to be a revert.',
|
|
),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('denies lower version that could not be a simple revert', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.5.1');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.6.2')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Incorrectly updated version.')]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows minor changes to platform interfaces', () async {
|
|
createFakePlugin(
|
|
'plugin_platform_interface',
|
|
packagesDir,
|
|
version: '1.1.0',
|
|
);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.0.0 -> 1.1.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin_platform_interface/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'disallows breaking changes to platform interfaces by default',
|
|
() async {
|
|
createFakePlugin(
|
|
'plugin_platform_interface',
|
|
packagesDir,
|
|
version: '2.0.0',
|
|
);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
' Breaking changes to platform interfaces are not allowed '
|
|
'without explicit justification.\n'
|
|
' See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md '
|
|
'for more information.',
|
|
),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin_platform_interface/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'allows breaking changes to platform interfaces with override label',
|
|
() async {
|
|
createFakePlugin(
|
|
'plugin_platform_interface',
|
|
packagesDir,
|
|
version: '2.0.0',
|
|
);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--pr-labels=some label,override: allow breaking change,another-label',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Allowing breaking change to plugin_platform_interface '
|
|
'due to the "override: allow breaking change" label.',
|
|
),
|
|
contains('Ran for 1 package(s) (1 with warnings)'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin_platform_interface/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'allows breaking changes to platform interfaces with bypass flag',
|
|
() async {
|
|
createFakePlugin(
|
|
'plugin_platform_interface',
|
|
packagesDir,
|
|
version: '2.0.0',
|
|
);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--ignore-platform-interface-breaks',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Allowing breaking change to plugin_platform_interface due '
|
|
'to --ignore-platform-interface-breaks',
|
|
),
|
|
contains('Ran for 1 package(s) (1 with warnings)'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin_platform_interface/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'Allow empty lines in front of the first version in CHANGELOG',
|
|
() async {
|
|
const version = '1.0.1';
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: version,
|
|
);
|
|
const changelog =
|
|
'''
|
|
|
|
## $version
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('Throws if versions in changelog and pubspec do not match', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.1',
|
|
);
|
|
const changelog = '''
|
|
## 1.0.2
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Versions in CHANGELOG.md and pubspec.yaml do not match.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('Success if CHANGELOG and pubspec versions match', () async {
|
|
const version = '1.0.1';
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: version,
|
|
);
|
|
|
|
const changelog =
|
|
'''
|
|
## $version
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'Fail if pubspec version only matches an older version listed in CHANGELOG',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.1
|
|
* Some changes.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
var hasError = false;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
},
|
|
);
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Versions in CHANGELOG.md and pubspec.yaml do not match.'),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'Allow NEXT as a placeholder for gathering CHANGELOG entries',
|
|
() async {
|
|
const version = '1.0.0';
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: version,
|
|
);
|
|
|
|
const changelog =
|
|
'''
|
|
## NEXT
|
|
* Some changes that won't be published until the next time there's a release.
|
|
## $version
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('Found NEXT; validating next version in the CHANGELOG.'),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('Fail if NEXT appears after a version', () async {
|
|
const version = '1.0.1';
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: version,
|
|
);
|
|
|
|
const changelog =
|
|
'''
|
|
## $version
|
|
* Some changes.
|
|
## NEXT
|
|
* Some changes that should have been folded in 1.0.1.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
var hasError = false;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
},
|
|
);
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'When bumping the version for release, the NEXT section '
|
|
"should be incorporated into the new version's release notes.",
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'Fail if NEXT is left in the CHANGELOG when adding a version bump',
|
|
() async {
|
|
const version = '1.0.1';
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: version,
|
|
);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
const changelog =
|
|
'''
|
|
## NEXT
|
|
* Some changes that should have been folded in 1.0.1.
|
|
## $version
|
|
* Some changes.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
|
|
var hasError = false;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
},
|
|
);
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'When bumping the version for release, the NEXT section '
|
|
"should be incorporated into the new version's release notes.",
|
|
),
|
|
contains(
|
|
'plugin:\n'
|
|
' CHANGELOG.md failed validation.',
|
|
),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('fails if the version increases without replacing NEXT', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.1',
|
|
);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
const changelog = '''
|
|
## NEXT
|
|
* Some changes that should be listed as part of 1.0.1.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
|
|
var hasError = false;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
},
|
|
);
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'When bumping the version for release, the NEXT section '
|
|
"should be incorporated into the new version's release notes.",
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows NEXT for a revert', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## NEXT
|
|
* Some changes that should be listed as part of 1.0.1.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.1')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'New version is lower than previous version. '
|
|
'This is assumed to be a revert.',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
// This handles imports of a package with a NEXT section.
|
|
test('allows NEXT for a new package', () async {
|
|
final RepositoryPackage plugin = createFakePackage(
|
|
'a_package',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## NEXT
|
|
* Some changes that should be listed in the next release.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to find previous version at git base'),
|
|
contains('Found NEXT; validating next version in the CHANGELOG'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails gracefully if the first entry uses the wrong style', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
# 1.0.0
|
|
* Some changes for a later release.
|
|
## 0.9.0
|
|
* Some earlier changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to find a version in CHANGELOG.md'),
|
|
contains(
|
|
'The current version should be on a line starting with '
|
|
'"## ", either on the first non-empty line or after a "## NEXT" '
|
|
'section.',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'fails gracefully if the version headers are not found due to using the wrong style',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## NEXT
|
|
* Some changes for a later release.
|
|
# 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to find a version in CHANGELOG.md'),
|
|
contains(
|
|
'The current version should be on a line starting with '
|
|
'"## ", either on the first non-empty line or after a "## NEXT" '
|
|
'section.',
|
|
),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('fails gracefully if the version is unparseable', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## Alpha
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('"Alpha" could not be parsed as a version.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
group('missing change detection', () {
|
|
Future<List<String>> runWithMissingChangeDetection(
|
|
List<String> extraArgs, {
|
|
void Function(Error error)? errorHandler,
|
|
}) async {
|
|
return runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--check-for-missing-changes',
|
|
...extraArgs,
|
|
], errorHandler: errorHandler);
|
|
}
|
|
|
|
test('passes for unchanged packages', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[FakeProcessInfo(MockProcess(stdout: ''))];
|
|
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
});
|
|
|
|
test('passes if the only change is in pending_changelogs', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/pending_changelogs/some_entry.yaml
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('No issues found'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if a version and CHANGELOG change is missing from a change '
|
|
'that does not pass the exemption check', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No version change found'),
|
|
contains('No CHANGELOG change found.'),
|
|
contains('"update-release-info" command'),
|
|
contains(
|
|
'plugin:\n'
|
|
' Missing version change',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes version change requirement when version changes', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.1',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.1
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
packages/plugin/CHANGELOG.md
|
|
packages/plugin/pubspec.yaml
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
});
|
|
|
|
test('version change check ignores files outside the package', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin_a/lib/plugin.dart
|
|
tool/plugin/lib/plugin.dart
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
});
|
|
|
|
test('allows missing version change for exempt changes', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/example/android/.pluginToolsConfig.yaml
|
|
packages/plugin/example/android/lint-baseline.xml
|
|
packages/plugin/example/android/src/androidTest/foo/bar/FooTest.java
|
|
packages/plugin/example/ios/RunnerTests/Foo.m
|
|
packages/plugin/example/ios/RunnerUITests/info.plist
|
|
packages/plugin/darwin/Tests/Foo.swift
|
|
packages/plugin/analysis_options.yaml
|
|
packages/plugin/CHANGELOG.md
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
});
|
|
|
|
test('allows missing version change with override label', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
packages/plugin/CHANGELOG.md
|
|
packages/plugin/pubspec.yaml
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
final List<String>
|
|
output = await runWithMissingChangeDetection(<String>[
|
|
'--pr-labels=some label,override: no versioning needed,another-label',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Ignoring lack of version change due to the '
|
|
'"override: no versioning needed" label.',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if a CHANGELOG change is missing', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/example/lib/foo.dart
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No CHANGELOG change found.\nIf'),
|
|
contains(
|
|
'plugin:\n'
|
|
' Missing CHANGELOG change',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes CHANGELOG check when the CHANGELOG is changed', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/example/lib/foo.dart
|
|
packages/plugin/CHANGELOG.md
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'fails CHANGELOG check if only another package CHANGELOG chages',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/example/lib/foo.dart
|
|
packages/another_plugin/CHANGELOG.md
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No CHANGELOG change found'),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('allows missing CHANGELOG change with justification', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/example/lib/foo.dart
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
final List<String>
|
|
output = await runWithMissingChangeDetection(<String>[
|
|
'--pr-labels=some label,override: no changelog needed,another-label',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Ignoring lack of CHANGELOG update due to the '
|
|
'"override: no changelog needed" label.',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
// This test ensures that Dependabot Gradle changes to test-only files
|
|
// aren't flagged by the version check.
|
|
test(
|
|
'allows missing CHANGELOG and version change for test-only Gradle changes',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
// File list.
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/android/build.gradle
|
|
''',
|
|
),
|
|
),
|
|
// build.gradle diff
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
- 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 List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'allows missing CHANGELOG and version change for dev-only-file changes',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
// File list.
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/tool/run_tests.dart
|
|
packages/plugin/run_tests.sh
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'allows missing CHANGELOG and version change for dev-only line-level '
|
|
'changes in production files',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner
|
|
.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
// File list.
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
''',
|
|
),
|
|
),
|
|
// Dart file diff.
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
+ // TODO(someone): Fix this.
|
|
+ // ignore: some_lint
|
|
''',
|
|
),
|
|
<String>['main', 'HEAD', '--', 'packages/plugin/lib/plugin.dart'],
|
|
),
|
|
];
|
|
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('Running for plugin')]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('documentation comments are not exempt', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
|
|
const changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
gitProcessRunner
|
|
.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
''',
|
|
),
|
|
),
|
|
// Dart file diff.
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
+ /// Important new information for API clients!
|
|
''',
|
|
),
|
|
<String>['main', 'HEAD', '--', 'packages/plugin/lib/plugin.dart'],
|
|
),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'No version change found, but the change to this package could '
|
|
'not be verified to be exempt\n',
|
|
),
|
|
contains(
|
|
'plugin:\n'
|
|
' Missing version change',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
});
|
|
|
|
test('allows valid against pub', () async {
|
|
mockHttpResponse = <String, dynamic>{
|
|
'name': 'some_package',
|
|
'versions': <String>['0.0.1', '0.0.2', '1.0.0'],
|
|
};
|
|
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--against-pub',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('plugin: Current largest version on pub: 1.0.0'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('denies invalid against pub', () async {
|
|
mockHttpResponse = <String, dynamic>{
|
|
'name': 'some_package',
|
|
'versions': <String>['0.0.1', '0.0.2'],
|
|
};
|
|
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
|
|
var hasError = false;
|
|
final List<String> result = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
},
|
|
);
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
result,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'''
|
|
${indentation}Incorrectly updated version.
|
|
${indentation}HEAD: 2.0.0, pub: 0.0.2.
|
|
${indentation}Allowed versions: {1.0.0: NextVersionType.BREAKING_MAJOR, 0.1.0: NextVersionType.MINOR, 0.0.3: NextVersionType.PATCH}''',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'throw and print error message if http request failed when checking against pub',
|
|
() async {
|
|
mockHttpStatus = 400;
|
|
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
var hasError = false;
|
|
final List<String> result = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
},
|
|
);
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
result,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('''
|
|
${indentation}Error fetching version on pub for plugin.
|
|
${indentation}HTTP Status 400
|
|
${indentation}HTTP response: null
|
|
'''),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'when checking against pub, allow any version if http status is 404.',
|
|
() async {
|
|
mockHttpStatus = 404;
|
|
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> result = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--against-pub',
|
|
]);
|
|
|
|
expect(
|
|
result,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to find previous version on pub server.'),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
group('prelease versions', () {
|
|
test(
|
|
'allow an otherwise-valid transition that also adds a pre-release component',
|
|
() async {
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0-dev');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.0.0 -> 2.0.0-dev'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('allow releasing a pre-release', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '1.2.0');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.2.0-dev')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.2.0-dev -> 1.2.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
|
|
// Allow abandoning a pre-release version in favor of a different version
|
|
// change type.
|
|
test(
|
|
'allow an otherwise-valid transition that also removes a pre-release component',
|
|
() async {
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.2.0-dev')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.2.0-dev -> 2.0.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('allow changing only the pre-release version', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '1.2.0-dev.2');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.2.0-dev.1')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.2.0-dev.1 -> 1.2.0-dev.2'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'denies invalid version change that also adds a pre-release',
|
|
() async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.2.0-dev');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Incorrectly updated version.'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'denies invalid version change that also removes a pre-release',
|
|
() async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.2.0');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1-dev')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Incorrectly updated version.'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('denies invalid version change between pre-releases', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.2.0-dev');
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1-dev')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Incorrectly updated version.'),
|
|
]),
|
|
);
|
|
expect(
|
|
gitProcessRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show', <String>[
|
|
'main:packages/plugin/pubspec.yaml',
|
|
], null),
|
|
]),
|
|
);
|
|
});
|
|
});
|
|
group('batch release', () {
|
|
test(
|
|
'fails for batch release package missing pending_changelogs',
|
|
() async {
|
|
final RepositoryPackage package = createFakePlugin(
|
|
'a_package',
|
|
packagesDir,
|
|
);
|
|
package.ciConfigFile.writeAsStringSync('''
|
|
release:
|
|
batch: true
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No pending_changelogs folder found for a_package.'),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'passes for batch release package with pending_changelogs',
|
|
() async {
|
|
final RepositoryPackage package = createFakePlugin(
|
|
'a_package',
|
|
packagesDir,
|
|
);
|
|
package.ciConfigFile.writeAsStringSync('''
|
|
release:
|
|
batch: true
|
|
''');
|
|
package.pendingChangelogsDirectory.createSync();
|
|
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'fails for non-batch release package with pending_changelogs',
|
|
() async {
|
|
final RepositoryPackage package = createFakePlugin(
|
|
'a_package',
|
|
packagesDir,
|
|
);
|
|
// No ci_config.yaml means batch release is false by default.
|
|
package.pendingChangelogsDirectory.createSync();
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Package does not use batch release but has pending changelogs.',
|
|
),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'ignores changelog and pubspec yaml version modifications check with post-release label',
|
|
() async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'package',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
package.ciConfigFile.writeAsStringSync('''
|
|
release:
|
|
batch: true
|
|
''');
|
|
// Create the pending_changelogs directory so the test doesn't fail on that check.
|
|
package.directory.childDirectory('pending_changelogs').createSync();
|
|
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/package/CHANGELOG.md
|
|
packages/package/pubspec.yaml
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--pr-labels=post-release-package',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for package'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('fails when there is changelog modifications', () async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'package',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
package.ciConfigFile.writeAsStringSync('''
|
|
release:
|
|
batch: true
|
|
''');
|
|
// Create the pending_changelogs directory so the test doesn't fail on that check.
|
|
package.directory.childDirectory('pending_changelogs').createSync();
|
|
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/package/CHANGELOG.md
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'This package uses batch release, so CHANGELOG.md should not be changed directly.',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when there is pubspec version modifications', () async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'package',
|
|
packagesDir,
|
|
version: '1.0.1',
|
|
);
|
|
package.ciConfigFile.writeAsStringSync('''
|
|
release:
|
|
batch: true
|
|
''');
|
|
// Create the pending_changelogs directory so the test doesn't fail on that check.
|
|
package.directory.childDirectory('pending_changelogs').createSync();
|
|
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/package/pubspec.yaml
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'This package uses batch release, so the version in pubspec.yaml should not be changed directly.',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'passes when there is pubspec modification but no version change',
|
|
() async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'package',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
package.ciConfigFile.writeAsStringSync('''
|
|
release:
|
|
batch: true
|
|
''');
|
|
// Create the pending_changelogs directory so the test doesn't fail on that check.
|
|
package.directory.childDirectory('pending_changelogs').createSync();
|
|
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/package/pubspec.yaml
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('No issues found!')]),
|
|
);
|
|
},
|
|
);
|
|
|
|
test('fails for batch release package with no new changelog', () async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'package',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
package.ciConfigFile.writeAsStringSync('''
|
|
release:
|
|
batch: true
|
|
''');
|
|
// Simulate a code change
|
|
package.libDirectory
|
|
.childFile('foo.dart')
|
|
.writeAsStringSync('void foo() {}');
|
|
// Create the pending_changelogs directory so the test doesn't fail on that check.
|
|
package.directory.childDirectory('pending_changelogs').createSync();
|
|
|
|
gitProcessRunner
|
|
.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'packages/package/lib/foo.dart')),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--check-for-missing-changes',
|
|
],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
},
|
|
);
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'No new changelog files found in the pending_changelogs folder.',
|
|
),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes for batch release package with new changelog', () async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'package',
|
|
packagesDir,
|
|
version: '1.0.0',
|
|
);
|
|
package.ciConfigFile.writeAsStringSync('''
|
|
release:
|
|
batch: true
|
|
''');
|
|
// Simulate a code change
|
|
package.libDirectory
|
|
.childFile('foo.dart')
|
|
.writeAsStringSync('void foo() {}');
|
|
// Create the pending_changelogs directory so the test doesn't fail on that check.
|
|
final Directory pendingChangelogs = package.directory.childDirectory(
|
|
'pending_changelogs',
|
|
);
|
|
pendingChangelogs.createSync();
|
|
pendingChangelogs.childFile('some_change.yaml').writeAsStringSync('''
|
|
changelog: "Some change"
|
|
version: patch
|
|
''');
|
|
|
|
gitProcessRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(
|
|
MockProcess(
|
|
stdout: '''
|
|
packages/package/lib/foo.dart
|
|
packages/package/pending_changelogs/some_change.yaml
|
|
''',
|
|
),
|
|
),
|
|
];
|
|
gitProcessRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--check-for-missing-changes',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[contains('No issues found!')]),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
group('Pre 1.0', () {
|
|
test('nextVersion allows patch version', () {
|
|
testAllowedVersion(
|
|
'0.12.0',
|
|
'0.12.0+1',
|
|
nextVersionType: NextVersionType.PATCH,
|
|
);
|
|
testAllowedVersion(
|
|
'0.12.0+4',
|
|
'0.12.0+5',
|
|
nextVersionType: NextVersionType.PATCH,
|
|
);
|
|
});
|
|
|
|
test('nextVersion does not allow jumping patch', () {
|
|
testAllowedVersion('0.12.0', '0.12.0+2', allowed: false);
|
|
testAllowedVersion('0.12.0+2', '0.12.0+4', allowed: false);
|
|
});
|
|
|
|
test('nextVersion does not allow going back', () {
|
|
testAllowedVersion('0.12.0', '0.11.0', allowed: false);
|
|
testAllowedVersion('0.12.0+2', '0.12.0+1', allowed: false);
|
|
testAllowedVersion('0.12.0+1', '0.12.0', allowed: false);
|
|
});
|
|
|
|
test('nextVersion allows minor version', () {
|
|
testAllowedVersion(
|
|
'0.12.0',
|
|
'0.12.1',
|
|
nextVersionType: NextVersionType.MINOR,
|
|
);
|
|
testAllowedVersion(
|
|
'0.12.0+4',
|
|
'0.12.1',
|
|
nextVersionType: NextVersionType.MINOR,
|
|
);
|
|
});
|
|
|
|
test('nextVersion does not allow jumping minor', () {
|
|
testAllowedVersion('0.12.0', '0.12.2', allowed: false);
|
|
testAllowedVersion('0.12.0+2', '0.12.3', allowed: false);
|
|
});
|
|
});
|
|
|
|
group('Releasing 1.0', () {
|
|
test('nextVersion allows releasing 1.0', () {
|
|
testAllowedVersion(
|
|
'0.12.0',
|
|
'1.0.0',
|
|
nextVersionType: NextVersionType.BREAKING_MAJOR,
|
|
);
|
|
testAllowedVersion(
|
|
'0.12.0+4',
|
|
'1.0.0',
|
|
nextVersionType: NextVersionType.BREAKING_MAJOR,
|
|
);
|
|
});
|
|
|
|
test('nextVersion does not allow jumping major', () {
|
|
testAllowedVersion('0.12.0', '2.0.0', allowed: false);
|
|
testAllowedVersion('0.12.0+4', '2.0.0', allowed: false);
|
|
});
|
|
|
|
test('nextVersion does not allow un-releasing', () {
|
|
testAllowedVersion('1.0.0', '0.12.0+4', allowed: false);
|
|
testAllowedVersion('1.0.0', '0.12.0', allowed: false);
|
|
});
|
|
});
|
|
|
|
group('Post 1.0', () {
|
|
test('nextVersion allows patch jumps', () {
|
|
testAllowedVersion(
|
|
'1.0.1',
|
|
'1.0.2',
|
|
nextVersionType: NextVersionType.PATCH,
|
|
);
|
|
testAllowedVersion(
|
|
'1.0.0',
|
|
'1.0.1',
|
|
nextVersionType: NextVersionType.PATCH,
|
|
);
|
|
});
|
|
|
|
test('nextVersion does not allow build jumps', () {
|
|
testAllowedVersion('1.0.1', '1.0.1+1', allowed: false);
|
|
testAllowedVersion('1.0.0+5', '1.0.0+6', allowed: false);
|
|
});
|
|
|
|
test('nextVersion does not allow skipping patches', () {
|
|
testAllowedVersion('1.0.1', '1.0.3', allowed: false);
|
|
testAllowedVersion('1.0.0', '1.0.6', allowed: false);
|
|
});
|
|
|
|
test('nextVersion allows minor version jumps', () {
|
|
testAllowedVersion(
|
|
'1.0.1',
|
|
'1.1.0',
|
|
nextVersionType: NextVersionType.MINOR,
|
|
);
|
|
testAllowedVersion(
|
|
'1.0.0',
|
|
'1.1.0',
|
|
nextVersionType: NextVersionType.MINOR,
|
|
);
|
|
});
|
|
|
|
test('nextVersion does not allow skipping minor versions', () {
|
|
testAllowedVersion('1.0.1', '1.2.0', allowed: false);
|
|
testAllowedVersion('1.1.0', '1.3.0', allowed: false);
|
|
});
|
|
|
|
test('nextVersion allows breaking changes', () {
|
|
testAllowedVersion(
|
|
'1.0.1',
|
|
'2.0.0',
|
|
nextVersionType: NextVersionType.BREAKING_MAJOR,
|
|
);
|
|
testAllowedVersion(
|
|
'1.0.0',
|
|
'2.0.0',
|
|
nextVersionType: NextVersionType.BREAKING_MAJOR,
|
|
);
|
|
});
|
|
|
|
test('nextVersion does not allow skipping major versions', () {
|
|
testAllowedVersion('1.0.1', '3.0.0', allowed: false);
|
|
testAllowedVersion('1.1.0', '2.3.0', allowed: false);
|
|
});
|
|
});
|
|
}
|