Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
name: release
name: Reusable Release

on:
push:
branches:
- main
workflow_call:
inputs:
publish-args:
required: true
type: string
workflow-name:
required: true
type: string

# Declare default permissions as read only.
permissions: read-all
Expand Down Expand Up @@ -82,5 +88,5 @@ jobs:
run: |
git config --global user.name ${{ secrets.USER_NAME }}
git config --global user.email ${{ secrets.USER_EMAIL }}
dart ./script/tool/lib/src/main.dart publish --all-changed --base-sha=HEAD~ --skip-confirmation --remote=origin
dart ./script/tool/lib/src/main.dart publish ${{ inputs.publish-args }}
env: {PUB_CREDENTIALS: "${{ secrets.PUB_CREDENTIALS }}"}
14 changes: 14 additions & 0 deletions .github/workflows/release_from_branches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Batch Release
on:
push:
branches:
- 'release-go-router'
- 'release-material'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's keep this out for now until the decoupling and opts into batch release.

- 'release-cupertino'
jobs:
release:
uses: ./.github/workflows/release.yml
with:
publish-args: '--all-changed --batch-release --base-sha=HEAD~ --skip-confirmation --remote=origin'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't we need to pass in the branch name for releasing? looking at the release.yaml it seems to be using the main branch when running the publish.

workflow-name: 'Batch Release ${{ github.ref_name }}'
secrets: inherit
12 changes: 12 additions & 0 deletions .github/workflows/release_from_main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Main Release
on:
push:
branches:
- main
jobs:
release:
uses: ./.github/workflows/release.yml
with:
publish-args: '--all-changed --base-sha=HEAD~ --skip-confirmation --remote=origin'
workflow-name: 'Main Release'
secrets: inherit
38 changes: 38 additions & 0 deletions script/tool/lib/src/publish_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class PublishCommand extends PackageLoopingCommand {
'Release all packages that contains pubspec changes at the current commit compares to the base-sha.\n'
'The --packages option is ignored if this is on.',
);
argParser.addFlag(
_batchReleaseFlag,
help: 'only release the packages that opt-in for batch release option.',
);
argParser.addFlag(
_dryRunFlag,
help:
Expand All @@ -109,6 +113,7 @@ class PublishCommand extends PackageLoopingCommand {
static const String _pubFlagsOption = 'pub-publish-flags';
static const String _remoteOption = 'remote';
static const String _allChangedFlag = 'all-changed';
static const String _batchReleaseFlag = 'batch-release';
static const String _dryRunFlag = 'dry-run';
static const String _skipConfirmationFlag = 'skip-confirmation';
static const String _tagForAutoPublishFlag = 'tag-for-auto-publish';
Expand Down Expand Up @@ -196,6 +201,39 @@ class PublishCommand extends PackageLoopingCommand {
.toList();

for (final pubspecPath in changedPubspecs) {
// Read the ci_config.yaml file if it exists

final String packageName = p.basename(p.dirname(pubspecPath));
bool isBatchReleasePackage;
try {
final File ciConfigFile = packagesDir.fileSystem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this may need to wait for #10485 where there is a new api to access batch release flags

.file(pubspecPath)
.parent
.childFile('ci_config.yaml');
if (!ciConfigFile.existsSync()) {
isBatchReleasePackage = false;
} else {
final ciConfig =
loadYaml(ciConfigFile.readAsStringSync()) as YamlMap?;
final dynamic batchValue =
(ciConfig?['release'] as YamlMap?)?['batch'];
if (batchValue is! bool) {
printError(
'`release.batch` key is missing or not a boolean in ci_config.yaml for $packageName.',
);
continue;
}
isBatchReleasePackage = batchValue;
}
} catch (e) {
printError('Could not parse ci_config.yaml for $packageName: $e');
continue;
}
// Skip the package if batch release flag is not set to match the ci_config.yaml
if (getBoolArg(_batchReleaseFlag) != isBatchReleasePackage) {
continue;
}

// git outputs a relativa, Posix-style path.
final File pubspecFile = childFileWithSubcomponents(
packagesDir.fileSystem.directory((await gitDir).path),
Expand Down
220 changes: 220 additions & 0 deletions script/tool/test/publish_command_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,226 @@ void main() {
);
});

group('--batch-release flag', () {
test(
'filters packages based on the existence of ci_config.yaml',
() async {
// Mock pub.dev responses.
mockHttpResponses['plugin1'] = <String, dynamic>{
'name': 'plugin1',
'versions': <String>['0.0.1'],
};
mockHttpResponses['plugin2'] = <String, dynamic>{
'name': 'plugin2',
'versions': <String>['0.0.1'],
};

// Mock packages.
final RepositoryPackage plugin1 = createFakePlugin(
'plugin1',
packagesDir,
version: '0.0.2',
batchRelease: true,
);

final RepositoryPackage plugin2 = createFakePlugin(
'plugin2',
packagesDir,
version: '0.0.2',
);

expect(plugin1.ciConfigFile.existsSync(), true);
expect(plugin2.ciConfigFile.existsSync(), false);

// Mock git diff to show both packages have changed.
processRunner
.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
FakeProcessInfo(
MockProcess(
stdout:
'${plugin1.pubspecFile.path}\n${plugin2.pubspecFile.path}',
),
),
];

mockStdin.readLineOutput = 'y';

final List<String> output = await runCapturingPrint(
commandRunner,
<String>[
'publish',
'--all-changed',
'--base-sha=HEAD~',
'--batch-release',
],
);
// Package1 is published in batch realease, pacakge2 is not.
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running `pub publish ` in ${plugin1.path}...'),
contains('Published plugin1 successfully!'),
]),
);

expect(
output,
isNot(
contains(
contains('Running `pub publish ` in ${plugin2.path}...!'),
),
),
);
expect(
output,
isNot(contains(contains('Published plugin2 successfully!'))),
);
},
);

test(
'filters packages based on the batch release flag value in ci_config.yaml',
() async {
// Mock pub.dev responses.
mockHttpResponses['plugin1'] = <String, dynamic>{
'name': 'plugin1',
'versions': <String>['0.0.1'],
};
mockHttpResponses['plugin2'] = <String, dynamic>{
'name': 'plugin2',
'versions': <String>['0.0.1'],
};

// Mock packages.
final RepositoryPackage plugin1 = createFakePlugin(
'plugin1',
packagesDir,
version: '0.0.2',
batchRelease: true,
);

final RepositoryPackage plugin2 = createFakePlugin(
'plugin2',
packagesDir,
version: '0.0.2',
batchRelease: false,
);

// Mock git diff to show both packages have changed.
processRunner
.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
FakeProcessInfo(
MockProcess(
stdout:
'${plugin1.pubspecFile.path}\n${plugin2.pubspecFile.path}',
),
),
];

mockStdin.readLineOutput = 'y';

final List<String> output = await runCapturingPrint(
commandRunner,
<String>[
'publish',
'--all-changed',
'--base-sha=HEAD~',
'--batch-release',
],
);
// Package1 is published in batch realease, pacakge2 is not.
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running `pub publish ` in ${plugin1.path}...'),
contains('Published plugin1 successfully!'),
]),
);

expect(
output,
isNot(
contains(
contains('Running `pub publish ` in ${plugin2.path}...!'),
),
),
);
expect(
output,
isNot(contains(contains('Published plugin2 successfully!'))),
);
},
);

test(
'when --batch-release flag value is false, batch release packages are filtered out',
() async {
// Mock pub.dev responses.
mockHttpResponses['plugin1'] = <String, dynamic>{
'name': 'plugin1',
'versions': <String>['0.0.1'],
};
mockHttpResponses['plugin2'] = <String, dynamic>{
'name': 'plugin2',
'versions': <String>['0.0.1'],
};

// Mock packages.
final RepositoryPackage plugin1 = createFakePlugin(
'plugin1',
packagesDir,
version: '0.0.2',
);

final RepositoryPackage plugin2 = createFakePlugin(
'plugin2',
packagesDir,
version: '0.0.2',
batchRelease: true,
);

// Mock git diff to show both packages have changed.
processRunner
.mockProcessesForExecutable['git-diff'] = <FakeProcessInfo>[
FakeProcessInfo(
MockProcess(
stdout:
'${plugin1.pubspecFile.path}\n${plugin2.pubspecFile.path}',
),
),
];

mockStdin.readLineOutput = 'y';

final List<String> output = await runCapturingPrint(
commandRunner,
<String>['publish', '--all-changed', '--base-sha=HEAD~'],
);
// Package1 is published in batch realease, pacakge2 is not.
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running `pub publish ` in ${plugin1.path}...'),
contains('Published plugin1 successfully!'),
]),
);

expect(
output,
isNot(
contains(
contains('Running `pub publish ` in ${plugin2.path}...!'),
),
),
);
expect(
output,
isNot(contains(contains('Published plugin2 successfully!'))),
);
},
);
});

test('Do not release flutter_plugin_tools', () async {
mockHttpResponses['plugin1'] = <String, dynamic>{
'name': 'flutter_plugin_tools',
Expand Down
16 changes: 16 additions & 0 deletions script/tool/test/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ RepositoryPackage createFakePlugin(
String? version = '0.0.1',
String flutterConstraint = _defaultFlutterConstraint,
String dartConstraint = _defaultDartConstraint,
bool? batchRelease,
}) {
final RepositoryPackage package = createFakePackage(
name,
Expand All @@ -117,6 +118,9 @@ RepositoryPackage createFakePlugin(
flutterConstraint: flutterConstraint,
dartConstraint: dartConstraint,
);
if (batchRelease != null) {
createFakeCiConfig(batchRelease, package);
}

return package;
}
Expand Down Expand Up @@ -287,6 +291,18 @@ $pluginSection
package.pubspecFile.writeAsStringSync(yaml);
}

/// Creates a `ci_config.yaml` file for [package].
void createFakeCiConfig(bool batchRelease, RepositoryPackage package) {
final yaml =
'''
release:
batch: $batchRelease
''';

package.ciConfigFile.createSync();
package.ciConfigFile.writeAsStringSync(yaml);
}

String _pluginPlatformSection(
String platform,
PlatformDetails support,
Expand Down