feat: implement comprehensive environment variable resolution in preparation functions, enhancing flexibility and support for nested references across services and environments

This commit is contained in:
Mauricio Siu
2025-09-03 21:41:11 -06:00
parent 4c5771b55b
commit fb749cd862
18 changed files with 441 additions and 41 deletions

View File

@@ -206,6 +206,7 @@ const createEnvFile = (compose: ComposeNested) => {
const envFileContent = prepareEnvironmentVariables(
envContent,
compose.environment.project.env,
compose.environment.env,
).join("\n");
if (!existsSync(dirname(envFilePath))) {
@@ -236,6 +237,7 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
const envFileContent = prepareEnvironmentVariables(
envContent,
compose.environment.project.env,
compose.environment.env,
).join("\n");
const encodedContent = encodeBase64(envFileContent);

View File

@@ -29,6 +29,7 @@ export const buildCustomDocker = async (
const args = prepareEnvironmentVariables(
buildArgs,
application.environment.project.env,
application.environment.env,
);
const dockerContextPath = getDockerContextPath(application);
@@ -51,7 +52,12 @@ export const buildCustomDocker = async (
as it could be publicly exposed.
*/
if (!publishDirectory) {
createEnvFile(dockerFilePath, env, application.environment.project.env);
createEnvFile(
dockerFilePath,
env,
application.environment.project.env,
application.environment.env,
);
}
await spawnAsync(
@@ -93,6 +99,7 @@ export const getDockerCommand = (
const args = prepareEnvironmentVariables(
buildArgs,
application.environment.project.env,
application.environment.env,
);
const dockerContextPath =
@@ -122,6 +129,7 @@ export const getDockerCommand = (
dockerFilePath,
env,
application.environment.project.env,
application.environment.env,
);
}

View File

@@ -14,6 +14,7 @@ export const buildHeroku = async (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
try {
const args = [
@@ -54,6 +55,7 @@ export const getHerokuCommand = (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
const args = [

View File

@@ -149,6 +149,7 @@ export const mechanizeDockerContainer = async (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
const image = getImageName(application);

View File

@@ -21,6 +21,7 @@ export const buildNixpacks = async (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
const writeToStream = (data: string) => {
@@ -102,6 +103,7 @@ export const getNixpacksCommand = (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
const args = ["build", buildAppDirectory, "--name", appName];

View File

@@ -13,6 +13,7 @@ export const buildPaketo = async (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
try {
const args = [
@@ -53,6 +54,7 @@ export const getPaketoCommand = (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
const args = [

View File

@@ -27,6 +27,7 @@ export const buildRailpack = async (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
try {
@@ -124,6 +125,7 @@ export const getRailpackCommand = (
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
// Prepare command

View File

@@ -6,14 +6,17 @@ export const createEnvFile = (
directory: string,
env: string | null,
projectEnv?: string | null,
environmentEnv?: string | null,
) => {
const envFilePath = join(dirname(directory), ".env");
if (!existsSync(dirname(envFilePath))) {
mkdirSync(dirname(envFilePath), { recursive: true });
}
const envFileContent = prepareEnvironmentVariables(env, projectEnv).join(
"\n",
);
const envFileContent = prepareEnvironmentVariables(
env,
projectEnv,
environmentEnv,
).join("\n");
writeFileSync(envFilePath, envFileContent);
};
@@ -21,10 +24,13 @@ export const createEnvFileCommand = (
directory: string,
env: string | null,
projectEnv?: string | null,
environmentEnv?: string | null,
) => {
const envFileContent = prepareEnvironmentVariables(env, projectEnv).join(
"\n",
);
const envFileContent = prepareEnvironmentVariables(
env,
projectEnv,
environmentEnv,
).join("\n");
const encodedContent = encodeBase64(envFileContent || "");
const envFilePath = join(dirname(directory), ".env");

View File

@@ -55,6 +55,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
const envVariables = prepareEnvironmentVariables(
defaultMariadbEnv,
mariadb.environment.project.env,
mariadb.environment.env,
);
const volumesMount = generateVolumeMounts(mounts);
const bindsMount = generateBindMounts(mounts);

View File

@@ -103,6 +103,7 @@ ${command ?? "wait $MONGOD_PID"}`;
const envVariables = prepareEnvironmentVariables(
defaultMongoEnv,
mongo.environment.project.env,
mongo.environment.env,
);
const volumesMount = generateVolumeMounts(mounts);
const bindsMount = generateBindMounts(mounts);

View File

@@ -61,6 +61,7 @@ export const buildMysql = async (mysql: MysqlNested) => {
const envVariables = prepareEnvironmentVariables(
defaultMysqlEnv,
mysql.environment.project.env,
mysql.environment.env,
);
const volumesMount = generateVolumeMounts(mounts);
const bindsMount = generateBindMounts(mounts);

View File

@@ -54,6 +54,7 @@ export const buildPostgres = async (postgres: PostgresNested) => {
const envVariables = prepareEnvironmentVariables(
defaultPostgresEnv,
postgres.environment.project.env,
postgres.environment.env,
);
const volumesMount = generateVolumeMounts(mounts);
const bindsMount = generateBindMounts(mounts);

View File

@@ -52,6 +52,7 @@ export const buildRedis = async (redis: RedisNested) => {
const envVariables = prepareEnvironmentVariables(
defaultRedisEnv,
redis.environment.project.env,
redis.environment.env,
);
const volumesMount = generateVolumeMounts(mounts);
const bindsMount = generateBindMounts(mounts);

View File

@@ -259,21 +259,44 @@ export const removeService = async (
export const prepareEnvironmentVariables = (
serviceEnv: string | null,
projectEnv?: string | null,
environmentEnv?: string | null,
) => {
const projectVars = parse(projectEnv ?? "");
const environmentVars = parse(environmentEnv ?? "");
const serviceVars = parse(serviceEnv ?? "");
const resolvedVars = Object.entries(serviceVars).map(([key, value]) => {
let resolvedValue = value;
// Replace project variables
if (projectVars) {
resolvedValue = value.replace(/\$\{\{project\.(.*?)\}\}/g, (_, ref) => {
if (projectVars[ref] !== undefined) {
return projectVars[ref];
}
throw new Error(`Invalid project environment variable: project.${ref}`);
});
resolvedValue = resolvedValue.replace(
/\$\{\{project\.(.*?)\}\}/g,
(_, ref) => {
if (projectVars[ref] !== undefined) {
return projectVars[ref];
}
throw new Error(
`Invalid project environment variable: project.${ref}`,
);
},
);
}
// Replace environment variables
if (environmentVars) {
resolvedValue = resolvedValue.replace(
/\$\{\{environment\.(.*?)\}\}/g,
(_, ref) => {
if (environmentVars[ref] !== undefined) {
return environmentVars[ref];
}
throw new Error(`Invalid environment variable: environment.${ref}`);
},
);
}
// Replace self-references (service variables)
resolvedValue = resolvedValue.replace(/\$\{\{(.*?)\}\}/g, (_, ref) => {
if (serviceVars[ref] !== undefined) {
return serviceVars[ref];
@@ -301,8 +324,9 @@ export const parseEnvironmentKeyValuePair = (
export const getEnviromentVariablesObject = (
input: string | null,
projectEnv?: string | null,
environmentEnv?: string | null,
) => {
const envs = prepareEnvironmentVariables(input, projectEnv);
const envs = prepareEnvironmentVariables(input, projectEnv, environmentEnv);
const jsonObject: Record<string, string> = {};