carlos / platform Public

deploy: no build-probe advisory on static deploys, and cover --label

A static route has no process and no /api/version, so telling the
operator to set a build probe there (the no-probe advisory in
runDeploy) sets them up to configure a probe that 404s — which then
fails and times out every future `carlos deploy` for that site. Gate
the advisory on !staticDeploy; the non-static message is unchanged.

Also add the test this branch was missing: TestE2EDeployThroughConsole
now passes --label through `carlos deploy` and asserts the stored
manifest carries it, since the bug this branch fixes was exactly a
silently-passed "". TestE2EStaticDeployThroughConsole pins the first
fix by asserting the build-probe advisory never appears on a static
deploy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XxxmnFjdNxLc3M16ooFG95
Paul Campbell pushed by paul@keymail.dev 63d42f5b72d710024c5b0f16e8e7461c716f6f0b parent 665a955
2 files changed, +25 −3
  • deploy.go +9 −1
  • deploy_e2e_test.go +16 −2

CI passed — run details

diff --git a/deploy.go b/deploy.go
index 8c1d5902b..ea045fb40 100644
--- a/deploy.go
+++ b/deploy.go
@@ -334,7 +334,15 @@ func runDeploy(args []string) {
// per app, so the line names it rather than describing the problem
// and leaving the reader to find the verb.
mutedf(stdout, "watching the X-Carlos-Version header: it reports the release ADOPTED, not the build running\n")
- mutedf(stdout, " a probe proves the process actually cycled — carlos apps build-probe --app %s /api/version\n", *app)
+ // The advisory below assumes a process to probe. A static route has
+ // none — no /api/version, no exec child to cycle — so the suggested
+ // command would only teach the operator to configure a probe that
+ // 404s, which then fails and times out every future `carlos deploy`
+ // for that site. Static deploys get the line above (still true: the
+ // header reports adoption, not a running build) and stop there.
+ if !staticDeploy {
+ mutedf(stdout, " a probe proves the process actually cycled — carlos apps build-probe --app %s /api/version\n", *app)
+ }
}
targets := make([]watchTarget, 0, len(hosts))
for _, h := range hosts {
diff --git a/deploy_e2e_test.go b/deploy_e2e_test.go
index 76f9e5357..dfc9ae2f9 100644
--- a/deploy_e2e_test.go
+++ b/deploy_e2e_test.go
@@ -212,6 +212,13 @@ func TestE2EStaticDeployThroughConsole(t *testing.T) {
if strings.Contains(out, "watching "+host) {
t.Fatalf("static note still repeats the host the deploying line just printed:\n%s", out)
}
+ // A static route has no process and no /api/version, so the no-probe
+ // advisory (deploy.go, gated on !staticDeploy) must not tell the
+ // operator to set one — following that advice on a static site sets a
+ // probe that 404s and fails every later `carlos deploy` for it.
+ if strings.Contains(out, "build-probe") {
+ t.Fatalf("static deploy suggested a build probe, which a static route can never serve:\n%s", out)
+ }
ptr, err := release.Channel(context.Background(), relStore, pub, release.OpsAccount, app, channel)
if err != nil {
t.Fatalf("channel after deploy: %v", err)
@@ -266,6 +273,7 @@ func TestE2EDeployThroughConsole(t *testing.T) {
app = "titogo"
channel = "canary/e2e"
version = "abc1234"
+ label = "e2e rollout"
)
tmp := t.TempDir()
@@ -377,10 +385,13 @@ func TestE2EDeployThroughConsole(t *testing.T) {
// --- the verb ------------------------------------------------------
buf.Reset()
- runDeploy([]string{"-app", app, "-account", "ops", "-version", version, "-timeout", "30s", artifact})
+ runDeploy([]string{"-app", app, "-account", "ops", "-version", version, "-label", label, "-timeout", "30s", artifact})
out := buf.String()
- // Ship happened, against the bucket, signed.
+ // Ship happened, against the bucket, signed — and --label, wired
+ // straight through to runShipAPI (deploy.go), landed in the manifest
+ // rather than the silently-passed "" this branch exists to fix
+ // (discussion #13).
m, err := release.GetManifest(context.Background(), relStore, release.OpsAccount, app, version)
if err != nil {
t.Fatalf("manifest after deploy: %v", err)
@@ -388,6 +399,9 @@ func TestE2EDeployThroughConsole(t *testing.T) {
if m.Version != version || m.Kind != "binary" {
t.Fatalf("manifest = %+v, want %s binary", m, version)
}
+ if m.Label != label {
+ t.Fatalf("manifest label = %q, want %q — carlos deploy --label is not reaching the release", m.Label, label)
+ }
// Promote happened, onto the channel the RECORD named — never typed.
ptr, err := release.Channel(context.Background(), relStore, pub, release.OpsAccount, app, channel)
if err != nil {