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

wp-env: override generated file directory with environment variable #20253

Merged
merged 11 commits into from
Feb 19, 2020
1 change: 1 addition & 0 deletions packages/env/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### New Feature

- You may now override the directory in which `wp-env` creates generated files with the `WP_ENV_HOME` environment variable. The default directory is `~/.wp-env/` (or `~/wp-env/` on Linux).
- 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)
Expand Down
2 changes: 2 additions & 0 deletions packages/env/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ $ wp-env start

## Command reference

`wp-env` creates generated files in the `wp-env` home directory. By default, this is `~/.wp-env`. The exception is Linux, where files are placed at `~/wp-env` [for compatibility with Snap Packages](https://github.com/WordPress/gutenberg/issues/20180#issuecomment-587046325). The `wp-env` home directory contains a subdirectory for each project named `/$md5_of_project_path`. To change the `wp-env` home directory, set the `WP_ENV_HOME` environment variable. For example, running `WP_ENV_HOME="something" wp-env start` will download the project files to the directory `./something/$md5_of_project_path` (relative to the current directory).

### `wp-env start [ref]`

```sh
Expand Down
29 changes: 27 additions & 2 deletions packages/env/lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,7 @@ module.exports = {
}

const workDirectoryPath = path.resolve(
os.homedir(),
'.wp-env',
getHomeDirectory(),
md5( configPath )
);

Expand Down Expand Up @@ -286,6 +285,32 @@ function getNumberFromEnvVariable( varName ) {
return maybeNumber;
}

/**
* Gets the `wp-env` home directory in which generated files are created.
*
* By default, '~/.wp-env/'. On Linux, '~/wp-env/'. Can be overriden with the
* WP_ENV_HOME environment variable.
*
* @return {string} The absolute path to the `wp-env` home directory.
*/
function getHomeDirectory() {
// Allow user to override download location.
if ( process.env.WP_ENV_HOME ) {
return path.resolve( process.env.WP_ENV_HOME );
}

/**
* Installing docker with Snap Packages on Linux is common, but does not
* support hidden directories. Therefore we use a public directory on Linux.
*
* @see https://github.com/WordPress/gutenberg/issues/20180#issuecomment-587046325
*/
return path.resolve(
os.homedir(),
os.platform() === 'linux' ? 'wp-env' : '.wp-env'
);
}

/**
* Hashes the given string using the MD5 algorithm.
*
Expand Down
46 changes: 46 additions & 0 deletions packages/env/lib/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ module.exports = {
* @param {Object} options.spinner A CLI spinner which indicates progress.
*/
async start( { spinner } ) {
/**
* If the Docker image is already running and the `wp-env` files have been
* deleted, the start command will not complete successfully. Stopping
* the container before continuing allows the docker entrypoint script,
* which restores the files, to run again when we start the containers.
*
* Additionally, this serves as a way to restart the container entirely
* should the need arise.
*
* @see https://github.com/WordPress/gutenberg/pull/20253#issuecomment-587228440
*/
await module.exports.stop( { spinner } );

const config = await initConfig();

spinner.text = 'Downloading WordPress.';
Expand Down Expand Up @@ -89,6 +102,14 @@ module.exports = {
config: config.dockerComposeConfigPath,
} );

// Set correct permissions for the wp-config.php file.
if ( config.coreSource ) {
await Promise.all( [
setCoreConfigPermissions( config.coreSource.path ),
setCoreConfigPermissions( config.coreSource.testsPath ),
] );
}

try {
await checkDatabaseConnection( config );
} catch ( error ) {
Expand Down Expand Up @@ -363,3 +384,28 @@ async function resetDatabase( environment, { dockerComposeConfigPath } ) {

await Promise.all( tasks );
}

/**
* Sets the correct user and permissions on the wp-config.php file.
*
* This is needed because the file can be generated under the wrong user and
* group, which means we may not have permission to access it at runtime.
*
* @see https://github.com/WordPress/gutenberg/pull/20253#issuecomment-586871441
*
* @param {string} coreSourcePath The path to the WordPress source code.
*/
async function setCoreConfigPermissions( coreSourcePath ) {
// For now, Linux appears to be the only platform with issues.
if ( process.platform !== 'linux' ) {
return;
}

// Get the UID and GID of an existing file that works correctly.
noahtallen marked this conversation as resolved.
Show resolved Hide resolved
const indexFile = path.resolve( coreSourcePath, 'index.php' );
const { uid, gid } = await fs.stat( indexFile );

// Use those IDs to set the user and group of the file with bad permissions.
const configFile = path.resolve( coreSourcePath, 'wp-config.php' );
await fs.chown( configFile, uid, gid );
}
66 changes: 66 additions & 0 deletions packages/env/test/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* External dependencies
*/
const { readFile } = require( 'fs' ).promises;
const os = require( 'os' );

/**
* Internal dependencies
Expand Down Expand Up @@ -327,6 +328,71 @@ describe( 'readConfig', () => {
testsPort: 8889,
} );
} );

it( 'should use the WP_ENV_HOME environment variable only if specified', async () => {
readFile.mockImplementation( () =>
Promise.resolve( JSON.stringify( {} ) )
);
const oldEnvHome = process.env.WP_ENV_HOME;

expect.assertions( 2 );

process.env.WP_ENV_HOME = 'here/is/a/path';
const configWith = await readConfig( '.wp-env.json' );
expect(
configWith.workDirectoryPath.includes( 'here/is/a/path' )
).toBe( true );

process.env.WP_ENV_HOME = undefined;
const configWithout = await readConfig( '.wp-env.json' );
expect(
configWithout.workDirectoryPath.includes( 'here/is/a/path' )
).toBe( false );

process.env.WP_ENV_HOME = oldEnvHome;
} );

it( 'should use the WP_ENV_HOME environment variable on Linux', async () => {
readFile.mockImplementation( () =>
Promise.resolve( JSON.stringify( {} ) )
);
const oldEnvHome = process.env.WP_ENV_HOME;
const oldOsPlatform = os.platform;
os.platform = () => 'linux';

expect.assertions( 2 );

process.env.WP_ENV_HOME = 'here/is/a/path';
const configWith = await readConfig( '.wp-env.json' );
expect(
configWith.workDirectoryPath.includes( 'here/is/a/path' )
).toBe( true );

process.env.WP_ENV_HOME = undefined;
const configWithout = await readConfig( '.wp-env.json' );
expect(
configWithout.workDirectoryPath.includes( 'here/is/a/path' )
).toBe( false );

process.env.WP_ENV_HOME = oldEnvHome;
os.platform = oldOsPlatform;
} );

it( 'should use a non-private folder on Linux', async () => {
readFile.mockImplementation( () =>
Promise.resolve( JSON.stringify( {} ) )
);
const oldOsPlatform = os.platform;
os.platform = () => 'linux';

expect.assertions( 2 );

const config = await readConfig( '.wp-env.json' );
expect( config.workDirectoryPath.includes( '.wp-env' ) ).toBe( false );
expect( config.workDirectoryPath.includes( 'wp-env' ) ).toBe( true );

os.platform = oldOsPlatform;
} );
} );

/**
Expand Down