Skip to content

Fix CLI symlink not updated after Studio app update - #3713

Merged
wojtekn merged 6 commits into
trunkfrom
fix-cli-symlink-after-update
Jun 10, 2026
Merged

Fix CLI symlink not updated after Studio app update#3713
wojtekn merged 6 commits into
trunkfrom
fix-cli-symlink-after-update

Conversation

@wojtekn

@wojtekn wojtekn commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Related issues

How AI was used in this PR

This PR was developed with Claude Code assistance.

Proposed Changes

After Studio is updated, the studio CLI command could continue running the binary from a previous app bundle. The root cause was that autoInstallIfNeeded used a single cliAutoInstalled flag to mean both "we've done first-time install" and "user deliberately uninstalled — don't reinstall". Once set, it prevented installCli() from ever running again, so a stale symlink would never get fixed.

This PR introduces a separate cliUserUninstalled flag that is explicitly set when the user uninstalls the CLI via Settings, and cleared when they reinstall it. autoInstallIfNeeded now skips only when cliUserUninstalled is true, and otherwise calls installCli() on every launch. Since installCli() already no-ops when the symlink is correct, this is safe and cheap — it only re-points the symlink when it's actually stale.

Testing Instructions

  • Install Studio, complete CLI installation
  • Uninstall CLI via Settings — confirm studio command is gone and re-launching Studio does not reinstall it
  • Reinstall CLI via Settings — confirm studio command works and re-launching Studio keeps it pointing to the correct app

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
@wojtekn

wojtekn commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

It seems it works:

  1. Remove symlink ~/.local/bin/studio
  2. Build Studio, open from out/, enable CLI in settings, close Studio
  3. Confirm the symlink was created and points to location in out/
% readlink ~/.local/bin/studio
/Users/USER/Sites/studio/apps/studio/out/make/zip/darwin/arm64/Studio.app/Contents/Resources/bin/studio-cli.sh
  1. Close Studio, move/copy it to /Applications/
  2. Open Studio from /Applications/
  3. Confirm the symlink resolves to the default location now:
% readlink ~/.local/bin/studio
/Applications/Studio.app/Contents/Resources/bin/studio-cli.sh

I thought I may be fixing an edge case that could happen only during development and testing local build, but it seems it can happen for the user, too:

  1. Download Studio, open DMG, drag and drop to the Downloads directory
  2. Open Studio, enable CLI, save
  3. Move Studio to /Applications/
  4. Symlink now points to a non-existent file and the studio command fails
@wojtekn
wojtekn marked this pull request as ready for review June 5, 2026 18:27
@wojtekn
wojtekn requested review from a team and fredrikekelund June 5, 2026 18:27

@bcotrim bcotrim left a comment

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.

Works as described.

I found one behavior change that may regress users who explicitly uninstalled the CLI before cliUserUninstalled existed.

With this state:

  • cliAutoInstalled: true
  • no cliUserUninstalled key
  • ~/.local/bin/studio absent

After launching this branch, Studio recreated ~/.local/bin/studio.

Was that intentional? This state is ambiguous, but it also matches the old “user uninstalled via Settings” state. The stale-bundle case should still have an existing symlink pointing to an old app path, so I wonder if absent symlink + legacy config should keep the CLI uninstalled.

@wojtekn

wojtekn commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@bcotrim thanks for testing it. I added handling for that case.

@wpmobilebot

wpmobilebot commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 9224436 vs trunk

app-size

Metric trunk 9224436 Diff Change
App Size (Mac) 1358.03 MB 1358.03 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 9224436 Diff Change
load 1632 ms 1686 ms +54 ms 🔴 3.3%

site-startup

Metric trunk 9224436 Diff Change
siteCreation 9040 ms 9035 ms 5 ms ⚪ 0.0%
siteStartup 4402 ms 4380 ms 22 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@wojtekn

wojtekn commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

I tested the main flow again, and it works fine:

% readlink ~/.local/bin/studio
/Users/USER/Sites/studio/apps/studio/out/Studio-darwin-arm64/Studio.app/Contents/Resources/bin/studio-cli.sh
% readlink ~/.local/bin/studio
/Applications/Studio.app/Contents/Resources/bin/studio-cli.sh
@wojtekn
wojtekn merged commit 13d87a1 into trunk Jun 10, 2026
11 checks passed
@wojtekn
wojtekn deleted the fix-cli-symlink-after-update branch June 10, 2026 06:43
@fredrikekelund

Copy link
Copy Markdown
Contributor

It took me a second to wrap my head around this, but if I understand it correctly, it's really meant to fix an issue where if you run a local dev build and install it, it might set up a weird symlink at ~/.local/bin compared to a production install.

This is kind of an edge case, but I don't have any strong objections to switching the paradigm to running an install on every launch unless we know the user explicitly uninstalled the CLI. However, I think we should consider removing userData.cliAutoInstalled entirely.

It looks like there's some data migration logic in there (and maybe that's the only real reason we kept that prop). If that's the case, I'd ask that we write an explicit data migration in apps/studio/src/migrations instead

@wojtekn

wojtekn commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

@fredrikekelund it could also happen for standard user:

  1. Download Studio, open DMG, drag and drop to the Downloads directory
  2. Open Studio, enable CLI, save
  3. Move Studio to /Applications/
  4. Symlink now points to a non-existent file and the studio command fails
@fredrikekelund

Copy link
Copy Markdown
Contributor

Right. I'd appreciate it if you could revisit the userData.cliAutoInstalled prop, though. I could be mistaken, but it doesn't appear to be needed anymore, and the CLI installation with the symlink chain is complex already.

@wojtekn

wojtekn commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Right. I'd appreciate it if you could revisit the userData.cliAutoInstalled prop, though. I could be mistaken, but it doesn't appear to be needed anymore, and the CLI installation with the symlink chain is complex already.

Good catch. We don't need to write it anymore, but we should support it to ensure we don't have to reinstall the CLI for users who uninstalled it in the past. I opened #3781

wojtekn added a commit that referenced this pull request Jun 17, 2026
…led (#3781)

## Related issues

- Follow-up to #3713

## How AI was used in this PR

Developed with Claude Code assistance.

## Proposed Changes

Now that `cliUserUninstalled` exists as an explicit signal,
`cliAutoInstalled` no longer serves an ongoing purpose. Its original
role — preventing re-installation after first launch — is fully handled
by `cliUserUninstalled` (absent = not uninstalled).

The only remaining use is a one-time migration: on first launch of the
new version, `cliAutoInstalled: true` + absent symlink/directory is
treated as a deliberate uninstall and sets `cliUserUninstalled: true`.
After that migration runs once per user, `cliAutoInstalled` is never
meaningfully consulted again.

This PR stops writing `cliAutoInstalled` on new installs and marks it
`@deprecated` in the storage type. The migration block still reads it,
so existing users who uninstalled before #3713 shipped are correctly
preserved. The flag and migration block can be removed entirely in a
follow-up release.

On Windows, this also fixes `updateWindowsCliVersionedPathIfNeeded()` to
run on every launch where the CLI is installed (not only when
`cliAutoInstalled` was set), keeping the proxy `.bat` path up to date
after app updates.

## Testing Instructions

- Launch Studio with `cliAutoInstalled: true` + symlink absent in
`app.json` → `cliUserUninstalled` should be set to `true`, CLI not
reinstalled
- Launch Studio with `cliAutoInstalled: true` + symlink present but
stale → symlink re-pointed to current app
- Fresh install (no flags set) → CLI installs as normal,
`cliAutoInstalled` is NOT written

## Pre-merge Checklist

- [ ] Have you checked for TypeScript, React or other console errors?

---------

Co-authored-by: Fredrik Rombach Ekelund <fredrik.rombach.ekelund@automattic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants