UNPKG

@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
"use strict"; 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