@wordpress/env 5.16.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +189 -120
  2. package/lib/build-docker-compose-config.js +67 -96
  3. package/lib/cache.js +1 -0
  4. package/lib/cli.js +23 -3
  5. package/lib/commands/clean.js +14 -1
  6. package/lib/commands/destroy.js +12 -37
  7. package/lib/commands/index.js +1 -0
  8. package/lib/commands/install-path.js +1 -0
  9. package/lib/commands/logs.js +1 -0
  10. package/lib/commands/run.js +79 -15
  11. package/lib/commands/start.js +32 -10
  12. package/lib/commands/stop.js +1 -0
  13. package/lib/config/add-or-replace-port.js +41 -0
  14. package/lib/config/db-env.js +1 -0
  15. package/lib/config/detect-directory-type.js +1 -1
  16. package/lib/config/get-cache-directory.js +39 -0
  17. package/lib/config/get-config-from-environment-vars.js +86 -0
  18. package/lib/config/index.js +7 -5
  19. package/lib/config/load-config.js +92 -0
  20. package/lib/config/merge-configs.js +104 -0
  21. package/lib/config/parse-config.js +418 -157
  22. package/lib/config/parse-source-string.js +164 -0
  23. package/lib/config/post-process-config.js +202 -0
  24. package/lib/config/read-raw-config-file.js +7 -33
  25. package/lib/config/test/__snapshots__/config-integration.js.snap +271 -0
  26. package/lib/config/test/add-or-replace-port.js +81 -0
  27. package/lib/config/test/config-integration.js +143 -0
  28. package/lib/config/test/get-cache-directory.js +57 -0
  29. package/lib/config/test/merge-configs.js +111 -0
  30. package/lib/config/test/parse-config.js +342 -0
  31. package/lib/config/test/parse-source-string.js +154 -0
  32. package/lib/config/test/post-process-config.js +295 -0
  33. package/lib/config/test/read-raw-config-file.js +19 -63
  34. package/lib/config/test/validate-config.js +305 -0
  35. package/lib/config/validate-config.js +103 -40
  36. package/lib/env.js +2 -0
  37. package/lib/execute-after-setup.js +51 -0
  38. package/lib/get-host-user.js +31 -0
  39. package/lib/init-config.js +181 -63
  40. package/lib/md5.js +1 -0
  41. package/lib/parse-xdebug-mode.js +1 -0
  42. package/lib/retry.js +1 -0
  43. package/lib/test/__snapshots__/md5.js.snap +9 -0
  44. package/lib/test/build-docker-compose-config.js +134 -0
  45. package/lib/test/cache.js +154 -0
  46. package/lib/test/cli.js +149 -0
  47. package/lib/test/execute-after-setup.js +66 -0
  48. package/lib/test/md5.js +35 -0
  49. package/lib/test/parse-xdebug-mode.js +41 -0
  50. package/lib/wordpress.js +5 -33
  51. package/package.json +2 -2
  52. package/lib/config/config.js +0 -357
  53. package/lib/config/test/__snapshots__/config.js.snap +0 -83
  54. package/lib/config/test/config.js +0 -1203
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * @typedef {import('./config').WPServiceConfig} WPServiceConfig
5
- * @typedef {import('./config').WPSource} WPSource
4
+ * @typedef {import('./parse-source-string').WPSource} WPSource
6
5
  */
7
6
 
8
7
  /**
@@ -12,81 +11,145 @@
12
11
  class ValidationError extends Error {}
13
12
 
14
13
  /**
15
- * Validates a config object by throwing a ValidationError if any of its properties
16
- * do not match the required format.
14
+ * Validates that the value is a string.
17
15
  *
18
- * @param {Object} config A config object to validate.
19
- * @param {?string} envLocation Identifies if the error occurred in a specific environment property.
20
- * @return {Object} The passed config object with no modifications.
16
+ * @param {string} configFile The configuration file we're validating.
17
+ * @param {string} configKey The configuration key we're validating.
18
+ * @param {number} value The value to check.
21
19
  */
22
- function validateConfig( config, envLocation ) {
23
- const envPrefix = envLocation ? `env.${ envLocation }.` : '';
24
- if ( config.core !== null && typeof config.core !== 'string' ) {
20
+ function checkString( configFile, configKey, value ) {
21
+ if ( typeof value !== 'string' ) {
25
22
  throw new ValidationError(
26
- `Invalid .wp-env.json: "${ envPrefix }core" must be null or a string.`
23
+ `Invalid ${ configFile }: "${ configKey }" must be a string.`
27
24
  );
28
25
  }
26
+ }
29
27
 
30
- if (
31
- ! Array.isArray( config.plugins ) ||
32
- config.plugins.some( ( plugin ) => typeof plugin !== 'string' )
33
- ) {
28
+ /**
29
+ * Validates the port and throws if it isn't valid.
30
+ *
31
+ * @param {string} configFile The configuration file we're validating.
32
+ * @param {string} configKey The configuration key we're validating.
33
+ * @param {number} port The port to check.
34
+ */
35
+ function checkPort( configFile, configKey, port ) {
36
+ if ( ! Number.isInteger( port ) ) {
34
37
  throw new ValidationError(
35
- `Invalid .wp-env.json: "${ envPrefix }plugins" must be an array of strings.`
38
+ `Invalid ${ configFile }: "${ configKey }" must be an integer.`
36
39
  );
37
40
  }
38
41
 
39
- if (
40
- ! Array.isArray( config.themes ) ||
41
- config.themes.some( ( theme ) => typeof theme !== 'string' )
42
- ) {
42
+ if ( port < 0 || port > 65535 ) {
43
43
  throw new ValidationError(
44
- `Invalid .wp-env.json: "${ envPrefix }themes" must be an array of strings.`
44
+ `Invalid ${ configFile }: "${ configKey }" must be a valid port.`
45
45
  );
46
46
  }
47
+ }
47
48
 
48
- if ( ! Number.isInteger( config.port ) ) {
49
+ /**
50
+ * Validates the array and throws if it isn't valid.
51
+ *
52
+ * @param {string} configFile The config file we're validating.
53
+ * @param {string} configKey The configuration key we're validating.
54
+ * @param {string[]} array The array that we're checking.
55
+ */
56
+ function checkStringArray( configFile, configKey, array ) {
57
+ if ( ! Array.isArray( array ) ) {
49
58
  throw new ValidationError(
50
- `Invalid .wp-env.json: "${ envPrefix }port" must be an integer.`
59
+ `Invalid ${ configFile }: "${ configKey }" must be an array.`
51
60
  );
52
61
  }
53
62
 
54
- if ( typeof config.config !== 'object' ) {
63
+ if ( array.some( ( value ) => typeof value !== 'string' ) ) {
55
64
  throw new ValidationError(
56
- `Invalid .wp-env.json: "${ envPrefix }config" must be an object.`
65
+ `Invalid ${ configFile }: "${ configKey }" must be an array of strings.`
57
66
  );
58
67
  }
68
+ }
59
69
 
60
- if ( typeof config.mappings !== 'object' ) {
70
+ /**
71
+ * Validates the object and throws if it isn't valid.
72
+ *
73
+ * @param {string} configFile The config file we're validating.
74
+ * @param {string} configKey The configuration key we're validating.
75
+ * @param {string[]} obj The object that we're checking.
76
+ * @param {string[]} allowTypes The types that are allowed.
77
+ */
78
+ function checkObjectWithValues( configFile, configKey, obj, allowTypes ) {
79
+ if ( allowTypes === undefined ) {
80
+ allowTypes = [];
81
+ }
82
+
83
+ if ( typeof obj !== 'object' || Array.isArray( obj ) ) {
61
84
  throw new ValidationError(
62
- `Invalid .wp-env.json: "${ envPrefix }mappings" must be an object.`
85
+ `Invalid ${ configFile }: "${ configKey }" must be an object.`
63
86
  );
64
87
  }
65
88
 
66
- for ( const [ wpDir, localDir ] of Object.entries( config.mappings ) ) {
67
- if ( ! localDir || typeof localDir !== 'string' ) {
89
+ for ( const key in obj ) {
90
+ if ( ! obj[ key ] && ! allowTypes.includes( 'empty' ) ) {
91
+ throw new ValidationError(
92
+ `Invalid ${ configFile }: "${ configKey }.${ key }" must not be empty.`
93
+ );
94
+ }
95
+
96
+ // Identify arrays uniquely.
97
+ const type = Array.isArray( obj[ key ] ) ? 'array' : typeof obj[ key ];
98
+
99
+ if ( ! allowTypes.includes( type ) ) {
68
100
  throw new ValidationError(
69
- `Invalid .wp-env.json: "${ envPrefix }mappings.${ wpDir }" should be a string.`
101
+ `Invalid ${ configFile }: "${ configKey }.${ key }" must be a ${ allowTypes.join(
102
+ ' or '
103
+ ) }.`
70
104
  );
71
105
  }
72
106
  }
107
+ }
73
108
 
74
- if (
75
- config.phpVersion &&
76
- ! (
77
- typeof config.phpVersion === 'string' &&
78
- config.phpVersion.length === 3
79
- )
80
- ) {
109
+ /**
110
+ * Validates the version and throws if it isn't valid.
111
+ *
112
+ * @param {string} configFile The config file we're validating.
113
+ * @param {string} configKey The configuration key we're validating.
114
+ * @param {string} version The version that we're checking.
115
+ */
116
+ function checkVersion( configFile, configKey, version ) {
117
+ if ( typeof version !== 'string' ) {
81
118
  throw new ValidationError(
82
- `Invalid .wp-env.json: "${ envPrefix }phpVersion" must be a string of the format "0.0".`
119
+ `Invalid ${ configFile }: "${ configKey }" must be a string.`
83
120
  );
84
121
  }
85
122
 
86
- return config;
123
+ if ( ! version.match( /[0-9]+(?:\.[0-9]+)*/ ) ) {
124
+ throw new ValidationError(
125
+ `Invalid ${ configFile }: "${ configKey }" must be a string of the format "X", "X.X", or "X.X.X".`
126
+ );
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Validates the url and throws if it isn't valid.
132
+ *
133
+ * @param {string} configFile The config file we're validating.
134
+ * @param {string} configKey The configuration key we're validating.
135
+ * @param {string} url The URL that we're checking.
136
+ */
137
+ function checkValidURL( configFile, configKey, url ) {
138
+ try {
139
+ new URL( url );
140
+ } catch {
141
+ throw new ValidationError(
142
+ `Invalid ${ configFile }: "${ configKey }" must be a valid URL.`
143
+ );
144
+ }
87
145
  }
88
146
 
89
147
  module.exports = {
90
- validateConfig,
91
148
  ValidationError,
149
+ checkString,
150
+ checkPort,
151
+ checkStringArray,
152
+ checkObjectWithValues,
153
+ checkVersion,
154
+ checkValidURL,
92
155
  };
package/lib/env.js CHANGED
@@ -3,9 +3,11 @@
3
3
  * Internal dependencies
4
4
  */
5
5
  const { ValidationError } = require( './config' );
6
+ const { AfterSetupError } = require( './execute-after-setup' );
6
7
  const commands = require( './commands' );
7
8
 
8
9
  module.exports = {
9
10
  ...commands,
10
11
  ValidationError,
12
+ AfterSetupError,
11
13
  };
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+ /**
3
+ * External dependencies
4
+ */
5
+ const { execSync } = require( 'child_process' );
6
+
7
+ /**
8
+ * @typedef {import('./config').WPConfig} WPConfig
9
+ */
10
+
11
+ /**
12
+ * Error subtype which indicates that the afterSetup command failed.
13
+ */
14
+ class AfterSetupError extends Error {}
15
+
16
+ /**
17
+ * Executes any defined afterSetup command.
18
+ *
19
+ * @param {WPConfig} config The config object to use.
20
+ * @param {Object} spinner A CLI spinner which indciates progress.
21
+ */
22
+ function executeAfterSetup( config, spinner ) {
23
+ if ( ! config.afterSetup ) {
24
+ return;
25
+ }
26
+
27
+ spinner.text = 'Executing Script: afterSetup';
28
+
29
+ try {
30
+ let output = execSync( config.afterSetup, {
31
+ encoding: 'utf-8',
32
+ stdio: 'pipe',
33
+ env: process.env,
34
+ } );
35
+
36
+ // Remove any trailing whitespace for nicer output.
37
+ output = output.trimRight();
38
+
39
+ // We don't need to bother with any output if there isn't any.
40
+ if ( output ) {
41
+ spinner.info( `After Setup:\n${ output }` );
42
+ }
43
+ } catch ( error ) {
44
+ throw new AfterSetupError( `After Setup:\n${ error.stderr }` );
45
+ }
46
+ }
47
+
48
+ module.exports = {
49
+ AfterSetupError,
50
+ executeAfterSetup,
51
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+ /**
3
+ * External dependencies
4
+ */
5
+ const os = require( 'os' );
6
+
7
+ /**
8
+ * Gets information about the host user.
9
+ *
10
+ * @return {Object} The host user's name, uid, and gid.
11
+ */
12
+ module.exports = function getHostUser() {
13
+ const hostUser = os.userInfo();
14
+
15
+ // On Windows the uid and gid will be -1. Since there isn't a great way to handle this,
16
+ // we're just going to say that the host user is root. On Windows you'll likely be
17
+ // using WSL to run commands inside the container, which has a uid and gid. If
18
+ // you aren't, you'll be mounting directories from Windows, to a Linux
19
+ // VM (Docker Desktop uses one), to the guest OS. I assume that
20
+ // when dealing with this configuration that file ownership
21
+ // has the same kind of magic handling that macOS uses.
22
+ const uid = ( hostUser.uid === -1 ? 0 : hostUser.uid ).toString();
23
+ const gid = ( hostUser.gid === -1 ? 0 : hostUser.gid ).toString();
24
+
25
+ return {
26
+ name: hostUser.username,
27
+ uid,
28
+ gid,
29
+ fullUser: uid + ':' + gid,
30
+ };
31
+ };
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
  /**
2
3
  * External dependencies
3
4
  */
@@ -5,12 +6,11 @@ const path = require( 'path' );
5
6
  const { writeFile, mkdir } = require( 'fs' ).promises;
6
7
  const { existsSync } = require( 'fs' );
7
8
  const yaml = require( 'js-yaml' );
8
- const os = require( 'os' );
9
9
 
10
10
  /**
11
11
  * Internal dependencies
12
12
  */
13
- const { readConfig } = require( './config' );
13
+ const { loadConfig, ValidationError } = require( './config' );
14
14
  const buildDockerComposeConfig = require( './build-docker-compose-config' );
15
15
 
16
16
  /**
@@ -39,8 +39,7 @@ module.exports = async function initConfig( {
39
39
  xdebug = 'off',
40
40
  writeChanges = false,
41
41
  } ) {
42
- const configPath = path.resolve( '.wp-env.json' );
43
- const config = await readConfig( configPath );
42
+ const config = await loadConfig( path.resolve( '.' ) );
44
43
  config.debug = debug;
45
44
 
46
45
  // Adding this to the config allows the start command to understand that the
@@ -81,13 +80,23 @@ module.exports = async function initConfig( {
81
80
  yaml.dump( dockerComposeConfig )
82
81
  );
83
82
 
84
- await writeFile(
85
- path.resolve( config.workDirectoryPath, 'Dockerfile' ),
86
- dockerFileContents(
87
- dockerComposeConfig.services.wordpress.image,
88
- config
89
- )
90
- );
83
+ // Write four Dockerfiles for each service we provided.
84
+ // (WordPress and CLI services, then a development and test environment for each.)
85
+ for ( const imageType of [ 'WordPress', 'CLI' ] ) {
86
+ for ( const envType of [ 'development', 'tests' ] ) {
87
+ await writeFile(
88
+ path.resolve(
89
+ config.workDirectoryPath,
90
+ `${
91
+ envType === 'tests' ? 'Tests-' : ''
92
+ }${ imageType }.Dockerfile`
93
+ ),
94
+ imageType === 'WordPress'
95
+ ? wordpressDockerFileContents( envType, config )
96
+ : cliDockerFileContents( envType, config )
97
+ );
98
+ }
99
+ }
91
100
  } else if ( ! existsSync( config.workDirectoryPath ) ) {
92
101
  spinner.fail(
93
102
  '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.'
@@ -99,80 +108,189 @@ module.exports = async function initConfig( {
99
108
  };
100
109
 
101
110
  /**
102
- * Checks the configured PHP version
103
- * against the minimum version supported by Xdebug
111
+ * Generates the Dockerfile used by wp-env's `wordpress` and `tests-wordpress` instances.
104
112
  *
105
- * @param {WPConfig} config
106
- * @return {boolean} Whether the PHP version is supported by Xdebug
113
+ * @param {string} env The environment we're installing -- development or tests.
114
+ * @param {WPConfig} config The configuration object.
115
+ * @return {string} The dockerfile contents.
107
116
  */
108
- function checkXdebugPhpCompatibility( config ) {
109
- // By default, an undefined phpVersion uses the version on the docker image,
110
- // which is supported by Xdebug 3.
111
- const phpCompatibility = true;
112
-
113
- // If PHP version is defined
114
- // ensure it meets the Xdebug minimum compatibility requirment.
115
- if ( config.env.development.phpVersion ) {
116
- const versionTokens = config.env.development.phpVersion.split( '.' );
117
- const majorVer = parseInt( versionTokens[ 0 ] );
118
- const minorVer = parseInt( versionTokens[ 1 ] );
117
+ function wordpressDockerFileContents( env, config ) {
118
+ const phpVersion = config.env[ env ].phpVersion
119
+ ? ':php' + config.env[ env ].phpVersion
120
+ : '';
119
121
 
120
- if ( isNaN( majorVer ) || isNaN( minorVer ) ) {
121
- throw new Error(
122
- 'Something went wrong when parsing the PHP version.'
123
- );
124
- }
122
+ return `FROM wordpress${ phpVersion }
125
123
 
126
- // Xdebug 3 supports 7.2 and higher
127
- // Ensure user has specified a compatible PHP version.
128
- if ( majorVer < 7 || ( majorVer === 7 && minorVer < 2 ) ) {
129
- throw new Error( 'Cannot use XDebug 3 on PHP < 7.2.' );
130
- }
131
- }
124
+ # Update apt sources for archived versions of Debian.
125
+
126
+ # stretch (https://lists.debian.org/debian-devel-announce/2023/03/msg00006.html)
127
+ RUN sed -i 's|deb.debian.org/debian stretch|archive.debian.org/debian stretch|g' /etc/apt/sources.list
128
+ RUN sed -i 's|security.debian.org/debian-security stretch|archive.debian.org/debian-security stretch|g' /etc/apt/sources.list
129
+ RUN sed -i '/stretch-updates/d' /etc/apt/sources.list
132
130
 
133
- return phpCompatibility;
131
+ # Create the host's user so that we can match ownership in the container.
132
+ ARG HOST_USERNAME
133
+ ARG HOST_UID
134
+ ARG HOST_GID
135
+ # When the IDs are already in use we can still safely move on.
136
+ RUN groupadd -g $HOST_GID $HOST_USERNAME || true
137
+ RUN useradd -m -u $HOST_UID -g $HOST_GID $HOST_USERNAME || true
138
+
139
+ # Install any dependencies we need in the container.
140
+ ${ installDependencies( 'wordpress', env, config ) }`;
134
141
  }
135
142
 
136
143
  /**
137
- * Generates the Dockerfile used by wp-env's development instance.
144
+ * Generates the Dockerfile used by wp-env's `cli` and `tests-cli` instances.
138
145
  *
139
- * @param {string} image The base docker image to use.
146
+ * @param {string} env The environment we're installing -- development or tests.
140
147
  * @param {WPConfig} config The configuration object.
141
- *
142
148
  * @return {string} The dockerfile contents.
143
149
  */
144
- function dockerFileContents( image, config ) {
145
- // Don't install XDebug unless it is explicitly required.
146
- let shouldInstallXdebug = false;
150
+ function cliDockerFileContents( env, config ) {
151
+ const phpVersion = config.env[ env ].phpVersion
152
+ ? '-php' + config.env[ env ].phpVersion
153
+ : '';
154
+
155
+ return `FROM wordpress:cli${ phpVersion }
156
+
157
+ # Switch to root so we can create users.
158
+ USER root
159
+
160
+ # Create the host's user so that we can match ownership in the container.
161
+ ARG HOST_USERNAME
162
+ ARG HOST_UID
163
+ ARG HOST_GID
164
+ # When the IDs are already in use we can still safely move on.
165
+ RUN addgroup -g $HOST_GID $HOST_USERNAME || true
166
+ RUN adduser -h /home/$HOST_USERNAME -G $( getent group $HOST_GID | cut -d: -f1 ) -u $HOST_UID $HOST_USERNAME || true
167
+
168
+ # Install any dependencies we need in the container.
169
+ ${ installDependencies( 'cli', env, config ) }
170
+
171
+ # Switch back to the original user now that we're done.
172
+ USER www-data
173
+
174
+ # Have the container sleep infinitely to keep it alive for us to run commands on it.
175
+ CMD [ "/bin/sh", "-c", "while true; do sleep 2073600; done" ]
176
+ `;
177
+ }
178
+
179
+ /**
180
+ * Generates content for the Dockerfile to install dependencies.
181
+ *
182
+ * @param {string} service The kind of service that we're installing dependencies on ('wordpress' or 'cli').
183
+ * @param {string} env The environment we're installing dependencies for ('development' or 'tests').
184
+ * @param {WPConfig} config The configuration object.
185
+ * @return {string} The Dockerfile content for installing dependencies.
186
+ */
187
+ function installDependencies( service, env, config ) {
188
+ let dockerFileContent = '';
189
+
190
+ // At times we may need to evaluate the environment. This is because the
191
+ // WordPress image uses Ubuntu while the CLI image uses Alpine.
147
192
 
148
- if ( config.xdebug !== 'off' ) {
149
- const usingCompatiblePhp = checkXdebugPhpCompatibility( config );
193
+ // Start with some environment-specific dependency installations.
194
+ switch ( service ) {
195
+ case 'wordpress': {
196
+ dockerFileContent += `
197
+ # Make sure we're working with the latest packages.
198
+ RUN apt-get -qy update
150
199
 
151
- if ( usingCompatiblePhp ) {
152
- shouldInstallXdebug = true;
200
+ # Install some basic PHP dependencies.
201
+ RUN apt-get -qy install $PHPIZE_DEPS && touch /usr/local/etc/php/php.ini
202
+
203
+ # Set up sudo so they can have root access.
204
+ RUN apt-get -qy install sudo
205
+ RUN echo "$HOST_USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers`;
206
+ break;
207
+ }
208
+ case 'cli': {
209
+ dockerFileContent += `
210
+ RUN apk update
211
+ RUN apk --no-cache add $PHPIZE_DEPS && touch /usr/local/etc/php/php.ini
212
+ RUN apk --no-cache add sudo linux-headers
213
+ RUN echo "$HOST_USERNAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers`;
214
+ break;
215
+ }
216
+ default: {
217
+ throw new Error( `Invalid service "${ service }" given` );
153
218
  }
154
219
  }
155
220
 
156
- return `FROM ${ image }
221
+ dockerFileContent += getXdebugConfig(
222
+ config.xdebug,
223
+ config.env[ env ].phpVersion
224
+ );
157
225
 
158
- RUN apt-get -qy install $PHPIZE_DEPS && touch /usr/local/etc/php/php.ini
159
- ${ shouldInstallXdebug ? installXdebug( config.xdebug ) : '' }
160
- `;
226
+ // Add better PHP settings.
227
+ dockerFileContent += `
228
+ RUN echo 'upload_max_filesize = 1G' >> /usr/local/etc/php/php.ini
229
+ RUN echo 'post_max_size = 1G' >> /usr/local/etc/php/php.ini`;
230
+
231
+ // Make sure Composer is available for use in all services.
232
+ dockerFileContent += `
233
+ RUN curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php
234
+ RUN export COMPOSER_HASH=\`curl -sS https://composer.github.io/installer.sig\` && php -r "if (hash_file('SHA384', '/tmp/composer-setup.php') === '$COMPOSER_HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('/tmp/composer-setup.php'); } echo PHP_EOL;"
235
+ RUN php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer
236
+ RUN rm /tmp/composer-setup.php`;
237
+
238
+ // Install any Composer packages we might need globally.
239
+ // Make sure to do this as the user and ensure the binaries are available in the $PATH.
240
+ dockerFileContent += `
241
+ USER $HOST_USERNAME
242
+ ENV PATH="\${PATH}:/home/$HOST_USERNAME/.composer/vendor/bin"
243
+ RUN composer global require --dev yoast/phpunit-polyfills:"^1.0"
244
+ USER root`;
245
+
246
+ return dockerFileContent;
161
247
  }
162
248
 
163
- function installXdebug( enableXdebug ) {
164
- const isLinux = os.type() === 'Linux';
165
- // Discover client host does not appear to work on macOS with Docker.
166
- const clientDetectSettings = isLinux
167
- ? 'xdebug.discover_client_host=true'
168
- : 'xdebug.client_host="host.docker.internal"';
249
+ /**
250
+ * Gets the Xdebug config based on the options in the config object.
251
+ *
252
+ * @param {string} xdebugMode The Xdebug mode set in the config.
253
+ * @param {string} phpVersion The php version set in the environment.
254
+ * @return {string} The Xdebug config -- can be an empty string when it's not used.
255
+ */
256
+ function getXdebugConfig( xdebugMode = 'off', phpVersion ) {
257
+ if ( xdebugMode === 'off' ) {
258
+ return '';
259
+ }
260
+
261
+ let xdebugVersion = 'xdebug';
262
+
263
+ if ( phpVersion ) {
264
+ const versionTokens = phpVersion.split( '.' );
265
+ const majorVer = parseInt( versionTokens[ 0 ] );
266
+ const minorVer = parseInt( versionTokens[ 1 ] );
267
+
268
+ if ( isNaN( majorVer ) || isNaN( minorVer ) ) {
269
+ throw new ValidationError(
270
+ 'Something went wrong when parsing the PHP version.'
271
+ );
272
+ }
273
+
274
+ // Throw an error if someone tries to use Xdebug with an unsupported PHP version.
275
+ // Xdebug 3 only supports 7.2 and higher.
276
+ if ( majorVer < 7 || ( majorVer === 7 && minorVer < 2 ) ) {
277
+ throw new ValidationError(
278
+ `Cannot use XDebug 3 with PHP < 7.2. Your PHP version is ${ phpVersion }.`
279
+ );
280
+ }
281
+
282
+ // For now, we support PHP 7 by installing the final version of Xdebug to
283
+ // support PHP 7 when the environment uses that version. By default, use the
284
+ // latest version.
285
+ if ( majorVer === 7 ) {
286
+ xdebugVersion = 'xdebug-3.1.6';
287
+ }
288
+ }
169
289
 
170
290
  return `
171
- # Install Xdebug:
172
- RUN if [ -z "$(pecl list | grep xdebug)" ] ; then pecl install xdebug ; fi
291
+ RUN if [ -z "$(pecl list | grep ${ xdebugVersion })" ] ; then pecl install ${ xdebugVersion } ; fi
173
292
  RUN docker-php-ext-enable xdebug
174
293
  RUN echo 'xdebug.start_with_request=yes' >> /usr/local/etc/php/php.ini
175
- RUN echo 'xdebug.mode=${ enableXdebug }' >> /usr/local/etc/php/php.ini
176
- RUN echo '${ clientDetectSettings }' >> /usr/local/etc/php/php.ini
177
- `;
294
+ RUN echo 'xdebug.mode=${ xdebugMode }' >> /usr/local/etc/php/php.ini
295
+ RUN echo 'xdebug.client_host="host.docker.internal"' >> /usr/local/etc/php/php.ini`;
178
296
  }
package/lib/md5.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
  /**
2
3
  * External dependencies
3
4
  */
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
  // See https://xdebug.org/docs/all_settings#mode
2
3
  const XDEBUG_MODES = [
3
4
  'develop',
package/lib/retry.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
  /**
2
3
  * External dependencies
3
4
  */
@@ -0,0 +1,9 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`md5 creates a hash of a number 1`] = `"55587a910882016321201e6ebbc9f595"`;
4
+
5
+ exports[`md5 creates a hash of a object 1`] = `"a0294b0e88367a0ff800d78413356538"`;
6
+
7
+ exports[`md5 creates a hash of a string 1`] = `"5d41402abc4b2a76b9719d911017c592"`;
8
+
9
+ exports[`md5 creates a hash of null 1`] = `"37a6259cc0c1dae299a7866489dff0bd"`;