@elementor/wp-lite-env 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- package/index.d.ts +3 -0
- package/index.d.ts.map +1 -0
- package/index.js +16 -0
- package/package.json +47 -0
- package/src/config.d.ts +16 -0
- package/src/config.d.ts.map +1 -0
- package/src/config.js +23 -0
- package/src/run.d.ts +11 -0
- package/src/run.d.ts.map +1 -0
- package/src/run.js +105 -0
- package/src/templates.d.ts +6 -0
- package/src/templates.d.ts.map +1 -0
- package/src/templates.js +111 -0
package/index.d.ts
ADDED
package/index.d.ts.map
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":""}
|
package/index.js
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
import { cleanup, commandMap, generateFiles, getCliCommand, getConfigFilePath, getPort } from './src/run';
|
3
|
+
const command = process.argv[2];
|
4
|
+
if (!commandMap[command]) {
|
5
|
+
console.log(`Valid commands: ${Object.keys(commandMap).join(', ')}. You used ${command}`);
|
6
|
+
}
|
7
|
+
const port = getPort(process.argv);
|
8
|
+
const configFilePath = getConfigFilePath(process.argv);
|
9
|
+
const runPath = generateFiles(port, configFilePath);
|
10
|
+
const cliCommand = getCliCommand(process.argv);
|
11
|
+
try {
|
12
|
+
await commandMap[command](port, runPath, cliCommand);
|
13
|
+
}
|
14
|
+
finally {
|
15
|
+
cleanup(port, runPath);
|
16
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
{
|
2
|
+
"name": "@elementor/wp-lite-env",
|
3
|
+
"version": "0.0.6",
|
4
|
+
"private": false,
|
5
|
+
"description": "A simple, lightweight, docker-based WordPress environment",
|
6
|
+
"main": "dist/index.js",
|
7
|
+
"type": "module",
|
8
|
+
"types": "dist/index.d.ts",
|
9
|
+
"bin": {
|
10
|
+
"wp-lite-env": "dist/index.js"
|
11
|
+
},
|
12
|
+
"scripts": {
|
13
|
+
"build": "tsc",
|
14
|
+
"lint": "eslint",
|
15
|
+
"release": "npm run build && changeset publish",
|
16
|
+
"test": "jest --coverage=true"
|
17
|
+
},
|
18
|
+
"repository": {
|
19
|
+
"type": "git",
|
20
|
+
"url": "git+https://github.com/elementor/wp-lite-env.git"
|
21
|
+
},
|
22
|
+
"publishConfig": {
|
23
|
+
"access": "public",
|
24
|
+
"directory": "dist"
|
25
|
+
},
|
26
|
+
"author": "Elementor Team",
|
27
|
+
"license": "ISC",
|
28
|
+
"bugs": {
|
29
|
+
"url": "https://github.com/elementor/wp-lite-env/issues"
|
30
|
+
},
|
31
|
+
"homepage": "https://github.com/elementor/wp-lite-env#readme",
|
32
|
+
"dependencies": {
|
33
|
+
"docker-compose": "^1.1.0"
|
34
|
+
},
|
35
|
+
"devDependencies": {
|
36
|
+
"@changesets/cli": "^2.27.9",
|
37
|
+
"@eslint/js": "^9.15.0",
|
38
|
+
"@jest/globals": "^29.7.0",
|
39
|
+
"@types/eslint__js": "^8.42.3",
|
40
|
+
"@types/node": "^22.9.0",
|
41
|
+
"eslint": "~9.14.0",
|
42
|
+
"jest": "^29.7.0",
|
43
|
+
"ts-jest": "^29.2.5",
|
44
|
+
"typescript": "^5.6.3",
|
45
|
+
"typescript-eslint": "^8.14.0"
|
46
|
+
}
|
47
|
+
}
|
package/src/config.d.ts
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
export type Config = {
|
2
|
+
core?: string;
|
3
|
+
phpVersion?: string;
|
4
|
+
plugins?: {
|
5
|
+
[key: string]: string;
|
6
|
+
};
|
7
|
+
themes?: {
|
8
|
+
[key: string]: string;
|
9
|
+
};
|
10
|
+
mappings?: {
|
11
|
+
[key: string]: string;
|
12
|
+
};
|
13
|
+
config?: Record<string, string | boolean>;
|
14
|
+
};
|
15
|
+
export declare const getConfig: (configFilePath?: string) => Config;
|
16
|
+
//# sourceMappingURL=config.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACpC,MAAM,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACnC,QAAQ,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAC,OAAO,CAAC,CAAC;CACxC,CAAA;AAED,eAAO,MAAM,SAAS,oBAAsB,MAAM,KAAI,MAuBrD,CAAC"}
|
package/src/config.js
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
import fs from 'fs';
|
2
|
+
export const getConfig = (configFilePath) => {
|
3
|
+
let configFile = {};
|
4
|
+
if (configFilePath) {
|
5
|
+
configFile = JSON.parse(fs.readFileSync(configFilePath, 'utf8'));
|
6
|
+
}
|
7
|
+
const defaultConfig = {
|
8
|
+
core: '6.7',
|
9
|
+
phpVersion: '8.1',
|
10
|
+
plugins: {},
|
11
|
+
themes: {},
|
12
|
+
mappings: {},
|
13
|
+
config: {},
|
14
|
+
};
|
15
|
+
return {
|
16
|
+
core: configFile.core || defaultConfig.core,
|
17
|
+
phpVersion: configFile.phpVersion || defaultConfig.phpVersion,
|
18
|
+
plugins: configFile.plugins || defaultConfig.plugins,
|
19
|
+
themes: configFile.themes || defaultConfig.themes,
|
20
|
+
mappings: configFile.mappings || defaultConfig.mappings,
|
21
|
+
config: configFile.config || defaultConfig.config,
|
22
|
+
};
|
23
|
+
};
|
package/src/run.d.ts
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
export declare const start: (port: string, runPath: string) => Promise<void>;
|
2
|
+
export declare const stop: (port: string, runPath: string) => Promise<void>;
|
3
|
+
export declare const commandMap: {
|
4
|
+
[key: string]: ((port: string) => Promise<void>) | ((port: string, runPath: string, command: string) => Promise<void>);
|
5
|
+
};
|
6
|
+
export declare const generateFiles: (port: string, configFilePath: string) => string;
|
7
|
+
export declare const getConfigFilePath: (processArgs: string[]) => string;
|
8
|
+
export declare const getCliCommand: (processArgs: string[]) => string;
|
9
|
+
export declare const getPort: (processArgs: string[]) => string;
|
10
|
+
export declare const cleanup: (port: string, runPath: string) => void;
|
11
|
+
//# sourceMappingURL=run.d.ts.map
|
package/src/run.d.ts.map
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/run.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,KAAK,SAAiB,MAAM,WAAW,MAAM,kBASzD,CAAC;AAEF,eAAO,MAAM,IAAI,SAAiB,MAAM,WAAW,MAAM,kBAOxD,CAAC;AAWF,eAAO,MAAM,UAAU,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,CAAE,CAAE,IAAI,EAAE,MAAM,KAAM,OAAO,CAAC,IAAI,CAAC,CAAE,GAAG,CAAE,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAM,OAAO,CAAC,IAAI,CAAC,CAAE,CAAA;CAIxJ,CAAC;AAIF,eAAO,MAAM,aAAa,SAAW,MAAM,kBAAkB,MAAM,WA4BlE,CAAC;AAYF,eAAO,MAAM,iBAAiB,gBAAkB,MAAM,EAAE,WAEvD,CAAC;AAEF,eAAO,MAAM,aAAa,gBAAkB,MAAM,EAAE,WAEnD,CAAC;AAEF,eAAO,MAAM,OAAO,gBAAkB,MAAM,EAAE,WAE7C,CAAC;AAEF,eAAO,MAAM,OAAO,SAAW,MAAM,WAAW,MAAM,SAGrD,CAAA"}
|
package/src/run.js
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
import { downAll, run, upAll } from "docker-compose";
|
2
|
+
import path from "path";
|
3
|
+
import { getConfig } from "./config";
|
4
|
+
import fs from "fs";
|
5
|
+
import { generateCliDockerfileTemplate, generateConfiguration, generateDockerComposeYmlTemplate, generateWordPressDockerfileTemplate } from "./templates";
|
6
|
+
import { createHash } from "crypto";
|
7
|
+
import os from "node:os";
|
8
|
+
const waitForServer = async (url, timeoutMs) => {
|
9
|
+
const startTime = Date.now();
|
10
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
11
|
+
while (startTime + timeoutMs > Date.now()) {
|
12
|
+
try {
|
13
|
+
const response = await fetch(url);
|
14
|
+
if (response.ok && (200 === response.status || 302 === response.status)) {
|
15
|
+
return true;
|
16
|
+
}
|
17
|
+
}
|
18
|
+
catch (e) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
19
|
+
// Ignore
|
20
|
+
}
|
21
|
+
finally {
|
22
|
+
await sleep(100);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
return false;
|
26
|
+
};
|
27
|
+
export const start = async (port, runPath) => {
|
28
|
+
await upAll({
|
29
|
+
commandOptions: ['--build'],
|
30
|
+
composeOptions: ['-p', `port${port}`],
|
31
|
+
cwd: runPath,
|
32
|
+
log: true,
|
33
|
+
});
|
34
|
+
await waitForServer(`http://localhost:${port}`, 10000);
|
35
|
+
await cli(port, runPath, 'bash wp-config/configure-wp.sh');
|
36
|
+
};
|
37
|
+
export const stop = async (port, runPath) => {
|
38
|
+
await downAll({
|
39
|
+
cwd: runPath,
|
40
|
+
commandOptions: ['--volumes', '--remove-orphans'],
|
41
|
+
composeOptions: ['-p', `port${port}`],
|
42
|
+
log: true,
|
43
|
+
});
|
44
|
+
};
|
45
|
+
const cli = async (port, runPath, command) => {
|
46
|
+
await run('cli', command, {
|
47
|
+
cwd: runPath,
|
48
|
+
commandOptions: ['--rm'],
|
49
|
+
composeOptions: ['-p', `port${port}`],
|
50
|
+
log: true,
|
51
|
+
});
|
52
|
+
};
|
53
|
+
export const commandMap = {
|
54
|
+
start,
|
55
|
+
stop,
|
56
|
+
cli,
|
57
|
+
};
|
58
|
+
const getWpConfigPath = (port) => path.resolve(process.cwd(), port);
|
59
|
+
export const generateFiles = (port, configFilePath) => {
|
60
|
+
const config = getConfig(configFilePath);
|
61
|
+
// Using a local path since Docker Compose cannot access /tmp
|
62
|
+
// See: https://github.com/docker/compose/issues/1153
|
63
|
+
const wpConfigPath = getWpConfigPath(port);
|
64
|
+
if (!fs.existsSync(wpConfigPath)) {
|
65
|
+
fs.mkdirSync(wpConfigPath, { recursive: true });
|
66
|
+
}
|
67
|
+
const wpConfig = generateConfiguration(config, port);
|
68
|
+
fs.writeFileSync(path.resolve(wpConfigPath, 'configure-wp.sh'), wpConfig);
|
69
|
+
const dockerComposeYmlTemplate = generateDockerComposeYmlTemplate(config, process.cwd(), port, wpConfigPath);
|
70
|
+
const wordPressDockerfileTemplate = generateWordPressDockerfileTemplate(config);
|
71
|
+
const cliDockerfileTemplate = generateCliDockerfileTemplate(config);
|
72
|
+
const hash = createHash('sha256');
|
73
|
+
hash.update(dockerComposeYmlTemplate + wordPressDockerfileTemplate + cliDockerfileTemplate + port);
|
74
|
+
const runPath = path.resolve(os.tmpdir(), `${hash.digest('hex')}`);
|
75
|
+
if (!fs.existsSync(runPath)) {
|
76
|
+
fs.mkdirSync(runPath);
|
77
|
+
}
|
78
|
+
console.log(`writing files to run path: ${runPath}`);
|
79
|
+
fs.writeFileSync(path.resolve(runPath, 'docker-compose.yml'), dockerComposeYmlTemplate);
|
80
|
+
fs.writeFileSync(path.resolve(runPath, 'WordPress.Dockerfile'), wordPressDockerfileTemplate);
|
81
|
+
fs.writeFileSync(path.resolve(runPath, 'CLI.Dockerfile'), cliDockerfileTemplate);
|
82
|
+
return runPath;
|
83
|
+
};
|
84
|
+
const getArgument = (argumentKey, processArgs) => {
|
85
|
+
for (let i = 3; i < processArgs.length; i++) {
|
86
|
+
const argument = processArgs[i];
|
87
|
+
if (argument.startsWith(`${argumentKey}=`)) {
|
88
|
+
return argument.substring(argumentKey.length + 1);
|
89
|
+
}
|
90
|
+
}
|
91
|
+
return undefined;
|
92
|
+
};
|
93
|
+
export const getConfigFilePath = (processArgs) => {
|
94
|
+
return path.resolve(getArgument('config', processArgs));
|
95
|
+
};
|
96
|
+
export const getCliCommand = (processArgs) => {
|
97
|
+
return getArgument('command', processArgs);
|
98
|
+
};
|
99
|
+
export const getPort = (processArgs) => {
|
100
|
+
return getArgument('port', processArgs) || '8888';
|
101
|
+
};
|
102
|
+
export const cleanup = (port, runPath) => {
|
103
|
+
fs.rmSync(getWpConfigPath(port), { recursive: true, force: true });
|
104
|
+
fs.rmSync(runPath, { recursive: true, force: true });
|
105
|
+
};
|
@@ -0,0 +1,6 @@
|
|
1
|
+
import { Config } from './config';
|
2
|
+
export declare const generateDockerComposeYmlTemplate: (config: Config, basePath: string, port: string, configPath: string) => string;
|
3
|
+
export declare const generateWordPressDockerfileTemplate: (config: Config) => string;
|
4
|
+
export declare const generateCliDockerfileTemplate: (config: Config) => string;
|
5
|
+
export declare const generateConfiguration: (config: Config, port: string) => string;
|
6
|
+
//# sourceMappingURL=templates.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGlC,eAAO,MAAM,gCAAgC,WAAa,MAAM,YAAY,MAAM,QAAQ,MAAM,cAAc,MAAM,WA0EnH,CAAC;AAEF,eAAO,MAAM,mCAAmC,WAAa,MAAM,WASlE,CAAC;AAEF,eAAO,MAAM,6BAA6B,WAAa,MAAM,WAa5D,CAAC;AAEF,eAAO,MAAM,qBAAqB,WAAa,MAAM,QAAQ,MAAM,WAWlE,CAAC"}
|
package/src/templates.js
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
import path from 'path';
|
2
|
+
export const generateDockerComposeYmlTemplate = (config, basePath, port, configPath) => {
|
3
|
+
const mappingsStringArray = Object.keys(config.mappings).map((key) => {
|
4
|
+
const value = config.mappings[key];
|
5
|
+
return ` - >-
|
6
|
+
${path.resolve(basePath, value)}:/var/www/html/${key}\n`;
|
7
|
+
});
|
8
|
+
const pluginsStringArray = Object.keys(config.plugins).map((key) => {
|
9
|
+
const value = config.plugins[key];
|
10
|
+
return ` - >-
|
11
|
+
${path.resolve(basePath, value)}:/var/www/html/wp-content/plugins/${key}\n`;
|
12
|
+
});
|
13
|
+
const themesStringArray = Object.keys(config.themes).map((key) => {
|
14
|
+
const value = config.themes[key];
|
15
|
+
return ` - >-
|
16
|
+
${path.resolve(basePath, value)}:/var/www/html/wp-content/themes/${key}\n`;
|
17
|
+
});
|
18
|
+
const wpContent = ` - >-
|
19
|
+
wpcontent:/var/www/html\n`;
|
20
|
+
const wpConfig = ` - >-
|
21
|
+
${configPath}:/var/www/html/wp-config\n`;
|
22
|
+
const volumes = mappingsStringArray.concat(pluginsStringArray).concat(themesStringArray).concat([wpContent, wpConfig]).join('');
|
23
|
+
return `services:
|
24
|
+
mysql:
|
25
|
+
image: 'mariadb:lts'
|
26
|
+
ports:
|
27
|
+
- '\${WP_ENV_MYSQL_PORT:-}:3306'
|
28
|
+
environment:
|
29
|
+
MYSQL_ROOT_HOST: '%'
|
30
|
+
MYSQL_ROOT_PASSWORD: password
|
31
|
+
MYSQL_DATABASE: wordpress
|
32
|
+
volumes:
|
33
|
+
- 'mysql:/var/lib/mysql'
|
34
|
+
wordpress:
|
35
|
+
depends_on:
|
36
|
+
- mysql
|
37
|
+
build:
|
38
|
+
context: .
|
39
|
+
dockerfile: WordPress.Dockerfile
|
40
|
+
no_cache: true
|
41
|
+
args: &ref_0
|
42
|
+
HOST_USERNAME: yotams
|
43
|
+
HOST_UID: '502'
|
44
|
+
HOST_GID: '20'
|
45
|
+
ports:
|
46
|
+
- '\${WP_ENV_PORT:-${port}}:80'
|
47
|
+
environment:
|
48
|
+
APACHE_RUN_USER: '#502'
|
49
|
+
APACHE_RUN_GROUP: '#20'
|
50
|
+
WORDPRESS_DB_USER: root
|
51
|
+
WORDPRESS_DB_PASSWORD: password
|
52
|
+
WORDPRESS_DB_NAME: wordpress
|
53
|
+
volumes: &ref_1
|
54
|
+
${volumes}
|
55
|
+
extra_hosts:
|
56
|
+
- 'host.docker.internal:host-gateway'
|
57
|
+
cli:
|
58
|
+
depends_on:
|
59
|
+
- wordpress
|
60
|
+
build:
|
61
|
+
context: .
|
62
|
+
dockerfile: CLI.Dockerfile
|
63
|
+
args: *ref_0
|
64
|
+
volumes: *ref_1
|
65
|
+
user: '502:20'
|
66
|
+
environment:
|
67
|
+
WORDPRESS_DB_USER: root
|
68
|
+
WORDPRESS_DB_PASSWORD: password
|
69
|
+
WORDPRESS_DB_NAME: wordpress
|
70
|
+
extra_hosts:
|
71
|
+
- 'host.docker.internal:host-gateway'
|
72
|
+
volumes:
|
73
|
+
mysql: {}
|
74
|
+
wpcontent: {}
|
75
|
+
`;
|
76
|
+
};
|
77
|
+
export const generateWordPressDockerfileTemplate = (config) => {
|
78
|
+
return `FROM wordpress:${config.core}-php${config.phpVersion}
|
79
|
+
ARG HOST_USERNAME
|
80
|
+
ARG HOST_UID
|
81
|
+
ARG HOST_GID
|
82
|
+
# When the IDs are already in use we can still safely move on.
|
83
|
+
RUN groupadd -o -g $HOST_GID $HOST_USERNAME || true
|
84
|
+
RUN useradd -mlo -u $HOST_UID -g $HOST_GID $HOST_USERNAME || true
|
85
|
+
`;
|
86
|
+
};
|
87
|
+
export const generateCliDockerfileTemplate = (config) => {
|
88
|
+
return `FROM wordpress:cli-php${config.phpVersion}
|
89
|
+
ARG HOST_USERNAME
|
90
|
+
ARG HOST_UID
|
91
|
+
ARG HOST_GID
|
92
|
+
# When the IDs are already in use we can still safely move on.
|
93
|
+
RUN addgroup -g $HOST_GID $HOST_USERNAME || true
|
94
|
+
RUN useradd -mlo -u $HOST_UID -g $HOST_GID $HOST_USERNAME || true
|
95
|
+
# RUN adduser -h /home/$HOST_USERNAME -G $( getent group $HOST_GID | cut -d: -f1 ) -u $HOST_UID $HOST_USERNAME || true
|
96
|
+
|
97
|
+
# Have the container sleep infinitely to keep it alive for us to run commands on it.
|
98
|
+
CMD [ "/bin/sh", "-c", "while true; do sleep 2073600; done" ]
|
99
|
+
`;
|
100
|
+
};
|
101
|
+
export const generateConfiguration = (config, port) => {
|
102
|
+
const header = `#!/bin/bash
|
103
|
+
set -eox pipefail
|
104
|
+
`;
|
105
|
+
const configStringArray = Object.keys(config.config).map((key) => {
|
106
|
+
const value = config.config[key];
|
107
|
+
return `wp config set ${key} ${value} --raw`;
|
108
|
+
});
|
109
|
+
const wpCoreInstall = `wp core install --url="http://localhost:${port}" --title="test" --admin_user=admin --admin_password=password --admin_email=wordpress@example.com --skip-email`;
|
110
|
+
return [header, wpCoreInstall].concat(configStringArray).join('\n');
|
111
|
+
};
|