Skip to content

✨ Moved installations of the released main layout onto this one (S6b) - #383

Merged
acburdine merged 6 commits into
next-dockerfrom
claude/s6b-migrate-installs-layout-bf7a27
Oct 9, 2026
Merged

acburdine merged 6 commits into
next-dockerfrom
claude/s6b-migrate-installs-layout-bf7a27

Conversation

@acburdine

@acburdine acburdine commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

closes https://linear.app/ghost/issue/PLA-482

Installations made from main update with git pull, and a pull across this layout breaks them. This adds migration 0001-compose-profiles, entered by the served launcher's self-update run inside the old checkout:

curl -fsSL https://docker.ghost.org/install.sh | bash -s -- self-update

What it does

Before changing anything (refusing with what to resolve):

  • git shows tracked stack files unedited (work tree or unpushed commits)
  • Ghost and MySQL running; official image; Ghost ≥ 6.61.0 (first next image)
  • project name kept from Compose/containers, so volumes and Caddy certificates stay the site's
  • Ghost pinned to ghost:<running version>-next-alpine by digest: same version
  • .env split into .env + ghost.env, Ghost values as Ghost received them; unescaped $ in operator values refused
  • caddy/Caddyfile kept as Caddyfile.local and carried as written into caddy/sites/site.caddy: {$DOMAIN}/{$ADMIN_DOMAIN}/{$ACTIVITYPUB_TARGET} filled in, import snippets/ pointed at main's own snippets kept in caddy/sites/legacy-snippets/, a leading global block lifted to caddy/global/legacy.caddy; bare upstreams left (they resolve on the site's network); loaded by caddy validate in the site's Caddy image
  • overrides resolved with the new layout; release images pulled while the site runs
  • ActivityPub stays where main sent it (hosted unless ACTIVITYPUB_TARGET said otherwise)

Then: snapshot, pause Ghost/ActivityPub, write layout + metadata, checked backup, start, verify through Caddy. Recovery follows self-update's boundary: before startup, files back and writers resumed; after, services stopped, main's files back, data left for the operator. Metadata written last is the only completion record; a second run is an ordinary self-update.

Also ports main's TINYBIRD_SYNC_AUTH (#332), which landed after next-docker was cut and would otherwise be lost by the migration.

Deferred

  • No general migration framework or journal: this one migration needs none.
  • No IPv6 network detection: compose.ipv6.yml used with -f by hand must be passed via GD_COMPOSE_OVERRIDES (documented).
  • Installs without .git are refused (edits can't be told apart).
  • tests/fixtures/released-main must be refreshed from main's final commit before S12 merges (added to the roadmap).

Tests

  • Unit: translation, split, and the migration under the scripted daemon (refusals, success, rerun, --check, failure before and after startup).
  • tests/e2e/migrate-main.sh + migrate-main-linux CI job: real main-layout clone with ActivityPub, custom route, global block and override; edited file and unpullable image refused; unhealthy start recovered and main restarted on its data; served-launcher migration verified; rerun no-op. Not run locally — first run is this CI.

🤖 Generated with Claude Code

ref https://linear.app/ghost/issue/PLA-482

main gained TINYBIRD_SYNC_AUTH (#332) after next-docker was cut from
it, so the new compose.yml never passed it to Ghost or Traffic
Analytics. Migrating a main installation would carry the setting in
.env and silently stop using it. Ported here first so the migration
from the released main layout preserves it.

- compose.yml: passes TINYBIRD_SYNC_AUTH to Ghost as
  tinybird__sync_auth_key and to Traffic Analytics.
- .env.example: documents it as an optional operator setting.
- TINYBIRD.md: says how to generate and apply it.
closes https://linear.app/ghost/issue/PLA-482

Installations made from main update with git pull, and a pull across
this layout breaks them: their .env selects no site mode, their Ghost
image has a layout this release refuses, and their hand-edited Caddyfile
reads environment variables the new Caddy no longer gets. This gates
merging next-docker into main.

The served launcher's self-update, run in such a checkout, finds no
metadata and main's .env, and runs migration 0001-compose-profiles. It
decides everything before changing anything (Compose resolves the
staged configuration with the operator's overrides, Caddy loads the
translated routes, git shows the stack files unedited), then follows
self-update's recovery boundary with its snapshot, writer pause and a
checked backup. Ghost keeps its exact version on that version's `next`
image. The Caddyfile is translated rather than replaced so custom routes
keep working. The metadata, written last, is the only record that the
migration is done; no general migration framework was built.

- manager/src/legacy.ts: the migration, its refusals and recovery.
- manager/src/legacy/caddy.ts: the Caddyfile translation.
- manager/src/legacy/config.ts: the .env / ghost.env split.
- manager/src/commands/self-update.ts: enters the migration.
- manager/src/backup.ts: a `migrating` backup that expects drift.
- manager/src/recovery.ts: Snapshot, shared with self-update.
- manager/src/compose.ts, commands/install.ts: variables of staged
  inputs; the loopback port search exported.
- manager/test/: translation, split, and migration under fakes.
- tests/e2e/migrate-main.sh, .github/workflows/test.yml: real
  migration, refusals and recovery on Linux CI.
- tests/fixtures/released-main/: main's stack files.
- docs/, README.md, .gitignore: operator steps, architecture, roadmap.
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: dc9ffafc-c401-4e93-8acb-5d7e7a919fe1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

ref https://linear.app/ghost/issue/PLA-482

Translating the operator's Caddyfile into this layout's shape (snippet
arguments, aliased upstreams, brace tracking) was the most fiddly part
of the migration, and only made the routes look like a fresh install's.
Keeping them working needs less: fill in the three variables main's
Caddy read from its environment, point `import snippets/` at main's own
snippets kept beside the routes, and lift a leading global options
block into caddy/global/. Bare service upstreams still resolve on the
site's own network. Caddy's validation remains the safety net.

- manager/src/legacy/caddy.ts: the carry, about a third of the size.
- manager/src/legacy.ts: keeps main's snippets in
  caddy/sites/legacy-snippets/, staged for validation too.
- manager/test/, tests/e2e/migrate-main.sh: expectations for the
  carried routes.
- docs/: what a moved site's routes look like, and that moving to this
  layout's snippets is optional until a shared Caddy.
…rate-installs-layout-bf7a27

# Conflicts:
#	README.md
#	docs/configuration.md
ref https://linear.app/ghost/issue/PLA-482

main keeps changing (Renovate bumps, the occasional fix) until
next-docker is merged into it, and a copied fixture would drift from it
unnoticed. The migration tests now read main itself: the unit tests
check out GD_RELEASED_MAIN (origin/main) from this repository, and the
e2e scenario clones the repository and checks it out, which is also
exactly how an operator's installation came to be. S12 pins the ref to
main's last commit before the merge.

- manager/test/site.ts: releasedMain(), main's files from git.
- manager/test/legacy.test.ts, migrate-main.test.ts: read them.
- tests/e2e/migrate-main.sh: a real clone of main.
- .github/workflows/test.yml: the manager job fetches main.
- tests/fixtures/released-main/: removed.
- docs/ghost-cli-replacement.md: S12 pins GD_RELEASED_MAIN.
…settings

ref https://linear.app/ghost/issue/PLA-482

Three problems review found in the migration from the released main
layout:

- The lock was taken only after preparation, which stages into and then
  removes .ghost-docker-update/. A second run overlapping the first could
  remove the first's snapshot, so a later failure in the first could not
  put the site's files back. The lock is now held from before anything
  is staged, --check included.
- An override mounting into /var/lib/ghost (main's image's install and
  content path) survived the move to an image that keeps content in
  /home/ghost/content: the uploads it held became invisible to Ghost and
  to the backup, and Compose accepted it. It is now refused, naming the
  mount.
- The new layout was first resolved with only the generated settings,
  so an override requiring an existing .env setting (`${SMTP_HOST:?}`)
  failed to resolve. Draft resolution now has every setting .env had.

- manager/src/legacy.ts: the three fixes.
- manager/test/migrate-main.test.ts: a test for each.
- docs/install.md: overrides resolve with .env; /var/lib/ghost mounts
  are refused.
@acburdine
acburdine merged commit e476849 into next-docker Oct 9, 2026
14 checks passed
@acburdine
acburdine deleted the claude/s6b-migrate-installs-layout-bf7a27 branch October 9, 2026 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant