Wire Coolify magic vars into patch.py + add CI test#1
Merged
Conversation
The previous patch only stripped Caddy and collapsed admin_url's
:+ conditional — leaving Ghost un-routable in Coolify (no port exposure,
no Traefik discovery), stamping admin_url as an empty string (which
Ghost 6 treats differently from "unset"), and requiring users to hand-set
DOMAIN / DATABASE_PASSWORD when Coolify already auto-generates these.
patch.py now swaps Ghost's url + MySQL credentials to Coolify's
SERVICE_URL_* / SERVICE_USER_* / SERVICE_PASSWORD_* magic vars, injects
SERVICE_URL_<NAME>_<PORT> declarations so Traefik finds the right port
on each service, adds a Ghost healthcheck for Coolify's UI indicator,
preserves upstream's \${ADMIN_DOMAIN:+...} conditional, deletes the
unused caddy/ directory, strips stale refs from .env.example, and
overwrites README.md from the new README.coolify.md source.
All edits are idempotent and fail loudly on missing anchors. A new
test-patch.sh + patch-test.yml workflow fetches upstream on every PR,
applies patch.py, validates with docker compose config --quiet, and
re-applies to assert idempotency — catching upstream restructures
before the nightly sync lands them on main.
sync.yml switches to --force-with-lease and runs the same
docker compose config gate before pushing. Renovate gets a regex
manager for GHOST_VERSION in .env.example (no-op while commented,
fires once a user pins).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
a198f97 to
06d1f1c
Compare
3 tasks
lunarthegrey
added a commit
that referenced
this pull request
Apr 24, 2026
Live deploy on Coolify showed a garbage env-var row named
"ADMIN_DOMAIN:+https://${ADMIN_DOMAIN}" — Coolify's parser doesn't
handle the nested ${ADMIN_DOMAIN:+...} conditional and extracted the
inner text as a literal var name.
Replace upstream's conditional with ${admin__url:-$SERVICE_URL_GHOST}:
a clean single-var reference Coolify shows as one editable row, with
a sensible fallback to the primary URL so Ghost always boots with a
valid admin URL whether the user sets one or not.
This reverses the G4 decision from #1 (preserving upstream's :+
syntax). That decision was based on theoretical concern about
Ghost 6's empty-string admin_url handling; the Coolify parsing bug
is a concrete problem that overrides the theoretical one. Defaulting
admin_url to $SERVICE_URL_GHOST sidesteps the empty-string question
entirely — Ghost always gets a real URL.
test-patch.sh: flip the admin_url assertion to the new form; guard
against the old :+ pattern leaking back in. Function definition
moved up so earlier assertions can use it.
README.coolify.md: rewrite the "Optional: separate admin domain"
section around the new admin__url env var.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.github/scripts/patch.pyto wire Coolify'sSERVICE_URL_*/SERVICE_USER_*/SERVICE_PASSWORD_*magic vars, injectSERVICE_URL_<NAME>_<PORT>declarations so Traefik discovers each service's port, add a Ghost healthcheck, deletecaddy/, strip stale refs from.env.example, and overwriteREADME.mdfrom the newREADME.coolify.md.${ADMIN_DOMAIN:+https://${ADMIN_DOMAIN}}conditional — the previous patch collapsed it to plain${ADMIN_DOMAIN}, which Ghost 6 stamped as empty-string (distinct from "unset" and known to break admin redirects)..github/workflows/patch-test.yml+.github/scripts/test-patch.sh— fetches upstream on every PR, appliespatch.py, validates withdocker compose config --quiet, re-applies to assert idempotency.sync.ymlnow runs the samedocker compose configgate before pushing, and uses--force-with-leaseinstead of--force..github/renovate.json5gets a regex manager forGHOST_VERSIONin.env.example— no-op while commented, fires once a user pins.Why
The previous patch only stripped Caddy and collapsed one env-var default syntax. The patched
compose.ymlstill left Ghost un-routable in Coolify (no port exposure for Traefik), forced users to manually setDOMAINand both MySQL passwords (which Coolify auto-generates via magic vars), and carried the empty-stringadmin_urlbug. A fresh Coolify user pointing at this repo would get a deployable resource that doesn't actually reach Ghost.Caveats / not tested yet
docker compose configvalidates the YAML but not Coolify's runtime magic-var substitution. TheSERVICE_URL_GHOST_2368: ""mapping-syntax declaration is inferred from Coolify's docs + official templates (which use list syntax); one sanity-check deploy should confirm Traefik picks up the port.@user@primary-domainneeds an additional Traefik label for/.well-known/webfinger— documented as an opt-in step inREADME.coolify.mdrather than automated.scripts/migrate.shis left as-is (bare-metal-only, not Coolify-runnable); mentioned in the new README for context.Test plan
bash .github/scripts/test-patch.shpasses locally (fetches upstream, applies patch,docker compose config --quietexits 0, idempotency diff-clean across 3 runs)shellcheck .github/scripts/test-patch.shcleanrenovate.json5parses as valid JSON5; regex pattern matches uncommentedGHOST_VERSION=…only (commented lines + leading whitespace rejected)COMPOSE_PROFILES=analytics: set FQDN ontraffic-analytics, verify/api/v1/page_hitreaches the serviceCOMPOSE_PROFILES=activitypub: set FQDN onactivitypub, verify webfinger resolves on the ActivityPub FQDN (primary-domain webfinger is opt-in per README)sync.ymlpost-merge; confirm the newdocker compose configstep passes and the--force-with-leasepush succeeds🤖 Generated with Claude Code