UNPKG

@arc-fusion/cli

Version:

CLI for running Arc Fusion on your local machine

187 lines (163 loc) 5.71 kB
'use strict' const fs = require('fs') const path = require('path') const yamljs = require('yamljs') const spawn = require('./spawn') const { exec, mkdirp, touch, writeFile, gentleCopy } = require('./promises') const { getEnvVariables, createDockerVolume, deleteDockerVolume } = require('./local') const { ADMIN_RELEASE, FUSION_RELEASE, FUSION_ROOT, PROJECT_ROOT, REPO_NAME, BOOTSTRAP_ROOT, CACHE_VOLUME_NAME } = require('../environment') async function dockerList (op) { const { stdout: processes } = await exec(`docker ${op}`) return processes .split('\n') .slice(1) } function generateFilename (name) { if (!name) return '' return name.replace(/^\.*/, '.').replace(/\.+$/, '') } function dockerComposeOp (op) { const cwd = PROJECT_ROOT return async function (name, options = {}) { const { compose } = options const command = compose === 'v2' ? 'docker' : 'docker-compose' const args = ['-f', path.join(PROJECT_ROOT, '.fusion', `docker-compose${generateFilename(name)}.yml`)].concat(op || []) return spawn( command, compose === 'v2' ? ['compose'].concat(...args) : args, { cwd, env: { ...process.env, COMPOSE_PROJECT_NAME: `fusion${name ? `_${name}` : ''}` }, stdio: 'inherit' } ) } } const daemon = dockerComposeOp(['up', '-d']) async function down (name, options) { return dockerComposeOp(['down', '-v'])(name, options) } async function build (name, options) { // don't pull images if we are linking the fusion repo locally (it'll use the cached one if you already pulled it before) const { FUSION_REPO } = await getEnvVariables(PROJECT_ROOT) const useImages = name || !FUSION_REPO // `docker-compose pull` updates all services that are image-based if (useImages) await dockerComposeOp('pull')(name, options) // need to build engine first if we are not using an image and it is the default start if (!useImages && !name) await dockerComposeOp(['build', '--progress=plain', 'engine'])(name, options) // `docker-compose build --pull` updates the base FROM image for all services that are built return dockerComposeOp(useImages ? ['build', '--pull'] : ['build', '--progress=plain'])(name, options) } async function generate (name, options = {}) { await touch(path.join(PROJECT_ROOT, '.env')) await mkdirp(path.join(PROJECT_ROOT, '.fusion')) await gentleCopy(path.join(BOOTSTRAP_ROOT, '.dockerignore'), path.join(PROJECT_ROOT, '.dockerignore')) const props = { ADMIN_RELEASE, FUSION_ROOT, PROJECT_ROOT, REPO_NAME, CACHE_VOLUME_NAME, ...options } const is20 = /^(2\.0\.|2\.0$)/i.test(FUSION_RELEASE) const dockerCompose = await require(`../../templates/${is20 ? '2.0/' : ''}docker-compose${generateFilename(name)}.yml`)(props) return writeFile(path.join(PROJECT_ROOT, '.fusion', `docker-compose${generateFilename(name)}.yml`), yamljs.stringify(dockerCompose, 10, 2)) } function cleanupVerifyArtifacts () { const fusionDir = path.join(PROJECT_ROOT, '.fusion') const verifyDir = path.join(fusionDir, 'verify') const composePath = path.join(fusionDir, 'docker-compose.verify.yml') const dockerfilePath = path.join(fusionDir, 'verify.Dockerfile') return spawn('docker', ['run', '--rm', '-v', `${verifyDir}:/cleanup`, 'alpine', 'sh', '-c', 'rm -rf /cleanup/* /cleanup/.[!.]*'], { stdio: 'inherit' }) .then(() => { if (fs.existsSync(verifyDir)) fs.rmdirSync(verifyDir) if (fs.existsSync(composePath)) fs.unlinkSync(composePath) if (fs.existsSync(dockerfilePath)) fs.unlinkSync(dockerfilePath) console.log('\x1b[32mCleaned up verify artifacts') }) } async function run (name, options) { try { await generate(name, options) await down(name, options) await build(name, options) return await start(name, options) // } catch (e) { } finally { await down(name, options) if (name === 'verify' && !options.keepArtifacts) { await cleanupVerifyArtifacts() } } } async function start (name, options = {}) { const { rebuild, verbose } = options const operation = ['up'] if (name) { operation.push('--abort-on-container-exit') operation.push(`--exit-code-from=${name}`) operation.push('--remove-orphans') } else { // create an external cache volume so we can persist webpack caching across runs if (rebuild) await deleteDockerVolume(CACHE_VOLUME_NAME) await createDockerVolume(CACHE_VOLUME_NAME) // don't output stdio from service dependencies if (!verbose) { operation.push('--no-attach=admin') operation.push('--no-attach=content-cache') operation.push('--no-attach=cache-proxy') operation.push('--no-attach=data') operation.push('--no-attach=fusion-cli-api') operation.push('--no-attach=origin') operation.push('--no-attach=resolver') operation.push('--no-attach=themes') } } return dockerComposeOp(operation, options)(name, options) } const stop = dockerComposeOp('stop') /** * getDockerImage checks the COMPILER_IMAGE and COMPILER_RELEASE environment variables * and generates the Docker image to be used for the webpack container * @returns [String] DockerHub image to be used as the base for the webpack container */ const getDockerImage = () => { const compilerRelease = process.env.COMPILER_RELEASE || 'latest' const dockerImage = `washpost/pagebuilder-compiler:${compilerRelease}` return (process.env.COMPILER_IMAGE) ? dockerImage : 'washpost/fusion-engine:$FUSION_RELEASE' } module.exports = { build, daemon, dockerList, down, generate, getDockerImage, run, start, stop }