Skip to content

Commit

Permalink
wp-env: Add custom ports to .wp-env.json (#20158)
Browse files Browse the repository at this point in the history
Allows the user to set `port` and `testsPort` in .wp-env.json.

- Port must be a number
- Ports must be different
- Ports can still be overridden with the environment variables
  • Loading branch information
noahtallen authored Feb 13, 2020
1 parent 7891953 commit cf8fa0a
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 4 deletions.
4 changes: 4 additions & 0 deletions packages/env/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Master

### New Feature

- The `.wp-env.json` coniguration file now accepts `port` and `testsPort` options which can be used to set the ports on which the docker instance is mounted.

## 1.0.0 (2020-02-10)

### Breaking Changes
Expand Down
24 changes: 22 additions & 2 deletions packages/env/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ $ WP_ENV_PORT=3333 wp-env start

Running `docker ps` and inspecting the `PORTS` column allows you to determine which port `wp-env` is currently using.

You may also specify the port numbers in your `.wp-env.json` file, but the environment variables take precedent.

### 3. Restart `wp-env`

Restarting `wp-env` will restart the underlying Docker containers which can fix many issues.
Expand Down Expand Up @@ -175,15 +177,19 @@ Positionals:
You can customize the WordPress installation, plugins and themes that the development environment will use by specifying a `.wp-env.json` file in the directory that you run `wp-env` from.
`.wp-env.json` supports three fields:
`.wp-env.json` supports five fields:
| Field | Type | Default | Description |
| -- | -- | -- | -- |
| `"core"` | `string|null` | `null` | The WordPress installation to use. If `null` is specified, `wp-env` will use the latest production release of WordPress. |
| `"plugins"` | `string[]` | `[]` | A list of plugins to install and activate in the environment. |
| `"themes"` | `string[]` | `[]` | A list of themes to install in the environment. The first theme in the list will be activated. |
| `"port"` | `string` | `"8888"` | The primary port number to use for the insallation. You'll access the instance through the port: 'http://localhost:8888'. |
| `"testsPort"` | `string` | `"8889"` | The port number to use for the tests instance. |

_Note: the port number environment variables (`WP_ENV_PORT` and `WP_ENV_TESTS_PORT`) take precedent over the .wp-env.json values._

Several types of strings can be passed into these fields:
Several types of strings can be passed into the `core`, `plugins`, and `themes` fields:

| Type | Format | Example(s) |
| -- | -- | -- |
Expand Down Expand Up @@ -251,4 +257,18 @@ This is useful for integration testing: that is, testing how old versions of Wor
}
```
#### 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.
```json
{
"plugins": [
".",
],
"port": 4013,
"testsPort": 4012
}
```
<br/><br/><p align="center"><img src="https://s.w.org/style/images/codeispoetry.png?1" alt="Code is Poetry." /></p>
8 changes: 6 additions & 2 deletions packages/env/lib/build-docker-compose-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ module.exports = function buildDockerComposeConfig( config ) {
...themeMounts,
];

// Set the default ports based on the config values.
const developmentPorts = `\${WP_ENV_PORT:-${ config.port }}:80`;
const testsPorts = `\${WP_ENV_TESTS_PORT:-${ config.testsPort }}:80`;

return {
version: '3.7',
services: {
Expand All @@ -63,7 +67,7 @@ module.exports = function buildDockerComposeConfig( config ) {
wordpress: {
depends_on: [ 'mysql' ],
image: 'wordpress',
ports: [ '${WP_ENV_PORT:-8888}:80' ],
ports: [ developmentPorts ],
environment: {
WORDPRESS_DEBUG: '1',
WORDPRESS_DB_NAME: 'wordpress',
Expand All @@ -73,7 +77,7 @@ module.exports = function buildDockerComposeConfig( config ) {
'tests-wordpress': {
depends_on: [ 'mysql' ],
image: 'wordpress',
ports: [ '${WP_ENV_TESTS_PORT:-8889}:80' ],
ports: [ testsPorts ],
environment: {
WORDPRESS_DEBUG: '1',
WORDPRESS_DB_NAME: 'tests-wordpress',
Expand Down
24 changes: 24 additions & 0 deletions packages/env/lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const HOME_PATH_PREFIX = `~${ path.sep }`;
* @property {Source|null} coreSource The WordPress installation to load in the environment.
* @property {Source[]} pluginSources Plugins to load in the environment.
* @property {Source[]} themeSources Themes to load in the environment.
* @property {number} port The port on which to start the development WordPress environment.
* @property {number} testsPort The port on which to start the testing WordPress environment.
*/

/**
Expand Down Expand Up @@ -96,6 +98,8 @@ module.exports = {
core: null,
plugins: [],
themes: [],
port: 8888,
testsPort: 8889,
},
config
);
Expand Down Expand Up @@ -124,6 +128,24 @@ module.exports = {
);
}

if ( ! Number.isInteger( config.port ) ) {
throw new ValidationError(
'Invalid .wp-env.json: "port" must be an integer.'
);
}

if ( ! Number.isInteger( config.testsPort ) ) {
throw new ValidationError(
'Invalid .wp-env.json: "testsPort" must be an integer.'
);
}

if ( config.port === config.testsPort ) {
throw new ValidationError(
'Invalid .wp-env.json: "testsPort" and "port" must be different.'
);
}

const workDirectoryPath = path.resolve(
os.homedir(),
'.wp-env',
Expand All @@ -134,6 +156,8 @@ module.exports = {
name: path.basename( configDirectoryPath ),
configDirectoryPath,
workDirectoryPath,
port: config.port,
testsPort: config.testsPort,
dockerComposeConfigPath: path.resolve(
workDirectoryPath,
'docker-compose.yml'
Expand Down
61 changes: 61 additions & 0 deletions packages/env/test/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,65 @@ describe( 'readConfig', () => {
);
}
} );

it( 'should throw a validaton error if the ports are not numbers', async () => {
expect.assertions( 10 );
testPortNumberValidation( 'port', 'string' );
testPortNumberValidation( 'testsPort', [] );
testPortNumberValidation( 'port', {} );
testPortNumberValidation( 'testsPort', false );
testPortNumberValidation( 'port', null );
} );

it( 'should throw a validaton error if the ports are the same', async () => {
expect.assertions( 2 );
readFile.mockImplementation( () =>
Promise.resolve( JSON.stringify( { port: 8888, testsPort: 8888 } ) )
);
try {
await readConfig( '.wp-env.json' );
} catch ( error ) {
expect( error ).toBeInstanceOf( ValidationError );
expect( error.message ).toContain(
'Invalid .wp-env.json: "testsPort" and "port" must be different.'
);
}
} );

it( 'should parse custom ports', async () => {
readFile.mockImplementation( () =>
Promise.resolve(
JSON.stringify( {
port: 1000,
} )
)
);
const config = await readConfig( '.wp-env.json' );
// Custom port is overriden while testsPort gets the deault value.
expect( config ).toMatchObject( {
port: 1000,
testsPort: 8889,
} );
} );
} );

/**
* Tests that readConfig will throw errors when invalid port numbers are passed.
*
* @param {string} portName The name of the port to test ('port' or 'testsPort')
* @param {any} value A value which should throw an error.
*/
async function testPortNumberValidation( portName, value ) {
readFile.mockImplementation( () =>
Promise.resolve( JSON.stringify( { [ portName ]: value } ) )
);
try {
await readConfig( '.wp-env.json' );
} catch ( error ) {
expect( error ).toBeInstanceOf( ValidationError );
expect( error.message ).toContain(
`Invalid .wp-env.json: "${ portName }" must be an integer.`
);
}
jest.clearAllMocks();
}

0 comments on commit cf8fa0a

Please sign in to comment.