@nestjs-mod/docker-compose
Version:
Docker Compose is a tool for defining and running multi-container applications. It is the key to unlocking a streamlined and efficient development and deployment experience. (Generator docker-compose.yml for https://docs.docker.com/compose)
327 lines • 21.4 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.DockerComposeBootstrapService = void 0;
const tslib_1 = require("tslib");
const common_1 = require("@nestjs-mod/common");
const common_2 = require("@nestjs/common");
const case_anything_1 = require("case-anything");
const path_1 = require("path");
const docker_compose_file_service_1 = require("./docker-compose-file.service");
const docker_compose_configuration_1 = require("./docker-compose.configuration");
const docker_compose_constants_1 = require("./docker-compose.constants");
const docker_compose_decorators_1 = require("./docker-compose.decorators");
const manual_docker_compose_service_1 = require("./manual-docker-compose.service");
let DockerComposeBootstrapService = class DockerComposeBootstrapService {
constructor(dockerComposeFeatureConfigurations, dockerComposeConfiguration, manualDockerComposeFeatures, dockerComposeFileService, packageJsonService, applicationPackageJsonService, gitignoreService, dotEnvService) {
this.dockerComposeFeatureConfigurations = dockerComposeFeatureConfigurations;
this.dockerComposeConfiguration = dockerComposeConfiguration;
this.manualDockerComposeFeatures = manualDockerComposeFeatures;
this.dockerComposeFileService = dockerComposeFileService;
this.packageJsonService = packageJsonService;
this.applicationPackageJsonService = applicationPackageJsonService;
this.gitignoreService = gitignoreService;
this.dotEnvService = dotEnvService;
}
async onApplicationBootstrap() {
await this.createDockerComposeFile();
this.updatePackageJson();
const { dockerComposeProdEnvFilePath } = this.getFilesPathes();
this.gitignoreService.addGitIgnoreEntry([
(0, path_1.basename)(this.dockerComposeConfiguration.dockerComposeFile),
(0, path_1.basename)(dockerComposeProdEnvFilePath),
'access.log',
'error.log',
]);
}
async createDockerComposeFile() {
const featureServices = Object.entries(this.dockerComposeFeatureConfigurations)
.map(([, services]) => services)
.reduce((all, cur) => ({ ...all, ...cur.reduce((curAll, curCur) => (0, common_1.merge)(curAll, curCur.featureConfiguration), {}) }), {});
// todo: add support featureModuleName
const manualServices = this.manualDockerComposeFeatures
.getManualDockerComposeFeatureConfigurations()
.reduce((all, cur) => (0, common_1.merge)(all, cur), {});
const bothServices = (0, common_1.merge)({ version: this.dockerComposeConfiguration.dockerComposeFileVersion }, (0, common_1.merge)(featureServices, manualServices));
const { dockerComposeExampleFilePath, dockerComposeProdFilePath, dockerComposeProdEnvFilePath } = this.getFilesPathes();
const bothServicesWithEnvs = { ...bothServices };
const envFilePath = this.dotEnvService.getEnvFilePath();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const lines = (envFilePath && this.dotEnvService.readFile(envFilePath, false)) || {};
for (const serviceName of Object.keys(bothServicesWithEnvs.services || {})) {
let writeTitle = true;
for (const eachEnvKey of Object.keys(bothServicesWithEnvs.services?.[serviceName].environment || {})) {
const envKey = (0, case_anything_1.constantCase)(eachEnvKey);
const fullEnvKey = bothServicesWithEnvs.services?.[serviceName].excludeContainerNameFromEnvironmentName ? envKey : [bothServicesWithEnvs.services?.[serviceName].container_name, envKey]
.filter(Boolean)
.map((v) => (0, case_anything_1.constantCase)(v || envKey))
.join('_');
let value =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bothServicesWithEnvs?.services?.[serviceName]?.environment?.[envKey] ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bothServicesWithEnvs?.services?.[serviceName]?.environment?.[fullEnvKey] ||
'';
value = typeof value === 'string' || typeof value === 'number' || !value ? value : String(value);
const keys = Object.keys(process.env);
if (value) {
for (const key of keys) {
value = String(value).replace(new RegExp(`%${key}%`, 'ig'), process.env[key] || '');
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bothServicesWithEnvs.services[serviceName].environment[envKey] = value;
if (!bothServicesWithEnvs.services[serviceName].keysOfEnvironmentsWithStaticValue?.some((k) => (0, case_anything_1.constantCase)(envKey).endsWith((0, case_anything_1.constantCase)(k)) || (0, case_anything_1.constantCase)(envKey) === (0, case_anything_1.constantCase)(k))) {
if (writeTitle) {
delete lines[`# ${serviceName} (generated)`];
lines[`# ${serviceName} (generated)`] = '';
writeTitle = false;
}
delete lines[envKey];
delete lines[fullEnvKey];
lines[fullEnvKey] = value;
}
else {
delete lines[envKey];
delete lines[fullEnvKey];
}
}
}
for (const [key] of Object.entries(lines)) {
const localKey = `LOCALHOST_${key}`;
for (const serviceName of Object.keys(bothServicesWithEnvs.services || {})) {
if (lines[key].includes(serviceName) && !key.startsWith('LOCALHOST')) {
const localValue = lines[localKey] !== undefined ? lines[localKey] : lines[key];
delete lines[`# ${key} (generated)`];
delete lines[localKey];
lines[`# ${key} (generated)`] = '';
lines[localKey] = localValue.split(serviceName).join('localhost');
}
}
}
const mainData = this.dockerComposeConfiguration.beforeSaveDockerComposeFile
? await this.dockerComposeConfiguration.beforeSaveDockerComposeFile({
data: bothServicesWithEnvs,
})
: {
data: bothServicesWithEnvs,
};
this.dockerComposeFileService.write(mainData.data);
if (envFilePath) {
await this.dotEnvService.writeFile(envFilePath, this.dockerComposeConfiguration.beforeSaveDockerComposeEnvFile
? await this.dockerComposeConfiguration.beforeSaveDockerComposeEnvFile(lines)
: lines);
}
// example
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const existsServices = this.dockerComposeFileService.readFile(dockerComposeExampleFilePath) ?? {};
const sampleBothServices = { ...bothServices };
for (const serviceName of Object.keys(sampleBothServices.services || {})) {
for (const eachEnvKey of Object.keys(sampleBothServices.services?.[serviceName].environment || {})) {
const envKey = (0, case_anything_1.constantCase)(eachEnvKey);
const fullEnvKey = bothServicesWithEnvs.services?.[serviceName].excludeContainerNameFromEnvironmentName ? envKey : [bothServicesWithEnvs.services?.[serviceName].container_name, envKey]
.filter(Boolean)
.map((v) => (0, case_anything_1.constantCase)(v || envKey))
.join('_');
if (!sampleBothServices.services[serviceName].environment) {
sampleBothServices.services[serviceName].environment = {};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete sampleBothServices.services[serviceName].environment[envKey];
if (bothServicesWithEnvs.services?.[serviceName].keysOfEnvironmentsWithStaticValue?.some((k) => (0, case_anything_1.constantCase)(envKey).endsWith((0, case_anything_1.constantCase)(k)) || (0, case_anything_1.constantCase)(envKey) === (0, case_anything_1.constantCase)(k))) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sampleBothServices.services[serviceName].environment[envKey] =
existsServices?.services?.[serviceName]?.environment?.[envKey] ||
existsServices?.services?.[serviceName]?.environment?.[fullEnvKey] ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bothServicesWithEnvs.services[serviceName].environment[envKey] ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bothServicesWithEnvs.services[serviceName].environment[fullEnvKey] ||
'';
}
else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sampleBothServices.services[serviceName].environment[envKey] =
existsServices?.services?.[serviceName]?.environment?.[envKey] ||
existsServices?.services?.[serviceName]?.environment?.[fullEnvKey] ||
'';
}
}
}
const header = '# Do not modify this file, it is generated using the DockerCompose module included with NestJS-mod.';
const sampleData = this.dockerComposeConfiguration.beforeSaveExampleDockerComposeFile
? await this.dockerComposeConfiguration.beforeSaveExampleDockerComposeFile({
data: sampleBothServices,
header,
})
: {
data: sampleBothServices,
header,
};
this.dockerComposeFileService.writeFile(dockerComposeExampleFilePath, sampleData.data, sampleData.header);
// prod
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const prodLines = this.dotEnvService.readFile(dockerComposeProdEnvFilePath, false) || {};
const sampleBothProdServices = { ...sampleBothServices };
for (const serviceName of Object.keys(sampleBothProdServices.services || {})) {
let writeTitle = true;
for (const eachEnvKey of Object.keys(sampleBothProdServices.services?.[serviceName].environment || {})) {
const envKey = (0, case_anything_1.constantCase)(eachEnvKey);
const fullEnvKey = bothServicesWithEnvs.services?.[serviceName].excludeContainerNameFromEnvironmentName ? envKey : [bothServicesWithEnvs.services?.[serviceName].container_name, envKey]
.filter(Boolean)
.map((v) => (0, case_anything_1.constantCase)(v || envKey))
.join('_');
if (!sampleBothProdServices.services[serviceName].environment) {
sampleBothProdServices.services[serviceName].environment = {};
}
if (!sampleBothProdServices.services[serviceName].keysOfEnvironmentsWithStaticValue?.some((k) => (0, case_anything_1.constantCase)(envKey).endsWith((0, case_anything_1.constantCase)(k)) || (0, case_anything_1.constantCase)(envKey) === (0, case_anything_1.constantCase)(k))) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete sampleBothServices.services[serviceName].environment[envKey];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
sampleBothProdServices.services[serviceName].environment[envKey] = `\${${fullEnvKey}}`;
}
if (!sampleBothProdServices.services[serviceName].keysOfEnvironmentsWithStaticValue?.some((k) => (0, case_anything_1.constantCase)(envKey).endsWith((0, case_anything_1.constantCase)(k)) || (0, case_anything_1.constantCase)(fullEnvKey) === (0, case_anything_1.constantCase)(k))) {
if (writeTitle) {
delete prodLines[`# ${serviceName} (generated)`];
prodLines[`# ${serviceName} (generated)`] = '';
writeTitle = false;
}
const value = prodLines[fullEnvKey] || lines[fullEnvKey] || '';
delete prodLines[envKey];
delete prodLines[fullEnvKey];
prodLines[fullEnvKey] = value;
}
else {
delete prodLines[envKey];
delete prodLines[fullEnvKey];
}
}
}
for (const [key] of Object.entries(prodLines)) {
const localKey = `LOCALHOST_${key}`;
for (const serviceName of Object.keys(sampleBothProdServices.services || {})) {
if (prodLines[key].includes(serviceName) && !key.startsWith('LOCALHOST')) {
const localValue = prodLines[localKey] !== undefined ? prodLines[localKey] : prodLines[key];
delete prodLines[`# ${key} (generated)`];
delete prodLines[localKey];
prodLines[`# ${key} (generated)`] = '';
prodLines[localKey] = localValue.split(serviceName).join('localhost');
}
}
}
const prodData = this.dockerComposeConfiguration.beforeSaveExampleDockerComposeFile
? await this.dockerComposeConfiguration.beforeSaveExampleDockerComposeFile({
data: sampleBothProdServices,
header,
})
: {
data: sampleBothProdServices,
header,
};
this.dockerComposeFileService.writeFile(dockerComposeProdFilePath, prodData.data, prodData.header);
await this.dotEnvService.writeFile(dockerComposeProdEnvFilePath, this.dockerComposeConfiguration.beforeSaveProdDockerComposeEnvFile
? await this.dockerComposeConfiguration.beforeSaveProdDockerComposeEnvFile(prodLines)
: prodLines);
}
getFilesPathes() {
const dockerComposeExampleFilePath = this.dockerComposeConfiguration.exampleDockerComposeFile ||
this.dockerComposeFileService
.getDockerComposeFilePath()
.replace('.yml', '-example.yml')
.replace('/-example.yml', '/example.yml');
const dockerComposeProdFilePath = this.dockerComposeConfiguration.prodDockerComposeFile ||
this.dockerComposeFileService
.getDockerComposeFilePath()
.replace('.yml', '-prod.yml')
.replace('/-prod.yml', '/prod.yml');
const dockerComposeProdEnvFilePath = this.dockerComposeConfiguration.prodDockerComposeEnvFile ||
this.dockerComposeFileService
.getDockerComposeFilePath()
.replace('.yml', '-prod.env')
.replace('/-prod.env', '/prod.env');
return { dockerComposeExampleFilePath, dockerComposeProdFilePath, dockerComposeProdEnvFilePath };
}
updatePackageJson() {
const packageJson = this.packageJsonService.read();
const applicationPackageJson = this.applicationPackageJsonService.read();
const packageJsonFilePath = this.packageJsonService.getPackageJsonFilePath();
if (packageJson && packageJsonFilePath) {
const dockerComposeFilePath = this.dockerComposeConfiguration.dockerComposeFile.replace((0, path_1.dirname)(packageJsonFilePath), '');
let { dockerComposeProdFilePath, dockerComposeProdEnvFilePath } = this.getFilesPathes();
dockerComposeProdFilePath = dockerComposeProdFilePath.replace((0, path_1.dirname)(packageJsonFilePath), '');
dockerComposeProdEnvFilePath = dockerComposeProdEnvFilePath.replace((0, path_1.dirname)(packageJsonFilePath), '');
if (packageJson.scripts) {
if ((0, path_1.dirname)(packageJsonFilePath.replace((0, path_1.dirname)(packageJsonFilePath), '')) === (0, path_1.dirname)(dockerComposeFilePath)) {
this.packageJsonService.addScripts(docker_compose_constants_1.DOCKER_COMPOSE_INFRA_CATEGORY_NAME, {
[`docker-compose:start`]: {
commands: [
`export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f .${dockerComposeFilePath} --compatibility up -d`,
],
comments: ['Running the main docker-compose infrastructure'],
},
[`docker-compose:stop`]: {
commands: [`export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f .${dockerComposeFilePath} down`],
comments: ['Stopping the main docker-compose infrastructure'],
},
}, packageJson);
this.packageJsonService.addScripts(docker_compose_constants_1.DOCKER_COMPOSE_PROD_INFRA_CATEGORY_NAME, {
[`docker-compose:start-prod`]: {
commands: [
`export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f .${dockerComposeProdFilePath} --env-file .${dockerComposeProdEnvFilePath} --compatibility up -d`,
],
comments: ['Running the main docker-compose prod infrastructure'],
},
[`docker-compose:stop-prod`]: {
commands: [
`export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f .${dockerComposeProdFilePath} --env-file .${dockerComposeProdEnvFilePath} down`,
],
comments: ['Stopping the main docker-compose prod infrastructure'],
},
}, packageJson);
}
else {
this.packageJsonService.addScripts(docker_compose_constants_1.DOCKER_COMPOSE_INFRA_CATEGORY_NAME, {
[`docker-compose:start:${applicationPackageJson?.name}`]: {
commands: [
`export COMPOSE_INTERACTIVE_NO_CLI=1`,
`docker compose -f .${dockerComposeFilePath} --compatibility up -d`,
],
comments: [`Running the docker-compose infrastructure for ${applicationPackageJson?.name}`],
},
[`docker-compose:stop:${applicationPackageJson?.name}`]: {
commands: [`export COMPOSE_INTERACTIVE_NO_CLI=1`, `docker compose -f .${dockerComposeFilePath} down`],
comments: [`Stopping the docker-compose infrastructure for ${applicationPackageJson?.name}`],
},
}, packageJson);
this.packageJsonService.addScripts(docker_compose_constants_1.DOCKER_COMPOSE_PROD_INFRA_CATEGORY_NAME, {
[`docker-compose:start-prod:${applicationPackageJson?.name}`]: {
commands: [
`export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f .${dockerComposeProdFilePath} --env-file .${dockerComposeProdEnvFilePath} --compatibility up -d`,
],
comments: [`Running the main docker-compose prod infrastructure for ${applicationPackageJson?.name}`],
},
[`docker-compose:stop-prod:${applicationPackageJson?.name}`]: {
commands: [
`export COMPOSE_INTERACTIVE_NO_CLI=1 && docker compose -f .${dockerComposeProdFilePath} --env-file .${dockerComposeProdEnvFilePath} down`,
],
comments: [`Stopping the main docker-compose prod infrastructure for ${applicationPackageJson?.name}`],
},
}, packageJson);
}
}
this.packageJsonService.write(packageJson);
}
}
};
exports.DockerComposeBootstrapService = DockerComposeBootstrapService;
exports.DockerComposeBootstrapService = DockerComposeBootstrapService = tslib_1.__decorate([
(0, common_2.Injectable)(),
tslib_1.__param(0, (0, docker_compose_decorators_1.InjectAllDockerComposeFeatures)()),
tslib_1.__metadata("design:paramtypes", [Object, docker_compose_configuration_1.DockerComposeConfiguration,
manual_docker_compose_service_1.ManualDockerComposeFeatures,
docker_compose_file_service_1.DockerComposeFileService,
common_1.PackageJsonService,
common_1.ApplicationPackageJsonService,
common_1.GitignoreService,
common_1.DotEnvService])
], DockerComposeBootstrapService);
//# sourceMappingURL=docker-compose-bootstrap.service.js.map
;