@elementor/wp-lite-env 0.0.6
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.
- 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
|
+
};
|