feat: basic auto-dockerization

This commit is contained in:
cha0s 2022-03-02 00:54:53 -06:00
parent 0dc49ecc8e
commit 3d3181ebf4
4 changed files with 154 additions and 8 deletions

View File

@ -45,13 +45,7 @@ export default class ServerFlecks extends Flecks {
Object.keys(this.flecks) Object.keys(this.flecks)
.sort((l, r) => (l < r ? 1 : -1)) .sort((l, r) => (l < r ? 1 : -1))
.forEach((fleck) => { .forEach((fleck) => {
const prefix = `FLECKS_ENV_${ const prefix = `FLECKS_ENV_${this.constructor.environmentalize(fleck).toUpperCase()}`;
fleck
// - `@flecks/core` -> `FLECKS_CORE`
.replace(/[^a-zA-Z0-9]/g, '_')
.replace(/_*(.*)_*/, '$1')
.toUpperCase()
}`;
keys keys
.filter((key) => key.startsWith(`${prefix}_`) && -1 === seen.indexOf(key)) .filter((key) => key.startsWith(`${prefix}_`) && -1 === seen.indexOf(key))
.map((key) => { .map((key) => {
@ -360,6 +354,13 @@ export default class ServerFlecks extends Flecks {
}); });
} }
static environmentalize(key) {
return key
// - `@flecks/core` -> `FLECKS_CORE`
.replace(/[^a-zA-Z0-9]/g, '_')
.replace(/_*(.*)_*/, '$1');
}
fleckIsAliased(fleck) { fleckIsAliased(fleck) {
return this.constructor.fleckIsAliased(this.resolver, fleck); return this.constructor.fleckIsAliased(this.resolver, fleck);
} }

View File

@ -0,0 +1,132 @@
import {spawn} from 'child_process';
import {writeFile} from 'fs/promises';
import {join, relative} from 'path';
import {D, dumpYml} from '@flecks/core';
const {
FLECKS_CORE_ROOT = process.cwd(),
} = process.env;
const debug = D('@flecks/docker/commands');
export default (program, flecks) => {
const commands = {};
commands.compose = {
options: [
['-r, --run', 'run docker-compose'],
],
description: 'generate a docker compose file',
action: async (opts) => {
const {
run,
} = opts;
const output = join(FLECKS_CORE_ROOT, 'dist');
const lines = [
"version: '3'",
'services:',
'',
];
const services = {
app: {
build: {
context: '..',
dockerfile: 'dist/Dockerfile',
},
environment: {
FLECKS_ENV_FLECKS_DOCKER_SERVER_enabled: 'false',
},
volumes: [
'../node_modules:/var/www/node_modules',
],
},
};
const containers = flecks.invoke('@flecks/docker/containers');
(
await Promise.all(
Object.entries(containers)
.map(async ([fleck, config]) => {
Object.entries(await config)
.forEach(([key, config]) => {
services[key] = {image: config.image, environment: {}};
});
return [
`FLECKS_ENV_${flecks.constructor.environmentalize(fleck).toUpperCase()}`,
config,
];
}),
)
)
// Set environment.
.forEach(([prefix, config]) => {
Object.values(config)
.forEach((config) => {
Object.entries(config.environment || {})
.forEach(([configService, environment]) => {
Object.entries(environment || {})
.forEach(([key, value]) => {
const qualified = 'app' === configService ? `${prefix}_${key}` : key;
services[configService].environment[qualified] = value;
});
});
});
});
Object.entries(services)
.forEach(([key, service]) => {
lines.push(
...dumpYml({[key]: service})
.split('\n')
.map((line) => ` ${line}`),
);
});
const composeFile = join(output, 'docker-compose.yml');
debug('writing %s...', composeFile);
await writeFile(
composeFile,
lines
.map((line) => ('' === line.trim() ? '' : line))
.join('\n'),
);
const dockerFile = join(output, 'Dockerfile');
debug('writing %s...', dockerFile);
await writeFile(
dockerFile,
[
'FROM node:16',
'',
'RUN mkdir -p /var/www',
'WORKDIR /var/www',
'COPY package.json /var/www',
'COPY build /var/www/build',
'COPY dist /var/www/dist',
'',
'ENV DEBUG=*',
'ENV NODE_ENV=production',
'',
'CMD ["node", "./dist/index.js"]',
'',
'VOLUME /var/www/node_modules',
'',
].join('\n'),
);
/* eslint-disable no-console */
console.log();
console.log('Outputs:');
console.log();
console.group();
console.log(relative(FLECKS_CORE_ROOT, composeFile));
console.log(relative(FLECKS_CORE_ROOT, dockerFile));
console.groupEnd();
console.log();
/* eslint-enable no-console */
if (run) {
spawn(
'docker-compose',
['-f', 'dist/docker-compose.yml', 'up', '--build'],
{stdio: 'inherit'},
);
}
},
};
return commands;
};

View File

@ -1,10 +1,18 @@
import {Hooks} from '@flecks/core'; import {Hooks} from '@flecks/core';
import commands from './commands';
import startContainer from './start-container'; import startContainer from './start-container';
export default { export default {
[Hooks]: { [Hooks]: {
'@flecks/core/config': () => ({
enabled: true,
}),
'@flecks/core/commands': commands,
'@flecks/server/up': async (flecks) => { '@flecks/server/up': async (flecks) => {
if (!flecks.get('@flecks/docker/server.enabled')) {
return;
}
const containers = await flecks.invokeReduceAsync('@flecks/docker/containers'); const containers = await flecks.invokeReduceAsync('@flecks/docker/containers');
await Promise.all( await Promise.all(
Object.entries(containers) Object.entries(containers)

View File

@ -1,6 +1,11 @@
export default (flecks) => ({ export default (flecks) => ({
redis: { redis: {
image: 'redis', environment: {
app: {
host: 'redis',
},
},
image: 'redis:6',
mount: '/data', mount: '/data',
ports: {[flecks.get('@flecks/redis/server.port')]: 6379}, ports: {[flecks.get('@flecks/redis/server.port')]: 6379},
}, },