-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathwordpress.js
267 lines (242 loc) · 7.85 KB
/
wordpress.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/**
* External dependencies
*/
const dockerCompose = require( 'docker-compose' );
const util = require( 'util' );
const fs = require( 'fs' ).promises;
const path = require( 'path' );
/**
* Promisified dependencies
*/
const copyDir = util.promisify( require( 'copy-dir' ) );
/**
* @typedef {import('./config').WPConfig} WPConfig
* @typedef {import('./config').WPServiceConfig} WPServiceConfig
* @typedef {'development'|'tests'} WPEnvironment
* @typedef {'development'|'tests'|'all'} WPEnvironmentSelection
*/
/**
* Makes the WordPress content directories (wp-content, wp-content/plugins,
* wp-content/themes) owned by the www-data user. This ensures that WordPress
* can write to these directories.
*
* This is necessary when running wp-env with `"core": null` because Docker
* will automatically create these directories as the root user when binding
* volumes during `docker-compose up`, and `docker-compose up` doesn't support
* the `-u` option.
*
* See https://github.com/docker-library/wordpress/issues/436.
*
* @param {WPEnvironment} environment The environment to check. Either 'development' or 'tests'.
* @param {WPConfig} config The wp-env config object.
*/
async function makeContentDirectoriesWritable(
environment,
{ dockerComposeConfigPath, debug }
) {
await dockerCompose.exec(
environment === 'development' ? 'wordpress' : 'tests-wordpress',
'chown www-data:www-data wp-content wp-content/plugins wp-content/themes',
{
config: dockerComposeConfigPath,
log: debug,
}
);
}
/**
* Checks a WordPress database connection. An error is thrown if the test is
* unsuccessful.
*
* @param {WPConfig} config The wp-env config object.
*/
async function checkDatabaseConnection( { dockerComposeConfigPath, debug } ) {
await dockerCompose.run( 'cli', 'wp db check', {
config: dockerComposeConfigPath,
commandOptions: [ '--rm' ],
log: debug,
} );
}
/**
* Configures WordPress for the given environment by installing WordPress,
* activating all plugins, and activating the first theme. These steps are
* performed sequentially so as to not overload the WordPress instance.
*
* @param {WPEnvironment} environment The environment to configure. Either 'development' or 'tests'.
* @param {WPConfig} config The wp-env config object.
* @param {Object} spinner A CLI spinner which indicates progress.
*/
async function configureWordPress( environment, config, spinner ) {
const installCommand = `wp core install --url="localhost:${ config.env[ environment ].port }" --title="${ config.name }" --admin_user=admin --admin_password=password --admin_email=wordpress@example.com --skip-email`;
// -eo pipefail exits the command as soon as anything fails in bash.
const setupCommands = [ 'set -eo pipefail', installCommand ];
// Set wp-config.php values.
for ( let [ key, value ] of Object.entries(
config.env[ environment ].config
) ) {
// Add quotes around string values to work with multi-word strings better.
value = typeof value === 'string' ? `"${ value }"` : value;
setupCommands.push(
`wp config set ${ key } ${ value }${
typeof value !== 'string' ? ' --raw' : ''
}`
);
}
// Activate all plugins.
for ( const pluginSource of config.env[ environment ].pluginSources ) {
setupCommands.push( `wp plugin activate ${ pluginSource.basename }` );
}
if ( config.debug ) {
spinner.info(
`Running the following setup commands on the ${ environment } instance:\n - ${ setupCommands.join(
'\n - '
) }\n`
);
}
// Execute all setup commands in a batch.
await dockerCompose.run(
environment === 'development' ? 'cli' : 'tests-cli',
[ 'bash', '-c', setupCommands.join( ' && ' ) ],
{
config: config.dockerComposeConfigPath,
log: config.debug,
}
);
/**
* Since wp-phpunit loads wp-settings.php at the end of its wp-config.php
* file, we need to avoid loading it too early in our own wp-config.php. If
* we load it too early, then some things (like MULTISITE) will be defined
* before wp-phpunit has a chance to configure them. To avoid this, create a
* copy of wp-config.php for phpunit which doesn't require wp-settings.php.
*
* Note that This needs to be executed using `exec` on the wordpress service
* so that file permissions work properly.
*
* This will be removed in the future. @see https://github.com/WordPress/gutenberg/issues/23171
*
*/
await dockerCompose.exec(
environment === 'development' ? 'wordpress' : 'tests-wordpress',
[
'sh',
'-c',
'sed "/^require.*wp-settings.php/d" /var/www/html/wp-config.php > /var/www/html/phpunit-wp-config.php',
],
{
config: config.dockerComposeConfigPath,
log: config.debug,
}
);
}
/**
* Resets the development server's database, the tests server's database, or both.
*
* @param {WPEnvironmentSelection} environment The environment to clean. Either 'development', 'tests', or 'all'.
* @param {WPConfig} config The wp-env config object.
*/
async function resetDatabase(
environment,
{ dockerComposeConfigPath, debug }
) {
const options = {
config: dockerComposeConfigPath,
commandOptions: [ '--rm' ],
log: debug,
};
const tasks = [];
if ( environment === 'all' || environment === 'development' ) {
tasks.push( dockerCompose.run( 'cli', 'wp db reset --yes', options ) );
}
if ( environment === 'all' || environment === 'tests' ) {
tasks.push(
dockerCompose.run( 'tests-cli', 'wp db reset --yes', options )
);
}
await Promise.all( tasks );
}
async function setupWordPressDirectories( config ) {
if (
config.env.development.coreSource &&
hasSameCoreSource( [ config.env.development, config.env.tests ] )
) {
await copyCoreFiles(
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 );
}
/**
* Returns true if all given environment configs have the same core source.
*
* @param {WPServiceConfig[]} envs An array of environments to check.
*
* @return {boolean} True if all the environments have the same core source.
*/
function hasSameCoreSource( envs ) {
if ( envs.length < 2 ) {
return true;
}
return ! envs.some( ( env ) =>
areCoreSourcesDifferent( envs[ 0 ].coreSource, env.coreSource )
);
}
function areCoreSourcesDifferent( coreSource1, coreSource2 ) {
if (
( ! coreSource1 && coreSource2 ) ||
( coreSource1 && ! coreSource2 )
) {
return true;
}
if ( coreSource1 && coreSource2 && coreSource1.path !== coreSource2.path ) {
return true;
}
return false;
}
/**
* Copies a WordPress installation, taking care to ignore large directories
* (.git, node_modules) and configuration files (wp-config.php).
*
* @param {string} fromPath Path to the WordPress directory to copy.
* @param {string} toPath Destination path.
*/
async function copyCoreFiles( fromPath, toPath ) {
await copyDir( fromPath, toPath, {
filter( stat, filepath, filename ) {
if ( stat === 'symbolicLink' ) {
return false;
}
if ( stat === 'directory' && filename === '.git' ) {
return false;
}
if ( stat === 'directory' && filename === 'node_modules' ) {
return false;
}
if ( stat === 'file' && filename === 'wp-config.php' ) {
return false;
}
return true;
},
} );
}
module.exports = {
hasSameCoreSource,
makeContentDirectoriesWritable,
checkDatabaseConnection,
configureWordPress,
resetDatabase,
setupWordPressDirectories,
};