-
Notifications
You must be signed in to change notification settings - Fork 16
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
feat: Invoke wp-cli
from terminal sessions originating from Studio
#213
Conversation
b55df25
to
dd6d29f
Compare
wp-cli
from shell sessions originating from Studiowp-cli
from terminal sessions originating from Studio
@@ -66,7 +66,7 @@ export async function executeWPCli ( projectPath: string, args: string[] ): Prom | |||
|
|||
return { stdout: result.text.replace('#!/usr/bin/env php', '').trim(), stderr: result.errors }; | |||
} catch (error) { | |||
const errorContent = php.readFileAsText(stderrPath); | |||
const errorContent = php.readFileAsText(stderrPath).replace('PHP.run() output was: #!/usr/bin/env php', '').trim(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to match the stdout
changes above and avoid unnecessary noise in the logs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of this logic was inspired by a previous exploration in https://github.com/Automattic/local-environment/pull/174. Some of the logic was removed to simplify the source for the current feature scope.
@@ -21,7 +21,7 @@ import type { ForgeConfig } from '@electron-forge/shared-types'; | |||
const config: ForgeConfig = { | |||
packagerConfig: { | |||
asar: true, | |||
extraResource: [ './wp-files', './assets' ], | |||
extraResource: [ './wp-files', './assets', './bin' ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This copies both bin/wp
(macOS) and bin/wp.bat
(Windows). Ideally, we copy only the relevant file for the given platform.
When I was testing https://github.com/Automattic/local-environment/pull/174, I recall that I managed to use the development server by doing the following steps:
|
const php: NodePHP = await NodePHP.load(options.phpVersion,{ | ||
requestHandler: { | ||
documentRoot: options.documentRoot, | ||
absoluteUrl: options.absoluteUrl, | ||
} | ||
}); | ||
const [, php] = phpInstances; | ||
php.mount(projectPath, options.documentRoot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve wp-cli
performance by avoiding starting the site. In theory, we only need a PHP instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Love it!
We cannot modify the search path with environment variables alone, we must execute an arbitrary script.
For shell sessions originating from Studio, the bundled `wp-cli` should take precedence over a user's existing `wp-cli` installation.
Previously, apparent windows from previous Terminal sessions would linger and appear when launching a new Terminal.
The preceding `C:` was duplicated in the CLI path.
The script must contain the `.bat` file ending in order to run the script on Windows.
The internals now return a result rather than logging the result.
Match the approach for `stdout` and avoid displaying unnecessary noise.
The `executeWpCli` command relies upon `console.warn`, which resulted in unnecessary noise when invoking the `wp-cli` in the console.
This logic is likely unnecessary for the current usage with `wp-cli`.
Mimic core `wp-cli`'s behavior for the `help` command.
9e12aaf
to
4320082
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing Commands
I tested different commands on macOS and found that some don't work as expected:
wp cli info
: The execution freezes and doesn't respond.wp cron test
: Only works when the site is running.wp db create
: The execution freezes and doesn't respond.wp db check
: The execution freezes and doesn't respond.wp db cli
: The execution freezes and doesn't respond.wp db query < debug.sql
: Produces errorenv: mysql: No such file or directory
.wp eval 'echo WP_CONTENT_DIR;'
: Produces error:Error: Too many positional arguments: WP_CONTENT_DIR;
.wp eval-file eval.php
: Produces error:Error: 'eval.php' does not exist.
- It works when pointing to the server path:
wp eval-file /var/www/html/eval.php
- It works when pointing to the server path:
wp export
: Export process succeed but the file can't be located.- It works when pointing to the server path:
wp export --dir=/var/www/html
- It works when pointing to the server path:
wp i18n make-pot . languages/hello-dolly.pot
: Succeeds but the file can't be located.- It works when pointing to the server path:
wp i18n make-pot . /var/www/html/languages/hello-dolly.pot
- It works when pointing to the server path:
wp import --authors=create export.xml
: Can't find the file.- It works when pointing to the server path:
wp import --authors=create /var/www/html/export.xml
- It works when pointing to the server path:
wp menu create "My Menu"
: Produces errorError: Too many positional arguments: Menu"
.wp package install wp-cli/server-command
: Produces error:Error: The "https://wp-cli.org/package-index/packages.json" file could not be downloaded: allow_url_fopen must be enabled in php.ini (https:// wrapper is disabled in the server configuration by allow_url_fopen=0
.wp server
: The execution freezes and doesn't respond.wp shell
: Produces errorError: The shell binary '/bin/bash' is not valid. You can override the shell to be used through the WP_CLI_CUSTOM_SHELL environment variable.
.
I also noticed that commands that prompt user's input are quitted, not allowing the user to input anything.
Based on the above, I propose three items to investigate:
- For frozen commands, we need to figure out how to debug them. It's likely that something is failing and we don't see the error.
- String arguments (wrapped with
"
) are not processed properly. We'd need to review how they are passed towp-cli
. - File paths need to be prefixed with the document root (e.g.
/var/www/html
). The fact that you are in the site folder but can't point to files will be confusing to users. Besides, we need to warn users that only files within the site folder can be accessed (this is a limitation as we only mount this path).
UPDATE: I saw in the Playground documentation that it doesn't support the full list of wp-cli
commands. Probably some of the ones mentioned above (especially the ones that freeze) are likely not working due to this limitation.
Other Tests
Additionally, I tested changes introduced in #203 (using the test button) and confirmed WP-CLI commands can be executed and display the excepted output.
const php: NodePHP = await NodePHP.load(options.phpVersion,{ | ||
requestHandler: { | ||
documentRoot: options.documentRoot, | ||
absoluteUrl: options.absoluteUrl, | ||
} | ||
}); | ||
const [, php] = phpInstances; | ||
php.mount(projectPath, options.documentRoot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference, when we bump the @php-wasm/node
version, we'll need to update this part to conform this breaking change.
const php: NodePHP = await NodePHP.load(options.phpVersion,{ | ||
requestHandler: { | ||
documentRoot: options.documentRoot, | ||
absoluteUrl: options.absoluteUrl, | ||
} | ||
}); | ||
const [, php] = phpInstances; | ||
php.mount(projectPath, options.documentRoot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference, the project path is mounted following the current logic when running the site in WordPress mode:
studio/vendor/wp-now/src/wp-now.ts
Lines 186 to 190 in 4320082
async function runWordPressMode( | |
php: NodePHP, | |
{ documentRoot, projectPath, absoluteUrl }: WPNowOptions | |
) { | |
php.mount(projectPath, documentRoot); |
process.env.NODE_ENV !== 'development' && process.env.NODE_ENV !== 'test' && ! process.env.E2E, | ||
release: `${ app.getVersion() ? app.getVersion() : COMMIT_HASH }-${ getPlatformName() }`, | ||
} ); | ||
if ( ! isCLI() ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should enable Sentry in second instances (like when invoking the CLI) to be able to collect exceptions produced when executing CLI commands. I originally disabled this in the PoC because it was interfering with the testing, but might be interesting to consider it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's explore this option in a separate PR.
|
||
const commands = { | ||
wp: async () => { | ||
disableConsole(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be interesting to rewire console logs to the logging system instead of disabling it. This will facilitate debugging potential issues on CLI commands.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's explore this option in a separate PR.
// When running PHP on Playground, the value of constant PHP_OS is set to Linux. | ||
// This implies that platform-specific logic won't work as expected. To solve this, | ||
// we use an environment variable to ensure WP-CLI runs the Windows-specific logic. | ||
putenv( 'WP_CLI_TEST_IS_WINDOWS=${isWindows ? 1 : 0}' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This environment variable is mostly used by unit tests in wp-cli
. As described in the inline comment, it allows us to tell wp-cli
when the command is being executed in Windows. It should be enough as a temporary workaround but in the future, we should plan a more robust solution to match the value provided in PHP_OS
with the actual OS.
This library is used to parse the wp-cli arguments.
) | ||
|
||
SET COMMAND=%* | ||
SET CLI=wp %COMMAND% |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that running commands that include quotes in the arguments don't work on Windows. I tried different approaches but nonce seemed to work.
E.g.: wp eval 'echo WP_CONTENT_DIR;'
# Conflicts: # package-lock.json # package.json # src/__mocks__/electron.ts # src/index.ts
@@ -115,6 +116,7 @@ | |||
"hpagent": "1.2.0", | |||
"react-markdown": "^9.0.1", | |||
"semver": "^7.6.0", | |||
"shell-quote": "^1.8.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This package is used to parse the CLI arguments passed as a single string to an array of strings, as need to invoke wp-cli
.
After the fixes 8b9c2aa, 97a6a4d, and aa9d943, I reviewed the failed commands and some of them now work 🎊. Here's the list of pending commands to be addressed:
For some of the above errors, the logs are showing an error related to the File System but couldn't pinpoint the code that produces it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dcalhoun and @fluiddot I created a PR that fixes terminal issues on linux: #257
Note that this fix the execution on development as well by using the local running app.
However, I didn't made any fixes for MacOS and Windows, as I couldn't test it.
Other than that, it works as expected, besides the wp shell command ( at least for me ). Ignore the warnings in the image, it's my problem
Updated the PR, as discussed @fluiddot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested it on Mac and it works as expected.
From my understanding, this PR is adding to the terminal PATH a bash script that calls Studio app with the --cli
flag which calls executeWPCli
that runs php-wasm
importing the wp-cli.phar
that lives in server-files
library directory.
wp-cli-success.mp4
I also tested using the parameter --path
and it correctly detected the site.
I also tested executing a non existing command and it display the error correctly. Although the text was returned as stdout and not stderr. Left a comment to fix it.
Congrats for this piece of engineering 🥳
bin/wp
Outdated
@@ -0,0 +1,11 @@ | |||
if [ -z "$APP_PATH" ]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add STUDIO as a prefix to reduce the possibility of conflicting with other env variables?
if [ -z "$APP_PATH" ]; then | |
if [ -z "$STUDIO_APP_PATH" ]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, good idea. Better to use a unique name to avoid conflicts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion applied in 9a4253a.
const php: NodePHP = await NodePHP.load(options.phpVersion,{ | ||
requestHandler: { | ||
documentRoot: options.documentRoot, | ||
absoluteUrl: options.absoluteUrl, | ||
} | ||
}); | ||
const [, php] = phpInstances; | ||
php.mount(projectPath, options.documentRoot); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Love it!
// Handle CLI commands | ||
listenCLICommands(); | ||
executeCLICommand(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we wrap these calls inside if ( isCLI() )
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isCLI
function only checks if the current instance was launched with a CLI command. listenCLICommands
needs to be run on the main instance to set up the second-instance
event, which is invoked when a second instance is launched. Hence, it needs to be executed independently if a CLI command is passed in the main instance. I'd like to note that this scenario doesn't apply to wp-cli
commands as they are meant to be executed in the same instance they are triggered. However, in the future, it might be interesting to run commands on the first instance to manage sites.
Here's a diagram that explains the process:
Note
In the diagram, wp-cli
commands are considered standalone commands.
executeCLICommand
could be wrapped with if ( isCLI() )
but the function already checks if a CLI command exists.
try { | ||
const result = await executeWPCli( projectPath, argsSansPath ); | ||
if ( result.stderr ) { | ||
systemLog( result.stderr ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets declare const systemError = console.error;
and call it on error to produce the exit in the right "file descriptor" 2
.
systemLog( result.stderr ); | |
systemError( result.stderr ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if using console.error
is actually pointing to the stderr
file descriptor. In any case, I'm ok using the error function to log errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion applied in e20496f.
* Studio: Fix wp-cli command for linux * Update: fix variables per environment * Update: Fix wp script to handle commands on both MacOS and linux
I checked it again, after merging my linux fix and works as expected! Amazing work! |
# Conflicts: # src/ipc-handlers.ts
const exePath = app.getPath( 'exe' ); | ||
const appDirectory = app.getAppPath(); | ||
const appPath = | ||
process.env.NODE_ENV === 'development' ? `${ exePath } ${ appDirectory }` : exePath; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spotted that this condition should be ! app.isPackaged
because the Electron shell app is invoked when the app is not packaged, i.e. produces its own executable. I'll address it in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is addressed in #268.
Relates to https://github.com/Automattic/dotcom-forge/issues/7327.
Proposed Changes
Expose the bundled
wp-cli
for terminal sessions originating from Studio.Testing Instructions
1. Generic commands succeed
npm run package
out
directory.wp help
orwp --version
.wp-cli
subcommand outputs the expected information.2. Site-specific commands succeed
npm run package
out
directory.wp user list
.wp-cli
subcommand outputs the expected information forthe targeted site.
3. Studio's bundled
wp-cli
is preferred over globalwp-cli
installwp-cli
on your system.npm run package
out
directory.which wp
(macOS) orwhere wp
(Windows).wp-cli
.which wp
(macOS) orwhere wp
(Windows).wp-cli
, but the globally installedwp-cli
instead.Pre-merge Checklist