-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] yarn workspaces foreach
features
#62
Comments
Hey, thanks for opening an issue about this! Overall I see the point of those features. Now that we've changed the CLI framework it's also much clearer how those options would interact with "proxied" commands (they would simply have to be put before the final command name component), so I don't have as many objections. One thing is that instead of Could you implement this command in a plugin? I think we would eventually want to bundle it by default anyway, but I think it would be worthwhile to have a plugin whose only goal is to contain the commands specific to workspaces, if only to make them more easily discoverable by the other contributors. The plugin documentation is currently here, and feel free to ask me any question on Discord.
This actually has a pretty simple solution - now that the CLI uses clipanion, we can simply override
Yep,
I'd prefer not - this type of settings is dangerous since it creates vastly different environments and might break users expectations when moving from a project to another.
My suggestion:
How does that sound? |
I incorporated your feedback regarding Also, do I have to worry about how many processes get spun up like if someone has 50 packages? Maybe we need The documentation links to example plugins are broken. Can you point me to some examples I can reference for what |
I know you wanted to do it as a separate command but I'm curious what would need to be changed for this PoC to work: import {Configuration, PluginConfiguration, Project} from '@berry/core';
// eslint-disable-next-line arca/no-default-export
export default (clipanion: any, pluginConfiguration: PluginConfiguration) => clipanion
.command(`workspaces foreach [... args]`)
.flags({proxyArguments: true, parallel: false, missingScripts: false})
.categorize(`Workspace commands`)
.describe(`run a command on all workspaces`)
.action(async ({cwd, args, ... env}: {cwd: string, args: Array<string>}) => {
const configuration = await Configuration.find(cwd, pluginConfiguration);
const {project} = await Project.find(configuration, cwd);
let workspaces = project.workspacesByCwd.keys();
// This feels really hacky
if (args[0].toLowerCase() === 'run' && !args.missingScripts) {
workspaces = workspaces.filter( cwd => {
let pkg = require(`${cwd}/package.json`);
return pkg.scripts && pkg.scripts[args[1]]
});
}
if (args.parallel) {
let runs = workspaces.map( cwd => clipanion.run(null, args, {... env, cwd}) );
return Promise.all(runs);
} else {
for (const cwd of project.workspacesByCwd.keys())
await clipanion.run(null, args, {... env, cwd});
return 0;
}
}); Is |
No, you'd instead:
This way you wouldn't have to wait for the whole command to finish. To change the const stdout = new PassThrough();
await clipanion.run(null, args, {... env, cwd, stdout});
Use
Instead of manually checking whether the first arg is
All the Yarn commands are implemented through plugins - check the packages directory to see them all. One very simple plugin is
Check how |
I've got two questions upon reading this thread: First and foremost: if the foreach command is run like Second question: in what order will the With respect to |
The way I made it work in clipanion is that options for the
While the following would send
There might be a bug or two around it (there's an extra subtlety in that
Yep I agree - proxy commands are a large part of why I made clipanion in the first place!
That's a great question - I guess we should standardize it and add a test since it would be easy to break it when doing a refactoring. Topological first degree then alphabetical second degree sort would make the most sense, like you said. The
I learned about interlaced via photoshop and png files! 😄 I don't have a strong opinion either way - I tend to prefer the word interlaced (less ambiguous imo than stream), but the semantic argument makes sense. The implementer gets to pick! |
I am going to add ordering as a feature to look into because I agree to it's relevance, however I'm not sure the best way to tackle it. |
Order matters, also in parallel. We always run our lerna scripts with Could we expose the ordering via plugins? That would allow us to keep the foreach command simple (we can default to alphabetically or in the order found in the workspace) and it allows for more complicated orderings (e.g. topological sort) to be implemented as separate plugins. EDIT: replaced |
Hm, I feel like there's a finite enough number of use case to not make it a plugin. We just have to figure out the right design 🤔 I also didn't realize that you considered that Given the discussion, here's what I suggest:
Do you see a design flaw in those options? |
I have very mixed feelings about Instead I'd rather suggest people bundle calls of serial and parallel into script tasks. If we have defaults and flags, then customization to ordering requires more flags, more options, it just overcomplicates everything. If you care about any particular ordering you should compose the ordering explicitly rather than relying on Yarn (which has much bigger problems to focus on) to just figure out what it thinks you want it to do. Otherwise, I agree with the rest of the API changes. If people want more complex task execution logic, they should resort to gulp or something more powerful. @bgotink to respond to your specific use-case: Assuming this even remotely resembles what you're trying to do, this to me is far more complex than a dependency resolution order. Do you want each package to go |
That's not what I'm trying to do. The command
runs Supposing a workspace consisting of packages
where the dependency graph is
Running
if the scripts take an identical time to run in all packages. The order is not deterministic because the timings affect the order, but we do know that the The pre and post script are run immediately before and after the script in every package, not as a whole. Lerna actually uses yarn behind the screens to execute the run-scripts.
That would require an extra option in the foreach command: a way to filter what workspaces the script is run in. It also delegates a bunch of work the yarn user. It would also require quite a bit of code on the user's side to sort the packages properly.
Isn't the purpose of this command to prevent people from writing the same script over and over again? Without topological sort this command boils down to a simple BASH script:
To be very explicit: I'm not refuting the usefulness of the foreach command. I want to prevent people from having to write the scripts above over and over again. However, providing a
Could you clarify this a bit? Lerna provides only two orders: topological sort (default) and "I don't care about order" ( |
After more thought I'm concluding emulating the Lerna approach may be the best solution. But now I'm just thinking of switching to Lerna... |
@arcanis What happened to the flag |
Background
Several feature requests repeatedly pop up for using yarn with workspaces. Most solutions involve using external packages or weird script hacks, voiding some of the merit for yarn workspaces. It would be nice if
yarn workspaces
commands had slightly more flexibility to remove external dependencies.See relevant code
Desired Features
List of
yarn workspaces foreach
(previouslyyarn workspaces
) desired features (with potential API):--parallel
Run scripts in parallel instead of sequentially--skip-missing
Skip packages that don't have the specified script--prefix
Prefix / colorize script output per packageDrawbacks
yarn workspaces foreach
is a dead-simple command that simply forwards an arbitrary yarn command to each package. It has no knowledge of what sort of command is being executed (npm run
, etc) so detecting missing scripts from here feels messy.Additionally, I'm not sure how to do
await
in parallel. Thinking aboutPromise.all()
assuming the Promise API is available within Yarn(?).We'll need a unique way to isolate missing script errors from regular script errors.
Describe alternatives you've considered
Promise.all()
seems promising if the Promise API is available in yarnyarn run [script] --skip-missing
might be a better place for the flag. While useless when used directly, it allows us to simply do:yarn workspaces foreach [workspace] run [script] --skip-missing
Note that this approach means you must ALWAYS pass
--skip-missing
when calling theyarn workspaces foreach
command. This could potentially be stored in a config but is that really a good idea?Additional context
Trying to adopt Yarn Workspaces for my new project, lots of redundant issues for these features exist on the v1 repo.
I'd be willing to implement a solution if I could get some suggestions/consensus on how to tackle the problem. I'm not sure the best way to go about doing it right now.
The text was updated successfully, but these errors were encountered: