From b61c0ef28474d0ea13e5e5a46113b599618baa3d Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 20 Apr 2023 09:32:40 -0700 Subject: [PATCH 01/18] Changed Environment Container User Instead of running the web service as `root` and the CLI service as `33:33`, we should be running them as the host user. By doing this we ensure ownership parity in mounted folders on platforms where having different owners would result in permission issues. --- .../env/lib/build-docker-compose-config.js | 54 +++++++++++++++---- packages/env/lib/commands/run.js | 9 ++++ packages/env/lib/init-config.js | 13 ++++- .../env/test/build-docker-compose-config.js | 4 +- 4 files changed, 67 insertions(+), 13 deletions(-) diff --git a/packages/env/lib/build-docker-compose-config.js b/packages/env/lib/build-docker-compose-config.js index 7b27f85a7f6cf..5ac6872d1cc7d 100644 --- a/packages/env/lib/build-docker-compose-config.js +++ b/packages/env/lib/build-docker-compose-config.js @@ -4,6 +4,7 @@ */ const fs = require( 'fs' ); const path = require( 'path' ); +const os = require( 'os' ); /** * Internal dependencies @@ -70,6 +71,24 @@ function getMounts( ]; } +/** + * Gets information about the host user so we can implement ownership parity. + * + * @return {Object} The host user's name, uid, and gid. + */ +function getHostUser() { + const hostUser = os.userInfo(); + const uid = ( hostUser.uid === -1 ? 0 : hostUser.uid ).toString(); + const gid = ( hostUser.gid === -1 ? 0 : hostUser.gid ).toString(); + + return { + name: hostUser.username, + uid, + gid, + fullUser: uid + ':' + gid, + }; +} + /** * Creates a docker-compose config object which, when serialized into a * docker-compose.yml file, tells docker-compose how to run the environment. @@ -78,7 +97,13 @@ function getMounts( * * @return {Object} A docker-compose config object, ready to serialize into YAML. */ -module.exports = function buildDockerComposeConfig( config ) { +function buildDockerComposeConfig( config ) { + // Since we are mounting files from the host operating system + // we want to create the host user in some of our containers. + // This ensures ownership parity and lets us access files + // and folders between the containers and the host. + const hostUser = getHostUser(); + const developmentMounts = getMounts( config.workDirectoryPath, config.env.development @@ -190,12 +215,6 @@ module.exports = function buildDockerComposeConfig( config ) { } const phpunitImage = `wordpressdevelop/phpunit:${ phpunitTag }`; - // The www-data user in wordpress:cli has a different UID (82) to the - // www-data user in wordpress (33). Ensure we use the wordpress www-data - // user for CLI commands. - // https://github.com/docker-library/wordpress/issues/256 - const cliUser = '33:33'; - // If the user mounted their own uploads folder, we should not override it in the phpunit service. const isMappingTestUploads = testsMounts.some( ( mount ) => mount.endsWith( ':/var/www/html/wp-content/uploads' ) @@ -227,11 +246,20 @@ module.exports = function buildDockerComposeConfig( config ) { volumes: [ 'mysql-test:/var/lib/mysql' ], }, wordpress: { - build: '.', + build: { + context: '.', + args: { + HOST_USERNAME: hostUser.name, + HOST_UID: hostUser.uid, + HOST_GID: hostUser.gid, + }, + }, depends_on: [ 'mysql' ], image: developmentWpImage, ports: [ developmentPorts ], environment: { + APACHE_RUN_USER: '#' + hostUser.uid, + APACHE_RUN_GROUP: '#' + hostUser.gid, ...dbEnv.credentials, ...dbEnv.development, WP_TESTS_DIR: '/wordpress-phpunit', @@ -243,6 +271,8 @@ module.exports = function buildDockerComposeConfig( config ) { image: testsWpImage, ports: [ testsPorts ], environment: { + APACHE_RUN_USER: '#' + hostUser.uid, + APACHE_RUN_GROUP: '#' + hostUser.gid, ...dbEnv.credentials, ...dbEnv.tests, WP_TESTS_DIR: '/wordpress-phpunit', @@ -253,7 +283,7 @@ module.exports = function buildDockerComposeConfig( config ) { depends_on: [ 'wordpress' ], image: developmentWpCliImage, volumes: developmentMounts, - user: cliUser, + user: hostUser.fullUser, environment: { ...dbEnv.credentials, ...dbEnv.development, @@ -264,7 +294,7 @@ module.exports = function buildDockerComposeConfig( config ) { depends_on: [ 'tests-wordpress' ], image: testsWpCliImage, volumes: testsMounts, - user: cliUser, + user: hostUser.fullUser, environment: { ...dbEnv.credentials, ...dbEnv.tests, @@ -300,4 +330,6 @@ module.exports = function buildDockerComposeConfig( config ) { 'phpunit-uploads': {}, }, }; -}; +} + +module.exports = { getHostUser, buildDockerComposeConfig }; diff --git a/packages/env/lib/commands/run.js b/packages/env/lib/commands/run.js index fd0f800041cb9..30a339cdb9cce 100644 --- a/packages/env/lib/commands/run.js +++ b/packages/env/lib/commands/run.js @@ -8,6 +8,7 @@ const path = require( 'path' ); * Internal dependencies */ const initConfig = require( '../init-config' ); +const { getHostUser } = require( '../build-docker-compose-config' ); /** * @typedef {import('../config').WPConfig} WPConfig @@ -52,6 +53,12 @@ module.exports = async function run( { * @param {Object} spinner A CLI spinner which indicates progress. */ function spawnCommandDirectly( config, container, command, envCwd, spinner ) { + // Both the `wordpress` and `tests-wordpress` containers have the host's + // user so that they can maintain ownership parity with the host OS. + // We should run any commands as that user so that they are able + // to interact with the files mounted from the host. + const hostUser = getHostUser(); + // We need to pass absolute paths to the container. envCwd = path.resolve( '/var/www/html', envCwd ); @@ -61,6 +68,8 @@ function spawnCommandDirectly( config, container, command, envCwd, spinner ) { 'run', '-w', envCwd, + '--user', + hostUser.fullUser, '--rm', container, ...command.split( ' ' ), // The command will fail if passed as a complete string. diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index 8bcb27e207b7a..027d2d1c7f557 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -11,7 +11,7 @@ const os = require( 'os' ); * Internal dependencies */ const { readConfig } = require( './config' ); -const buildDockerComposeConfig = require( './build-docker-compose-config' ); +const { buildDockerComposeConfig } = require( './build-docker-compose-config' ); /** * @typedef {import('./config').WPConfig} WPConfig @@ -157,6 +157,17 @@ function dockerFileContents( image, config ) { RUN apt-get -qy install $PHPIZE_DEPS && touch /usr/local/etc/php/php.ini ${ shouldInstallXdebug ? installXdebug( config.xdebug ) : '' } + +# Create the host's user so that we can match ownership in the container. +ARG HOST_USERNAME +ARG HOST_UID +ARG HOST_GID +RUN useradd -M -u $HOST_UID -g $HOST_GID $HOST_USERNAME + +# Set up sudo so they can have root access when using 'run' commands. +RUN apt-get update +RUN apt-get -qy install sudo +RUN echo "$HOST_USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers `; } diff --git a/packages/env/test/build-docker-compose-config.js b/packages/env/test/build-docker-compose-config.js index 5927e188c9df0..c807aaaee80d8 100644 --- a/packages/env/test/build-docker-compose-config.js +++ b/packages/env/test/build-docker-compose-config.js @@ -1,7 +1,9 @@ /** * Internal dependencies */ -const buildDockerComposeConfig = require( '../lib/build-docker-compose-config' ); +const { + buildDockerComposeConfig, +} = require( '../lib/build-docker-compose-config' ); // The basic config keys which build docker compose config requires. const CONFIG = { From 027a071e79ec152393436223543ae72c21138476 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 20 Apr 2023 09:37:39 -0700 Subject: [PATCH 02/18] Changelog --- packages/env/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index bb68681c18f0f..fe73592f54c61 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -6,6 +6,12 @@ - `run` command now has a `--env-cwd` option to set the working directory in the container for the command to execute from. +### Bug fix + +- Docker containers now run as the host user. This should resolve problems with permissions arising from different owners +between the host, web container, and cli container. + + ## 5.16.0 (2023-04-12) ## 5.15.0 (2023-03-29) From cdaf5449f9f3e98e95f16a4ded225c34da8a9810 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 20 Apr 2023 10:48:58 -0700 Subject: [PATCH 03/18] Create Group When It Doesn't Exist --- packages/env/lib/init-config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index 027d2d1c7f557..72c221f3e20c2 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -162,6 +162,7 @@ ${ shouldInstallXdebug ? installXdebug( config.xdebug ) : '' } ARG HOST_USERNAME ARG HOST_UID ARG HOST_GID +RUN groupadd -o -g $HOST_GID $HOST_USERNAME RUN useradd -M -u $HOST_UID -g $HOST_GID $HOST_USERNAME # Set up sudo so they can have root access when using 'run' commands. From 6c0253892dcf7dfaa3d07f43b0af0c7cd0dcabb8 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:39:51 -0700 Subject: [PATCH 04/18] Marked Breaking Change --- packages/env/CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index fe73592f54c61..fe08d1749303d 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -5,9 +5,6 @@ ### Breaking Changes - `run` command now has a `--env-cwd` option to set the working directory in the container for the command to execute from. - -### Bug fix - - Docker containers now run as the host user. This should resolve problems with permissions arising from different owners between the host, web container, and cli container. From 97c23c5b3ebdc99ac537364b9924dab1aff06620 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 20 Apr 2023 16:40:12 -0700 Subject: [PATCH 05/18] Moved `getHostUser()` To Dedicated File --- .../env/lib/build-docker-compose-config.js | 26 +++---------------- packages/env/lib/commands/run.js | 2 +- packages/env/lib/get-host-user.js | 22 ++++++++++++++++ packages/env/lib/init-config.js | 2 +- .../env/test/build-docker-compose-config.js | 4 +-- 5 files changed, 28 insertions(+), 28 deletions(-) create mode 100644 packages/env/lib/get-host-user.js diff --git a/packages/env/lib/build-docker-compose-config.js b/packages/env/lib/build-docker-compose-config.js index 5ac6872d1cc7d..7790e4e3d5afb 100644 --- a/packages/env/lib/build-docker-compose-config.js +++ b/packages/env/lib/build-docker-compose-config.js @@ -4,13 +4,13 @@ */ const fs = require( 'fs' ); const path = require( 'path' ); -const os = require( 'os' ); /** * Internal dependencies */ const { hasSameCoreSource } = require( './wordpress' ); const { dbEnv } = require( './config' ); +const getHostUser = require( './get-host-user' ); /** * @typedef {import('./config').WPConfig} WPConfig @@ -71,24 +71,6 @@ function getMounts( ]; } -/** - * Gets information about the host user so we can implement ownership parity. - * - * @return {Object} The host user's name, uid, and gid. - */ -function getHostUser() { - const hostUser = os.userInfo(); - const uid = ( hostUser.uid === -1 ? 0 : hostUser.uid ).toString(); - const gid = ( hostUser.gid === -1 ? 0 : hostUser.gid ).toString(); - - return { - name: hostUser.username, - uid, - gid, - fullUser: uid + ':' + gid, - }; -} - /** * Creates a docker-compose config object which, when serialized into a * docker-compose.yml file, tells docker-compose how to run the environment. @@ -97,7 +79,7 @@ function getHostUser() { * * @return {Object} A docker-compose config object, ready to serialize into YAML. */ -function buildDockerComposeConfig( config ) { +module.exports = function buildDockerComposeConfig( config ) { // Since we are mounting files from the host operating system // we want to create the host user in some of our containers. // This ensures ownership parity and lets us access files @@ -330,6 +312,4 @@ function buildDockerComposeConfig( config ) { 'phpunit-uploads': {}, }, }; -} - -module.exports = { getHostUser, buildDockerComposeConfig }; +}; diff --git a/packages/env/lib/commands/run.js b/packages/env/lib/commands/run.js index 30a339cdb9cce..98ea82a77b276 100644 --- a/packages/env/lib/commands/run.js +++ b/packages/env/lib/commands/run.js @@ -8,7 +8,7 @@ const path = require( 'path' ); * Internal dependencies */ const initConfig = require( '../init-config' ); -const { getHostUser } = require( '../build-docker-compose-config' ); +const getHostUser = require( '../get-host-user' ); /** * @typedef {import('../config').WPConfig} WPConfig diff --git a/packages/env/lib/get-host-user.js b/packages/env/lib/get-host-user.js new file mode 100644 index 0000000000000..77d737085c626 --- /dev/null +++ b/packages/env/lib/get-host-user.js @@ -0,0 +1,22 @@ +/** + * External dependencies + */ +const os = require( 'os' ); + +/** + * Gets information about the host user. + * + * @return {Object} The host user's name, uid, and gid. + */ +module.exports = function getHostUser() { + const hostUser = os.userInfo(); + const uid = ( hostUser.uid === -1 ? 0 : hostUser.uid ).toString(); + const gid = ( hostUser.gid === -1 ? 0 : hostUser.gid ).toString(); + + return { + name: hostUser.username, + uid, + gid, + fullUser: uid + ':' + gid, + }; +}; diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index 72c221f3e20c2..51c77fef15a23 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -11,7 +11,7 @@ const os = require( 'os' ); * Internal dependencies */ const { readConfig } = require( './config' ); -const { buildDockerComposeConfig } = require( './build-docker-compose-config' ); +const buildDockerComposeConfig = require( './build-docker-compose-config' ); /** * @typedef {import('./config').WPConfig} WPConfig diff --git a/packages/env/test/build-docker-compose-config.js b/packages/env/test/build-docker-compose-config.js index c807aaaee80d8..5927e188c9df0 100644 --- a/packages/env/test/build-docker-compose-config.js +++ b/packages/env/test/build-docker-compose-config.js @@ -1,9 +1,7 @@ /** * Internal dependencies */ -const { - buildDockerComposeConfig, -} = require( '../lib/build-docker-compose-config' ); +const buildDockerComposeConfig = require( '../lib/build-docker-compose-config' ); // The basic config keys which build docker compose config requires. const CONFIG = { From bd9164a84ac37bf40693a5cbc14e50ba82041b78 Mon Sep 17 00:00:00 2001 From: Christopher Allford Date: Fri, 21 Apr 2023 13:11:09 -0700 Subject: [PATCH 06/18] Removed Upload Permission Mod With the changes in this pull request we no longer need to do this. --- packages/env/lib/wordpress.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/env/lib/wordpress.js b/packages/env/lib/wordpress.js index fc8f6db567077..2275526b3847a 100644 --- a/packages/env/lib/wordpress.js +++ b/packages/env/lib/wordpress.js @@ -147,24 +147,7 @@ async function setupWordPressDirectories( config ) { config.env.development.coreSource.path, config.env.development.coreSource.testsPath ); - await createUploadsDir( config.env.development.coreSource.testsPath ); } - - const checkedPaths = {}; - for ( const { coreSource } of Object.values( config.env ) ) { - if ( coreSource && ! checkedPaths[ coreSource.path ] ) { - await createUploadsDir( coreSource.path ); - checkedPaths[ coreSource.path ] = true; - } - } -} - -async function createUploadsDir( corePath ) { - // Ensure the tests uploads folder is writeable for travis, - // creating the folder if necessary. - const uploadPath = path.join( corePath, 'wp-content/uploads' ); - await fs.mkdir( uploadPath, { recursive: true } ); - await fs.chmod( uploadPath, 0o0767 ); } /** From 3ffa6f435b61b1be1a0c894abe53661e8642f576 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:07:08 -0700 Subject: [PATCH 07/18] Added Shared Home Directory This will ensure things like `wp plugin install` have a directory to use as a cache. Anything else can now refer to the home directory for the same purpose. --- packages/env/README.md | 6 +-- .../env/lib/build-docker-compose-config.js | 32 +++++++++++--- packages/env/lib/commands/start.js | 5 +++ packages/env/lib/init-config.js | 44 ++++++++++++++++--- .../env/test/build-docker-compose-config.js | 16 +++++++ 5 files changed, 89 insertions(+), 14 deletions(-) diff --git a/packages/env/README.md b/packages/env/README.md index 7a22dfe88428c..f9e913ea91761 100644 --- a/packages/env/README.md +++ b/packages/env/README.md @@ -173,17 +173,17 @@ $ wp-env destroy $ wp-env start ``` -### 7. Debug mode and inspecting the generated dockerfile. +### 7. Debug mode and inspecting the generated docker files. `wp-env` uses docker behind the scenes. Inspecting the generated docker-compose file can help to understand what's going on. -Start `wp-env` in debug mode +Start `wp-env` in debug mode: ```sh wp-env start --debug ``` -`wp-env` will output its config which includes `dockerComposeConfigPath`. +`wp-env` will output its config which includes `dockerComposeConfigPath`: ```sh ℹ Config: diff --git a/packages/env/lib/build-docker-compose-config.js b/packages/env/lib/build-docker-compose-config.js index 7790e4e3d5afb..917ce50080290 100644 --- a/packages/env/lib/build-docker-compose-config.js +++ b/packages/env/lib/build-docker-compose-config.js @@ -22,6 +22,7 @@ const getHostUser = require( './get-host-user' ); * * @param {string} workDirectoryPath The working directory for wp-env. * @param {WPServiceConfig} config The service config to get the mounts from. + * @param {string} hostUsername The username of the host running wp-env. * @param {string} wordpressDefault The default internal path for the WordPress * source code (such as tests-wordpress). * @@ -30,6 +31,7 @@ const getHostUser = require( './get-host-user' ); function getMounts( workDirectoryPath, config, + hostUsername, wordpressDefault = 'wordpress' ) { // Top-level WordPress directory mounts (like wp-content/themes) @@ -47,9 +49,10 @@ function getMounts( `${ source.path }:/var/www/html/wp-content/themes/${ source.basename }` ); - const coreMount = `${ - config.coreSource ? config.coreSource.path : wordpressDefault - }:/var/www/html`; + const userHomeMount = + wordpressDefault === 'wordpress' + ? `user-home:/home/${ hostUsername }` + : `tests-user-home:/home/${ hostUsername }`; const corePHPUnitMount = `${ path.join( workDirectoryPath, @@ -60,10 +63,15 @@ function getMounts( 'phpunit' ) }:/wordpress-phpunit`; + const coreMount = `${ + config.coreSource ? config.coreSource.path : wordpressDefault + }:/var/www/html`; + return [ ...new Set( [ - coreMount, + coreMount, // Must be first because of some operations later that expect it to be! corePHPUnitMount, + userHomeMount, ...directoryMounts, ...pluginMounts, ...themeMounts, @@ -88,11 +96,13 @@ module.exports = function buildDockerComposeConfig( config ) { const developmentMounts = getMounts( config.workDirectoryPath, - config.env.development + config.env.development, + hostUser.name ); const testsMounts = getMounts( config.workDirectoryPath, config.env.tests, + hostUser.name, 'tests-wordpress' ); @@ -230,6 +240,7 @@ module.exports = function buildDockerComposeConfig( config ) { wordpress: { build: { context: '.', + dockerfile: 'WordPress.Dockerfile', args: { HOST_USERNAME: hostUser.name, HOST_UID: hostUser.uid, @@ -262,6 +273,15 @@ module.exports = function buildDockerComposeConfig( config ) { volumes: testsMounts, }, cli: { + build: { + context: '.', + dockerfile: 'CLI.Dockerfile', + args: { + HOST_USERNAME: hostUser.name, + HOST_UID: hostUser.uid, + HOST_GID: hostUser.gid, + }, + }, depends_on: [ 'wordpress' ], image: developmentWpCliImage, volumes: developmentMounts, @@ -310,6 +330,8 @@ module.exports = function buildDockerComposeConfig( config ) { mysql: {}, 'mysql-test': {}, 'phpunit-uploads': {}, + 'user-home': {}, + 'tests-user-home': {}, }, }; }; diff --git a/packages/env/lib/commands/start.js b/packages/env/lib/commands/start.js index a217d5492d2b4..c0e8aaaea12f2 100644 --- a/packages/env/lib/commands/start.js +++ b/packages/env/lib/commands/start.js @@ -159,6 +159,11 @@ module.exports = async function start( { spinner, debug, update, xdebug } ) { : [], } ); + // Make sure we've consumed the custom CLI dockerfile. + if ( shouldConfigureWp ) { + await dockerCompose.buildOne( [ 'cli' ], { ...dockerComposeConfig } ); + } + // Only run WordPress install/configuration when config has changed. if ( shouldConfigureWp ) { spinner.text = 'Configuring WordPress.'; diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index 51c77fef15a23..b31271e30951d 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -82,12 +82,17 @@ module.exports = async function initConfig( { ); await writeFile( - path.resolve( config.workDirectoryPath, 'Dockerfile' ), - dockerFileContents( + path.resolve( config.workDirectoryPath, 'WordPress.Dockerfile' ), + wordpressDockerFileContents( dockerComposeConfig.services.wordpress.image, config ) ); + + await writeFile( + path.resolve( config.workDirectoryPath, 'CLI.Dockerfile' ), + cliDockerFileContents( dockerComposeConfig.services.cli.image ) + ); } else if ( ! existsSync( config.workDirectoryPath ) ) { spinner.fail( 'wp-env has not yet been initialized. Please run `wp-env start` to install the WordPress instance before using any other commands. This is only necessary to set up the environment for the first time; it is typically not necessary for the instance to be running after that in order to use other commands.' @@ -134,14 +139,14 @@ function checkXdebugPhpCompatibility( config ) { } /** - * Generates the Dockerfile used by wp-env's development instance. + * Generates the Dockerfile used by wp-env's `wordpress` and `tests-wordpress` instances. * * @param {string} image The base docker image to use. * @param {WPConfig} config The configuration object. * * @return {string} The dockerfile contents. */ -function dockerFileContents( image, config ) { +function wordpressDockerFileContents( image, config ) { // Don't install XDebug unless it is explicitly required. let shouldInstallXdebug = false; @@ -162,8 +167,9 @@ ${ shouldInstallXdebug ? installXdebug( config.xdebug ) : '' } ARG HOST_USERNAME ARG HOST_UID ARG HOST_GID -RUN groupadd -o -g $HOST_GID $HOST_USERNAME -RUN useradd -M -u $HOST_UID -g $HOST_GID $HOST_USERNAME +# When the IDs are already in use we can still safely move on. +RUN groupadd -g $HOST_GID $HOST_USERNAME || true +RUN useradd -m -u $HOST_UID -g $HOST_GID $HOST_USERNAME || true # Set up sudo so they can have root access when using 'run' commands. RUN apt-get update @@ -188,3 +194,29 @@ RUN echo 'xdebug.mode=${ enableXdebug }' >> /usr/local/etc/php/php.ini RUN echo '${ clientDetectSettings }' >> /usr/local/etc/php/php.ini `; } + +/** + * Generates the Dockerfile used by wp-env's `cli` and `tests-cli` instances. + * + * @param {string} image The base docker image to use. + * + * @return {string} The dockerfile contents. + */ +function cliDockerFileContents( image ) { + return `FROM ${ image } + +# Switch to root so we can create users. +USER root + +# Create the host's user so that we can match ownership in the container. +ARG HOST_USERNAME +ARG HOST_UID +ARG HOST_GID +# When the IDs are already in use we can still safely move on. +RUN addgroup -g $HOST_GID $HOST_USERNAME || true +RUN adduser -h /home/$HOST_USERNAME -G $( getent group $HOST_GID | cut -d: -f1 ) -u $HOST_UID $HOST_USERNAME || true + +# Switch back now that we're done. +USER www-data +`; +} diff --git a/packages/env/test/build-docker-compose-config.js b/packages/env/test/build-docker-compose-config.js index 5927e188c9df0..444589d6ad637 100644 --- a/packages/env/test/build-docker-compose-config.js +++ b/packages/env/test/build-docker-compose-config.js @@ -2,6 +2,7 @@ * Internal dependencies */ const buildDockerComposeConfig = require( '../lib/build-docker-compose-config' ); +const getHostUser = require( '../lib/get-host-user' ); // The basic config keys which build docker compose config requires. const CONFIG = { @@ -12,6 +13,16 @@ const CONFIG = { configDirectoryPath: '/path/to/config', }; +jest.mock( '../lib/get-host-user', () => jest.fn() ); +getHostUser.mockImplementation( () => { + return { + name: 'test', + uid: 1, + gid: 2, + fullUser: '1:2', + }; +} ); + describe( 'buildDockerComposeConfig', () => { it( 'should map directories before individual sources', () => { const envConfig = { @@ -33,6 +44,7 @@ describe( 'buildDockerComposeConfig', () => { expect( volumes ).toEqual( [ 'wordpress:/var/www/html', // WordPress root. '/path/WordPress-PHPUnit/tests/phpunit:/wordpress-phpunit', // WordPress test library, + 'user-home:/home/test', '/path/to/wp-plugins:/var/www/html/wp-content/plugins', // Mapped plugins root. '/path/to/local/plugin:/var/www/html/wp-content/plugins/test-name', // Mapped plugin. ] ); @@ -68,6 +80,7 @@ describe( 'buildDockerComposeConfig', () => { let localSources = [ '/path/to/wp-plugins:/var/www/html/wp-content/plugins', '/path/WordPress-PHPUnit/tests/phpunit:/wordpress-phpunit', + 'user-home:/home/test', '/path/to/local/plugin:/var/www/html/wp-content/plugins/test-name', '/path/to/local/theme:/var/www/html/wp-content/themes/test-theme', ]; @@ -76,6 +89,7 @@ describe( 'buildDockerComposeConfig', () => { localSources = [ '/path/to/wp-plugins:/var/www/html/wp-content/plugins', '/path/tests-WordPress-PHPUnit/tests/phpunit:/wordpress-phpunit', + 'tests-user-home:/home/test', '/path/to/local/plugin:/var/www/html/wp-content/plugins/test-name', '/path/to/local/theme:/var/www/html/wp-content/themes/test-theme', ]; @@ -100,6 +114,7 @@ describe( 'buildDockerComposeConfig', () => { const expectedVolumes = [ 'tests-wordpress:/var/www/html', '/path/tests-WordPress-PHPUnit/tests/phpunit:/wordpress-phpunit', + 'tests-user-home:/home/test', '/path/to/wp-uploads:/var/www/html/wp-content/uploads', ]; expect( dockerConfig.services.phpunit.volumes ).toEqual( @@ -123,6 +138,7 @@ describe( 'buildDockerComposeConfig', () => { const expectedVolumes = [ 'tests-wordpress:/var/www/html', '/path/tests-WordPress-PHPUnit/tests/phpunit:/wordpress-phpunit', + 'tests-user-home:/home/test', 'phpunit-uploads:/var/www/html/wp-content/uploads', ]; expect( dockerConfig.services.phpunit.volumes ).toEqual( From 269b574d8cfc37b4171b4b1b5a6a542cf13d64eb Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Fri, 21 Apr 2023 16:10:27 -0700 Subject: [PATCH 08/18] Fixed Dangling Containers Some of our `docker-compose run` actions weren't removing the container after being executed. This was breaking `wp-env destroy` becuase of the volumes that were in-use. --- packages/env/lib/wordpress.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/env/lib/wordpress.js b/packages/env/lib/wordpress.js index 2275526b3847a..23b6820a1d9d0 100644 --- a/packages/env/lib/wordpress.js +++ b/packages/env/lib/wordpress.js @@ -86,6 +86,7 @@ async function configureWordPress( environment, config, spinner ) { [ 'bash', '-c', setupCommands.join( ' && ' ) ], { config: config.dockerComposeConfigPath, + commandOptions: [ '--rm' ], log: config.debug, } ); From 2c2347fe9b5705d4221ef9fbae4d68f0bf64f751 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Fri, 21 Apr 2023 21:54:34 -0700 Subject: [PATCH 09/18] Isolated Docker Service Images By defining an `image` property in our `docker-compose.yml` we were replacing the corresponding image locally after building. This meant `tests-wordpress` would have the same image as `wordpress` as long as the PHP versions matched. This only worked on accident, and as a consequence, also replaced the local `wordpress` image. This commit isolates all of the `build` operations so they don't replace the images. This also adds support so that `destroy` will remove them. --- packages/env/CHANGELOG.md | 3 + .../env/lib/build-docker-compose-config.js | 67 ++++++++----------- packages/env/lib/commands/destroy.js | 18 +++-- packages/env/lib/init-config.js | 47 ++++++++++++- 4 files changed, 87 insertions(+), 48 deletions(-) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index 2141e1fbfcafd..e5be1d83d8f49 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -10,6 +10,9 @@ - Docker containers now run as the host user. This should resolve problems with permissions arising from different owners between the host, web container, and cli container. +### Bug fix + +- Ensure `wordpress`, `tests-wordpress`, `cli`, and `tests-cli` always build the correct Docker image. ## 5.16.0 (2023-04-12) diff --git a/packages/env/lib/build-docker-compose-config.js b/packages/env/lib/build-docker-compose-config.js index 917ce50080290..e58a9c06b5dd6 100644 --- a/packages/env/lib/build-docker-compose-config.js +++ b/packages/env/lib/build-docker-compose-config.js @@ -160,48 +160,23 @@ module.exports = function buildDockerComposeConfig( config ) { const developmentPorts = `\${WP_ENV_PORT:-${ config.env.development.port }}:80`; const testsPorts = `\${WP_ENV_TESTS_PORT:-${ config.env.tests.port }}:80`; - // Set the WordPress, WP-CLI, PHPUnit PHP version if defined. - const developmentPhpVersion = config.env.development.phpVersion - ? config.env.development.phpVersion - : ''; - const testsPhpVersion = config.env.tests.phpVersion - ? config.env.tests.phpVersion - : ''; - - // Set the WordPress images with the PHP version tag. - const developmentWpImage = `wordpress${ - developmentPhpVersion ? ':php' + developmentPhpVersion : '' - }`; - const testsWpImage = `wordpress${ - testsPhpVersion ? ':php' + testsPhpVersion : '' - }`; - // Set the WordPress CLI images with the PHP version tag. - const developmentWpCliImage = `wordpress:cli${ - ! developmentPhpVersion || developmentPhpVersion.length === 0 - ? '' - : '-php' + developmentPhpVersion - }`; - const testsWpCliImage = `wordpress:cli${ - ! testsPhpVersion || testsPhpVersion.length === 0 - ? '' - : '-php' + testsPhpVersion - }`; - // Defaults are to use the most recent version of PHPUnit that provides // support for the specified version of PHP. // PHP Unit is assumed to be for Tests so use the testsPhpVersion. let phpunitTag = 'latest'; - const phpunitPhpVersion = '-php-' + testsPhpVersion + '-fpm'; - if ( testsPhpVersion === '5.6' ) { + const phpunitPhpVersion = '-php-' + config.env.tests.phpVersion + '-fpm'; + if ( config.env.tests.phpVersion === '5.6' ) { phpunitTag = '5' + phpunitPhpVersion; - } else if ( testsPhpVersion === '7.0' ) { + } else if ( config.env.tests.phpVersion === '7.0' ) { phpunitTag = '6' + phpunitPhpVersion; - } else if ( testsPhpVersion === '7.1' ) { + } else if ( config.env.tests.phpVersion === '7.1' ) { phpunitTag = '7' + phpunitPhpVersion; - } else if ( testsPhpVersion === '7.2' ) { + } else if ( config.env.tests.phpVersion === '7.2' ) { phpunitTag = '8' + phpunitPhpVersion; } else if ( - [ '7.3', '7.4', '8.0', '8.1', '8.2' ].indexOf( testsPhpVersion ) >= 0 + [ '7.3', '7.4', '8.0', '8.1', '8.2' ].indexOf( + config.env.tests.phpVersion + ) >= 0 ) { phpunitTag = '9' + phpunitPhpVersion; } @@ -238,6 +213,7 @@ module.exports = function buildDockerComposeConfig( config ) { volumes: [ 'mysql-test:/var/lib/mysql' ], }, wordpress: { + depends_on: [ 'mysql' ], build: { context: '.', dockerfile: 'WordPress.Dockerfile', @@ -247,8 +223,6 @@ module.exports = function buildDockerComposeConfig( config ) { HOST_GID: hostUser.gid, }, }, - depends_on: [ 'mysql' ], - image: developmentWpImage, ports: [ developmentPorts ], environment: { APACHE_RUN_USER: '#' + hostUser.uid, @@ -261,7 +235,15 @@ module.exports = function buildDockerComposeConfig( config ) { }, 'tests-wordpress': { depends_on: [ 'tests-mysql' ], - image: testsWpImage, + build: { + context: '.', + dockerfile: 'Tests-WordPress.Dockerfile', + args: { + HOST_USERNAME: hostUser.name, + HOST_UID: hostUser.uid, + HOST_GID: hostUser.gid, + }, + }, ports: [ testsPorts ], environment: { APACHE_RUN_USER: '#' + hostUser.uid, @@ -273,6 +255,7 @@ module.exports = function buildDockerComposeConfig( config ) { volumes: testsMounts, }, cli: { + depends_on: [ 'wordpress' ], build: { context: '.', dockerfile: 'CLI.Dockerfile', @@ -282,8 +265,6 @@ module.exports = function buildDockerComposeConfig( config ) { HOST_GID: hostUser.gid, }, }, - depends_on: [ 'wordpress' ], - image: developmentWpCliImage, volumes: developmentMounts, user: hostUser.fullUser, environment: { @@ -294,7 +275,15 @@ module.exports = function buildDockerComposeConfig( config ) { }, 'tests-cli': { depends_on: [ 'tests-wordpress' ], - image: testsWpCliImage, + build: { + context: '.', + dockerfile: 'Tests-CLI.Dockerfile', + args: { + HOST_USERNAME: hostUser.name, + HOST_UID: hostUser.uid, + HOST_GID: hostUser.gid, + }, + }, volumes: testsMounts, user: hostUser.fullUser, environment: { diff --git a/packages/env/lib/commands/destroy.js b/packages/env/lib/commands/destroy.js index 5b081392209f9..6f8ec3e23e5ff 100644 --- a/packages/env/lib/commands/destroy.js +++ b/packages/env/lib/commands/destroy.js @@ -39,7 +39,7 @@ module.exports = async function destroy( { spinner, debug } ) { } spinner.info( - 'WARNING! This will remove Docker containers, volumes, and networks associated with the WordPress instance.' + 'WARNING! This will remove Docker containers, volumes, networks, and images associated with the WordPress instance.' ); const { yesDelete } = await inquirer.prompt( [ @@ -69,10 +69,13 @@ module.exports = async function destroy( { spinner, debug } ) { const directoryHash = path.basename( workDirectoryPath ); spinner.text = 'Removing docker volumes.'; - await removeDockerItems( 'volume', directoryHash ); + await removeDockerItems( 'volume', 'name', directoryHash ); spinner.text = 'Removing docker networks.'; - await removeDockerItems( 'network', directoryHash ); + await removeDockerItems( 'network', 'name', directoryHash ); + + spinner.text = 'Removing docker images.'; + await removeDockerItems( 'image', 'reference', directoryHash + '*' ); spinner.text = 'Removing local files.'; @@ -84,12 +87,13 @@ module.exports = async function destroy( { spinner, debug } ) { /** * Removes docker items, like networks or volumes, matching the given name. * - * @param {string} itemType The item type, like "network" or "volume" - * @param {string} name Remove items whose name match this string. + * @param {string} itemType The item type, like "volume", or "network". + * @param {string} filter The filtering to search using. + * @param {string} filterValue The filtering value that we're looking for. */ -async function removeDockerItems( itemType, name ) { +async function removeDockerItems( itemType, filter, filterValue ) { const { stdout: items } = await exec( - `docker ${ itemType } ls -q --filter name=${ name }` + `docker ${ itemType } ls -q --filter ${ filter }='${ filterValue }'` ); if ( items ) { await exec( diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index b31271e30951d..1f12d8362d029 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -84,14 +84,34 @@ module.exports = async function initConfig( { await writeFile( path.resolve( config.workDirectoryPath, 'WordPress.Dockerfile' ), wordpressDockerFileContents( - dockerComposeConfig.services.wordpress.image, + getBaseDockerImage( config.env.development.phpVersion, false ), + config + ) + ); + await writeFile( + path.resolve( + config.workDirectoryPath, + 'Tests-WordPress.Dockerfile' + ), + wordpressDockerFileContents( + getBaseDockerImage( config.env.tests.phpVersion, false ), config ) ); await writeFile( path.resolve( config.workDirectoryPath, 'CLI.Dockerfile' ), - cliDockerFileContents( dockerComposeConfig.services.cli.image ) + cliDockerFileContents( + getBaseDockerImage( config.env.development.phpVersion, true ), + config + ) + ); + await writeFile( + path.resolve( config.workDirectoryPath, 'Tests-CLI.Dockerfile' ), + cliDockerFileContents( + getBaseDockerImage( config.env.tests.phpVersion, true ), + config + ) ); } else if ( ! existsSync( config.workDirectoryPath ) ) { spinner.fail( @@ -103,6 +123,29 @@ module.exports = async function initConfig( { return config; }; +/** + * Gets the base docker image to use based on our input. + * + * @param {string} phpVersion The version of PHP to get an image for. + * @param {boolean} isCLI Indicates whether or not the image is for a CLI. + * @return {string} The Docker image to use. + */ +function getBaseDockerImage( phpVersion, isCLI ) { + // We can rely on a consistent format for PHP versions. + if ( phpVersion ) { + phpVersion = ( isCLI ? '-' : ':' ) + 'php' + phpVersion; + } else { + phpVersion = ''; + } + + let wordpressImage = 'wordpress'; + if ( isCLI ) { + wordpressImage += ':cli'; + } + + return wordpressImage + phpVersion; +} + /** * Checks the configured PHP version * against the minimum version supported by Xdebug From 9b9eddae22befe661071e1afe83b123263705934 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:15:11 -0700 Subject: [PATCH 10/18] Removed Some Duplication --- .../env/lib/build-docker-compose-config.js | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/env/lib/build-docker-compose-config.js b/packages/env/lib/build-docker-compose-config.js index e58a9c06b5dd6..eef6882dc1b64 100644 --- a/packages/env/lib/build-docker-compose-config.js +++ b/packages/env/lib/build-docker-compose-config.js @@ -106,6 +106,14 @@ module.exports = function buildDockerComposeConfig( config ) { 'tests-wordpress' ); + // We use a custom Dockerfile in order to make sure that + // the current host user exists inside the container. + const imageBuildArgs = { + HOST_USERNAME: hostUser.name, + HOST_UID: hostUser.uid, + HOST_GID: hostUser.gid, + }; + // When both tests and development reference the same WP source, we need to // ensure that tests pulls from a copy of the files so that it maintains // a separate DB and config. Additionally, if the source type is local we @@ -217,11 +225,7 @@ module.exports = function buildDockerComposeConfig( config ) { build: { context: '.', dockerfile: 'WordPress.Dockerfile', - args: { - HOST_USERNAME: hostUser.name, - HOST_UID: hostUser.uid, - HOST_GID: hostUser.gid, - }, + args: imageBuildArgs, }, ports: [ developmentPorts ], environment: { @@ -238,11 +242,7 @@ module.exports = function buildDockerComposeConfig( config ) { build: { context: '.', dockerfile: 'Tests-WordPress.Dockerfile', - args: { - HOST_USERNAME: hostUser.name, - HOST_UID: hostUser.uid, - HOST_GID: hostUser.gid, - }, + args: imageBuildArgs, }, ports: [ testsPorts ], environment: { @@ -259,11 +259,7 @@ module.exports = function buildDockerComposeConfig( config ) { build: { context: '.', dockerfile: 'CLI.Dockerfile', - args: { - HOST_USERNAME: hostUser.name, - HOST_UID: hostUser.uid, - HOST_GID: hostUser.gid, - }, + args: imageBuildArgs, }, volumes: developmentMounts, user: hostUser.fullUser, @@ -278,11 +274,7 @@ module.exports = function buildDockerComposeConfig( config ) { build: { context: '.', dockerfile: 'Tests-CLI.Dockerfile', - args: { - HOST_USERNAME: hostUser.name, - HOST_UID: hostUser.uid, - HOST_GID: hostUser.gid, - }, + args: imageBuildArgs, }, volumes: testsMounts, user: hostUser.fullUser, From 8ddef456ba274a65419f789e8c8767a5446d089a Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:15:40 -0700 Subject: [PATCH 11/18] Added Destroy Note To Changelog --- packages/env/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index e5be1d83d8f49..4f96bf03240f2 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -8,7 +8,8 @@ - Automatically add the environment's port to `WP_TESTS_DOMAIN`. - `run` command now has a `--env-cwd` option to set the working directory in the container for the command to execute from. - Docker containers now run as the host user. This should resolve problems with permissions arising from different owners -between the host, web container, and cli container. +between the host, web container, and cli container. You _may_ need to run `npx wp-env destroy` to get everything working as +expected. ### Bug fix From d252b8bd1ed48074d36cd0beffe9a19ba54e1197 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:19:44 -0700 Subject: [PATCH 12/18] Documented getHostUser Windows Handling --- packages/env/lib/get-host-user.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/env/lib/get-host-user.js b/packages/env/lib/get-host-user.js index 77d737085c626..ab88b3f728d6b 100644 --- a/packages/env/lib/get-host-user.js +++ b/packages/env/lib/get-host-user.js @@ -10,6 +10,14 @@ const os = require( 'os' ); */ module.exports = function getHostUser() { const hostUser = os.userInfo(); + + // On Windows the uid and gid will be -1. Since there isn't a great way to handle this, + // we're just going to say that the host user is root. On Windows you'll likely be + // using WSL to run commands inside the container, which has a uid and gid. If + // you aren't, you'll be mounting directories from Windows, to a Linux + // VM (Docker Desktop uses one), to the guest OS. I assume that + // when dealing with this configuration that file ownership + // has the same kind of magic handling that macOS uses. const uid = ( hostUser.uid === -1 ? 0 : hostUser.uid ).toString(); const gid = ( hostUser.gid === -1 ? 0 : hostUser.gid ).toString(); From 3867a1d732ad177c18a3faeb26b0cc0244730e90 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:11:32 -0700 Subject: [PATCH 13/18] Update packages/env/CHANGELOG.md Co-authored-by: Noah Allen --- packages/env/CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index 4f96bf03240f2..dbb2e245d37fe 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -8,8 +8,7 @@ - Automatically add the environment's port to `WP_TESTS_DOMAIN`. - `run` command now has a `--env-cwd` option to set the working directory in the container for the command to execute from. - Docker containers now run as the host user. This should resolve problems with permissions arising from different owners -between the host, web container, and cli container. You _may_ need to run `npx wp-env destroy` to get everything working as -expected. +between the host, web container, and cli container. If you still encounter permissions issues, try running `npx wp-env destroy` so that the environment can be recreated with the correct permissions. ### Bug fix From 27eaca168a7c26b1390bcec4f6a0626f1cc823bb Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 27 Apr 2023 16:06:22 -0700 Subject: [PATCH 14/18] Fixed Debian Stretch Repositories Debian stretch was just removed from deb.debian.org and moved to the archive repository. This commit changes the sources so that, on stretch, the apt-get update won't break. --- packages/env/lib/init-config.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index 1f12d8362d029..1c09bcca37d01 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -203,6 +203,14 @@ function wordpressDockerFileContents( image, config ) { return `FROM ${ image } +# Update apt sources for archived versions of Debian. + +# stretch +RUN sed -i 's|deb.debian.org/debian stretch|archive.debian.org/debian stretch|g' /etc/apt/sources.list +RUN sed -i 's|security.debian.org/debian-security stretch|archive.debian.org/debian-security stretch|g' /etc/apt/sources.list +RUN sed -i '/stretch-updates/d' /etc/apt/sources.list + +# Prepare dependencies RUN apt-get -qy install $PHPIZE_DEPS && touch /usr/local/etc/php/php.ini ${ shouldInstallXdebug ? installXdebug( config.xdebug ) : '' } @@ -215,7 +223,7 @@ RUN groupadd -g $HOST_GID $HOST_USERNAME || true RUN useradd -m -u $HOST_UID -g $HOST_GID $HOST_USERNAME || true # Set up sudo so they can have root access when using 'run' commands. -RUN apt-get update +RUN apt-get update -qy RUN apt-get -qy install sudo RUN echo "$HOST_USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers `; From 9cd128e51619bf8502984d3401762765c85aa315 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 27 Apr 2023 16:13:52 -0700 Subject: [PATCH 15/18] Updated Documentation --- packages/env/README.md | 28 +--------------------------- packages/env/lib/cli.js | 2 +- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/packages/env/README.md b/packages/env/README.md index f9e913ea91761..1919fbd1a0416 100644 --- a/packages/env/README.md +++ b/packages/env/README.md @@ -173,25 +173,6 @@ $ wp-env destroy $ wp-env start ``` -### 7. Debug mode and inspecting the generated docker files. - -`wp-env` uses docker behind the scenes. Inspecting the generated docker-compose file can help to understand what's going on. - -Start `wp-env` in debug mode: - -```sh -wp-env start --debug -``` - -`wp-env` will output its config which includes `dockerComposeConfigPath`: - -```sh -ℹ Config: - ... - "dockerComposeConfigPath": "/Users/$USERNAME/.wp-env/5a619d332a92377cd89feb339c67b833/docker-compose.yml", - ... -``` - ## Using included WordPress PHPUnit test files Out of the box `wp-env` includes the [WordPress' PHPUnit test files](https://develop.svn.wordpress.org/trunk/tests/phpunit/) corresponding to the version of WordPress installed. There is an environment variable, `WP_TESTS_DIR`, which points to the location of these files within each container. By including these files in the environment, we remove the need for you to use a package or install and mount them yourself. If you do not want to use these files, you should ignore the `WP_TESTS_DIR` environment variable and load them from the location of your choosing. @@ -409,13 +390,6 @@ Success: Installed 1 of 1 plugins. ✔ Ran `plugin install custom-post-type-ui` in 'cli'. (in 6s 483ms) ``` -**NOTE**: Depending on your host OS, you may experience errors when trying to install plugins or themes (e.g. `Warning: Could not create directory.`). This is typically because the user ID used within the container does not have write access to the mounted directories created by `wp-env`. To resolve this, run the `docker-compose` command directly from the directory created by `wp-env` and add `-u $(id -u)` and `-e HOME=/tmp` the `run` command as options: - -```sh -$ cd ~/wp-env/500cd328b649d63e882d5c4695871d04 -$ docker-compose run --rm -u $(id -u) -e HOME=/tmp cli [plugin|theme] install -``` - ### `wp-env destroy` ```sh @@ -445,7 +419,7 @@ Options: ### `wp-env install-path` -Outputs the absolute path to the WordPress environment files. +Get the path where all of the environment files are stored. This includes the Docker files, WordPress, PHPUnit files, and any sources that were downloaded. Example: diff --git a/packages/env/lib/cli.js b/packages/env/lib/cli.js index 68316855c6bed..13bee46974bea 100644 --- a/packages/env/lib/cli.js +++ b/packages/env/lib/cli.js @@ -215,7 +215,7 @@ module.exports = function cli() { ); yargs.command( 'install-path', - 'Get the path where environment files are located.', + 'Get the path where all of the environment files are stored. This includes the Docker files, WordPress, PHPUnit files, and any sources that were downloaded.', () => {}, withSpinner( env.installPath ) ); From 3a4146a39468be00852ee85be80063595b61ec8e Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 27 Apr 2023 16:15:42 -0700 Subject: [PATCH 16/18] Added Debian Stretch Archive List Link --- packages/env/lib/init-config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index 1c09bcca37d01..00d397f408e7e 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -205,7 +205,7 @@ function wordpressDockerFileContents( image, config ) { # Update apt sources for archived versions of Debian. -# stretch +# stretch (https://lists.debian.org/debian-devel-announce/2023/03/msg00006.html) RUN sed -i 's|deb.debian.org/debian stretch|archive.debian.org/debian stretch|g' /etc/apt/sources.list RUN sed -i 's|security.debian.org/debian-security stretch|archive.debian.org/debian-security stretch|g' /etc/apt/sources.list RUN sed -i '/stretch-updates/d' /etc/apt/sources.list From 5d9f976fc53047ab9037476997b799d0ed70bc2a Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Thu, 27 Apr 2023 16:51:05 -0700 Subject: [PATCH 17/18] Moved CLI Sleep Into Dockerfile --- packages/env/lib/build-docker-compose-config.js | 2 -- packages/env/lib/init-config.js | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/env/lib/build-docker-compose-config.js b/packages/env/lib/build-docker-compose-config.js index 131cc8c1af077..eef6882dc1b64 100644 --- a/packages/env/lib/build-docker-compose-config.js +++ b/packages/env/lib/build-docker-compose-config.js @@ -263,7 +263,6 @@ module.exports = function buildDockerComposeConfig( config ) { }, volumes: developmentMounts, user: hostUser.fullUser, - command: 'sleep infinity', // Keeps the service alive. environment: { ...dbEnv.credentials, ...dbEnv.development, @@ -279,7 +278,6 @@ module.exports = function buildDockerComposeConfig( config ) { }, volumes: testsMounts, user: hostUser.fullUser, - command: 'sleep infinity', // Keeps the service alive. environment: { ...dbEnv.credentials, ...dbEnv.tests, diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index 00d397f408e7e..547cc372f8988 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -269,5 +269,8 @@ RUN adduser -h /home/$HOST_USERNAME -G $( getent group $HOST_GID | cut -d: -f1 ) # Switch back now that we're done. USER www-data + +# Have the container sleep infinitely to keep it alive for us to run commands on it. +CMD [ "/bin/sh", "-c", "while true; do sleep 2073600; done" ] `; } From 3424a05fea3eeb04cf90306d1ccbd72233e80e04 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 27 Apr 2023 18:35:33 -0700 Subject: [PATCH 18/18] Update CHANGELOG.md --- packages/env/CHANGELOG.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index a0abbaf84a689..aef44414c0db6 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,6 +2,15 @@ ## Unreleased +### Breaking Change + +- Docker containers now run as the host user. This should resolve problems with permissions arising from different owners +between the host, web container, and cli container. If you still encounter permissions issues, try running `npx wp-env destroy` so that the environment can be recreated with the correct permissions. + +### Bug fix + +- Ensure `wordpress`, `tests-wordpress`, `cli`, and `tests-cli` always build the correct Docker image. + ### Enhancement - `wp-env run ...` now uses docker-compose exec instead of docker-compose run. As a result, it is much faster, since commands are executed against existing services, rather than creating them from scratch each time. @@ -13,12 +22,6 @@ - Use test environment's `WP_SITEURL` instead of `WP_TESTS_DOMAIN` as the WordPress URL. - Automatically add the environment's port to `WP_TESTS_DOMAIN`. - `run` command now has a `--env-cwd` option to set the working directory in the container for the command to execute from. -- Docker containers now run as the host user. This should resolve problems with permissions arising from different owners -between the host, web container, and cli container. If you still encounter permissions issues, try running `npx wp-env destroy` so that the environment can be recreated with the correct permissions. - -### Bug fix - -- Ensure `wordpress`, `tests-wordpress`, `cli`, and `tests-cli` always build the correct Docker image. ## 5.16.0 (2023-04-12)