Skip to content

Refactor CLI to cac#2009

Merged
bluwy merged 7 commits into
nextfrom
cac
May 17, 2026
Merged

Refactor CLI to cac#2009
bluwy merged 7 commits into
nextfrom
cac

Conversation

@bluwy
Copy link
Copy Markdown
Member

@bluwy bluwy commented May 14, 2026

Main changes:

  1. Refactor each src/commands/* to independently resolve the required monorepo root and config etc. This makes calling the command function simple.

    (This changes the command function signature. Let me know if you feel differently of how this is refactored. I went back-and-forth between cwd being its own param vs inside the options object.)

  2. src/run.ts removed. Move the CLI handling all to src/index.ts.

  3. src/run.tests.ts removed. Move to respective commands tests.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 14, 2026

🦋 Changeset detected

Latest commit: 26135a6

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

💥 An error occurred when fetching the changed packages and changesets in this PR
Some errors occurred when validating the changesets config:
The package "@changesets/apply-release-plan" depends on the ignored package "@changesets/test-utils", but "@changesets/apply-release-plan" is not being ignored. Please add "@changesets/apply-release-plan" to the `ignore` option.
The package "@changesets/cli" depends on the ignored package "@changesets/test-utils", but "@changesets/cli" is not being ignored. Please add "@changesets/cli" to the `ignore` option.
The package "@changesets/config" depends on the ignored package "@changesets/test-utils", but "@changesets/config" is not being ignored. Please add "@changesets/config" to the `ignore` option.
The package "@changesets/get-dependents-graph" depends on the ignored package "@changesets/test-utils", but "@changesets/get-dependents-graph" is not being ignored. Please add "@changesets/get-dependents-graph" to the `ignore` option.
The package "@changesets/git" depends on the ignored package "@changesets/test-utils", but "@changesets/git" is not being ignored. Please add "@changesets/git" to the `ignore` option.
The package "@changesets/pre" depends on the ignored package "@changesets/test-utils", but "@changesets/pre" is not being ignored. Please add "@changesets/pre" to the `ignore` option.
The package "@changesets/read" depends on the ignored package "@changesets/test-utils", but "@changesets/read" is not being ignored. Please add "@changesets/read" to the `ignore` option.
The package "@changesets/release-utils" depends on the ignored package "@changesets/test-utils", but "@changesets/release-utils" is not being ignored. Please add "@changesets/release-utils" to the `ignore` option.
The package "@changesets/should-skip-package" depends on the ignored package "@changesets/test-utils", but "@changesets/should-skip-package" is not being ignored. Please add "@changesets/should-skip-package" to the `ignore` option.
The package "@changesets/write" depends on the ignored package "@changesets/test-utils", but "@changesets/write" is not being ignored. Please add "@changesets/write" to the `ignore` option.
The package "@changesets/cli" depends on the ignored package "@changesets/color", but "@changesets/cli" is not being ignored. Please add "@changesets/cli" to the `ignore` option.
The package "@changesets/get-dependents-graph" depends on the ignored package "@changesets/color", but "@changesets/get-dependents-graph" is not being ignored. Please add "@changesets/get-dependents-graph" to the `ignore` option.
The package "@changesets/logger" depends on the ignored package "@changesets/color", but "@changesets/logger" is not being ignored. Please add "@changesets/logger" to the `ignore` option.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

❌ Patch coverage is 89.55224% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.88%. Comparing base (47e7c7d) to head (26135a6).
⚠️ Report is 5 commits behind head on next.

Files with missing lines Patch % Lines
packages/cli/src/commands/publish/index.ts 46.15% 6 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             next    #2009      +/-   ##
==========================================
- Coverage   80.94%   80.88%   -0.07%     
==========================================
  Files          54       53       -1     
  Lines        2362     2302      -60     
  Branches      722      710      -12     
==========================================
- Hits         1912     1862      -50     
+ Misses        408      389      -19     
- Partials       42       51       +9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@beeequeue beeequeue mentioned this pull request May 16, 2026
26 tasks
@beeequeue beeequeue added this to the v3 milestone May 16, 2026
Comment thread packages/cli/src/index.ts
Comment thread packages/cli/src/index.ts
Copy link
Copy Markdown
Member

@Andarist Andarist left a comment

Choose a reason for hiding this comment

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

I have not finished reviewing yet, but this one definitely feels blocking

Comment thread packages/cli/src/commands/version/index.ts
const cwd = options?.cwd ?? process.cwd();

const packages = await getPackages(cwd);
await ensureChangesetFolder(packages.rootDir);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

basically all commands (but init) need this - maybe instead of doing ensureChangesetFolder it would be good to push this into createCommand factory? that could accept an option to disable this requirement (just to support init).

The cwd + getPackages resolution could also be shoved there to be shared across all commands (those are actually resolved by all of them)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Similarly, the config is being resolved for all of the commands... minus init (duh) and 🥁 pre.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe we can make a resolveCommandContext(cwd, { ... }) function that returns the cwd, packages, config etc? I think it's nice if we can see the execution step-by-step instead of an abstraction.

But also I think the current step is fine for granular control, and repetition is sometimes fine, but it's not a strong pattern I'll insist on.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yeah, such heloer would work too 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it's what i did for my cleye and gunshi implementations as well

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I tried to implement this but in the end I've written more code for the function than it's worth. I think we should stick to what we have now and revisit again once we get the changesets config refactor in and the rootDir sorted out so we get a better picture of the abstraction.

Comment thread packages/cli/src/commands/add/index.ts
await getVersionableChangedPackages(config, {
cwd,
ref: since,
cwd: packages.rootDir,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wooooonder... what do we really need cwd for? Perhaps we should just always default to packages.rootDir? cwd is useful for the initial resolution of packages but I wonder if it's useful for anything else.

Just food for thought here, we certainly don't have to touch this in this PR

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It might have not been clear what I was alluding to here, so let me clarify - perhaps we should rename things to use rootDir name over cwd so it's more apparent what's that value and what's its origin. And pass packages.rootDir to those few places in which we actually still use the input cwd

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah I think we need to follow-up making cwd and rootDir separation clearer. It's a bit tricky for some @changesets/* packages because they're sometimes used directly and some people may expect those functions to auto-resolve to the root-dir as well. We should revisit for sure and decide how to handle this better.

Comment thread packages/cli/src/commands/status/index.ts Outdated
Comment thread packages/cli/src/index.ts Outdated
Comment on lines +56 to +60
if (options.ignore) {
options.ignore = Array.isArray(options.ignore)
? options.ignore
: [options.ignore];
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

note to myself: it would be nice to report the original issue related to this workaround to cac

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I ended up putting another fix where if a flag is passed multiple times (the option becomes an array), we pick the last value. And put these inside normalizeOptions. So even if this is fixed upstream, it might be better to stick with our little utility.

Note the flag array issue also happens in main with plain mri usage.

Comment thread packages/cli/src/index.ts
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be nice if we could get a test for this file that would test the discovered ignore thing and perhaps support for mixed snapshot boolean/string support. But it's not super important

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Agree, regression tests are awesome 😎

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I wrote some tests initially, exporting the cli which exposes some things to test, but since a lot of the handlers are thin and we should be trusting cac's behaviour, the tests ended up not useful I think. Probably useful now with some quirks we found from cac though.

@bluwy
Copy link
Copy Markdown
Member Author

bluwy commented May 17, 2026

I'll merge this in now, but I'll follow-up on the tests later as I'm thinking of moving some things around and I don't want to touch to much to review again.

@bluwy bluwy added this pull request to the merge queue May 17, 2026
Merged via the queue into next with commit 44df27d May 17, 2026
12 checks passed
@bluwy bluwy deleted the cac branch May 17, 2026 08:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants