Commit Graph

8 Commits

Author SHA1 Message Date
ghost
f7c633ef20 fix(preview): bind :3000 instantly and bound diagnostics posts (#2286)
* fix(preview): bind :3000 instantly and bound diagnostics posts

The trusted preview deploy chain now succeeds end to end (after #2124,
#2207, #2217), but the preview container itself dies on Cloudflare with
"Container crashed while checking for ports" and no entrypoint
diagnostics ever arrive (run 27186150190).

Two compounding causes, both reproduced/measured locally against the
pinned @cloudflare/containers 0.3.3 behavior:

1. The port window is unwinnable. The library waits a hardcoded ~20s for
   the container port, but the entrypoint only binds :3000 after redis,
   postgres, and rails db:prepare complete -- measured at 69s under a
   basic instance's 1/4 vCPU. Fix: bind :3000 within ~1s via a tiny Ruby
   placeholder responder (static 503 + meta-refresh, input ignored),
   verified with a TCP connect poll, and released just before the real
   server starts. The worker still gates previewReady on the real Rails
   /up probe and sample data, so readiness semantics are unchanged.

2. Diagnostics could stall boot and never deliver. emit_status used curl
   with no timeout against the worker, whose Durable Object can be
   unresponsive while it waits for this container's port (observed as
   15s status timeouts in the failed run) -- so boot could deadlock
   against the port check, and failure detail (#2217) never reached the
   diagnostics artifact. Fix: --connect-timeout 2 --max-time 5 on all
   posts, progress events fire-and-forget in the background, and failure
   paths flush synchronously before exit.

Validated locally at Cloudflare basic limits (1 GiB / 0.25 vCPU): first
:3000 response in 1s, full boot to Rails /up=200 at ~76s with no OOM,
and a forced postgres failure exits 1 in 4s with the failure event
flushed. With the port window satisfied, the next real failure will
finally surface postgres detail in _container_status and CI artifacts.

* fix(preview): never read from placeholder clients

Superagent flagged on PR #2286 that the single-threaded :3000 placeholder
blocked on client.readpartial, so one connection that sends nothing -- such
as a bare TCP port probe, which is exactly what the Cloudflare port check
performs -- would wedge the accept loop and starve every later probe.

The response is static, so drop the read entirely: each connection is
written the 503 warming page and closed immediately, making the loop
effectively non-blocking per client.

Regression-tested by holding three idle TCP connections open while HTTP
probes still answered 503 immediately; full boot under basic-instance
limits (1 GiB / 0.25 vCPU) still reaches Rails /up=200 with the placeholder
answering at 1s.

* fix(preview): stop postgres crashing on Cloudflare's small /dev/shm

This is the actual root cause of the preview container dying on Cloudflare
with "Container crashed while checking for ports" (run 27341808838) and the
maintainer's "dies immediately after postgres-start" (#2217).

Reproduced locally: running the preview image with a tiny /dev/shm and a
WRITABLE root (the realistic Cloudflare model) crashes postgres on startup
with:

  FATAL: could not resize shared memory segment "/PostgreSQL..." to
         1048576 bytes: No space left on device

PostgreSQL 17 defaults to dynamic_shared_memory_type = posix, which
allocates dynamic shared memory in /dev/shm. Cloudflare Containers provide
only a tiny /dev/shm, so postgres FATALs before the entrypoint can bind a
port, and the container exits -> the supervisor reports it crashed during
the port check. Local Docker hid this because its default /dev/shm is 64MB.
Memory was ruled out (boots fine at -m 512m); it is specifically /dev/shm.

Fix: set dynamic_shared_memory_type = mmap so DSM is file-backed in the
data directory instead of /dev/shm. The build comments out the default
posix line, appends mmap, verifies the result, and fails hard if
postgresql.conf is missing so this critical setting cannot silently regress.

Also log fail_preview reasons to stderr so the real failure is captured by
Cloudflare container observability even when the HTTP diagnostics channel is
blocked by the worker's port wait.

Verified at Cloudflare basic-equivalent limits (1 GiB / 0.25 vCPU,
/dev/shm 64k): before, exit 1 right after "Starting PostgreSQL..."; after,
"PostgreSQL is ready" -> Rails /up=200 (~97s), placeholder answering :3000
within 1s. Normal-resource boot still reaches /up=200 in ~9s.

* fix(preview): use standard-1 instance and widen CI readiness poll

Two changes the preview needs to actually deploy and be reported ready on
Cloudflare, both validated by deploying to a real CF account.

- instance_type basic -> standard-1. The container runs postgres + redis +
  puma AND generates the full demo dataset (Demo::Generator, ~12 years of
  transactions), which peaks just over basic's 1 GiB and OOM-kills the
  container (exit 137) before demo-data-ready. standard-1 (1/2 vCPU, 4 GiB)
  completes it. Measured on real Cloudflare: rails ready ~46s, demo data
  ~149s, peak well under 4 GiB, no OOM.

- "Collect preview diagnostics" poll budget 40 -> 100 (~128s -> ~350s), with
  the matching guard in bin/preview_deploy_security_check.rb. A real CF
  standard-1 run reached previewReady at ~195s, so the old 40-poll budget
  would have failed a working preview before it finished warming up. The
  loop still breaks early on previewReady/previewFailed.

* fix(preview): apply DSM override to the cluster the entrypoint starts

The build selected postgresql.conf via `find ... | head -1`, which picks an
arbitrary cluster, while the entrypoint starts the highest-version cluster
(ls /etc/postgresql | sort -V | tail -1). Today only PG17 is installed so they
coincide, but if a second major version were ever present the override could
land on a cluster that never runs, silently reintroducing the /dev/shm crash.

Derive PG_CONF from the same highest-version cluster the entrypoint starts so
the dynamic_shared_memory_type=mmap override always applies to the active
cluster. Verified: build edits /etc/postgresql/17/main/postgresql.conf, and at
runtime under a tiny /dev/shm postgres starts with SHOW
dynamic_shared_memory_type = mmap.

* fix(preview): apply pg_hba trust rules to the cluster the entrypoint starts

Same latent issue as the dynamic_shared_memory_type override: the build wrote
the local `trust` rules to `find ... | head -1` (arbitrary cluster), while the
entrypoint starts the highest-version cluster. With a single PG17 they coincide,
but a second major version would send the trust rules to a cluster that never
runs, breaking the entrypoint's trust-auth db setup.

Derive PG_HBA from the same highest-version cluster (ls /etc/postgresql |
sort -V | tail -1) and fail the build if it's missing. Verified under a tiny
/dev/shm: postgres starts and CREATE ROLE/CREATE DATABASE succeed via trust.
2026-06-12 10:40:35 +02:00
Sure Admin (bot)
cf60e2c1d6 chore(ci): finish Node 24 GitHub Actions migration (#2221)
* chore(ci): finish Node 24 GitHub Actions migration

* chore(ci): update preview security check for github-script v8
2026-06-06 16:41:07 +02:00
ghost
85d7695d1f ci(preview): render Cloudflare config from trusted template (#2207) 2026-06-06 05:54:40 +02:00
ghost
92fa73ef00 ci(preview): fix Cloudflare registry image deployment (#2124)
* ci(preview): fix Cloudflare registry image deployment

Keep the preview workflow's secret-bearing deploy path on trusted tooling while
rewriting Wrangler config through registry-shaped image refs for push and deploy.
Centralize preview log redaction and extend resolver/security guard coverage for
artifact identity conflicts.

* ci(preview): keep failure diagnostics resilient

* ci(preview): redact private key diagnostics
2026-06-03 00:11:36 +02:00
ghost
1af880aa2a ci(preview): stabilize image push and readiness diagnostics (#2084)
* ci(preview): rewrite image config before registry push

Point the trusted preview deploy config at the loaded CI image before Wrangler validates the worker config for the Cloudflare registry push. This keeps the existing trusted deploy boundary intact while fixing the post-2062 image-push ordering regression.

* ci(preview): require trusted readiness diagnostics

* ci(preview): use nonce for diagnostics events

* ci(preview): retain diagnostics timing anchors
2026-06-01 10:51:29 +02:00
ghost
5f8452d63b ci(preview): stabilize Cloudflare preview deployments (#2062)
* ci(preview): stabilize Cloudflare preview deployments

* ci(preview): bound diagnostics and cover artifact fallback

* ci(preview): isolate artifact deploy permissions

* ci(preview): tidy deployment comment rendering

* ci(preview): harden preview manifest generation

* ci(preview): fail on preview diagnostics failure
2026-05-31 13:30:03 +02:00
ghost
e28b883107 ci(preview): split PR image builds from trusted deploys (#2057)
* ci(preview): split PR image builds from trusted deploys

* ci(preview): harden preview artifact handoff

Move the preview image artifact into the trusted preview workflow as a no-secret build job, gate deployment on base-trusted workflow definitions, and keep Cloudflare credentials isolated to the deploy-only job.

Also fail closed when the pushed image reference is not written into wrangler.toml and expand the preview deploy guard to enforce the same-run artifact and permission boundaries.

* ci(preview): move preview builds out of privileged trigger

* ci(preview): avoid secret-shaped wrangler env assignments

* ci(preview): keep wrangler credential env explicit
2026-05-30 15:45:43 +02:00
ghost
adabc55937 ci(preview): isolate preview deployment tooling (#2025)
* ci(preview): isolate deployment tooling

Keep PR preview source separate from the deployment toolchain by building a temporary deploy workspace from base-revision preview metadata and PR-owned source.

Add a focused CI guard so future preview workflow edits preserve the trusted tooling split.

* ci(preview): harden workflow guard checks

Address CodeRabbit feedback by making the preview deploy guard assertions collision-proof and more resilient to equivalent GitHub Actions expression and workspace path forms.

* ci(preview): normalize workflow guard paths

* ci(preview): defer workflow guard validation

* revert(preview): restore workflow guard validation

* ci(preview): gate preview deployments
2026-05-30 00:54:20 +02:00