cmd / deploy
deploy is a Go CLI that deploys services to Render
from the latest origin/main.
Run from any branch or git worktree:
go run ./cmd/deploy
It requires these environment variables (loaded from .env):
RENDER_API_KEYSENTRY_AUTH_TOKEN
Flow
- Fetch
origin/mainto get the latest merged commit. - Prompt to check Render status for incidents.
- For each service, compare the live deploy commit to
origin/main. If there are new commits, show the log and prompt to deploy. - Deploy selected services at
origin/main. - Wait for
app-jobsto reachliveso db migrations complete before deploying other services. - If anything deployed, create and tag a Sentry release.
Example session:
$ go run ./cmd/deploy
From https://github.com/org/repo
* 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...
Services
The script deploys multiple Render services:
app-jobs(migration gate)app-web
Render API client
The render package (render/client.go) wraps these endpoints:
GET /services/:id/deploys?limit=5: find the live deploy's commit SHAPOST /services/:id/deploys: trigger a deploy at a specific commitGET /services/:id/deploys/:deployID: poll deploy status untillive
WaitForDeploy polls every 10s with a 30 minute timeout. Any terminal
state other than live (build_failed, update_failed,
pre_deploy_failed, deactivated, canceled) is an error. A failed
migration surfaces as pre_deploy_failed.
Sentry release (API, not CLI)
After deploying, the script calls Sentry's API:
- Create release (version = short SHA, what the UI shows)
- Set release refs (
repository+ full SHA, so the GitHub integration can pull commit metadata) - Record deploy for
production
This connects Sentry errors to the deploy that introduced them.
The three calls are idempotent, and the release runs last, after every
deploy is live. A failed release only warns and prints the retry
command: the deploys already succeeded.
Design
The script uses origin/main. This makes it work from any git worktree and
ignores local main commits that haven't been pushed yet.
app-jobs is the migration gate: waiting for it to reach live confirms
its pre-deploy db migration step finished before other services.
Skipping it prints a warning that later services may run against an old schema.
Service order matters, and app-web waits too, so a failed web deploy
aborts before the Sentry release is tagged.
Each service is deployed independently with a y/n prompt, so you can skip a service if it has unrelated changes or you want to deploy incrementally.