feat(resolve): derive base-URL env vars from manifest domains via ${domain:...} (#66) #67
No reviewers
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:apply
scope:capture
scope:coolify-api
scope:fleet
scope:manifest
scope:secrets
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/cast#67
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/derive-domain-refs"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #66.
Adds a
${domain:<app>[.<service>]}env-template ref that resolves to a public domain the manifest already declares — the samedomains/service_domainscast parses to reconcile Coolify domains. Kills the hand-transcription of base-URL env vars that drift from the domains cast creates (incubator's prodLANDING_BASE_URLhad silently kept a pre-apex host — the exact bug this closes).Design — symmetric with
${resource:...}(#60), but simplerSame shape: parse → sentinel → validate → fill. The difference: a resource URL needs the live database (
internal_db_url) and defers to the executor; a domain is pure manifest data, known at plan time, so it resolves fully indesiredFromManifestagainst a map built from the manifest — no live read, no executor step, no unresolved-at-write path.{ value, secret: false }(printed in diffs), and reads as a plain literal downstream — so nodiff.tschange.templateRefs;capturenever looks for it; never enters the store.assertDomainRefsis the single validation gate (apply/diff/capture), refusing before any write: an undeclared app/service, a wrong-shape ref (${domain:app}on a compose app, or${domain:app.svc}on a plain-domainsapp), or an empty/blank domain list — so the unresolved sentinel can never escape to Coolify.REPORTING_TZ-style operator literals stay literal — out of scope.Tests
Extends
test/envtemplate.test.ts+test/resolve.test.ts(500 passing): parse/classify + mutual exclusivity with the other two ref kinds; sentinel-not-literal;fillDomainEnvresolution + marker drop;templateDomainRefsextraction withtemplateRefs/requiredexclusion; one case perassertDomainRefserror branch including the schema-validdomains: [""]blank-first-entry leak (validation refuses it; the sentinel does not reach a returned env).Consumer
heavy-duty/incubatorPR#12 switches its prod base-URL literals to these refs once this merges. Like${resource:...}, an older cast writes the literal string${domain:...}— so consumers must re-run the installer onto the merged cast before applying.Gates:
npm run build,npm test(500),npm run check— all green.