cmd / deploy

deploy is a Go CLI that deploys services to Render from the latest origin/main.

Run it:

go run ./cmd/deploy

It reads these environment variables from .env:

Flow

  1. Fetch origin/main merged by sockeye.
  2. Compare each service's live commit to origin/main.
  3. If new commits exist, show the log and prompt to deploy.
  4. Deploy the selected services at origin/main.
  5. Wait for app-jobs to reach live so its migration finishes before app-web.
  6. Record each service in sockeye as it reaches live.

Example session:

$ go run ./cmd/deploy
From https://sockeye.example.com/git/app
 * branch            main       -> FETCH_HEAD
Check https://status.render.com/ for incidents before continuing.
Press any key to continue or ctrl+c to exit...

```
a1b2c3d4e fix session expiry on token refresh
f5e6d7c8b add retry logic to webhook delivery
```

deploy app-jobs? (y/n) y
deploying app-jobs...
waiting for app-jobs deploy dep-abc123 to go live...
app-jobs live

skipping app-web, already up to date...

Render API client

The render package (render/client.go) wraps these endpoints:

WaitForDeploy polls every 10s with a 30 minute timeout. Any terminal state other than live is an error. A failed migration appears as pre_deploy_failed.

Recording the deploy

The script POSTs a record to sockeye as each service reaches live. A failure partway through still records what shipped.

The script holds no sockeye token. It asks git for the token that soc login stored for the host:

in := "protocol=https\nhost=sockeye.example.com\n\n"
c := exec.Command("git", "credential", "fill")
c.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")

The script retrieves the token first, so a missing token halts the deploy before any service deploys without a record. Recording deploys connects errors to the release SHA that introduced them.

Design

The script reads origin/main, so it runs from any branch or git worktree. If I skip app-jobs, the script warns that subsequent services may run against an older schema. Each service prompts individually, allowing partial deploys.

← All articles