@wordpress/env 5.0.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 5.2.0 (2022-08-16)
6
+
7
+ ### Enhancement
8
+ - Query parameters can now be used in .zip source URLs.
9
+
10
+ ## 5.1.1 (2022-08-16)
11
+
12
+ ### Bug Fix
13
+ - Fix a crash when "core" was set to `null` in a `.wp-env.json` file. We now use the latest stable WordPress version in that case. This also restores the previous behavior of `"core": null` in `.wp-env.override.json`, which was to use the latest stable WordPress version.
14
+
15
+ ## 5.1.0 (2022-08-10)
16
+
17
+ ### Enhancement
18
+ - Previously, wp-env used the WordPress version provided by Docker in the WordPress image for installations which don't specify a WordPress version. Now, wp-env will find the latest stable version on WordPress.org and check out the https://github.com/WordPress/WordPress repository at the tag matching that version. In most cases, this will match what Docker provides. The benefit is that wp-env (and WordPress.org) now controls the default WordPress version rather than Docker.
19
+
20
+ ### Bug Fix
21
+ - Downloading a default WordPress version also resolves a bug where the wrong WordPress test files were used if no core source was specified in wp-env.json. The current trunk test files were downloaded rather than the stable version. Now, the test files will match the default stable version.
22
+
5
23
  ## 5.0.0 (2022-07-27)
6
24
 
7
25
  ### Breaking Changes
package/README.md CHANGED
@@ -16,7 +16,7 @@ The local environment will be available at http://localhost:8888 (Username: `adm
16
16
 
17
17
  ## Prerequisites
18
18
 
19
- `wp-env` requires Docker to be installed. There are instructions available for installing Docker on [Windows 10 Pro](https://docs.docker.com/docker-for-windows/install/), [all other versions of Windows](https://docs.docker.com/toolbox/toolbox_install_windows/), [macOS](https://docs.docker.com/docker-for-mac/install/), and [Linux](https://docs.docker.com/v17.12/install/linux/docker-ce/ubuntu/#install-using-the-convenience-script).
19
+ `wp-env` requires Docker to be installed. There are instructions available for installing Docker on [Windows](https://docs.docker.com/desktop/install/windows-install/), [macOS](https://docs.docker.com/docker-for-mac/install/), and [Linux](https://docs.docker.com/desktop/install/linux-install/).
20
20
 
21
21
  Node.js and NPM are required. The latest LTS version of Node.js is used to develop `wp-env` and is recommended.
22
22
 
@@ -40,7 +40,9 @@ If your project already has a package.json, it's also possible to use `wp-env` a
40
40
  $ npm i @wordpress/env --save-dev
41
41
  ```
42
42
 
43
- Then modify your package.json and add an extra command to npm `scripts` (https://docs.npmjs.com/misc/scripts):
43
+ At this point, you can use the local, project-level version of wp-env via [`npx`](https://www.npmjs.com/package/npx), a utility automatically installed with `npm`.`npx` finds binaries like wp-env installed through node modules. As an example: `npx wp-env start --update`.
44
+
45
+ If you don't wish to use `npx`, modify your package.json and add an extra command to npm `scripts` (https://docs.npmjs.com/misc/scripts):
44
46
 
45
47
  ```json
46
48
  "scripts": {
@@ -51,7 +53,7 @@ Then modify your package.json and add an extra command to npm `scripts` (https:/
51
53
  When installing `wp-env` in this way, all `wp-env` commands detailed in these docs must be prefixed with `npm run`, for example:
52
54
 
53
55
  ```sh
54
- # You must add another double dash to pass the "update" flag to wp-env
56
+ # You must add another double dash to pass flags to the script (wp-env) rather than to npm itself
55
57
  $ npm run wp-env start -- --update
56
58
  ```
57
59
 
@@ -117,15 +119,14 @@ Running `docker ps` and inspecting the `PORTS` column allows you to determine wh
117
119
 
118
120
  You may also specify the port numbers in your `.wp-env.json` file, but the environment variables take precedent.
119
121
 
120
- ### 3. Restart `wp-env`
122
+ ### 3. Restart `wp-env` with updates
121
123
 
122
124
  Restarting `wp-env` will restart the underlying Docker containers which can fix many issues.
123
125
 
124
- To restart `wp-env`:
126
+ To restart `wp-env`, just run `wp-env start` again. It will automatically stop and start the container. If you also pass the `--update` argument, it will download updates and configure WordPress agian.
125
127
 
126
128
  ```sh
127
- $ wp-env stop
128
- $ wp-env start
129
+ $ wp-env start --update
129
130
  ```
130
131
 
131
132
  ### 4. Restart Docker
@@ -156,16 +157,17 @@ $ wp-env clean all
156
157
  $ wp-env start
157
158
  ```
158
159
 
159
- ### 6. Nuke everything and start again 🔥
160
+ ### 6. Destroy everything and start again 🔥
160
161
 
161
- When all else fails, you can use `wp-env destroy` to forcibly remove all of the underlying Docker containers and volumes. This will allow you to start from scratch.
162
+ When all else fails, you can use `wp-env destroy` to forcibly remove all of the underlying Docker containers, volumes, and files. This will allow you to start from scratch.
162
163
 
163
- To nuke everything:
164
+ To do so:
164
165
 
165
166
  **⚠️ WARNING: This will permanently delete any posts, pages, media, etc. in the local WordPress installation.**
166
167
 
167
168
  ```sh
168
169
  $ wp-env destroy
170
+ # This new instance is a fresh start with no existing data:
169
171
  $ wp-env start
170
172
  ```
171
173
 
@@ -211,10 +213,12 @@ wp-env start
211
213
  wp-env start --xdebug=profile,trace,debug
212
214
  ```
213
215
 
214
- When you're running `wp-env` using `npm run`, like when working in the Gutenberg repo or when having `wp-env` as a local project dependency, don't forget to add an extra double dash before the `--xdebug` command:
216
+ When you're running `wp-env` using `npm run`, like when working in the Gutenberg repo or when `wp-env` is a local project dependency, don't forget to add an extra double dash before the `--xdebug` command:
215
217
 
216
218
  ```sh
217
219
  npm run wp-env start -- --xdebug
220
+ # Alternatively, use npx:
221
+ npx wp-env start --xdebug
218
222
  ```
219
223
 
220
224
  If you forget about that, the `--xdebug` parameter will be passed to NPM instead of the `wp-env start` command and it will be ignored.
@@ -532,7 +536,7 @@ Additionally, the values referencing a URL include the specified port for the gi
532
536
 
533
537
  ### Examples
534
538
 
535
- #### Latest production WordPress + current directory as a plugin
539
+ #### Latest stable WordPress + current directory as a plugin
536
540
 
537
541
  This is useful for plugin development.
538
542
 
@@ -33,7 +33,7 @@ const md5 = require( '../md5' );
33
33
  * Base-level config for any particular environment. (development/tests/etc)
34
34
  *
35
35
  * @typedef WPServiceConfig
36
- * @property {?WPSource} coreSource The WordPress installation to load in the environment.
36
+ * @property {WPSource} coreSource The WordPress installation to load in the environment.
37
37
  * @property {WPSource[]} pluginSources Plugins to load in the environment.
38
38
  * @property {WPSource[]} themeSources Themes to load in the environment.
39
39
  * @property {number} port The port to use.
@@ -68,9 +68,25 @@ module.exports = async function readConfig( configPath ) {
68
68
  md5( configPath )
69
69
  );
70
70
 
71
+ // The specified base configuration from .wp-env.json or from the local
72
+ // source type which was automatically detected.
73
+ const baseConfig =
74
+ ( await readRawConfigFile( '.wp-env.json', configPath ) ) ||
75
+ ( await getDefaultBaseConfig( configPath ) );
76
+
77
+ // Overriden .wp-env.json on a per-user case.
78
+ const overrideConfig =
79
+ ( await readRawConfigFile(
80
+ '.wp-env.override.json',
81
+ configPath.replace( /\.wp-env\.json$/, '.wp-env.override.json' )
82
+ ) ) || {};
83
+
84
+ const detectedLocalConfig =
85
+ Object.keys( { ...baseConfig, ...overrideConfig } ).length > 0;
86
+
71
87
  // Default configuration which is overridden by .wp-env.json files.
72
88
  const defaultConfiguration = {
73
- core: null,
89
+ core: null, // Indicates that the latest stable version should ultimately be used.
74
90
  phpVersion: null,
75
91
  plugins: [],
76
92
  themes: [],
@@ -96,22 +112,6 @@ module.exports = async function readConfig( configPath ) {
96
112
  },
97
113
  };
98
114
 
99
- // The specified base configuration from .wp-env.json or from the local
100
- // source type which was automatically detected.
101
- const baseConfig =
102
- ( await readRawConfigFile( '.wp-env.json', configPath ) ) ||
103
- ( await getDefaultBaseConfig( configPath ) );
104
-
105
- // Overriden .wp-env.json on a per-user case.
106
- const overrideConfig =
107
- ( await readRawConfigFile(
108
- '.wp-env.override.json',
109
- configPath.replace( /\.wp-env\.json$/, '.wp-env.override.json' )
110
- ) ) || {};
111
-
112
- const detectedLocalConfig =
113
- Object.keys( { ...baseConfig, ...overrideConfig } ).length > 0;
114
-
115
115
  // A quick validation before merging on a service by service level allows us
116
116
  // to check the root configuration options and provide more helpful errors.
117
117
  validateConfig(
@@ -142,23 +142,21 @@ module.exports = async function readConfig( configPath ) {
142
142
 
143
143
  // Merge each of the specified environment-level overrides.
144
144
  const allPorts = new Set(); // Keep track of unique ports for validation.
145
- const env = allEnvs.reduce( ( result, environment ) => {
146
- result[ environment ] = parseConfig(
145
+ const env = {};
146
+ for ( const envName of allEnvs ) {
147
+ env[ envName ] = await parseConfig(
147
148
  validateConfig(
148
149
  mergeWpServiceConfigs( [
149
- ...getEnvConfig( defaultConfiguration, environment ),
150
- ...getEnvConfig( baseConfig, environment ),
151
- ...getEnvConfig( overrideConfig, environment ),
150
+ ...getEnvConfig( defaultConfiguration, envName ),
151
+ ...getEnvConfig( baseConfig, envName ),
152
+ ...getEnvConfig( overrideConfig, envName ),
152
153
  ] ),
153
- environment
154
+ envName
154
155
  ),
155
- {
156
- workDirectoryPath,
157
- }
156
+ { workDirectoryPath }
158
157
  );
159
- allPorts.add( result[ environment ].port );
160
- return result;
161
- }, {} );
158
+ allPorts.add( env[ envName ].port );
159
+ }
162
160
 
163
161
  if ( allPorts.size !== allEnvs.length ) {
164
162
  throw new ValidationError(
@@ -249,6 +247,7 @@ async function getDefaultBaseConfig( configPath ) {
249
247
  * @return {WPConfig} configuration object with overrides applied.
250
248
  */
251
249
  function withOverrides( config ) {
250
+ const workDirectoryPath = config.workDirectoryPath;
252
251
  // Override port numbers with environment variables.
253
252
  config.env.development.port =
254
253
  getNumberFromEnvVariable( 'WP_ENV_PORT' ) ||
@@ -260,10 +259,8 @@ function withOverrides( config ) {
260
259
  // Override WordPress core with environment variable.
261
260
  if ( process.env.WP_ENV_CORE ) {
262
261
  const coreSource = includeTestsPath(
263
- parseSourceString( process.env.WP_ENV_CORE, {
264
- workDirectoryPath: config.workDirectoryPath,
265
- } ),
266
- { workDirectoryPath: config.workDirectoryPath }
262
+ parseSourceString( process.env.WP_ENV_CORE, { workDirectoryPath } ),
263
+ { workDirectoryPath }
267
264
  );
268
265
  config.env.development.coreSource = coreSource;
269
266
  config.env.tests.coreSource = coreSource;
@@ -9,6 +9,7 @@ const os = require( 'os' );
9
9
  * Internal dependencies
10
10
  */
11
11
  const { ValidationError } = require( './validate-config' );
12
+ const { getLatestWordPressVersion } = require( '../wordpress' );
12
13
 
13
14
  /**
14
15
  * @typedef {import('./config').WPServiceConfig} WPServiceConfig
@@ -32,12 +33,12 @@ const HOME_PATH_PREFIX = `~${ path.sep }`;
32
33
  * @param {string} options.workDirectoryPath Path to the work directory located in ~/.wp-env.
33
34
  * @return {WPServiceConfig} Parsed environment-level configuration.
34
35
  */
35
- module.exports = function parseConfig( config, options ) {
36
+ module.exports = async function parseConfig( config, options ) {
36
37
  return {
37
38
  port: config.port,
38
39
  phpVersion: config.phpVersion,
39
40
  coreSource: includeTestsPath(
40
- parseSourceString( config.core, options ),
41
+ await parseCoreSource( config.core, options ),
41
42
  options
42
43
  ),
43
44
  pluginSources: config.plugins.map( ( sourceString ) =>
@@ -58,6 +59,21 @@ module.exports = function parseConfig( config, options ) {
58
59
  };
59
60
  };
60
61
 
62
+ async function parseCoreSource( coreSource, options ) {
63
+ // An empty source means we should use the latest version of WordPress.
64
+ if ( ! coreSource ) {
65
+ const wpVersion = await getLatestWordPressVersion();
66
+ if ( ! wpVersion ) {
67
+ throw new ValidationError(
68
+ 'Could not find the latest WordPress version. There may be a network issue.'
69
+ );
70
+ }
71
+
72
+ coreSource = `WordPress/WordPress#${ wpVersion }`;
73
+ }
74
+ return parseSourceString( coreSource, options );
75
+ }
76
+
61
77
  /**
62
78
  * Parses a source string into a source object.
63
79
  *
@@ -95,7 +111,7 @@ function parseSourceString( sourceString, { workDirectoryPath } ) {
95
111
  }
96
112
 
97
113
  const zipFields = sourceString.match(
98
- /^https?:\/\/([^\s$.?#].[^\s]*)\.zip$/
114
+ /^https?:\/\/([^\s$.?#].[^\s]*)\.zip(\?.+)?$/
99
115
  );
100
116
 
101
117
  if ( zipFields ) {
@@ -21,7 +21,6 @@ Object {
21
21
  "WP_TESTS_EMAIL": "admin@example.org",
22
22
  "WP_TESTS_TITLE": "Test Blog",
23
23
  },
24
- "coreSource": null,
25
24
  "mappings": Object {},
26
25
  "phpVersion": null,
27
26
  "pluginSources": Array [],
@@ -44,7 +43,6 @@ Object {
44
43
  "WP_TESTS_EMAIL": "admin@example.org",
45
44
  "WP_TESTS_TITLE": "Test Blog",
46
45
  },
47
- "coreSource": null,
48
46
  "mappings": Object {},
49
47
  "phpVersion": null,
50
48
  "pluginSources": Array [],
@@ -19,6 +19,23 @@ jest.mock( 'fs', () => ( {
19
19
  },
20
20
  } ) );
21
21
 
22
+ // This mocks a small response with a format matching the stable-check API.
23
+ // It makes getLatestWordPressVersion resolve to "100.0.0".
24
+ jest.mock( 'got', () =>
25
+ jest.fn( ( url ) => ( {
26
+ json: () => {
27
+ if ( url === 'https://api.wordpress.org/core/stable-check/1.0/' ) {
28
+ return Promise.resolve( {
29
+ '1.0': 'insecure',
30
+ '99.1.1': 'outdated',
31
+ '100.0.0': 'latest',
32
+ '100.0.1': 'fancy',
33
+ } );
34
+ }
35
+ },
36
+ } ) )
37
+ );
38
+
22
39
  jest.mock( '../detect-directory-type', () => jest.fn() );
23
40
 
24
41
  describe( 'readConfig', () => {
@@ -59,19 +76,38 @@ describe( 'readConfig', () => {
59
76
  );
60
77
  detectDirectoryType.mockImplementation( () => 'core' );
61
78
  const config = await readConfig( '.wp-env.json' );
62
- expect( config.env.development.coreSource ).not.toBeNull();
79
+ expect( config.env.development.coreSource.type ).toBe( 'local' );
63
80
  expect( config.env.tests.coreSource ).not.toBeNull();
64
81
  expect( config.env.development.pluginSources ).toHaveLength( 0 );
65
82
  expect( config.env.development.themeSources ).toHaveLength( 0 );
66
83
  } );
67
84
 
85
+ it( 'should use the most recent stable WordPress version for the default core source', async () => {
86
+ readFile.mockImplementation( () =>
87
+ Promise.resolve( JSON.stringify( {} ) )
88
+ );
89
+ const config = await readConfig( '.wp-env.json' );
90
+
91
+ const expected = {
92
+ url: 'https://github.com/WordPress/WordPress.git',
93
+ type: 'git',
94
+ basename: 'WordPress',
95
+ ref: '100.0.0', // From the mock of https at the top of the file.
96
+ };
97
+
98
+ expect( config.env.development.coreSource ).toMatchObject(
99
+ expected
100
+ );
101
+ expect( config.env.tests.coreSource ).toMatchObject( expected );
102
+ } );
103
+
68
104
  it( 'should infer a plugin config when ran from a plugin directory', async () => {
69
105
  readFile.mockImplementation( () =>
70
106
  Promise.reject( { code: 'ENOENT' } )
71
107
  );
72
108
  detectDirectoryType.mockImplementation( () => 'plugin' );
73
109
  const config = await readConfig( '.wp-env.json' );
74
- expect( config.env.development.coreSource ).toBeNull();
110
+ expect( config.env.development.coreSource.type ).toBe( 'git' );
75
111
  expect( config.env.development.pluginSources ).toHaveLength( 1 );
76
112
  expect( config.env.tests.pluginSources ).toHaveLength( 1 );
77
113
  expect( config.env.development.themeSources ).toHaveLength( 0 );
@@ -83,8 +119,8 @@ describe( 'readConfig', () => {
83
119
  );
84
120
  detectDirectoryType.mockImplementation( () => 'theme' );
85
121
  const config = await readConfig( '.wp-env.json' );
86
- expect( config.env.development.coreSource ).toBeNull();
87
- expect( config.env.tests.coreSource ).toBeNull();
122
+ expect( config.env.development.coreSource.type ).toBe( 'git' );
123
+ expect( config.env.tests.coreSource.type ).toBe( 'git' );
88
124
  expect( config.env.development.themeSources ).toHaveLength( 1 );
89
125
  expect( config.env.tests.themeSources ).toHaveLength( 1 );
90
126
  expect( config.env.development.pluginSources ).toHaveLength( 0 );
@@ -197,6 +233,11 @@ describe( 'readConfig', () => {
197
233
  // Remove generated values which are different on other machines.
198
234
  delete config.dockerComposeConfigPath;
199
235
  delete config.workDirectoryPath;
236
+
237
+ // This encodes both the version of WordPress (which can change frequently)
238
+ // as well as the wp-env directory which is unique on every machine.
239
+ delete config.env.development.coreSource;
240
+ delete config.env.tests.coreSource;
200
241
  expect( config ).toMatchSnapshot();
201
242
  } );
202
243
  } );
@@ -485,6 +526,7 @@ describe( 'readConfig', () => {
485
526
  plugins: [
486
527
  'https://www.example.com/test/path/to/gutenberg.zip',
487
528
  'https://www.example.com/test/path/to/gutenberg.8.1.0.zip',
529
+ 'https://www.example.com/test/path/to/gutenberg.8.1.0.zip?auth=thisIsAString&token=secondString',
488
530
  'https://www.example.com/test/path/to/twentytwenty.zip',
489
531
  'https://www.example.com/test/path/to/twentytwenty.1.3.zip',
490
532
  'https://example.com/twentytwenty.1.3.zip',
@@ -509,6 +551,14 @@ describe( 'readConfig', () => {
509
551
  ),
510
552
  basename: 'gutenberg.8.1.0',
511
553
  },
554
+ {
555
+ type: 'zip',
556
+ url: 'https://www.example.com/test/path/to/gutenberg.8.1.0.zip?auth=thisIsAString&token=secondString',
557
+ path: expect.stringMatching(
558
+ /^(\/|\\).*gutenberg.8.1.0$/
559
+ ),
560
+ basename: 'gutenberg.8.1.0',
561
+ },
512
562
  {
513
563
  type: 'zip',
514
564
  url: 'https://www.example.com/test/path/to/twentytwenty.zip',
package/lib/wordpress.js CHANGED
@@ -5,6 +5,7 @@ const dockerCompose = require( 'docker-compose' );
5
5
  const util = require( 'util' );
6
6
  const fs = require( 'fs' ).promises;
7
7
  const path = require( 'path' );
8
+ const got = require( 'got' );
8
9
 
9
10
  /**
10
11
  * Promisified dependencies
@@ -246,11 +247,6 @@ async function copyCoreFiles( fromPath, toPath ) {
246
247
  * @return {string} The version of WordPress the source is for.
247
248
  */
248
249
  async function readWordPressVersion( coreSource, spinner, debug ) {
249
- // No source means they're using the bleeding edge.
250
- if ( coreSource === null ) {
251
- return null;
252
- }
253
-
254
250
  const versionFilePath = path.join(
255
251
  coreSource.path,
256
252
  'wp-includes',
@@ -275,6 +271,31 @@ async function readWordPressVersion( coreSource, spinner, debug ) {
275
271
  return versionMatch[ 1 ];
276
272
  }
277
273
 
274
+ /**
275
+ * Returns the latest stable version of WordPress by requesting the stable-check
276
+ * endpoint on WordPress.org.
277
+ *
278
+ * @return {string} The latest stable version of WordPress, like "6.0.1"
279
+ */
280
+ let CACHED_WP_VERSION;
281
+ async function getLatestWordPressVersion() {
282
+ // Avoid extra network requests.
283
+ if ( CACHED_WP_VERSION ) {
284
+ return CACHED_WP_VERSION;
285
+ }
286
+
287
+ const versions = await got(
288
+ 'https://api.wordpress.org/core/stable-check/1.0/'
289
+ ).json();
290
+
291
+ for ( const [ version, status ] of Object.entries( versions ) ) {
292
+ if ( status === 'latest' ) {
293
+ CACHED_WP_VERSION = version;
294
+ return version;
295
+ }
296
+ }
297
+ }
298
+
278
299
  module.exports = {
279
300
  hasSameCoreSource,
280
301
  checkDatabaseConnection,
@@ -282,4 +303,5 @@ module.exports = {
282
303
  resetDatabase,
283
304
  setupWordPressDirectories,
284
305
  readWordPressVersion,
306
+ getLatestWordPressVersion,
285
307
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/env",
3
- "version": "5.0.0",
3
+ "version": "5.2.0",
4
4
  "description": "A zero-config, self contained local WordPress environment for development and testing.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -51,5 +51,5 @@
51
51
  "scripts": {
52
52
  "test": "echo \"Error: run tests from root\" && exit 1"
53
53
  },
54
- "gitHead": "0315dbc240cb2aa146d7c1bafd251f004b88300e"
54
+ "gitHead": "e4603f9843063edf33d32d6d43629f2f1c2c530b"
55
55
  }