Variable Substitution
How ${VAR} and ${VAR:-default} placeholders in Compose bundles are resolved when a per-env docker-compose.yml is rendered.
Placeholder syntax
Supported inside Compose blocks and .env files:
| Syntax | Meaning |
|---|---|
|
Value of |
|
Value of |
${VAR:?err} and ${VAR:+value} are not used inside this library. If you need them, keep them confined to a single Compose file and resolve before serviceComposer sees them.
Resolution order
utils.substituteVariables(text, binding) walks the binding in this order — the first match wins:
-
Service-specific env:
resources/services/<service>/.envor.env.<db>(whichever matches the DB of the env being rendered). -
Global env:
resources/services/.env.<db>. -
Template default: the
:-defaultfallback inside the Compose block.
Example: rendering ${MYSQL_PORT:-3306} for an env with DATABASE_TYPE=mysql:
| Priority | Source | Outcome |
|---|---|---|
1 |
|
|
2 |
|
|
3 |
neither file sets it |
|
Overrides collected from the Jenkins GLOBAL_VARIABLES / SERVICE_VARIABLES parameters are applied as-if they lived in the corresponding .env file — the same precedence chain applies.
Recursion and nesting
substituteVariables is recursive with a bounded depth. You can reference a variable that itself contains another placeholder:
FTACS_DB_HOST=${DB_HOST:-mysql}
JDBC_URL=jdbc:mysql://${FTACS_DB_HOST}:3306/ftacs
Unbounded recursion (a variable that references itself) triggers a hard fail rather than an infinite loop.
Sensitive values
Any variable whose name matches QAConfig.Validation.SENSITIVE_PATTERN ((?i).(PASSWORD|SECRET|TOKEN|_KEY|API_KEY|CREDENTIAL).) is masked by utils.maskSensitiveValues in:
-
FINAL_PREVIEWrendering. -
The summary box printed at the end of Create / Update.
-
Failure-hint output from
utils.failureHints.
Mask form is *. Use utils.isSensitiveVariable(name) if you need to gate logging in new helpers.
Rewriting .env files losslessly
utils.updateEnvVariables(envContent, varsToUpdate):
-
Preserves comments and blank lines.
-
Preserves original ordering of keys.
-
Updates
KEY=valin place for keys present invarsToUpdate. -
Pre-processes lines glued by the Active Choices DRP by calling
utils.splitConcatenatedAssignmentsfirst — see trailing-comma trap.
utils.parseEnvFileComments is the underlying helper; it is @NonCPS and returns a structure you can toBasic-ify before persisting.
Extracting keys
utils.extractEnvKeys(content) returns an ordered Set<String> of keys. Used by applyServiceVarsOverlay to know which keys in the user-submitted overlay already exist in the target file (those are updated) versus which are new (those are appended).
Rendering flow for a single env
-
Pick
DATABASE_TYPE→ decide which bundle (compose-mysql.yml/compose-oracle.yml) and which.env.<db>to read. -
Pick
SELECTED_SERVICES→ filter the bundle to just those services. Stripprofiles:from each kept service (commitcc8566b). -
Build the binding:
-
Parse
resources/services/.env.<db>→ global map. -
For each selected service, parse
<service>/.env.<db>or.env→ overlay onto the global map (service-specific wins). -
Overlay any
GLOBAL_VARIABLES/SERVICE_VARIABLESsubmitted by the operator.
-
-
utils.substituteVariableswalks the Compose text and resolves every placeholder. -
Write the rendered file to
MASTER_ENVS_DIR/<env>/docker-compose.yml.
See vars/serviceComposer.groovy for the actual implementation.