Deploy Executor

Deploy Executor

Track B Task 22 ships the first real deploy adapter for R1:

fly.io via flyctl. This document is the operator-facing reference

for the r1 deploy command and the internal/executor

DeployExecutor.

Provider status

ProviderStatusNotes
fly.ioGA (this commit)Shells out to flyctl; dry-run + verify-only
VercelDeferredSee specs/deploy-phase2.md
CloudflareDeferredSee specs/deploy-phase2.md
DockerDeferredNamed in the provider enum so callers compile
KamalDeferredNamed in the provider enum so callers compile

Selecting a deferred provider fails fast with exit code 2 and a

message pointing at specs/deploy-phase2.md; no partial invocation

leaks through.

Required environment

flyctl must be on $PATH (or passed via --flyctl /path/to/flyctl).

Authentication is delegated to flyctl's existing mechanisms:

before invoking r1 deploy. R1 does NOT read this token

itself — it is passed through to the child flyctl process via

the inherited environment.

R1 never logs the token, never writes it to a file, and never

passes it on the command line.

Dry-run

r1 deploy --provider fly --app my-app --dry-run

Renders a minimal fly.toml preview to stdout and exits 0. No

subprocess, no network, no filesystem writes. Use this to review

what R1 would ship before committing:

# fly.toml preview (provider=fly, dry-run)
app = "my-app"
primary_region = "iad"

[build]
  dockerfile = "Dockerfile"

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 0

[[http_service.checks]]
  grace_period = "10s"
  interval = "30s"
  method = "GET"
  path = "/"
  timeout = "5s"

When --image <ref> is supplied, the [build] block references the

registry image instead of a Dockerfile, matching what flyctl

itself would emit.

Verify-only

r1 deploy --verify-only --health-url https://my-app.fly.dev --expected-body "OK"

Skips the deploy entirely and runs a single HTTP GET against the

supplied URL. Exit codes:

substring matched

transport error)

--app)

This is the CI-friendly "is prod still up?" recipe — pair it with

your scheduler to alert on exit code 1.

Real deploy

r1 deploy --provider fly --app my-app --region iad

Runs in cwd by default; pass --dir path/to/service when the

fly.toml lives in a subdirectory. Flow:

1. Resolve flyctl (from --flyctl or exec.LookPath).

2. Invoke flyctl deploy --app <name> --region <region> (plus

--image <ref> when --image is set) with the child's cmd.Dir

set to --dir.

3. Capture stdout + stderr; on non-zero exit, include the stderr

tail in the error (first 500 chars, prefixed with ...).

4. On success, run a single net/http.Get against the deployed URL

(derived as https://<app>.fly.dev unless --health-url

overrides it). Non-200 → exit 1; all good → exit 0.

Acceptance criteria (executor integration)

When the DeployExecutor is driven through internal/descent, it

exposes two acceptance criteria, both using VerifyFunc rather

than a shell Command:

IDWhat it checks
DEPLOY-COMMIT-MATCHLocal git rev-parse HEAD agrees with the deploy's captured SHA.
DEPLOY-HEALTH-200GET deployed URL → 200, non-empty body, substring match.

DEPLOY-COMMIT-MATCH soft-passes in dry-run mode so r1 deploy

--dry-run` stays a read-only preview.

The descent engine's repair tier (BuildRepairFunc) retries the

deploy with DryRun forced off; the env-fix tier

(BuildEnvFixFunc) returns true for transient failure signals

(timeout, 502, 503, 504, temporary failure, i/o

timeout, connection reset, no such host) so the engine knows

a retry is worth attempting, and false for permanent failures

(auth, 4xx, config) so the operator is surfaced immediately.

Error taxonomy

Exit codeCondition
0Healthy deploy (or dry-run / verify-only success).
1Deploy succeeded but post-deploy health check failed.
2Usage error; unsupported provider; flyctl missing; --app missing.
3flyctl deploy itself returned non-zero (auth, build, invalid config).

Sentinel errors exposed on the deploy package (for programmatic

callers):

What this MVP intentionally does NOT do

Per the task spec, the following are deferred to

specs/deploy-executor.md and specs/deploy-phase2.md:

console-error capture).

These are scoped into follow-up commits so this MVP stays a

surgical, verifiable change.

Pages in this directory