sync: CLI defect fixes (L4) + preview readiness probe (004743c) - #25
Merged
Merged
Conversation
…llow-ip (corpus rev 6) Implements the teploy-cli half of Tyler's 2026-09-24 preview ruling (DELEGATED_DECISIONS_2026-09-23 section 10): previews served on plain HTTP under <target-tailnet-ip>.sslip.io, gated to tailnet addresses. - preview deploy flags: --base-domain <domain> (hostname base instead of the app domain; hyphen or dot sslip form), --http-only (explicit http:// site address, no tls directive - Caddy never attempts ACME), --allow-ip <ip|cidr> (repeatable/comma; maps to caddy.Firewall.AllowIPs). Validated before connecting and again in Manager.Deploy before any mutation. - caddy.TLS gains HTTPOnly (wins over Cert/Key/Internal). - Mode persisted in the preview record as base_domain / http_only / allow_ips (all omitempty). Updates inherit each field unless the deploy overrides it (--http-only=false, --allow-ip "" clears), so a blue/green swap never re-enables HTTPS or drops the allowlist. Records without the fields behave exactly as before (app-domain host, automatic HTTPS, no gate). C06 identity (PreviewID/previewIDHex, route key, state path) and the blue/green machinery are unchanged. - preview list --json rows gain url (http:// iff http_only, else https://); domain kept; empty list still []. Text list and deploy output print the real scheme. - preview-exposure capability token (additive). - contracts rev 6 (additive): preview-state schema gains optional record/list-row fields on both eras with the url-scheme invariant; two list-row fixtures generated from the real encoder; version-handshake fixture regenerated for the new token. No MI bump. Tests: route written HTTP-only + gated, update inherits mode, field-by- field overrides, default/legacy records unchanged, State round-trip incl. pre-field and slug-era records, invalid input refused before mutation, flag parsing through the real cobra command, list JSON url scheme, caddy HTTPOnly rendering. Real Caddy (caddy:2-alpine via podman) accepted the rendered blocks: adapt OK with no tls app, :80 only; non-allowed source 403, allowed source proxied. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…efore the worker starts admit's running case parked the item in the pending slot and spawned the worker; a second delivery arriving before the worker goroutine took it saw workerLive && pending != nil and superseded it. The webhook had already told the sender that delivery was running; it never ran. Found as a ~3% flake of TestAdmission_NoGoroutinePileup under -race (GitHub CI on the mirror sync PR; 6/200 locally). The running item now goes to the worker directly. Pinned deterministically: TestAdmission_RunningDeliveryIsNeverSupersedable fails 5/5 on the old code, passes 50/50 under -race; all admission tests 300/300 under -race after the fix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…st box1) ResolveServer's flag branch took --host verbatim, so every --app/--host command (log, status, logs, rollback --app, ...) dialed the literal name instead of the registered entry. Found by the R02 docs lane against `teploy log`. A --host naming a servers.yml entry now resolves to the entry's host/user; --user/--key still win; unregistered values and a missing/unreadable servers.yml keep the raw-host behavior. Pins: config ResolveServer named/override/raw cases; teploy log --app --host box1 connects to the entry's user@host. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A connect-only probe passed against any published port: Docker's userland proxy accepts on the host side and only then dials the container, so a container with nothing listening still passed the tcp gate (and auto's 404/3xx fallback, and heal's). The probe now connects and holds up to 1s: a byte or the connection staying open is ready; the proxy's immediate close (dead backend), a refused connect, or a bad host is not. One command builder (deploy.TCPProbeCommand) shared by the deploy gate and heal. Proven live on colima: a sleep-only container published on :18096 with health.mode tcp - old probe rc=0, new probe rc=1, the deploy gate now times out; the busybox-httpd quickstart fixture still passes its tcp gate. Pin (health_tcp_backend_test.go) runs the real command against accept-then-close / silent-live / banner-live / closed listeners; red on the old probe on Linux, skipped on bash<4 (macOS bash 3.2 reports a read timeout as EOF - fail closed). internal/preview's probeTCP has the same shape and is NOT changed here (L1 lane owns internal/preview). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… hint
Found proving the tcp-gate fix live: a container with nothing listening
was diagnosed as 'the app is listening on port 46107' - Docker's
embedded resolver (127.0.0.11:<random>) inside every container on a
user-defined network. It is now skipped (its presence still proves the
listener tool ran), so the correct 'nothing is listening' finding fires.
The slow-boot hint named a nonexistent key (health: { timeout: 90s });
the grammar is timeout_seconds.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…pus rev 7) A52 creates web/worker containers from the immutable image ID, so docker ps reports a bare 12-hex short ID as the container image. Ship's wave-9 lesson: ID-created containers never matched the artifact tag. Not display-only. Surfaces fixed: - teploy status (text + --json), app status --json and server status --json: docker.ResolveImageTags batches one `docker image inspect` over the ID-form images (no call when none) and reports image = first repo tag, image_id = full sha256, image_tags = all tags. Untagged, removed or uninspectable images keep the ID (best effort; reporting never fails on it). - Rollback recorded ImageRef from the target container's docker ps Image, OVERWRITING the target release record's requested ref: after a rollback of an ID-created container the state carried an unpullable short ID (DR bundle restore resolves the image from ImageRef). The release record's ref now wins; without one the container image is resolved to its tag. Unchanged by design: prune's best-effort rmi and the predecessor snapshot keep the raw docker ps value (they act on identity, not names). Corpus rev 7 (additive, no MI bump): optional image_id/image_tags on the app-list and server-status container objects; no fixture changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ship wave-9: `teploy env set` did not feed secret-backed vars; `secret set` did. Root cause is precedence, which is by design: the server .env (written by env set) is the FIRST --env-file at deploy; teploy.yml env: plus decrypted secrets and resolved secret: references ride the later attempt env file and win. The defect was env set reporting success for a value that could never reach the container. env set now checks before writing: a key in the app's secret store, or a teploy.yml env: key holding a secret: (OpenBao) reference, is refused (nothing written) with the remedy (secret set / secret rm / change it in OpenBao); a plain teploy.yml env: key that shadows it gets a warning. README states the precedence. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-gate refusal (X05) examples/quickstart/app is the maintained deployable CLI fixture (X05 needed one in-repo; observe's fixtures/x05 has no Dockerfile). make quickstart now runs deploy -> verify -> redeploy -> teploy health -> status (asserts the image is reported by TAG, the ID-created-container fix) -> rollback (asserts v1 served again) -> a qs3 container that runs but never listens, asserting the tcp readiness gate refuses it and v1 keeps serving (the docker-proxy fix, end to end). Fixture fixes found running it: the sh-wrapped httpd ignored SIGTERM as PID 1, so every retirement waited out the 10s kill timeout (Exited 137; deploys took ~11.5s) - a TERM trap makes stops immediate (deploys ~1.5s, Exited 0); a real 200 /health so teploy health exercises HTTP, not the 404->TCP fallback. status' IMAGE column widened for tag names. Verified green on the local colima fixture. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nding) probeTCP was the connect-only /dev/tcp check L4 replaced in the deploy gate: docker-proxy accepts the connect even when the backend is dead, so a preview could be declared ready with nothing serving. It now runs deploy.TCPProbeCommand, the same live-listener probe the deploy gate uses. Co-Authored-By: Claude Opus 5.5 (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.
Mirror sync from Forgejo main at 004743c (tree identical): --host resolves servers.yml names; TCP readiness requires a live listener (deploy gate, heal, preview); ID-created containers report their tag and rollback keeps the release image (contracts rev 7); env set refuses secret-backed keys; maintained quickstart fixture + extended make quickstart.
🤖 Generated with Claude Code