Skip to content

Commit

Permalink
New CLI works with docker now -- commands will run on the local host …
Browse files Browse the repository at this point in the history
…unless specified to the dev container, like build-image
  • Loading branch information
kraftbj committed Jan 9, 2025
1 parent 6c6152a commit 7c58092
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 44 deletions.
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

162 changes: 149 additions & 13 deletions projects/js-packages/jetpack-cli/bin/jp.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#!/usr/bin/env node

import { spawnSync } from 'child_process';
import fs from 'fs';
import fs, { readFileSync } from 'fs';
import { dirname, resolve } from 'path';
import process from 'process';
import { fileURLToPath } from 'url';
import chalk from 'chalk';
import dotenv from 'dotenv';
import prompts from 'prompts';

// Get package.json path relative to this file
const __dirname = dirname( fileURLToPath( import.meta.url ) );
const packageJson = JSON.parse( readFileSync( resolve( __dirname, '../package.json' ), 'utf8' ) );

/**
* Check if a directory is the monorepo root.
*
Expand Down Expand Up @@ -45,7 +51,6 @@ const findMonorepoRoot = startDir => {
* @throws {Error} If clone fails
*/
const cloneMonorepo = async targetDir => {
// eslint-disable-next-line no-console
console.log( chalk.blue( 'Cloning Jetpack monorepo...' ) );
const result = spawnSync(
'git',
Expand Down Expand Up @@ -83,15 +88,15 @@ const initJetpack = async () => {

try {
await cloneMonorepo( targetDir );
// eslint-disable-next-line no-console

console.log( chalk.green( '\nJetpack monorepo has been cloned successfully!' ) );
// eslint-disable-next-line no-console

console.log( '\nNext steps:' );
// eslint-disable-next-line no-console

console.log( '1. cd', response.directory );
// eslint-disable-next-line no-console

console.log( '2. jp docker up' );
// eslint-disable-next-line no-console

console.log( '3. jp docker install' );
} catch ( error ) {
throw new Error( `Failed to initialize Jetpack: ${ error.message }` );
Expand All @@ -103,6 +108,12 @@ const main = async () => {
try {
const args = process.argv.slice( 2 );

// Handle version flag
if ( args[ 0 ] === '--version' || args[ 0 ] === '-v' ) {
console.log( chalk.green( packageJson.version ) );
return;
}

// Handle 'init' command specially
if ( args[ 0 ] === 'init' ) {
await initJetpack();
Expand All @@ -113,19 +124,145 @@ const main = async () => {
const monorepoRoot = findMonorepoRoot( process.cwd() );

if ( ! monorepoRoot ) {
// eslint-disable-next-line no-console
console.error( chalk.red( 'Could not find Jetpack monorepo.' ) );
// eslint-disable-next-line no-console

console.log( '\nTo get started:' );
// eslint-disable-next-line no-console

console.log( '1. Run', chalk.blue( 'jp init' ), 'to clone the repository' );
// eslint-disable-next-line no-console

console.log( ' OR' );
// eslint-disable-next-line no-console

console.log( '2. Navigate to an existing Jetpack monorepo directory' );
throw new Error( 'Monorepo not found' );
}

// Handle docker commands on the host
if ( args[ 0 ] === 'docker' ) {
// Commands that should run in the container
const containerCommands = [ 'build-image', 'install' ];
if ( containerCommands.includes( args[ 1 ] ) ) {
const result = spawnSync(
resolve( monorepoRoot, 'tools/docker/bin/monorepo' ),
[ 'pnpm', 'jetpack', ...args ],
{
stdio: 'inherit',
shell: true,
cwd: monorepoRoot,
}
);

if ( result.status !== 0 ) {
throw new Error( `Command failed with status ${ result.status }` );
}
return;
}

// Run config generation first if this is an 'up' command
if ( args[ 1 ] === 'up' ) {
// Create required directories
fs.mkdirSync( resolve( monorepoRoot, 'tools/docker/data/jetpack_dev_mysql' ), {
recursive: true,
} );
fs.mkdirSync( resolve( monorepoRoot, 'tools/docker/data/ssh.keys' ), { recursive: true } );
fs.mkdirSync( resolve( monorepoRoot, 'tools/docker/wordpress' ), { recursive: true } );

const images = [
{ name: 'mariadb:lts' },
{ name: 'automattic/jetpack-wordpress-dev:latest' },
{ name: 'phpmyadmin/phpmyadmin:latest', platform: 'linux/amd64' },
{ name: 'maildev/maildev', platform: 'linux/amd64' },
{ name: 'atmoz/sftp', platform: 'linux/amd64' },
];

for ( const image of images ) {
const inspect = spawnSync( 'docker', [ 'image', 'inspect', image.name ], {
stdio: 'ignore',
} );
if ( inspect.status !== 0 ) {
console.log( chalk.blue( `Pulling ${ image.name }...` ) );
const pullArgs = [ 'pull', image.name ];
if ( image.platform ) {
pullArgs.splice( 1, 0, '--platform', image.platform );
}
const pull = spawnSync( 'docker', pullArgs, { stdio: 'inherit' } );
if ( pull.status !== 0 ) {
throw new Error( `Failed to pull ${ image.name }` );
}
}
}

const configResult = spawnSync(
resolve( monorepoRoot, 'tools/docker/bin/monorepo' ),
[ 'pnpm', 'jetpack', 'docker', 'config' ],
{
stdio: 'inherit',
shell: true,
cwd: monorepoRoot,
}
);

if ( configResult.status !== 0 ) {
throw new Error( 'Failed to generate Docker config' );
}
}

// Get project name (from docker.js)
const projectName = args.includes( '--type=e2e' ) ? 'jetpack_e2e' : 'jetpack_dev';

// Load versions from .github/versions.sh
const versionsPath = resolve( monorepoRoot, '.github/versions.sh' );
const versions = fs.readFileSync( versionsPath, 'utf8' );
const versionVars = {};
versions.split( '\n' ).forEach( line => {
const match = line.match( /^([A-Z_]+)=(.+)$/ );
if ( match ) {
versionVars[ match[ 1 ] ] = match[ 2 ].replace( /['"]/g, '' );
}
} );

// Build environment variables (from docker.js)
const envVars = {
...process.env,
// Load from default.env
...( fs.existsSync( resolve( monorepoRoot, 'tools/docker/default.env' ) )
? dotenv.parse( fs.readFileSync( resolve( monorepoRoot, 'tools/docker/default.env' ) ) )
: {} ),
// Load from .env if it exists
...( fs.existsSync( resolve( monorepoRoot, 'tools/docker/.env' ) )
? dotenv.parse( fs.readFileSync( resolve( monorepoRoot, 'tools/docker/.env' ) ) )
: {} ),
HOST_CWD: monorepoRoot,
PHP_VERSION: versionVars.PHP_VERSION,
COMPOSER_VERSION: versionVars.COMPOSER_VERSION,
NODE_VERSION: versionVars.NODE_VERSION,
PNPM_VERSION: versionVars.PNPM_VERSION,
COMPOSE_PROJECT_NAME: projectName,
PORT_WORDPRESS: args.includes( '--type=e2e' ) ? '8889' : '80',
};

// Build the list of compose files to use
const composeFiles = [
'-f',
resolve( monorepoRoot, 'tools/docker/docker-compose.yml' ),
'-f',
resolve( monorepoRoot, 'tools/docker/compose-mappings.built.yml' ),
'-f',
resolve( monorepoRoot, 'tools/docker/compose-extras.built.yml' ),
];

const result = spawnSync( 'docker', [ 'compose', ...composeFiles, ...args.slice( 1 ) ], {
stdio: 'inherit',
shell: true,
cwd: resolve( monorepoRoot, 'tools/docker' ),
env: envVars,
} );

if ( result.status !== 0 ) {
throw new Error( `Docker command failed with status ${ result.status }` );
}
return;
}

// Run the monorepo script with the original arguments
const result = spawnSync(
resolve( monorepoRoot, 'tools/docker/bin/monorepo' ),
Expand All @@ -141,7 +278,6 @@ const main = async () => {
throw new Error( `Command failed with status ${ result.status }` );
}
} catch ( error ) {
// eslint-disable-next-line no-console
console.error( chalk.red( error.message ) );
process.exitCode = 1;
}
Expand Down
11 changes: 11 additions & 0 deletions projects/js-packages/jetpack-cli/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import makeBaseConfig from 'jetpack-js-tools/eslintrc/base.mjs';

export default [
...makeBaseConfig( import.meta.url, { envs: [ 'node' ] } ),
{
rules: {
'no-console': 'off',
'n/no-process-exit': 'off',
},
},
];
3 changes: 2 additions & 1 deletion projects/js-packages/jetpack-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@automattic/jetpack-cli",
"version": "0.1.0-beta.1",
"version": "0.1.0-beta.2",
"description": "Docker-based CLI for Jetpack development",
"bin": {
"jp": "bin/jp.js"
Expand All @@ -11,6 +11,7 @@
"type": "module",
"dependencies": {
"chalk": "^4.1.2",
"dotenv": "^16.3.1",
"prompts": "^2.4.2"
},
"publishConfig": {
Expand Down
21 changes: 18 additions & 3 deletions tools/cli/commands/docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,14 +554,23 @@ const execJtCmdHandler = argv => {
}
};

/**
* Generate Docker configuration files.
*
* @param {object} argv - The command line arguments
*/
async function generateConfig( argv ) {
await setConfig( argv );
}

/**
* Definition for the Docker commands.
*
* @param {object} yargs - The Yargs dependency.
* @return {object} Yargs with the Docker commands defined.
*/
export function dockerDefine( yargs ) {
yargs.command( {
return yargs.command( {
command: 'docker <cmd>',
description: 'Docker stuff',
builder: yarg => {
Expand Down Expand Up @@ -832,9 +841,15 @@ export function dockerDefine( yargs ) {
const envOpts = buildEnv( argv );
composeExecutor( argv, opts, envOpts );
},
} )
.command( {
command: 'config',
description: 'Generate Docker configuration files',
builder: yargCmd => defaultOpts( yargCmd ),
handler: async argv => {
await generateConfig( argv );
},
} );
},
} );

return yargs;
}
28 changes: 4 additions & 24 deletions tools/docker/bin/monorepo
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,8 @@ echo "Running command in monorepo container: $*"
# Source the versions file
source "$MONOREPO_ROOT/.github/versions.sh"

# Pre-pull Docker images if this is a docker command
if [ "$1" = "pnpm" ] && [ "$2" = "jetpack" ] && [ "$3" = "docker" ] && [ "$4" = "up" ]; then
echo "Pre-pulling required Docker images..."
# Ensure MySQL data directory exists
mkdir -p "$MONOREPO_ROOT/tools/docker/data/jetpack_dev_mysql"
# Ensure SSH keys directory exists
mkdir -p "$MONOREPO_ROOT/tools/docker/data/ssh.keys"
# Only pull images that aren't present
if ! docker image inspect mariadb:lts >/dev/null 2>&1; then
docker pull mariadb:lts
fi
if ! docker image inspect automattic/jetpack-wordpress-dev:latest >/dev/null 2>&1; then
docker pull automattic/jetpack-wordpress-dev:latest
fi
if ! docker image inspect phpmyadmin/phpmyadmin:latest >/dev/null 2>&1; then
docker pull --platform linux/arm64 phpmyadmin/phpmyadmin:latest
fi
if ! docker image inspect maildev/maildev >/dev/null 2>&1; then
docker pull --platform linux/arm64 maildev/maildev
fi
if ! docker image inspect atmoz/sftp >/dev/null 2>&1; then
docker pull --platform linux/arm64 atmoz/sftp
fi
fi
# Export variables needed by docker-compose
export HOST_CWD="$MONOREPO_ROOT"

# Build the image if it doesn't exist
if ! docker image inspect jetpack-monorepo:latest >/dev/null 2>&1; then
Expand Down Expand Up @@ -67,6 +45,8 @@ docker run --rm -it \
-e TERM=$TERM \
-e COLORTERM=$COLORTERM \
-e DOCKER_ROOT="$MONOREPO_ROOT/tools/docker" \
-e HOST_CWD="$MONOREPO_ROOT" \
-e WORKSPACE_PATH="$MONOREPO_ROOT" \
-e NPM_CONFIG_USERCONFIG=/root/.npmrc \
-e NPM_CONFIG_CACHE=/root/.npm \
-e PNPM_HOME=/root/.local/share/pnpm \
Expand Down
6 changes: 3 additions & 3 deletions tools/docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ services:

monorepo:
build:
context: .
context: ${HOST_CWD}/tools/docker
dockerfile: Dockerfile.monorepo
args:
PHP_VERSION: ${PHP_VERSION}
COMPOSER_VERSION: ${COMPOSER_VERSION}
NODE_VERSION: ${NODE_VERSION}
PNPM_VERSION: ${PNPM_VERSION}
volumes:
- ../..:${WORKSPACE_PATH:-/workspace}
working_dir: ${WORKSPACE_PATH:-/workspace}
- ${HOST_CWD}:/usr/local/src/jetpack-monorepo
working_dir: /usr/local/src/jetpack-monorepo

0 comments on commit 7c58092

Please sign in to comment.