Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[tool] add all and dry-run flags to publish-plugin command #3776

Merged
merged 4 commits into from
Apr 30, 2021

Conversation

cyanglaz
Copy link
Contributor

@cyanglaz cyanglaz commented Mar 31, 2021

Add a new all flag to the publish plugin command does the following:

  1. Look for all the pubspec changes in the current commit and determine what plugin(s) need to be released. If the new version is lower than the existing latest tagged version, skip.
  2. Release the plugin as how publish-plugin works: publishing to pub, git tag release etc.

Add a new dry-run flag to dry-run the publish and tag release process. This is for testing locally and ci without actually publishing or git tag anything.

This command is going to be part of auto-publish workflow.

part of flutter/flutter#79830

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I signed the CLA.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@cyanglaz
Copy link
Contributor Author

cyanglaz commented Apr 1, 2021

As we discussed in the doc, we are probably going to use the alternative if LUCI push to git works.
If that's the case, the command should include both publish and git tag. I will convert this to draft until implement the publish part.

@cyanglaz cyanglaz marked this pull request as draft April 1, 2021 01:34
@cyanglaz cyanglaz removed the request for review from stuartmorgan April 1, 2021 01:34
@cyanglaz cyanglaz changed the title [ci] add tag-release command [tool] add all and dry-run flags to publish-plugin command Apr 21, 2021
@cyanglaz cyanglaz marked this pull request as ready for review April 21, 2021 22:00
StreamSubscription<String> _stdinSubscription;

@override
Future<void> run() async {
final String package = argResults[_packageOption] as String;
if (package == null) {
final bool all = argResults[_allFlag] as bool;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: how about publishAll as the variable name so it's more descriptive.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -64,13 +68,31 @@ class PublishPluginCommand extends PluginCommand {
// Flutter convention is to use "upstream" for the single source of truth, and "origin" for personal forks.
defaultsTo: 'upstream',
);
argParser.addFlag(
_allFlag,
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should call this flag all-changed? --all sounds more like it should do something like the Melos publish-all command.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sounds good! DONE

@@ -111,7 +135,74 @@ class PublishPluginCommand extends PluginCommand {
}
_print('Local repo is ready!');

final Directory packageDir = _getPackageDir(package);
if (all) {
await _publishAllPackages(
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly, _publishAllChangedPackages

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

final List<String> changedPubspecs =
await gitVersionFinder.getChangedPubSpecs();
if (changedPubspecs.isEmpty) {
_print('No version updates in this commit, exiting...');
Copy link
Contributor

Choose a reason for hiding this comment

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

"..." indicates more to follow, which seems odd for something that says it's exiting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, remove the exit part.

await baseGitDir.runCommand(<String>['tag', '--sort=-committerdate']);
final List<String> existingTags = (existingTagsResult.stdout as String)
.split('\n')
..removeWhere((element) => element == '');
Copy link
Contributor

Choose a reason for hiding this comment

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

isEmpty

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -120,7 +211,50 @@ class PublishPluginCommand extends PluginCommand {
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag);
}
await _finishSuccesfully();
_print('Release ${packageDir.basename} successful.');
Copy link
Contributor

Choose a reason for hiding this comment

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

There aren't any bools for the sub-steps; does that mean they are fatal errors when something goes wrong?

If we try to release 3 packages, an error with one of them presumably shouldn't kill the whole thing, but instead move on to the next, and then at the end list all the successes and failures.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done. Made all sub-steps within publishing a plugin to return a bool instead of throw directly, then the main process will decide to throw depends on the returned value.

}

if (pubspec.name == null) {
printErrorAndExit(errorMessage: 'Fatal: Package name is null.');
Copy link
Contributor

Choose a reason for hiding this comment

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

Same concerns here; if this is fatal, then all packages will fail, not just the one that's wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed, commented above.

final String latestTag = existingTags.isNotEmpty
? existingTags
.firstWhere((String tag) => tag.split('-v').first == pubspec.name)
: '';
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use orElse here instead of checking isNotEmpty.

In fact, I think without that change the current logic is wrong, since just because the set of tags isn't empty doesn't mean that this particular package has been released before, so it seems like this would throw as soon as we had a new plugin. (Which suggests there's a test case missing.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Thanks! Fixed and added a test for it

Copy link
Contributor Author

@cyanglaz cyanglaz left a comment

Choose a reason for hiding this comment

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

Updated per review comments. PTAL @stuartmorgan

@@ -64,13 +68,31 @@ class PublishPluginCommand extends PluginCommand {
// Flutter convention is to use "upstream" for the single source of truth, and "origin" for personal forks.
defaultsTo: 'upstream',
);
argParser.addFlag(
_allFlag,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

sounds good! DONE

StreamSubscription<String> _stdinSubscription;

@override
Future<void> run() async {
final String package = argResults[_packageOption] as String;
if (package == null) {
final bool all = argResults[_allFlag] as bool;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -111,7 +135,74 @@ class PublishPluginCommand extends PluginCommand {
}
_print('Local repo is ready!');

final Directory packageDir = _getPackageDir(package);
if (all) {
await _publishAllPackages(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

final List<String> changedPubspecs =
await gitVersionFinder.getChangedPubSpecs();
if (changedPubspecs.isEmpty) {
_print('No version updates in this commit, exiting...');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, remove the exit part.

await baseGitDir.runCommand(<String>['tag', '--sort=-committerdate']);
final List<String> existingTags = (existingTagsResult.stdout as String)
.split('\n')
..removeWhere((element) => element == '');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -120,7 +211,50 @@ class PublishPluginCommand extends PluginCommand {
remoteUrl: remoteUrl,
shouldPushTag: shouldPushTag);
}
await _finishSuccesfully();
_print('Release ${packageDir.basename} successful.');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

done. Made all sub-steps within publishing a plugin to return a bool instead of throw directly, then the main process will decide to throw depends on the returned value.

}

if (pubspec.name == null) {
printErrorAndExit(errorMessage: 'Fatal: Package name is null.');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed, commented above.

final String latestTag = existingTags.isNotEmpty
? existingTags
.firstWhere((String tag) => tag.split('-v').first == pubspec.name)
: '';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Thanks! Fixed and added a test for it

{String remote,
String remoteUrl,
bool shouldPushTag,
GitDir baseGitDir}) async {
Copy link
Contributor

Choose a reason for hiding this comment

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

Optional nit: add trailing commas here and below to improve formatting

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

if (packagesFailed.isNotEmpty) {
return false;
}
return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

These four lines can be simplified to: return packagesFailed.isEmpty;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

if (!needsRelease) {
continue;
}
} on ToolExit catch (e) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Catching a ToolExit seems potentially very confusing. How about making _checkNeedsRelease return a release/norelease/failure enum?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, sounds good!

],
workingDir: packageDir);
} on ToolExit catch (e) {
print(e);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why print and _print?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, this was a testing code, removed

Future<bool> _checkGitStatus(Directory packageDir) async {
io.ProcessResult statusResult;
try {
statusResult = await processRunner.runAndExitOnError(
Copy link
Contributor

Choose a reason for hiding this comment

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

The naming here suggests there's a version that doesn't exit on error, so we wouldn't need to catch ToolExits?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

runAndExitOnError does something extra (printing error message in a specific format etc), if we don't use it, then we have to re-write that code. I think we should rename this method to run(bool logError = true, bool exitOnError = true...).

Actually, I just saw that the run method that we have already has a exitOnError flag which doesn't do anything, which suggests that we used to have it like this but somehow refactored into 2 methods. https://github.com/flutter/plugins/blob/master/script/tool/lib/src/common.dart#L507

I'm happy to do the refactoring in a separate PR! Then rebase this on top after that lands.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

#3827 @stuartmorgan here you go

workingDir: packagesDir);
}
} on ToolExit catch (e) {
print(e);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same question as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

same answer above :)

Copy link
Contributor Author

@cyanglaz cyanglaz left a comment

Choose a reason for hiding this comment

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

@stuartmorgan Fixed all review comments as well as removing all the unnecessary try-catch. PTAL

Copy link
Contributor

@stuartmorgan stuartmorgan left a comment

Choose a reason for hiding this comment

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

LGTM with nits.

Future<void> _publishPlugin({@required Directory packageDir}) async {
await _checkGitStatus(packageDir);
await _publish(packageDir);
// Returns `true` if needs to release the version, `false` if needs to skip
Copy link
Contributor

Choose a reason for hiding this comment

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

Fix comment for new return type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}
if (packagesFailed.isNotEmpty) {
_print(
'Packages failed to release: ${packagesFailed.join(', ')}, see above for details.');
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: "Failed to release the following packages:"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

if (pubspec.version == null) {
_print('No version found. A package that '
'intentionally has no version should be marked '
'"publish_to: none".');
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Fix the excessive line breaking here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// The package does not need to be released.
noRelease,

// There's an error when trying to access if the package needs to be released.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: s/access if/determine whether/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -85,14 +87,14 @@ Directory createFakePlugin(
final Directory exampleDir = pluginDirectory.childDirectory('example')
..createSync();
createFakePubspec(exampleDir,
name: '${name}_example', isFlutter: isFlutter);
name: '${name}_example', isFlutter: isFlutter, includeVersion: false, publish_to: 'none');
Copy link
Contributor

Choose a reason for hiding this comment

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

While you're touching the utils, could you fix the naming of this parameter? It should be publishTo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@cyanglaz cyanglaz added the waiting for tree to go green (Use "autosubmit") This PR is approved and tested, but waiting for the tree to be green to land. label Apr 30, 2021
@fluttergithubbot fluttergithubbot merged commit a2ce3da into flutter:master Apr 30, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request May 1, 2021
fluttergithubbot pushed a commit to flutter/flutter that referenced this pull request May 1, 2021
samandmoore added a commit to Betterment/plugins that referenced this pull request May 6, 2021
* upstream/master: (383 commits)
  Add implement and registerWith method to plugins (flutter#3833)
  Update third_party license checking (flutter#3844)
  [tool] add `all` and `dry-run` flags to publish-plugin command (flutter#3776)
  Re-add bin/ to flutter_plugin_tools (flutter#3839)
  switch from using 'tuneup' to analyze to 'dart analyze' (flutter#3837)
  [in_app_purchase] Federated iOS implementation (flutter#3832)
  Prep the tools for publishing (flutter#3836)
  [in_app_purchase] Make PurchaseDetails.status mandatory (flutter#3831)
  Fix grammatical error in contributing guide (flutter#3217)
  [google_sign_in_web] fix README typos.
  [tool] combine run and runAndExitOnError (flutter#3827)
  [camera] android-rework part 2: Android auto focus feature (flutter#3796)
  [in_app_purchase_platform_interface] Added additional fields to ProductDetails (flutter#3826)
  Move all null safety packages' min dart sdk to 2.12.0 (flutter#3822)
  [path_provider_*] code cleanup: sort directives (flutter#3823)
  [in_app_purchase] Implementation of platform interface (flutter#3781)
  [google_sign_in] Add todo WRT correctly setting X-Goog-AuthUser header (flutter#3819)
  [tools] fix version check command not working for new packages (flutter#3818)
  [camera] android-rework part 1: Base classes to support Android Camera features (flutter#3795)
  fix MD (flutter#3815)
  ...
fotiDim pushed a commit to fotiDim/plugins that referenced this pull request Sep 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes waiting for tree to go green (Use "autosubmit") This PR is approved and tested, but waiting for the tree to be green to land.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants