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

Add config file support to @wordpress/env #18121

Merged
merged 9 commits into from
Oct 30, 2019
12 changes: 8 additions & 4 deletions packages/env/lib/create-docker-compose-config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
module.exports = function createDockerComposeConfig(
cwd,
cwdName,
cwdTestsPath,
context
context,
dependencies,
) {
const { path: cwd, pathBasename: cwdName } = context;

const dependencyMappings = [ ...dependencies, context ].map(
( { path, pathBasename, type } ) => ` - ${ path }/:/var/www/html/wp-content/${ type }s/${ pathBasename }/\n`
).join( '' );
const commonVolumes = `
- ${ cwd }/:/var/www/html/wp-content/${ context.type }s/${ cwdName }/
${ dependencyMappings }
- ${ cwd }${ cwdTestsPath }/e2e-tests/mu-plugins/:/var/www/html/wp-content/mu-plugins/
- ${ cwd }${ cwdTestsPath }/e2e-tests/plugins/:/var/www/html/wp-content/plugins/${ cwdName }-test-plugins/`;
const volumes = `
Expand Down
28 changes: 25 additions & 3 deletions packages/env/lib/detect-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,43 @@ const path = require( 'path' );
const readDir = util.promisify( fs.readdir );
const finished = util.promisify( stream.finished );

module.exports = async function detectContext() {
/**
* @typedef Context
* @type {Object}
* @property {string} type
* @property {string} path
* @property {string} pathBasename
*/

/**
* Detects the context of a given path.
*
* @param {string} [directoryPath=process.cwd()] The directory to detect. Should point to a directory, defaulting to the current working directory.
*
* @return {Context} The context of the directory. If a theme or plugin, the type property will contain 'theme' or 'plugin'.
*/
module.exports = async function detectContext( directoryPath = process.cwd() ) {
const context = {};

// Use absolute paths to files so that we can properly read
// dependencies not in the current working directory.
const absPath = path.resolve( directoryPath );

// Race multiple file read streams against each other until
// a plugin or theme header is found.
const files = ( await readDir( './' ) ).filter(
const files = ( await readDir( absPath ) ).filter(
( file ) => path.extname( file ) === '.php' || path.basename( file ) === 'style.css'
);
).map( ( fileName ) => path.join( absPath, fileName ) );

const streams = [];
for ( const file of files ) {
const fileStream = fs.createReadStream( file, 'utf8' );
fileStream.on( 'data', ( text ) => {
const [ , type ] = text.match( /(Plugin|Theme) Name: .*[\r\n]/ ) || [];
if ( type ) {
context.type = type.toLowerCase();
context.path = absPath;
context.pathBasename = path.basename( absPath );

// Stop the creation of new streams by mutating the iterated array. We can't `break`, because we are inside a function.
files.splice( 0 );
Expand Down
14 changes: 10 additions & 4 deletions packages/env/lib/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ const wait = require( 'util' ).promisify( setTimeout );
/**
* Internal dependencies
*/
const detectContext = require( './detect-context' );
const createDockerComposeConfig = require( './create-docker-compose-config' );
const detectContext = require( './detect-context' );
const resolveDependencies = require( './resolve-dependencies' );

// Config Variables
const cwd = process.cwd();
Expand All @@ -38,14 +39,15 @@ const setupSite = ( isTests = false ) =>
} --title=${ cwdName } --admin_user=admin --admin_password=password --admin_email=admin@wordpress.org`,
isTests
);
const activateContext = ( context, isTests = false ) =>
wpCliRun( `wp ${ context.type } activate ${ cwdName }`, isTests );
const activateContext = ( { type, pathBasename }, isTests = false ) =>
wpCliRun( `wp ${ type } activate ${ pathBasename }`, isTests );
const resetDatabase = ( isTests = false ) =>
wpCliRun( 'wp db reset --yes', isTests );

module.exports = {
async start( { ref, spinner = {} } ) {
const context = await detectContext();
const dependencies = await resolveDependencies();

spinner.text = `Downloading WordPress@${ ref } 0/100%.`;
const gitFetchOptions = {
Expand Down Expand Up @@ -100,7 +102,7 @@ module.exports = {
spinner.text = `Starting WordPress@${ ref }.`;
fs.writeFileSync(
dockerComposeOptions.config,
createDockerComposeConfig( cwd, cwdName, cwdTestsPath, context )
createDockerComposeConfig( cwdTestsPath, context, dependencies )
);

// These will bring up the database container,
Expand All @@ -127,6 +129,7 @@ module.exports = {
await Promise.all( [
activateContext( context ),
activateContext( context, true ),
...dependencies.map( activateContext ),
noahtallen marked this conversation as resolved.
Show resolved Hide resolved
] );

// Remove dangling containers and finish.
Expand All @@ -142,6 +145,8 @@ module.exports = {

async clean( { environment, spinner } ) {
const context = await detectContext();
const dependencies = await resolveDependencies();
const activateDependencies = () => Promise.all( dependencies.map( activateContext ) );

const description = `${ environment } environment${
environment === 'all' ? 's' : ''
Expand All @@ -155,6 +160,7 @@ module.exports = {
resetDatabase()
.then( setupSite )
.then( activateContext.bind( null, context ) )
.then( activateDependencies )
.catch( () => {} )
);
}
Expand Down
44 changes: 44 additions & 0 deletions packages/env/lib/resolve-dependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict';

/**
* External dependencies
*/
const util = require( 'util' );
const fs = require( 'fs' );

/**
* Internal dependencies
*/
const detectContext = require( './detect-context' );

/**
* Promisified dependencies
*/
const readFile = util.promisify( fs.readFile );

/**
* Returns an array of dependencies to be mounted in the Docker image.
*
* Reads from the wp-env.json file in the current directory and uses detect
* context to make sure the specified dependencies exist and are plugins
* and/or themes.
*
* @return {Array<detectContext.Context>} An array of dependencies in the context format.
*/
module.exports = async function resolveDependencies() {
const envFile = await readFile( './wp-env.json' );
const { themes, plugins } = JSON.parse( envFile );

const dependencyResolvers = [];
if ( Array.isArray( themes ) ) {
dependencyResolvers.push( ...themes.map( detectContext ) );
}

if ( Array.isArray( plugins ) ) {
dependencyResolvers.push( ...plugins.map( detectContext ) );
}

// Return all dependencies which have been detected to be a plugin or a theme.
const dependencies = await Promise.all( dependencyResolvers );
return dependencies.filter( ( { type } ) => !! type );
};