Something that has always annoyed me is how verbose it is to run WP-CLI commands in wp-env projects. For example, to list out all posts:
npm run wp-env run cli wp post list
Code language: Bash (bash)
The “npm run wp-env run cli
” part I have to copy for each command, and I often forget the right order or miss a component. It gets worse when needing to pass command line options to WP-CLI, because the options could inadvertently get consumed either by npm
or by wp-env
. For example, neither of these work as expected:
npm run wp-env run cli wp post list --help
Code language: Bash (bash)
Output
Run arbitrary package scripts
Usage:
npm run-script <command> [-- <args>]
Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root] [--if-present] [--ignore-scripts]
[--foreground-scripts] [--script-shell <script-shell>]
aliases: run, rum, urn
Run "npm help run-script" for more info
Code language: Shell Session (shell)
npm run -- wp-env run cli wp post list --help
Code language: Bash (bash)
Output
> wp-env
> wp-env run cli wp post list --help
wp-env run <container> [command...]
Runs an arbitrary command in one of the underlying Docker containers. A double
dash can be used to pass arguments to the container without parsing them. This
is necessary if you are using an option that is defined below. You can use
`bash` to open a shell session and both `composer` and `phpunit` are available
in all WordPress and CLI containers. WP-CLI is also available in the CLI
containers.
Positionals:
container The underlying Docker service to run the command on.
[string] [required] [choices: "mysql", "tests-mysql", "wordpress",
"tests-wordpress", "cli", "tests-cli"]
command The command to run. [array] [default: []]
Options:
--help Show help [boolean]
--debug Enable debug output. [boolean] [default: false]
--version Show version number [boolean]
--env-cwd The command's working directory inside of the container. Paths
without a leading slash are relative to the WordPress root.
[string] [default: "."]
--silent Whether to omit the command tip and spinner text.
[boolean] [default: false]
Code language: Shell Session (shell)
To get the help output for the wp post list
command, you need to add two instances of “--
” in the command:
npm run -- wp-env run cli -- wp post list --help
Code language: Bash (bash)
The verbosity gets even worse when needing to nest WP-CLI commands in subshells, for example to delete all posts of a given post type:
npm run -- wp-env run cli -- wp post delete --force $(
npm run --silent -- wp-env run cli --
wp post list --post_type=foo --format=ids 2>/dev/null)
Code language: Bash (bash)
Here the --silent
and 2>/dev/null
are both needed to remove extra output from being printed to prevent passing them into the wp post delete
command (with the first two lines coming from npm run
and suppressed by --silent
, and the remaining lines coming from wp-env run
:
> wp-env
> wp-env run cli -- wp post list --post_type=post --format=ids
ℹ Starting 'wp post list --post_type=post --format=ids' on the cli container.
...
✔ Ran `wp post list --post_type=post --format=ids` in 'cli'. (in 0s 683ms)
Code language: Shell Session (shell)
It would be nice if the wp-env run
command had an option to omit this verbosity (which I’ve proposed in #65015).
The Solution
To make this more ergonomic, I added a wp
wrapper script on my machine:
#!/bin/bash
npm run --silent -- wp-env run cli -- wp "$@" 2>/dev/null
Code language: Bash (bash)
I located this at /usr/local/bin/wp
and gave it the execute bit (chmod +x /usr/local/bin/wp
). Now when I want to delete all posts of a given post type, I can just do:
wp post delete --force $(wp post list --post_type=foo --format=ids)
Code language: Bash (bash)
Much nicer! It’s as if I’m not using wp-env at all. But then my teammate Felix Arntz raised what would happen if there was a global wp
command already for WP-CLI to manage sites not using wp-env. It would be ideal if it could detect whether you’re currently in a project using wp-env and route to the regular WP-CLI wp
global command when you aren’t. With some help from Gemini, I found this is doable by getting all instances of wp
on the $PATH
(via which -a
), omitting the current wp
, and then using the first remaining. Here’s the full script I’m using now:
#!/bin/bash
# WP-CLI `wp` command wrapper for wp-env
#
# Falls back to the global wp next on the PATH when not in
# a directory tree containing .wp-env.json.
# Save this as a file named `wp` in a directory first on
# the $PATH environment variable, with a secondary $PATH
# directory containing the global WP-CLI command.
is_wp_env() {
local current_dir="$PWD"
while [ "$current_dir" != '/' ]; do
if [ -f "$current_dir/.wp-env.json" ]; then
return 0
fi
current_dir="$(dirname "$current_dir")"
done
return 1
}
if is_wp_env; then
exec npm run --silent -- wp-env run cli -- wp "$@" 2>/dev/null
else
other_wp=$(which -a "$(basename "$0")" | grep -v "$0" | head -n 1)
if [ -z "$other_wp" ]; then
echo "Error: Not located inside a wp-env directory and no global wp command available." >&2
exit 1
else
exec "$other_wp" "$@"
fi
fi
Code language: Bash (bash)
Bonus: This also works with WP-CLI tab completion! However, given the additional latency of wp-env it’s not a particularly great experience.
Enjoy!