fix(qenv): replace smartfile-based config loading with native fs and yaml parsing for env files

This commit is contained in:
2026-05-01 15:54:39 +00:00
parent c0d33340c3
commit 5e31dc93a3
13 changed files with 3777 additions and 3804 deletions
+47 -20
View File
@@ -2,18 +2,19 @@ import { CloudlyAdapter } from './qenv.classes.configvaultadapter.js';
import * as plugins from './qenv.plugins.js';
export type TEnvVarRef = string | (() => Promise<string>);
type TKeyValueObject = Record<string, any>;
export class Qenv {
public requiredEnvVars: string[] = [];
public availableEnvVars: string[] = [];
public missingEnvVars: string[] = [];
public keyValueObject: { [key: string]: any } = {};
public keyValueObject: TKeyValueObject = {};
public logger = new plugins.smartlog.ConsoleLog();
public cloudlyAdapter: CloudlyAdapter;
public qenvFilePathAbsolute: string;
public envFilePathAbsolute: string;
public qenvFilePathAbsolute = '';
public envFilePathAbsolute?: string;
constructor(
qenvFileBasePathArg: string = process.cwd(),
@@ -27,7 +28,7 @@ export class Qenv {
this.checkForMissingEnvVars(failOnMissing);
}
private initializeFilePaths(qenvFileBasePathArg: string, envFileBasePathArg: string) {
private initializeFilePaths(qenvFileBasePathArg: string, envFileBasePathArg?: string) {
this.qenvFilePathAbsolute = plugins.path.join(
plugins.path.resolve(qenvFileBasePathArg),
'qenv.yml'
@@ -40,9 +41,9 @@ export class Qenv {
const envFileYmlPath = plugins.path.join(envFileBasePath, 'env.yml');
const envFileYamlPath = plugins.path.join(envFileBasePath, 'env.yaml');
const envFileJsonExists = plugins.smartfile.fs.fileExistsSync(envFileJsonPath);
const envFileYmlExists = plugins.smartfile.fs.fileExistsSync(envFileYmlPath);
const envFileYamlExists = plugins.smartfile.fs.fileExistsSync(envFileYamlPath);
const envFileJsonExists = this.fileExists(envFileJsonPath);
const envFileYmlExists = this.fileExists(envFileYmlPath);
const envFileYamlExists = this.fileExists(envFileYamlPath);
if (envFileJsonExists && (envFileYmlExists || envFileYamlExists)) {
this.logger.log('warn', 'Both env.json and env.yml files exist! Using env.json');
@@ -58,10 +59,13 @@ export class Qenv {
}
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);
if (this.fileExists(this.qenvFilePathAbsolute)) {
const qenvFile = this.readObjectFromFile(this.qenvFilePathAbsolute);
const requiredEnvVars = qenvFile.required;
if (Array.isArray(requiredEnvVars)) {
this.requiredEnvVars.push(
...requiredEnvVars.filter((envVar): envVar is string => typeof envVar === 'string')
);
} else {
this.logger.log('warn', 'qenv.yml does not contain a "required" Array!');
}
@@ -194,16 +198,16 @@ export class Qenv {
}
private getFromEnvYamlOrJsonFile(envVarName: string): string | undefined {
if (!plugins.smartfile.fs.fileExistsSync(this.envFilePathAbsolute)) {
if (!this.fileExists(this.envFilePathAbsolute)) {
return undefined;
}
try {
const envJson = plugins.smartfile.fs.toObjectSync(this.envFilePathAbsolute);
const envJson = this.readObjectFromFile(this.envFilePathAbsolute);
const value = envJson[envVarName];
if (value === undefined) {
return undefined;
}
if (typeof value === 'object') {
if (typeof value === 'object' && value !== null) {
return 'base64Object:' + this.encodeBase64(value);
}
return String(value);
@@ -214,23 +218,23 @@ export class Qenv {
private getFromDockerSecret(envVarName: string): string | undefined {
const secretPath = `/run/secrets/${envVarName}`;
if (plugins.smartfile.fs.fileExistsSync(secretPath)) {
return plugins.smartfile.fs.toStringSync(secretPath);
if (this.fileExists(secretPath)) {
return plugins.fs.readFileSync(secretPath, 'utf8');
}
return undefined;
}
private getFromDockerSecretJson(envVarName: string): string | undefined {
if (plugins.smartfile.fs.isDirectory('/run/secrets')) {
const availableSecrets = plugins.smartfile.fs.listAllItemsSync('/run/secrets');
if (this.directoryExists('/run/secrets')) {
const availableSecrets = plugins.fs.readdirSync('/run/secrets');
for (const secret of availableSecrets) {
if (secret.includes('secret.json')) {
const secretObject = plugins.smartfile.fs.toObjectSync(`/run/secrets/${secret}`);
const secretObject = this.readObjectFromFile(`/run/secrets/${secret}`);
const value = secretObject[envVarName];
if (value === undefined) {
continue;
}
if (typeof value === 'object') {
if (typeof value === 'object' && value !== null) {
return 'base64Object:' + this.encodeBase64(value);
}
return String(value);
@@ -249,4 +253,27 @@ export class Qenv {
const decodedString = Buffer.from(encodedString, 'base64').toString('utf-8');
return JSON.parse(decodedString);
}
private fileExists(filePath: string | undefined): filePath is string {
if (!filePath) {
return false;
}
return plugins.fs.existsSync(filePath);
}
private directoryExists(directoryPath: string): boolean {
try {
return plugins.fs.statSync(directoryPath).isDirectory();
} catch {
return false;
}
}
private readObjectFromFile(filePath: string): TKeyValueObject {
const fileString = plugins.fs.readFileSync(filePath, 'utf8');
const parsedObject = filePath.endsWith('.json')
? JSON.parse(fileString)
: plugins.yaml.parse(fileString);
return typeof parsedObject === 'object' && parsedObject !== null ? parsedObject : {};
}
}