#!/usr/bin/env node const execa = require('execa'); const { parse } = require('dotenv'); const main = require('apr-main'); const map = require('apr-map'); const series = require('apr-series'); const globby = require('globby'); const path = require('path'); const flatten = require('lodash.flatten'); const readPkg = require('read-pkg'); const figures = require('figures'); const COMPOSE_PROJECT_NAME = `cp-${process.env.CIRCLE_BRANCH}`; const NAMESPACE = 'quay.io/yldio'; const ROOT = path.join(__dirname, '..'); const LOG = console.log; const createTritonProfile = async () => { const { SDC_ACCOUNT = '', SDC_KEY_ID = '' } = process.env; LOG( `${figures.arrowRight} create triton profile SDC_ACCOUNT=${SDC_ACCOUNT.length} SDC_KEY_ID=${SDC_KEY_ID.length}` ); const payload = JSON.stringify({ url: 'https://eu-ams-1.api.joyent.com', account: SDC_ACCOUNT, keyId: SDC_KEY_ID, name: 'eu-ams-1', curr: true }); const cp = execa('triton', ['profile', 'create', '--yes', '-f', '-'], { input: payload }); cp.stdout.pipe(process.stdout); cp.stderr.pipe(process.stderr); return cp; }; const getEnv = async () => { await createTritonProfile(); const tritonEnv = await execa.stdout('triton', ['env']); const dotEnv = tritonEnv.replace(/^export /gim, ''); return Object.assign({}, process.env, parse(dotEnv)); }; const login = async () => { LOG(`${figures.arrowRight} login`); const { _DOCKER_LOGIN_USERNAME, _DOCKER_LOGIN_PASSWORD, _DOCKER_REGISTRY } = process.env; return execa( 'docker', [ 'login', '--email="."', `--username="${_DOCKER_LOGIN_USERNAME}"`, `--password="${_DOCKER_LOGIN_PASSWORD}"`, _DOCKER_REGISTRY ], { stdio: 'inherit' } ); }; const build = async () => { LOG(`${figures.arrowRight} build`); const { CIRCLE_BRANCH } = process.env; const dockerfiles = await globby(['packages/*/Dockerfile'], { cwd: ROOT }); LOG(`${figures.arrowRight} build.dockerfiles`); LOG(JSON.stringify(dockerfiles, null, 2)); const images = await map(dockerfiles, async dockerfile => { const folder = path.resolve(ROOT, path.dirname(dockerfile)); const { name } = await readPkg(folder); const tags = [`${name}:${CIRCLE_BRANCH}`, `${name}:latest`]; // todo remove this if (name === 'portal-api') { return; } LOG(`${figures.arrowRight} build.name ${name}`); LOG(JSON.stringify(tags, null, 2)); await execa( 'docker', flatten([ 'build', flatten(tags.map(name => ['-t', `${NAMESPACE}/${name}`])), '-f', dockerfile, '.' ]), { stdio: 'inherit' } ); return `${NAMESPACE}/${name}`; }); await map(images.filter(Boolean), image => { LOG(`${figures.arrowRight} build.push ${image}`); return execa('docker', ['push', `${image}`], { stdio: 'inherit' }); }); }; const logout = () => { LOG(`${figures.arrowRight} logout`); return execa('docker', ['logout'], { stdio: 'inherit' }); }; const deploy = async () => { const env = await getEnv(); LOG(`${figures.arrowRight} deploy`); // stop containers await execa('docker-compose', ['stop'], { stdio: 'inherit', env: Object.assign({}, env, { COMPOSE_PROJECT_NAME }) }); // kill containers await execa('docker-compose', ['kill'], { stdio: 'inherit', env: Object.assign({}, env, { COMPOSE_PROJECT_NAME }) }); // rm containers await execa('docker-compose', ['rm', '-f', '-v'], { stdio: 'inherit', env: Object.assign({}, env, { COMPOSE_PROJECT_NAME }) }); // pull new images await execa('docker-compose', ['pull', '--parallel'], { stdio: 'inherit', env: Object.assign({}, env, { COMPOSE_PROJECT_NAME }) }); // up project return execa('docker-compose', ['up', '-d', '--build', '--force-recreate'], { stdio: 'inherit', env: Object.assign({}, env, { COMPOSE_PROJECT_NAME }) }); }; const run = async () => { LOG(`${figures.arrowRight} .env`); LOG(JSON.stringify(Object.keys(process.env), null, 2)); return series([login, build, logout, deploy]); }; LOG(`${figures.arrowRight} DEPLOY`); main(run());