Skip to content

Commit

Permalink
Add wp-env After Setup Command (#50196)
Browse files Browse the repository at this point in the history
* Added `afterSetup` Root Config Option

This runs a user-provided script as a lifecycle hook
after WordPress setup completes.

* Moved Test Files

The `config` test files are under `lib/config` but the `lib`
test files are under the root. This moves them to be
consistent with the other test files.

* Enforced Strict Mode

* Execute `afterSetup` on `start` and `clean`

* Documented `afterSetup` Option
  • Loading branch information
ObliviousHarmony authored May 2, 2023
1 parent b2010ab commit 628a1f6
Show file tree
Hide file tree
Showing 43 changed files with 386 additions and 109 deletions.
7 changes: 7 additions & 0 deletions packages/env/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
- Docker containers now run as the host user. This should resolve problems with permissions arising from different owners
between the host, web container, and cli container. If you still encounter permissions issues, try running `npx wp-env destroy` so that the environment can be recreated with the correct permissions.

### New feature

- Create an `afterSetup` option in `.wp-env.json` files for setting arbitrary commands to run after setting up WordPress when using `wp-env start` and `wp-env clean`.
- Add a `WP_ENV_AFTER_SETUP` environment variable to override the `afterSetup` option.
- Execute the `afterSetup` command on `wp-env start` after the environment is set up. This can happen when your config changes, WordPress updates, or you pass the `--update` flag.
- Execute the `afterSetup` command on `wp-env clean`.

### Bug fix

- Ensure `wordpress`, `tests-wordpress`, `cli`, and `tests-cli` always build the correct Docker image.
Expand Down
70 changes: 50 additions & 20 deletions packages/env/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,15 +253,14 @@ The start command installs and initializes the WordPress environment, which incl
```sh
wp-env start

Starts WordPress for development on port 8888 (override with WP_ENV_PORT) and
tests on port 8889 (override with WP_ENV_TESTS_PORT). The current working
directory must be a WordPress installation, a plugin, a theme, or contain a
.wp-env.json file. After first install, use the '--update' flag to download
updates to mapped sources and to re-apply WordPress configuration options.
Starts WordPress for development on port 8888 (​http://localhost:8888​)
(override with WP_ENV_PORT) and tests on port 8889 (​http://localhost:8889​)
(override with WP_ENV_TESTS_PORT). The current working directory must be a
WordPress installation, a plugin, a theme, or contain a .wp-env.json file. After
first install, use the '--update' flag to download updates to mapped sources and
to re-apply WordPress configuration options.

Options:
--help Show help [boolean]
--version Show version number [boolean]
--debug Enable debug output. [boolean] [default: false]
--update Download source updates and apply WordPress configuration.
[boolean] [default: false]
Expand All @@ -270,6 +269,7 @@ Options:
them in a comma-separated list: `--xdebug=develop,coverage`. See
https://xdebug.org/docs/all_settings#mode for information about
Xdebug modes. [string]
--scripts Execute any configured lifecycle scripts. [boolean] [default: true]
```

### `wp-env stop`
Expand All @@ -278,6 +278,9 @@ Options:
wp-env stop

Stops running WordPress for development and tests and frees the ports.

Options:
--debug Enable debug output. [boolean] [default: false]
```

### `wp-env clean [environment]`
Expand All @@ -290,6 +293,10 @@ Cleans the WordPress databases.
Positionals:
environment Which environments' databases to clean.
[string] [choices: "all", "development", "tests"] [default: "tests"]
Options:
--debug Enable debug output. [boolean] [default: false]
--scripts Execute any configured lifecycle scripts. [boolean] [default: true]
```
### `wp-env run [container] [command]`
Expand Down Expand Up @@ -327,8 +334,6 @@ Positionals:
command The command to run. [array] [default: []]
Options:
--help Show help [boolean]
--version Show version number [boolean]
--debug Enable debug output. [boolean] [default: false]
--env-cwd The command's working directory inside of the container. Paths
without a leading slash are relative to the WordPress root.
Expand Down Expand Up @@ -397,6 +402,9 @@ wp-env destroy

Destroy the WordPress environment. Deletes docker containers, volumes, and
networks associated with the WordPress environment and removes local files.

Options:
--debug Enable debug output. [boolean] [default: false]
```

### `wp-env logs [environment]`
Expand All @@ -411,8 +419,6 @@ Positionals:
[string] [choices: "development", "tests", "all"] [default: "development"]

Options:
--help Show help [boolean]
--version Show version number [boolean]
--debug Enable debug output. [boolean] [default: false]
--watch Watch for logs as they happen. [boolean] [default: true]
```
Expand Down Expand Up @@ -514,9 +520,23 @@ These can be overridden by setting a value within the `config` configuration. Se

Additionally, the values referencing a URL include the specified port for the given environment. So if you set `testsPort: 3000, port: 2000`, `WP_HOME` (for example) will be `http://localhost:3000` on the tests instance and `http://localhost:2000` on the development instance.

### Examples
## Lifecycle Hooks

These hooks are executed at certain points during the lifecycle of a command's execution. Keep in mind that these will be executed on both fresh and existing
environments, so, ensure any commands you build won't break on subsequent executions.

### After Setup

Using the `afterSetup` option in `.wp-env.json` files will allow you to configure an arbitrary command to execute after the environment's setup is complete:

#### Latest stable WordPress + current directory as a plugin
- `wp-env start`: Runs when the config changes, WordPress updates, or you pass the `--update` flag.
- `wp-env clean`: Runs after the selected environments have been cleaned.

You can override the `afterSetup` option using the `WP_ENV_AFTER_SETUP` environment variable.

## Examples

### Latest stable WordPress + current directory as a plugin

This is useful for plugin development.

Expand All @@ -538,7 +558,7 @@ This is useful for plugin development when upstream Core changes need to be test
}
```

#### Local `wordpress-develop` + current directory as a plugin
### Local `wordpress-develop` + current directory as a plugin

This is useful for working on plugins and WordPress Core at the same time.

Expand All @@ -560,7 +580,7 @@ If you are running `wordpress-develop` in a dev mode (e.g. the watch command `de
}
```

#### A complete testing environment
### A complete testing environment

This is useful for integration testing: that is, testing how old versions of WordPress and different combinations of plugins and themes impact each other.

Expand All @@ -572,7 +592,7 @@ This is useful for integration testing: that is, testing how old versions of Wor
}
```

#### Add mu-plugins and other mapped directories
### Add mu-plugins and other mapped directories

You can add mu-plugins via the mapping config. The mapping config also allows you to mount a directory to any location in the wordpress install, so you could even mount a subdirectory. Note here that theme-1, will not be activated.

Expand All @@ -587,7 +607,7 @@ You can add mu-plugins via the mapping config. The mapping config also allows yo
}
```

#### Avoid activating plugins or themes on the instance
### Avoid activating plugins or themes on the instance

Since all plugins in the `plugins` key are activated by default, you should use the `mappings` key to avoid this behavior. This might be helpful if you have a test plugin that should not be activated all the time.

Expand All @@ -600,7 +620,7 @@ Since all plugins in the `plugins` key are activated by default, you should use
}
```

#### Map a plugin only in the tests environment
### Map a plugin only in the tests environment

If you need a plugin active in one environment but not the other, you can use `env.<envName>` to set options specific to one environment. Here, we activate cwd and a test plugin on the tests instance. This plugin is not activated on any other instances.

Expand All @@ -615,7 +635,7 @@ If you need a plugin active in one environment but not the other, you can use `e
}
```

#### Custom Port Numbers
### Custom Port Numbers

You can tell `wp-env` to use a custom port number so that your instance does not conflict with other `wp-env` instances.

Expand All @@ -631,7 +651,7 @@ You can tell `wp-env` to use a custom port number so that your instance does not
}
```

#### Specific PHP Version
### Specific PHP Version

You can tell `wp-env` to use a specific PHP version for compatibility and testing. This can also be set via the environment variable `WP_ENV_PHP_VERSION`.

Expand All @@ -642,6 +662,16 @@ You can tell `wp-env` to use a specific PHP version for compatibility and testin
}
```

### Node Lifecycle Script

This is useful for performing some actions after setting up the environment, such as bootstrapping an E2E test environment.

```json
{
"afterSetup": "node tests/e2e/bin/setup-env.js"
}
```

## Contributing to this package

This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects.
Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/cache.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* External dependencies
*/
Expand Down
17 changes: 15 additions & 2 deletions packages/env/lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ const withSpinner =
process.exit( 0 );
},
( error ) => {
if ( error instanceof env.ValidationError ) {
// Error is a validation error. That means the user did something wrong.
if (
error instanceof env.ValidationError ||
error instanceof env.AfterSetupError
) {
// Error is a configuration error. That means the user did something wrong.
spinner.fail( error.message );
process.exit( 1 );
} else if (
Expand Down Expand Up @@ -124,6 +127,11 @@ module.exports = function cli() {
coerce: parseXdebugMode,
type: 'string',
} );
args.option( 'scripts', {
type: 'boolean',
describe: 'Execute any configured lifecycle scripts.',
default: true,
} );
},
withSpinner( env.start )
);
Expand All @@ -145,6 +153,11 @@ module.exports = function cli() {
choices: [ 'all', 'development', 'tests' ],
default: 'tests',
} );
args.option( 'scripts', {
type: 'boolean',
describe: 'Execute any configured lifecycle scripts.',
default: true,
} );
},
withSpinner( env.clean )
);
Expand Down
15 changes: 14 additions & 1 deletion packages/env/lib/commands/clean.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* External dependencies
*/
Expand All @@ -8,6 +9,7 @@ const dockerCompose = require( 'docker-compose' );
*/
const initConfig = require( '../init-config' );
const { configureWordPress, resetDatabase } = require( '../wordpress' );
const { executeAfterSetup } = require( '../execute-after-setup' );

/**
* @typedef {import('../wordpress').WPEnvironment} WPEnvironment
Expand All @@ -20,9 +22,15 @@ const { configureWordPress, resetDatabase } = require( '../wordpress' );
* @param {Object} options
* @param {WPEnvironmentSelection} options.environment The environment to clean. Either 'development', 'tests', or 'all'.
* @param {Object} options.spinner A CLI spinner which indicates progress.
* @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
* @param {boolean} options.debug True if debug mode is enabled.
*/
module.exports = async function clean( { environment, spinner, debug } ) {
module.exports = async function clean( {
environment,
spinner,
scripts,
debug,
} ) {
const config = await initConfig( { spinner, debug } );

const description = `${ environment } environment${
Expand Down Expand Up @@ -57,5 +65,10 @@ module.exports = async function clean( { environment, spinner, debug } ) {

await Promise.all( tasks );

// Execute any configured command that should run after the environment has finished being set up.
if ( scripts ) {
executeAfterSetup( config, spinner );
}

spinner.text = `Cleaned ${ description }.`;
};
1 change: 1 addition & 0 deletions packages/env/lib/commands/destroy.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* External dependencies
*/
Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/commands/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* Internal dependencies
*/
Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/commands/install-path.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* Internal dependencies
*/
Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/commands/logs.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* External dependencies
*/
Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/commands/run.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* External dependencies
*/
Expand Down
18 changes: 16 additions & 2 deletions packages/env/lib/commands/start.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* External dependencies
*/
Expand Down Expand Up @@ -30,6 +31,7 @@ const {
} = require( '../wordpress' );
const { didCacheChange, setCache } = require( '../cache' );
const md5 = require( '../md5' );
const { executeAfterSetup } = require( '../execute-after-setup' );

/**
* @typedef {import('../config').WPConfig} WPConfig
Expand All @@ -41,11 +43,18 @@ const CONFIG_CACHE_KEY = 'config_checksum';
*
* @param {Object} options
* @param {Object} options.spinner A CLI spinner which indicates progress.
* @param {boolean} options.debug True if debug mode is enabled.
* @param {boolean} options.update If true, update sources.
* @param {string} options.xdebug The Xdebug mode to set.
* @param {boolean} options.scripts Indicates whether or not lifecycle scripts should be executed.
* @param {boolean} options.debug True if debug mode is enabled.
*/
module.exports = async function start( { spinner, debug, update, xdebug } ) {
module.exports = async function start( {
spinner,
update,
xdebug,
scripts,
debug,
} ) {
spinner.text = 'Reading configuration.';
await checkForLegacyInstall( spinner );

Expand Down Expand Up @@ -194,6 +203,11 @@ module.exports = async function start( { spinner, debug, update, xdebug } ) {
} ),
] );

// Execute any configured command that should run after the environment has finished being set up.
if ( scripts ) {
executeAfterSetup( config, spinner );
}

// Set the cache key once everything has been configured.
await setCache( CONFIG_CACHE_KEY, configHash, {
workDirectoryPath,
Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/commands/stop.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* External dependencies
*/
Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/config/add-or-replace-port.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
/**
* Internal dependencies
*/
Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/config/db-env.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict';
// Username and password used in all databases.
const credentials = {
WORDPRESS_DB_USER: 'root',
Expand Down
11 changes: 10 additions & 1 deletion packages/env/lib/config/get-config-from-environment-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const {
parseSourceString,
includeTestsPath,
} = require( './parse-source-string' );
const { checkPort, checkVersion } = require( './validate-config' );
const { checkPort, checkVersion, checkString } = require( './validate-config' );

/**
* @typedef {import('./parse-source-string').WPSource} WPSource
Expand Down Expand Up @@ -53,6 +53,15 @@ module.exports = function getConfigFromEnvironmentVars( cacheDirectoryPath ) {
environmentConfig.phpVersion = process.env.WP_ENV_PHP_VERSION;
}

if ( process.env.WP_ENV_AFTER_SETUP ) {
checkString(
'environment variable',
'WP_ENV_AFTER_SETUP',
process.env.WP_ENV_AFTER_SETUP
);
environmentConfig.afterSetup = process.env.WP_ENV_AFTER_SETUP;
}

return environmentConfig;
};

Expand Down
Loading

0 comments on commit 628a1f6

Please sign in to comment.