Skip to content

Commit

Permalink
Adding a docker-based dev env
Browse files Browse the repository at this point in the history
  • Loading branch information
kraftbj committed Jan 9, 2025
1 parent f54f6be commit 67296ba
Show file tree
Hide file tree
Showing 10 changed files with 441 additions and 0 deletions.
94 changes: 94 additions & 0 deletions .github/workflows/build-docker-monorepo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Build Monorepo Docker
on:
push:
branches: [ 'trunk' ]
paths:
- 'tools/docker/Dockerfile.monorepo'
- 'tools/docker/bin/monorepo'
- '.github/versions.sh'
- '.github/workflows/build-docker-monorepo.yml'
pull_request:
paths:
- 'tools/docker/Dockerfile.monorepo'
- 'tools/docker/bin/monorepo'
- '.github/versions.sh'
- '.github/workflows/build-docker-monorepo.yml'
concurrency:
group: build-docker-monorepo-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build and publish Jetpack Monorepo Environment
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
timeout-minutes: 60

steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: matticbot
password: ${{ secrets.DOCKER_HUB_MATTICBOT_TOKEN }}

- name: Log in to GitHub Packages
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Fetch build args
id: buildargs
run: |
source .github/versions.sh
source .github/files/gh-funcs.sh
gh_set_output php-version "$PHP_VERSION"
gh_set_output composer-version "$COMPOSER_VERSION"
gh_set_output node-version "$NODE_VERSION"
gh_set_output pnpm-version "$PNPM_VERSION"
if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
gh_set_output tags "type=raw,latest"
gh_set_output images $'automattic/jetpack-monorepo\nghcr.io/automattic/jetpack-monorepo'
elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
gh_set_output tags "type=ref,event=pr"
gh_set_output images "ghcr.io/automattic/jetpack-monorepo"
else
echo "Unknown GITHUB_EVENT_NAME $GITHUB_EVENT_NAME"
exit 1
fi
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
flavor: latest=false
tags: ${{ steps.buildargs.outputs.tags }}
images: ${{ steps.buildargs.outputs.images }}
labels: |
org.opencontainers.image.title=Jetpack Monorepo Environment
org.opencontainers.image.description=Environment for building and testing the Jetpack Monorepo.
org.opencontainers.image.documentation=${{ github.server_url }}/${{ github.repository }}/blob/trunk/tools/docker/README.md
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: tools/docker
file: tools/docker/Dockerfile.monorepo
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
PHP_VERSION=${{ steps.buildargs.outputs.php-version }}
COMPOSER_VERSION=${{ steps.buildargs.outputs.composer-version }}
NODE_VERSION=${{ steps.buildargs.outputs.node-version }}
PNPM_VERSION=${{ steps.buildargs.outputs.pnpm-version }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,6 @@ phpcs.xml
# VS Code setting files
*.code-workspace
/.vscode/settings.json

.pnpm-debug.log
.pnpm-error.log
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.

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

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

/**
* Check if a directory is the monorepo root.
*
* @param {string} dir - Directory to check
* @return {boolean} True if this is the monorepo root
*/
const isMonorepoRoot = dir => {
try {
return fs.existsSync( resolve( dir, 'tools/docker/bin/monorepo' ) );
} catch {
return false;
}
};

/**
* Find monorepo root from a starting directory.
*
* @param {string} startDir - Directory to start searching from
* @return {string|null} Path to monorepo root, or null if not found
*/
const findMonorepoRoot = startDir => {
let dir = startDir;
while ( dir !== '/' ) {
if ( isMonorepoRoot( dir ) ) {
return dir;
}
dir = dirname( dir );
}
return null;
};

/**
* Clone the monorepo.
*
* @param {string} targetDir - Directory to clone into
* @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',
[ 'clone', 'https://github.com/Automattic/jetpack.git', targetDir ],
{ stdio: 'inherit' }
);

if ( result.status !== 0 ) {
throw new Error( 'Failed to clone repository' );
}
};

/**
* Initialize a new Jetpack development environment.
*
* @throws {Error} If initialization fails
*/
const initJetpack = async () => {
const response = await prompts( {
type: 'text',
name: 'directory',
message: 'Where would you like to clone the Jetpack monorepo?',
initial: './jetpack',
} );

if ( ! response.directory ) {
throw new Error( 'Setup cancelled' );
}

const targetDir = resolve( process.cwd(), response.directory );

if ( fs.existsSync( targetDir ) ) {
throw new Error( `Directory ${ targetDir } already exists` );
}

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 }` );
}
};

// Main execution
const main = async () => {
try {
const args = process.argv.slice( 2 );

// Handle 'init' command specially
if ( args[ 0 ] === 'init' ) {
await initJetpack();
return;
}

// Try to find monorepo root from current directory
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' );
}

// Run the monorepo script with the original arguments
const result = spawnSync(
resolve( monorepoRoot, 'tools/docker/bin/monorepo' ),
[ 'pnpm', 'jetpack', ...args ],
{
stdio: 'inherit',
shell: true,
cwd: monorepoRoot, // Ensure we're in the monorepo root when running commands
}
);

if ( result.status !== 0 ) {
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;
}
};

main();
19 changes: 19 additions & 0 deletions projects/js-packages/jetpack-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@automattic/jetpack-cli",
"version": "0.1.0-beta.1",
"description": "Docker-based CLI for Jetpack development",
"bin": {
"jp": "bin/jp.js"
},
"files": [
"bin"
],
"type": "module",
"dependencies": {
"chalk": "^4.1.2",
"prompts": "^2.4.2"
},
"publishConfig": {
"access": "public"
}
}
31 changes: 31 additions & 0 deletions tools/cli/commands/docker.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ const buildEnv = argv => {
}

envOpts.COMPOSE_PROJECT_NAME = getProjectName( argv );

// Add versions from versions.sh
const versions = envfile.parse(
fs.readFileSync( `${ dockerFolder }/../../.github/versions.sh`, 'utf8' )
);
Object.assign( envOpts, versions );

return envOpts;
};

Expand Down Expand Up @@ -801,6 +808,30 @@ export function dockerDefine( yargs ) {
command: 'jt-config',
description: 'Set jurassic tube config',
handler: argv => execJtCmdHandler( argv ),
} )
.command( {
command: 'monorepo',
description: 'Run commands in monorepo container',
builder: yargCmd =>
defaultOpts( yargCmd ).option( 'cmd', {
alias: 'c',
describe: 'Command to run',
type: 'string',
demandOption: true,
} ),
handler: argv => {
const opts = buildComposeFiles().concat( [
'run',
'--rm',
'monorepo',
'bash',
'-c',
argv.cmd,
] );

const envOpts = buildEnv( argv );
composeExecutor( argv, opts, envOpts );
},
} );
},
} );
Expand Down
61 changes: 61 additions & 0 deletions tools/docker/Dockerfile.monorepo
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
FROM ubuntu:24.04

# Import version variables from .github/versions.sh
ARG PHP_VERSION
ARG COMPOSER_VERSION
ARG NODE_VERSION
ARG PNPM_VERSION

ENV LANG=en_US.UTF-8 \
LC_ALL=en_US.UTF-8

WORKDIR /app

# Install basic packages and PHP
RUN --mount=type=cache,target=/var/lib/apt/lists/,sharing=private \
export DEBIAN_FRONTEND=noninteractive \
&& apt-get update \
&& apt-get install -y curl gpg language-pack-en-base software-properties-common \
&& add-apt-repository ppa:ondrej/php \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& apt-get update \
&& apt-get --purge install -y \
git \
unzip \
zip \
"php${PHP_VERSION}" \
"php${PHP_VERSION}-cli" \
"php${PHP_VERSION}-curl" \
"php${PHP_VERSION}-dom" \
"php${PHP_VERSION}-mbstring" \
"php${PHP_VERSION}-xml" \
"php${PHP_VERSION}-zip" \
&& apt-get remove --purge --auto-remove -y gpg software-properties-common \
&& find /var/ -name '*-old' -delete && rm -rf /var/log/dpkg.log /var/log/alternatives.log /var/log/apt/ ~/.launchpadlib

# Install Composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer --version=$COMPOSER_VERSION \
&& php -r "unlink('composer-setup.php');"

# Install Node.js
RUN --mount=type=cache,target=/var/lib/apt/lists/,sharing=private \
export DEBIAN_FRONTEND=noninteractive \
&& N=${NODE_VERSION%%.*} \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$N.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
&& apt-get -q update \
&& VER="$(apt-cache show nodejs | sed -n "/^Version: ${NODE_VERSION}-/ { s/^Version: /=/p; q }" )" \
&& apt-get install -y nodejs$VER

# Install pnpm
RUN npm install --global pnpm@$PNPM_VERSION \
&& SHELL=/bin/bash pnpm setup

WORKDIR /workspace

# Add entrypoint script
COPY bin/monorepo-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/monorepo-entrypoint.sh

ENTRYPOINT ["/usr/local/bin/monorepo-entrypoint.sh"]
CMD ["bash"]
Loading

0 comments on commit 67296ba

Please sign in to comment.