Skip to content

Commit

Permalink
Use git for wp-env's default WordPress version (#42826)
Browse files Browse the repository at this point in the history
* Add function to retrieve the latest stable WordPress version

* Change the default core source to the latest stable tag in the WordPress git repository
  • Loading branch information
noahtallen authored Aug 9, 2022
1 parent 4f6bfe5 commit 6dcf85a
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 41 deletions.
6 changes: 6 additions & 0 deletions packages/env/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

### Enhancement
- 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.

### Bug Fix
- 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.

## 5.0.0 (2022-07-27)

### Breaking Changes
Expand Down
28 changes: 16 additions & 12 deletions packages/env/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The local environment will be available at http://localhost:8888 (Username: `adm

## Prerequisites

`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).
`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/).

Node.js and NPM are required. The latest LTS version of Node.js is used to develop `wp-env` and is recommended.

Expand All @@ -40,7 +40,9 @@ If your project already has a package.json, it's also possible to use `wp-env` a
$ npm i @wordpress/env --save-dev
```

Then modify your package.json and add an extra command to npm `scripts` (https://docs.npmjs.com/misc/scripts):
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`.

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):

```json
"scripts": {
Expand All @@ -51,7 +53,7 @@ Then modify your package.json and add an extra command to npm `scripts` (https:/
When installing `wp-env` in this way, all `wp-env` commands detailed in these docs must be prefixed with `npm run`, for example:

```sh
# You must add another double dash to pass the "update" flag to wp-env
# You must add another double dash to pass flags to the script (wp-env) rather than to npm itself
$ npm run wp-env start -- --update
```

Expand Down Expand Up @@ -117,15 +119,14 @@ Running `docker ps` and inspecting the `PORTS` column allows you to determine wh

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

### 3. Restart `wp-env`
### 3. Restart `wp-env` with updates

Restarting `wp-env` will restart the underlying Docker containers which can fix many issues.

To restart `wp-env`:
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.

```sh
$ wp-env stop
$ wp-env start
$ wp-env start --update
```

### 4. Restart Docker
Expand Down Expand Up @@ -156,16 +157,17 @@ $ wp-env clean all
$ wp-env start
```

### 6. Nuke everything and start again 🔥
### 6. Destroy everything and start again 🔥

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.
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.

To nuke everything:
To do so:

**⚠️ WARNING: This will permanently delete any posts, pages, media, etc. in the local WordPress installation.**

```sh
$ wp-env destroy
# This new instance is a fresh start with no existing data:
$ wp-env start
```

Expand Down Expand Up @@ -211,10 +213,12 @@ wp-env start
wp-env start --xdebug=profile,trace,debug
```

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:
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:

```sh
npm run wp-env start -- --xdebug
# Alternatively, use npx:
npx wp-env start --xdebug
```

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.
Expand Down Expand Up @@ -532,7 +536,7 @@ Additionally, the values referencing a URL include the specified port for the gi
### Examples
#### Latest production WordPress + current directory as a plugin
#### Latest stable WordPress + current directory as a plugin
This is useful for plugin development.
Expand Down
49 changes: 31 additions & 18 deletions packages/env/lib/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const readRawConfigFile = require( './read-raw-config-file' );
const parseConfig = require( './parse-config' );
const { includeTestsPath, parseSourceString } = parseConfig;
const md5 = require( '../md5' );
const { getLatestWordPressVersion } = require( '../wordpress' );

/**
* wp-env configuration.
Expand All @@ -33,7 +34,7 @@ const md5 = require( '../md5' );
* Base-level config for any particular environment. (development/tests/etc)
*
* @typedef WPServiceConfig
* @property {?WPSource} coreSource The WordPress installation to load in the environment.
* @property {WPSource} coreSource The WordPress installation to load in the environment.
* @property {WPSource[]} pluginSources Plugins to load in the environment.
* @property {WPSource[]} themeSources Themes to load in the environment.
* @property {number} port The port to use.
Expand Down Expand Up @@ -68,9 +69,37 @@ module.exports = async function readConfig( configPath ) {
md5( configPath )
);

// The specified base configuration from .wp-env.json or from the local
// source type which was automatically detected.
const baseConfig =
( await readRawConfigFile( '.wp-env.json', configPath ) ) ||
( await getDefaultBaseConfig( configPath ) );

// Overriden .wp-env.json on a per-user case.
const overrideConfig =
( await readRawConfigFile(
'.wp-env.override.json',
configPath.replace( /\.wp-env\.json$/, '.wp-env.override.json' )
) ) || {};

const detectedLocalConfig =
Object.keys( { ...baseConfig, ...overrideConfig } ).length > 0;

// If there is no local WordPress version, use the latest stable version from GitHub.
let coreRef;
if ( ! overrideConfig.core && ! baseConfig.core ) {
const wpVersion = await getLatestWordPressVersion();
if ( ! wpVersion ) {
throw new ValidationError(
'Could not find the latest WordPress version. There may be a network issue.'
);
}
coreRef = `WordPress/WordPress#${ wpVersion }`;
}

// Default configuration which is overridden by .wp-env.json files.
const defaultConfiguration = {
core: null,
core: coreRef,
phpVersion: null,
plugins: [],
themes: [],
Expand All @@ -96,22 +125,6 @@ module.exports = async function readConfig( configPath ) {
},
};

// The specified base configuration from .wp-env.json or from the local
// source type which was automatically detected.
const baseConfig =
( await readRawConfigFile( '.wp-env.json', configPath ) ) ||
( await getDefaultBaseConfig( configPath ) );

// Overriden .wp-env.json on a per-user case.
const overrideConfig =
( await readRawConfigFile(
'.wp-env.override.json',
configPath.replace( /\.wp-env\.json$/, '.wp-env.override.json' )
) ) || {};

const detectedLocalConfig =
Object.keys( { ...baseConfig, ...overrideConfig } ).length > 0;

// A quick validation before merging on a service by service level allows us
// to check the root configuration options and provide more helpful errors.
validateConfig(
Expand Down
2 changes: 0 additions & 2 deletions packages/env/lib/config/test/__snapshots__/config.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ Object {
"WP_TESTS_EMAIL": "admin@example.org",
"WP_TESTS_TITLE": "Test Blog",
},
"coreSource": null,
"mappings": Object {},
"phpVersion": null,
"pluginSources": Array [],
Expand All @@ -44,7 +43,6 @@ Object {
"WP_TESTS_EMAIL": "admin@example.org",
"WP_TESTS_TITLE": "Test Blog",
},
"coreSource": null,
"mappings": Object {},
"phpVersion": null,
"pluginSources": Array [],
Expand Down
49 changes: 45 additions & 4 deletions packages/env/lib/config/test/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ jest.mock( 'fs', () => ( {
},
} ) );

// This mocks a small response with a format matching the stable-check API.
// It makes getLatestWordPressVersion resolve to "100.0.0".
jest.mock( 'got', () =>
jest.fn( ( url ) => ( {
json: () => {
if ( url === 'https://api.wordpress.org/core/stable-check/1.0/' ) {
return Promise.resolve( {
'1.0': 'insecure',
'99.1.1': 'outdated',
'100.0.0': 'latest',
'100.0.1': 'fancy',
} );
}
},
} ) )
);

jest.mock( '../detect-directory-type', () => jest.fn() );

describe( 'readConfig', () => {
Expand Down Expand Up @@ -59,19 +76,38 @@ describe( 'readConfig', () => {
);
detectDirectoryType.mockImplementation( () => 'core' );
const config = await readConfig( '.wp-env.json' );
expect( config.env.development.coreSource ).not.toBeNull();
expect( config.env.development.coreSource.type ).toBe( 'local' );
expect( config.env.tests.coreSource ).not.toBeNull();
expect( config.env.development.pluginSources ).toHaveLength( 0 );
expect( config.env.development.themeSources ).toHaveLength( 0 );
} );

it( 'should use the most recent stable WordPress version for the default core source', async () => {
readFile.mockImplementation( () =>
Promise.resolve( JSON.stringify( {} ) )
);
const config = await readConfig( '.wp-env.json' );

const expected = {
url: 'https://github.com/WordPress/WordPress.git',
type: 'git',
basename: 'WordPress',
ref: '100.0.0', // From the mock of https at the top of the file.
};

expect( config.env.development.coreSource ).toMatchObject(
expected
);
expect( config.env.tests.coreSource ).toMatchObject( expected );
} );

it( 'should infer a plugin config when ran from a plugin directory', async () => {
readFile.mockImplementation( () =>
Promise.reject( { code: 'ENOENT' } )
);
detectDirectoryType.mockImplementation( () => 'plugin' );
const config = await readConfig( '.wp-env.json' );
expect( config.env.development.coreSource ).toBeNull();
expect( config.env.development.coreSource.type ).toBe( 'git' );
expect( config.env.development.pluginSources ).toHaveLength( 1 );
expect( config.env.tests.pluginSources ).toHaveLength( 1 );
expect( config.env.development.themeSources ).toHaveLength( 0 );
Expand All @@ -83,8 +119,8 @@ describe( 'readConfig', () => {
);
detectDirectoryType.mockImplementation( () => 'theme' );
const config = await readConfig( '.wp-env.json' );
expect( config.env.development.coreSource ).toBeNull();
expect( config.env.tests.coreSource ).toBeNull();
expect( config.env.development.coreSource.type ).toBe( 'git' );
expect( config.env.tests.coreSource.type ).toBe( 'git' );
expect( config.env.development.themeSources ).toHaveLength( 1 );
expect( config.env.tests.themeSources ).toHaveLength( 1 );
expect( config.env.development.pluginSources ).toHaveLength( 0 );
Expand Down Expand Up @@ -197,6 +233,11 @@ describe( 'readConfig', () => {
// Remove generated values which are different on other machines.
delete config.dockerComposeConfigPath;
delete config.workDirectoryPath;

// This encodes both the version of WordPress (which can change frequently)
// as well as the wp-env directory which is unique on every machine.
delete config.env.development.coreSource;
delete config.env.tests.coreSource;
expect( config ).toMatchSnapshot();
} );
} );
Expand Down
25 changes: 20 additions & 5 deletions packages/env/lib/wordpress.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const dockerCompose = require( 'docker-compose' );
const util = require( 'util' );
const fs = require( 'fs' ).promises;
const path = require( 'path' );
const got = require( 'got' );

/**
* Promisified dependencies
Expand Down Expand Up @@ -246,11 +247,6 @@ async function copyCoreFiles( fromPath, toPath ) {
* @return {string} The version of WordPress the source is for.
*/
async function readWordPressVersion( coreSource, spinner, debug ) {
// No source means they're using the bleeding edge.
if ( coreSource === null ) {
return null;
}

const versionFilePath = path.join(
coreSource.path,
'wp-includes',
Expand All @@ -275,11 +271,30 @@ async function readWordPressVersion( coreSource, spinner, debug ) {
return versionMatch[ 1 ];
}

/**
* Returns the latest stable version of WordPress by requesting the stable-check
* endpoint on WordPress.org.
*
* @return {string} The latest stable version of WordPress, like "6.0.1"
*/
async function getLatestWordPressVersion() {
const versions = await got(
'https://api.wordpress.org/core/stable-check/1.0/'
).json();

for ( const [ version, status ] of Object.entries( versions ) ) {
if ( status === 'latest' ) {
return version;
}
}
}

module.exports = {
hasSameCoreSource,
checkDatabaseConnection,
configureWordPress,
resetDatabase,
setupWordPressDirectories,
readWordPressVersion,
getLatestWordPressVersion,
};

0 comments on commit 6dcf85a

Please sign in to comment.