@automattic/jetpack-cli 0.1.0-beta.2 → 1.0.0-beta1
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/README.md +18 -0
- package/bin/jp.js +177 -126
- package/package.json +4 -3
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Jetpack
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## How to install Jetpack plugin on your site
|
|
5
|
+
### Installation From Git Repo
|
|
6
|
+
|
|
7
|
+
## Contribute
|
|
8
|
+
|
|
9
|
+
## Get Help
|
|
10
|
+
|
|
11
|
+
## Security
|
|
12
|
+
|
|
13
|
+
Need to report a security vulnerability? Go to [https://automattic.com/security/](https://automattic.com/security/) or directly to our security bug bounty site [https://hackerone.com/automattic](https://hackerone.com/automattic).
|
|
14
|
+
|
|
15
|
+
## License
|
|
16
|
+
|
|
17
|
+
Licensed under [GNU General Public License v2 (or later)](./LICENSE.txt).
|
|
18
|
+
|
package/bin/jp.js
CHANGED
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawnSync } from 'child_process';
|
|
4
|
-
import fs from 'fs';
|
|
4
|
+
import fs, { readFileSync } from 'fs';
|
|
5
5
|
import { dirname, resolve } from 'path';
|
|
6
6
|
import process from 'process';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
7
8
|
import chalk from 'chalk';
|
|
8
9
|
import dotenv from 'dotenv';
|
|
9
10
|
import prompts from 'prompts';
|
|
11
|
+
import updateNotifier from 'update-notifier';
|
|
12
|
+
|
|
13
|
+
// Get package.json path relative to this file
|
|
14
|
+
const __dirname = dirname( fileURLToPath( import.meta.url ) );
|
|
15
|
+
const packageJson = JSON.parse( readFileSync( resolve( __dirname, '../package.json' ), 'utf8' ) );
|
|
16
|
+
|
|
17
|
+
// Check for updates
|
|
18
|
+
const notifier = updateNotifier( {
|
|
19
|
+
pkg: packageJson,
|
|
20
|
+
updateCheckInterval: 1000 * 60 * 60 * 24, // Check once per day
|
|
21
|
+
} );
|
|
22
|
+
|
|
23
|
+
// Show update notification
|
|
24
|
+
notifier.notify( {
|
|
25
|
+
message:
|
|
26
|
+
'Update available for Jetpack CLI: {currentVersion} → {latestVersion}\n' +
|
|
27
|
+
'Run {updateCommand} to update',
|
|
28
|
+
isGlobal: true,
|
|
29
|
+
} );
|
|
10
30
|
|
|
11
31
|
/**
|
|
12
32
|
* Check if a directory is the monorepo root.
|
|
@@ -30,10 +50,13 @@ const isMonorepoRoot = dir => {
|
|
|
30
50
|
*/
|
|
31
51
|
const findMonorepoRoot = startDir => {
|
|
32
52
|
let dir = startDir;
|
|
33
|
-
|
|
53
|
+
let prevDir;
|
|
54
|
+
while ( dir !== prevDir ) {
|
|
55
|
+
// Keep going until dirname() stops changing the path
|
|
34
56
|
if ( isMonorepoRoot( dir ) ) {
|
|
35
57
|
return dir;
|
|
36
58
|
}
|
|
59
|
+
prevDir = dir;
|
|
37
60
|
dir = dirname( dir );
|
|
38
61
|
}
|
|
39
62
|
return null;
|
|
@@ -46,7 +69,6 @@ const findMonorepoRoot = startDir => {
|
|
|
46
69
|
* @throws {Error} If clone fails
|
|
47
70
|
*/
|
|
48
71
|
const cloneMonorepo = async targetDir => {
|
|
49
|
-
// eslint-disable-next-line no-console
|
|
50
72
|
console.log( chalk.blue( 'Cloning Jetpack monorepo...' ) );
|
|
51
73
|
const result = spawnSync(
|
|
52
74
|
'git',
|
|
@@ -84,15 +106,15 @@ const initJetpack = async () => {
|
|
|
84
106
|
|
|
85
107
|
try {
|
|
86
108
|
await cloneMonorepo( targetDir );
|
|
87
|
-
|
|
109
|
+
|
|
88
110
|
console.log( chalk.green( '\nJetpack monorepo has been cloned successfully!' ) );
|
|
89
|
-
|
|
111
|
+
|
|
90
112
|
console.log( '\nNext steps:' );
|
|
91
|
-
|
|
113
|
+
|
|
92
114
|
console.log( '1. cd', response.directory );
|
|
93
|
-
|
|
115
|
+
|
|
94
116
|
console.log( '2. jp docker up' );
|
|
95
|
-
|
|
117
|
+
|
|
96
118
|
console.log( '3. jp docker install' );
|
|
97
119
|
} catch ( error ) {
|
|
98
120
|
throw new Error( `Failed to initialize Jetpack: ${ error.message }` );
|
|
@@ -104,6 +126,12 @@ const main = async () => {
|
|
|
104
126
|
try {
|
|
105
127
|
const args = process.argv.slice( 2 );
|
|
106
128
|
|
|
129
|
+
// Handle version flag
|
|
130
|
+
if ( args[ 0 ] === '--version' || args[ 0 ] === '-v' ) {
|
|
131
|
+
console.log( chalk.green( packageJson.version ) );
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
107
135
|
// Handle 'init' command specially
|
|
108
136
|
if ( args[ 0 ] === 'init' ) {
|
|
109
137
|
await initJetpack();
|
|
@@ -114,144 +142,168 @@ const main = async () => {
|
|
|
114
142
|
const monorepoRoot = findMonorepoRoot( process.cwd() );
|
|
115
143
|
|
|
116
144
|
if ( ! monorepoRoot ) {
|
|
117
|
-
// eslint-disable-next-line no-console
|
|
118
145
|
console.error( chalk.red( 'Could not find Jetpack monorepo.' ) );
|
|
119
|
-
|
|
146
|
+
|
|
120
147
|
console.log( '\nTo get started:' );
|
|
121
|
-
|
|
148
|
+
|
|
122
149
|
console.log( '1. Run', chalk.blue( 'jp init' ), 'to clone the repository' );
|
|
123
|
-
|
|
150
|
+
|
|
124
151
|
console.log( ' OR' );
|
|
125
|
-
|
|
152
|
+
|
|
126
153
|
console.log( '2. Navigate to an existing Jetpack monorepo directory' );
|
|
127
154
|
throw new Error( 'Monorepo not found' );
|
|
128
155
|
}
|
|
129
156
|
|
|
130
|
-
// Handle docker commands on the host
|
|
157
|
+
// Handle docker commands that must run on the host machine
|
|
131
158
|
if ( args[ 0 ] === 'docker' ) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
159
|
+
const hostCommands = [ 'up', 'down', 'stop', 'clean' ];
|
|
160
|
+
if ( hostCommands.includes( args[ 1 ] ) ) {
|
|
161
|
+
// Handle command-specific setup/cleanup
|
|
162
|
+
if ( args[ 1 ] === 'up' ) {
|
|
163
|
+
// Create required directories
|
|
164
|
+
fs.mkdirSync( resolve( monorepoRoot, 'tools/docker/data/jetpack_dev_mysql' ), {
|
|
165
|
+
recursive: true,
|
|
166
|
+
} );
|
|
167
|
+
fs.mkdirSync( resolve( monorepoRoot, 'tools/docker/data/ssh.keys' ), {
|
|
168
|
+
recursive: true,
|
|
169
|
+
} );
|
|
170
|
+
fs.mkdirSync( resolve( monorepoRoot, 'tools/docker/wordpress' ), { recursive: true } );
|
|
144
171
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
172
|
+
// Create empty .env file
|
|
173
|
+
fs.writeFileSync( resolve( monorepoRoot, 'tools/docker/.env' ), '' );
|
|
150
174
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
fs.mkdirSync( resolve( monorepoRoot, 'tools/docker/wordpress' ), { recursive: true } );
|
|
159
|
-
|
|
160
|
-
const images = [
|
|
161
|
-
{ name: 'mariadb:lts' },
|
|
162
|
-
{ name: 'automattic/jetpack-wordpress-dev:latest' },
|
|
163
|
-
{ name: 'phpmyadmin/phpmyadmin:latest', platform: 'linux/amd64' },
|
|
164
|
-
{ name: 'maildev/maildev', platform: 'linux/amd64' },
|
|
165
|
-
{ name: 'atmoz/sftp', platform: 'linux/amd64' },
|
|
166
|
-
];
|
|
167
|
-
|
|
168
|
-
for ( const image of images ) {
|
|
169
|
-
const inspect = spawnSync( 'docker', [ 'image', 'inspect', image.name ], {
|
|
170
|
-
stdio: 'ignore',
|
|
171
|
-
} );
|
|
172
|
-
if ( inspect.status !== 0 ) {
|
|
173
|
-
console.log( `Pulling ${ image.name }...` );
|
|
174
|
-
const args = [ 'pull', image.name ];
|
|
175
|
-
if ( image.platform ) {
|
|
176
|
-
args.splice( 1, 0, '--platform', image.platform );
|
|
177
|
-
}
|
|
178
|
-
const pull = spawnSync( 'docker', args, { stdio: 'inherit' } );
|
|
179
|
-
if ( pull.status !== 0 ) {
|
|
180
|
-
throw new Error( `Failed to pull ${ image.name }` );
|
|
175
|
+
const configResult = spawnSync(
|
|
176
|
+
resolve( monorepoRoot, 'tools/docker/bin/monorepo' ),
|
|
177
|
+
[ 'pnpm', 'jetpack', 'docker', 'config' ],
|
|
178
|
+
{
|
|
179
|
+
stdio: 'inherit',
|
|
180
|
+
shell: true,
|
|
181
|
+
cwd: monorepoRoot,
|
|
181
182
|
}
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
if ( configResult.status !== 0 ) {
|
|
186
|
+
throw new Error( 'Failed to generate Docker config' );
|
|
182
187
|
}
|
|
188
|
+
} else if ( args[ 1 ] === 'clean' ) {
|
|
189
|
+
// After docker-compose down -v, also remove local files
|
|
190
|
+
const projectName = args.includes( '--type=e2e' ) ? 'jetpack_e2e' : 'jetpack_dev';
|
|
191
|
+
const cleanupPaths = [
|
|
192
|
+
resolve( monorepoRoot, 'tools/docker/wordpress/' ),
|
|
193
|
+
resolve( monorepoRoot, 'tools/docker/wordpress-develop/*' ),
|
|
194
|
+
resolve( monorepoRoot, 'tools/docker/logs/', projectName ),
|
|
195
|
+
resolve( monorepoRoot, 'tools/docker/data/', `${ projectName }_mysql` ),
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
// Function to clean up after docker-compose down
|
|
199
|
+
const cleanupFiles = () => {
|
|
200
|
+
for ( const path of cleanupPaths ) {
|
|
201
|
+
try {
|
|
202
|
+
fs.rmSync( path, { recursive: true, force: true } );
|
|
203
|
+
} catch ( error ) {
|
|
204
|
+
console.warn(
|
|
205
|
+
chalk.yellow( `Warning: Could not remove ${ path }: ${ error.message }` )
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Add cleanup to process events to ensure it runs after docker-compose
|
|
212
|
+
process.once( 'beforeExit', cleanupFiles );
|
|
213
|
+
|
|
214
|
+
// Replace 'clean' with 'down -v' in the arguments
|
|
215
|
+
args.splice( 1, 1, 'down', '-v' );
|
|
183
216
|
}
|
|
184
217
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
);
|
|
218
|
+
// Get project name (from docker.js)
|
|
219
|
+
const projectName = args.includes( '--type=e2e' ) ? 'jetpack_e2e' : 'jetpack_dev';
|
|
220
|
+
|
|
221
|
+
// Build environment variables (from docker.js)
|
|
222
|
+
const envVars = {
|
|
223
|
+
...process.env, // Start with process.env
|
|
224
|
+
};
|
|
194
225
|
|
|
195
|
-
|
|
196
|
-
|
|
226
|
+
// Add default env vars if they exist
|
|
227
|
+
if ( fs.existsSync( resolve( monorepoRoot, 'tools/docker/default.env' ) ) ) {
|
|
228
|
+
Object.assign(
|
|
229
|
+
envVars,
|
|
230
|
+
dotenv.parse( fs.readFileSync( resolve( monorepoRoot, 'tools/docker/default.env' ) ) )
|
|
231
|
+
);
|
|
197
232
|
}
|
|
198
|
-
}
|
|
199
233
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const versionVars = {};
|
|
207
|
-
versions.split( '\n' ).forEach( line => {
|
|
208
|
-
const match = line.match( /^([A-Z_]+)=(.+)$/ );
|
|
209
|
-
if ( match ) {
|
|
210
|
-
versionVars[ match[ 1 ] ] = match[ 2 ].replace( /['"]/g, '' );
|
|
234
|
+
// Add user overrides from .env if they exist
|
|
235
|
+
if ( fs.existsSync( resolve( monorepoRoot, 'tools/docker/.env' ) ) ) {
|
|
236
|
+
Object.assign(
|
|
237
|
+
envVars,
|
|
238
|
+
dotenv.parse( fs.readFileSync( resolve( monorepoRoot, 'tools/docker/.env' ) ) )
|
|
239
|
+
);
|
|
211
240
|
}
|
|
212
|
-
} );
|
|
213
|
-
|
|
214
|
-
// Build environment variables (from docker.js)
|
|
215
|
-
const envVars = {
|
|
216
|
-
...process.env,
|
|
217
|
-
// Load from default.env
|
|
218
|
-
...( fs.existsSync( resolve( monorepoRoot, 'tools/docker/default.env' ) )
|
|
219
|
-
? dotenv.parse( fs.readFileSync( resolve( monorepoRoot, 'tools/docker/default.env' ) ) )
|
|
220
|
-
: {} ),
|
|
221
|
-
// Load from .env if it exists
|
|
222
|
-
...( fs.existsSync( resolve( monorepoRoot, 'tools/docker/.env' ) )
|
|
223
|
-
? dotenv.parse( fs.readFileSync( resolve( monorepoRoot, 'tools/docker/.env' ) ) )
|
|
224
|
-
: {} ),
|
|
225
|
-
HOST_CWD: monorepoRoot,
|
|
226
|
-
PHP_VERSION: versionVars.PHP_VERSION,
|
|
227
|
-
COMPOSER_VERSION: versionVars.COMPOSER_VERSION,
|
|
228
|
-
NODE_VERSION: versionVars.NODE_VERSION,
|
|
229
|
-
PNPM_VERSION: versionVars.PNPM_VERSION,
|
|
230
|
-
COMPOSE_PROJECT_NAME: projectName,
|
|
231
|
-
PORT_WORDPRESS: args.includes( '--type=e2e' ) ? '8889' : '80',
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// Build the list of compose files to use
|
|
235
|
-
const composeFiles = [
|
|
236
|
-
'-f',
|
|
237
|
-
resolve( monorepoRoot, 'tools/docker/docker-compose.yml' ),
|
|
238
|
-
'-f',
|
|
239
|
-
resolve( monorepoRoot, 'tools/docker/compose-mappings.built.yml' ),
|
|
240
|
-
'-f',
|
|
241
|
-
resolve( monorepoRoot, 'tools/docker/compose-extras.built.yml' ),
|
|
242
|
-
];
|
|
243
|
-
|
|
244
|
-
const result = spawnSync( 'docker', [ 'compose', ...composeFiles, ...args.slice( 1 ) ], {
|
|
245
|
-
stdio: 'inherit',
|
|
246
|
-
shell: true,
|
|
247
|
-
cwd: resolve( monorepoRoot, 'tools/docker' ),
|
|
248
|
-
env: envVars,
|
|
249
|
-
} );
|
|
250
241
|
|
|
251
|
-
|
|
252
|
-
|
|
242
|
+
// Only set these specific vars if they're not already set in .env
|
|
243
|
+
if ( ! envVars.COMPOSE_PROJECT_NAME ) {
|
|
244
|
+
envVars.COMPOSE_PROJECT_NAME = projectName;
|
|
245
|
+
}
|
|
246
|
+
if ( ! envVars.PORT_WORDPRESS ) {
|
|
247
|
+
envVars.PORT_WORDPRESS = args.includes( '--type=e2e' ) ? '8889' : '80';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Load versions from .github/versions.sh if not already set
|
|
251
|
+
if (
|
|
252
|
+
! (
|
|
253
|
+
envVars.PHP_VERSION &&
|
|
254
|
+
envVars.COMPOSER_VERSION &&
|
|
255
|
+
envVars.NODE_VERSION &&
|
|
256
|
+
envVars.PNPM_VERSION
|
|
257
|
+
)
|
|
258
|
+
) {
|
|
259
|
+
const versionsPath = resolve( monorepoRoot, '.github/versions.sh' );
|
|
260
|
+
const versions = fs.readFileSync( versionsPath, 'utf8' );
|
|
261
|
+
const versionVars = {};
|
|
262
|
+
versions.split( '\n' ).forEach( line => {
|
|
263
|
+
const match = line.match( /^([A-Z_]+)=(.+)$/ );
|
|
264
|
+
if ( match ) {
|
|
265
|
+
versionVars[ match[ 1 ] ] = match[ 2 ].replace( /['"]/g, '' );
|
|
266
|
+
}
|
|
267
|
+
} );
|
|
268
|
+
|
|
269
|
+
// Only set version vars if they're not already set
|
|
270
|
+
if ( ! envVars.PHP_VERSION ) envVars.PHP_VERSION = versionVars.PHP_VERSION;
|
|
271
|
+
if ( ! envVars.COMPOSER_VERSION ) envVars.COMPOSER_VERSION = versionVars.COMPOSER_VERSION;
|
|
272
|
+
if ( ! envVars.NODE_VERSION ) envVars.NODE_VERSION = versionVars.NODE_VERSION;
|
|
273
|
+
if ( ! envVars.PNPM_VERSION ) envVars.PNPM_VERSION = versionVars.PNPM_VERSION;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Always set HOST_CWD as it's required for Docker context
|
|
277
|
+
envVars.HOST_CWD = monorepoRoot;
|
|
278
|
+
|
|
279
|
+
// Build the list of compose files to use
|
|
280
|
+
const composeFiles =
|
|
281
|
+
args[ 0 ] === 'docker' && [ 'build-image', 'install' ].includes( args[ 1 ] )
|
|
282
|
+
? [ '-f', resolve( monorepoRoot, 'tools/docker/docker-compose-monorepo.yml' ) ]
|
|
283
|
+
: [
|
|
284
|
+
'-f',
|
|
285
|
+
resolve( monorepoRoot, 'tools/docker/docker-compose.yml' ),
|
|
286
|
+
'-f',
|
|
287
|
+
resolve( monorepoRoot, 'tools/docker/compose-mappings.built.yml' ),
|
|
288
|
+
'-f',
|
|
289
|
+
resolve( monorepoRoot, 'tools/docker/compose-extras.built.yml' ),
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
// Add dev profile for monorepo service
|
|
293
|
+
const composeArgs = [ 'compose', '--profile', 'dev', ...composeFiles, ...args.slice( 1 ) ];
|
|
294
|
+
|
|
295
|
+
const result = spawnSync( 'docker', composeArgs, {
|
|
296
|
+
stdio: 'inherit',
|
|
297
|
+
shell: true,
|
|
298
|
+
cwd: resolve( monorepoRoot, 'tools/docker' ),
|
|
299
|
+
env: envVars,
|
|
300
|
+
} );
|
|
301
|
+
|
|
302
|
+
if ( result.status !== 0 ) {
|
|
303
|
+
throw new Error( `Docker command failed with status ${ result.status }` );
|
|
304
|
+
}
|
|
305
|
+
return;
|
|
253
306
|
}
|
|
254
|
-
return;
|
|
255
307
|
}
|
|
256
308
|
|
|
257
309
|
// Run the monorepo script with the original arguments
|
|
@@ -269,7 +321,6 @@ const main = async () => {
|
|
|
269
321
|
throw new Error( `Command failed with status ${ result.status }` );
|
|
270
322
|
}
|
|
271
323
|
} catch ( error ) {
|
|
272
|
-
// eslint-disable-next-line no-console
|
|
273
324
|
console.error( chalk.red( error.message ) );
|
|
274
325
|
process.exitCode = 1;
|
|
275
326
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-beta1",
|
|
4
4
|
"description": "Docker-based CLI for Jetpack development",
|
|
5
5
|
"bin": {
|
|
6
6
|
"jp": "bin/jp.js"
|
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
],
|
|
11
11
|
"type": "module",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"chalk": "^4.1
|
|
13
|
+
"chalk": "^5.4.1",
|
|
14
14
|
"dotenv": "^16.3.1",
|
|
15
|
-
"prompts": "^2.4.2"
|
|
15
|
+
"prompts": "^2.4.2",
|
|
16
|
+
"update-notifier": "^7.0.0"
|
|
16
17
|
},
|
|
17
18
|
"publishConfig": {
|
|
18
19
|
"access": "public"
|