Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use git for wp-env's default WordPress version #42826

Merged
merged 8 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is never null now that we default to a git source when the user doesn't provide one. As a result, there is probably more code to cleanup, but everything should work correctly in the meantime.

* @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;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lines above were just moved from lower in the file so that the information is available for the following if statement

// 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,
};