Skip to content

Always use LOCALAPPDATA to generate Windows CLI install path#2465

Draft
fredrikekelund wants to merge 5 commits intotrunkfrom
stu-1249-fix-cli-installation-appx
Draft

Always use LOCALAPPDATA to generate Windows CLI install path#2465
fredrikekelund wants to merge 5 commits intotrunkfrom
stu-1249-fix-cli-installation-appx

Conversation

@fredrikekelund
Copy link
Contributor

Related issues

Proposed Changes

As noted by @gcsecsey in #2463 (comment), CLI installation doesn't work when the app was installed using an AppX installer on Windows.

When Studio installs the CLI on Windows, it writes a "proxy studio.bat file" to a directory relative to its installation directory (as determined by the value Electron returns from app.getPath( 'exe' ). However, Microsoft Store installs Studio in a directory like C:\Program Files\WindowsApps\22490Automattic.StudiobyWordPress.com_1.6.8.0_arm64__9h07f78gwnchp that has heavy read and write limitations. Therefore, we believe the CLI installation fails because Studio cannot write the file.

This PR solves the problem by always writing the proxy batch file to a known stable location in %LOCALAPPDATA%.

Testing Instructions

I haven't figured out how to install an AppX dev build on Windows, so code review might have to suffice

Pre-merge Checklist

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

@fredrikekelund fredrikekelund requested review from a team and gcsecsey January 23, 2026 13:15
@fredrikekelund fredrikekelund self-assigned this Jan 23, 2026
@fredrikekelund
Copy link
Contributor Author

This Sentry issue confirms our theory. It was triggered by me and the error message reads:

Error: EPERM: operation not permitted, mkdir 'C:\Program Files\WindowsApps\22490Automattic.StudiobyWordPress.com_1.6.8.0_arm64__9h07f78gwnchp\bin'

Comment on lines +25 to +27
if ( ! process.env.LOCALAPPDATA ) {
throw new Error( 'LOCALAPPDATA environment variable is not set' );
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

An obvious question is, how do we know that process.env.LOCALAPPDATA is defined? Well, there's no official guarantee, and I can't find any Microsoft documentation on this, but it appears the system sets this variable for each process. It's not editable by users either, AFAICT. That's probably as good a guarantee as we'll get.

Still, it makes sense to ensure that we handle errors thrown from here gracefully, so I'll take a look at that now…

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 in b942405

@wpmobilebot
Copy link

wpmobilebot commented Jan 23, 2026

📊 Performance Test Results

Comparing 61e372a vs trunk

site-editor

Metric trunk 61e372a Diff Change
load 2863.00 ms 2887.00 ms +24.00 ms 🔴 0.8%

site-startup

Metric trunk 61e372a Diff Change
siteCreation 7104.00 ms 7072.00 ms -32.00 ms 🟢 -0.5%
siteStartup 3923.00 ms 3933.00 ms +10.00 ms 🔴 0.3%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change

@fredrikekelund fredrikekelund mentioned this pull request Jan 23, 2026
1 task
Copy link
Contributor

@gcsecsey gcsecsey left a comment

Choose a reason for hiding this comment

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

This LGTM and I can consistently see the toggle working correctly now, both on Parallels and on x64. 👍

When trying to open a Command Prompt using the button on the site details page, I see an Access denied message when I try to use the studio command.

Image

Opening a new tab and using the command in a different directory also prints the same message:

Image

The contents of the bat file seems correct to me, I think the issue might be that we don't have rights to run the batch file under WindowsApps:

@echo off
"C:\Program Files\WindowsApps\22490Automattic.StudiobyWordPress.com_1.6.8.0_arm64__9qb1eemr42qj0\app\resources\bin\studio-cli.bat" %*

@fredrikekelund
Copy link
Contributor Author

I think the issue might be that we don't have rights to run the batch file under WindowsApps

That seems like a good theory. We probably lack permission to execute that file (but we can read it).

I'm not immediately sure what the best way to solve that is…

@fredrikekelund fredrikekelund marked this pull request as draft January 27, 2026 14:53
@gcsecsey
Copy link
Contributor

gcsecsey commented Feb 4, 2026

@fredrikekelund I dug a bit deeper into this, and added a commit that fixes the CLI for the AppX version as far as I could test.

The Windows-recommended solution is https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap5-appexecutionalias. When declared in the manifest, this creates a lightweight stub in %LOCALAPPDATA%\Microsoft\WindowsApps\, which activates the package through the container APIs. The big downside is that this approach only supports .exe files, so we need to create/maintain a proxy binary for AppX versions only.

The commit adds:

  • bin/studio-cli-launcher.js — a small script that does what studio-cli.bat does (finds bundled node.exe and cli/main.js, spawns the CLI). This is compiled to a standalone .exe via @yao-pkg/pkg during the prePackage build step.
  • scripts/package-appx.mjs — adds the uap5:AppExecutionAlias extension to the AppxManifest, pointing at studio-cli.exe with alias studio.exe.

I could test it on Windows by removing all previous installations and also uninstalling the CLI from the dev folder using the Settings dialog. Then, downloading the appx artifact from the build and installing it, invoking the studio command from the terminal works as expected.

The where command now prints the path of the main executable, but the CLI features still seem to work normally, even when Studio is not launched:
CleanShot 2026-02-04 at 15 32 03@2x

To package the exe, I also needed to add a new dependency. The alternative would be to use Node's Single Executable Application feature to package the .exe. However, it would produce a larger binary (~70-80MB vs ~40-50MB from pkg) and requires a more complex multi-step build (generate blob, copy node.exe, inject blob, remove signature, re-sign). Still, it could be worth revisiting if we want to drop the @yao-pkg/pkg dependency.

What do you think about this approach?

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