From 8cc054389abc87c651888ec8112a2a17b07a83df Mon Sep 17 00:00:00 2001 From: Scai <59282365+alexevladgabriel@users.noreply.github.com> Date: Mon, 18 Aug 2025 02:04:23 +0300 Subject: [PATCH] feat: add self reference for env variables --- apps/dokploy/__test__/env/shared.test.ts | 74 +++++++++++++++++++++++ packages/server/src/utils/docker/utils.ts | 8 +++ 2 files changed, 82 insertions(+) diff --git a/apps/dokploy/__test__/env/shared.test.ts b/apps/dokploy/__test__/env/shared.test.ts index 4a8448aa9..5e231a5cc 100644 --- a/apps/dokploy/__test__/env/shared.test.ts +++ b/apps/dokploy/__test__/env/shared.test.ts @@ -177,3 +177,77 @@ COMPLEX_VAR="'Prefix \"DoubleQuoted\" and \${{project.APP_NAME}}'" ]); }); }); + +describe("prepareEnvironmentVariables (self references)", () => { + it("resolves self references correctly", () => { + const serviceEnv = ` +ENVIRONMENT=staging +DATABASE_URL=postgres://postgres:postgres@localhost:5432/project_db +SELF_REF=\${{ENVIRONMENT}} +`; + + const resolved = prepareEnvironmentVariables(serviceEnv, ""); + + expect(resolved).toEqual([ + "ENVIRONMENT=staging", + "DATABASE_URL=postgres://postgres:postgres@localhost:5432/project_db", + "SELF_REF=staging", + ]); + }); + + it("throws on undefined self references", () => { + const serviceEnv = ` +MISSING_VAR=\${{UNDEFINED_VAR}} +`; + + expect(() => prepareEnvironmentVariables(serviceEnv, "")).toThrow( + "Invalid service environment variable: UNDEFINED_VAR", + ); + }); + + it("allows overriding and still resolving from self", () => { + const serviceEnv = ` +ENVIRONMENT=production +OVERRIDE_ENV=\${{ENVIRONMENT}} +`; + + const resolved = prepareEnvironmentVariables(serviceEnv, ""); + + expect(resolved).toEqual([ + "ENVIRONMENT=production", + "OVERRIDE_ENV=production", + ]); + }); + + it("resolves multiple self references inside one value", () => { + const serviceEnv = ` +ENVIRONMENT=staging +APP_NAME=MyApp +COMPLEX=\${{APP_NAME}}-\${{ENVIRONMENT}}-\${{APP_NAME}} +`; + + const resolved = prepareEnvironmentVariables(serviceEnv, ""); + + expect(resolved).toEqual([ + "ENVIRONMENT=staging", + "APP_NAME=MyApp", + "COMPLEX=MyApp-staging-MyApp", + ]); + }); + + it("handles quotes with self references", () => { + const serviceEnv = ` +ENVIRONMENT=production +QUOTED="'\${{ENVIRONMENT}}'" +MIXED="\"Double \${{ENVIRONMENT}}\"" +`; + + const resolved = prepareEnvironmentVariables(serviceEnv, ""); + + expect(resolved).toEqual([ + "ENVIRONMENT=production", + "QUOTED='production'", + 'MIXED="Double production"', + ]); + }); +}); diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts index 12f46218f..ef7abf613 100644 --- a/packages/server/src/utils/docker/utils.ts +++ b/packages/server/src/utils/docker/utils.ts @@ -273,6 +273,14 @@ export const prepareEnvironmentVariables = ( throw new Error(`Invalid project environment variable: project.${ref}`); }); } + + resolvedValue = resolvedValue.replace(/\$\{\{(.*?)\}\}/g, (_, ref) => { + if (serviceVars[ref] !== undefined) { + return serviceVars[ref]; + } + throw new Error(`Invalid service environment variable: ${ref}`); + }); + return `${key}=${resolvedValue}`; });