import * as plugins from './qenv.plugins.js'; export class Qenv { public requiredEnvVars: string[] = []; public availableEnvVars: string[] = []; public missingEnvVars: string[] = []; public keyValueObject: { [key: string]: any } = {}; public logger = new plugins.smartlog.ConsoleLog(); public qenvFilePathAbsolute: string; public envFilePathAbsolute: string; constructor( qenvFileBasePathArg: string = process.cwd(), envFileBasePathArg: string, failOnMissing: boolean = true ) { this.initializeFilePaths(qenvFileBasePathArg, envFileBasePathArg); this.loadRequiredEnvVars(); this.loadAvailableEnvVars(); this.checkForMissingEnvVars(failOnMissing); } private initializeFilePaths(qenvFileBasePathArg: string, envFileBasePathArg: string) { this.qenvFilePathAbsolute = plugins.path.join(plugins.path.resolve(qenvFileBasePathArg), 'qenv.yml'); this.envFilePathAbsolute = plugins.path.join(plugins.path.resolve(envFileBasePathArg), 'env.json'); } private loadRequiredEnvVars() { if (plugins.smartfile.fs.fileExistsSync(this.qenvFilePathAbsolute)) { const qenvFile = plugins.smartfile.fs.toObjectSync(this.qenvFilePathAbsolute); if (qenvFile?.required && Array.isArray(qenvFile.required)) { this.requiredEnvVars.push(...qenvFile.required); } else { this.logger.log('warn', 'qenv.yml does not contain a "required" Array!'); } } } private loadAvailableEnvVars() { for (const envVar of this.requiredEnvVars) { const value = this.getEnvVarOnDemand(envVar); if (value) { this.availableEnvVars.push(envVar); this.keyValueObject[envVar] = value; } } } private checkForMissingEnvVars(failOnMissing: boolean) { this.missingEnvVars = this.requiredEnvVars.filter((envVar) => !this.availableEnvVars.includes(envVar)); if (this.missingEnvVars.length > 0) { console.info('Required Env Vars are:', this.requiredEnvVars); console.error('Missing Env Vars:', this.missingEnvVars); if (failOnMissing) { this.logger.log('error', 'Exiting due to missing env vars!'); process.exit(1); } else { this.logger.log('warn', 'qenv is not set to fail on missing environment variables'); } } } public getEnvVarOnDemand(envVarName: string): string | undefined { return ( this.getFromEnvironmentVariable(envVarName) || this.getFromEnvJsonFile(envVarName) || this.getFromDockerSecret(envVarName) || this.getFromDockerSecretJson(envVarName) ); } public getEnvVarOnDemandAsObject(envVarName: string): any { const rawValue = this.getEnvVarOnDemand(envVarName); if (rawValue && rawValue.startsWith('base64Object:')) { const base64Part = rawValue.split('base64Object:')[1]; return this.decodeBase64(base64Part); } return rawValue; } private getFromEnvironmentVariable(envVarName: string): string | undefined { return process.env[envVarName]; } private getFromEnvJsonFile(envVarName: string): string | undefined { try { const envJson = plugins.smartfile.fs.toObjectSync(this.envFilePathAbsolute); const value = envJson[envVarName]; if (typeof value === 'object') { return 'base64Object:' + this.encodeBase64(value); } return value; } catch (error) { return undefined; } } private getFromDockerSecret(envVarName: string): string | undefined { const secretPath = `/run/secrets/${envVarName}`; if (plugins.smartfile.fs.fileExistsSync(secretPath)) { return plugins.smartfile.fs.toStringSync(secretPath); } return undefined; } private getFromDockerSecretJson(envVarName: string): string | undefined { if (plugins.smartfile.fs.isDirectory('/run/secrets')) { const availableSecrets = plugins.smartfile.fs.listAllItemsSync('/run/secrets'); for (const secret of availableSecrets) { if (secret.includes('secret.json')) { const secretObject = plugins.smartfile.fs.toObjectSync(`/run/secrets/${secret}`); const value = secretObject[envVarName]; if (typeof value === 'object') { return 'base64Object:' + this.encodeBase64(value); } return value; } } } return undefined; } private encodeBase64(data: any): string { const jsonString = JSON.stringify(data); return Buffer.from(jsonString).toString('base64'); } private decodeBase64(encodedString: string): any { const decodedString = Buffer.from(encodedString, 'base64').toString('utf-8'); return JSON.parse(decodedString); } }