README
¶
Ploy Workflow CLI
ploy is the operator CLI for the Ploy control plane. It submits runs, manages
mig projects, follows run and job status, applies run diffs locally, downloads
run artifacts, and administers nodes and tokens. Global environment management is
available under ploy config env.
Usage
The CLI uses Cobra for command structure and help. To see available commands:
ploy --help # List all top-level commands
ploy <command> --help # Get help for a specific command
ploy help <command> # Alternative help syntax
Common command patterns:
ploy run <spec-path>[:<step-name>] [<repo-path>|<namespace/repo[:ref]>] [--follow] [--apply] [--pull[=path]] [--build-gate-forced <lang>@<release>[/<tool>]] [--gitlab-token-env ENV_NAME|--gitlab-token-prompt] # submit a single-repo run
ploy run <name> [<repo-path>|<namespace/repo[:ref]>] [--follow] [--pull[=path]] [--build-gate-forced-pre <lang>@<release>[/<tool>]] [--gitlab-token-env ENV_NAME|--gitlab-token-prompt] # submit a named spec
ploy run <namespace/repo>:<name> [<repo-path>|<namespace/repo[:ref]>] # submit a named spec from a repo
ploy run <domain>/<namespace/repo>:<name> [<repo-path>|<namespace/repo[:ref]>] # submit a named spec from a domain/repo
ploy run status <run-id> [--json|--follow] # inspect a run
ploy run restart <run-id> [--gitlab-token-env ENV_NAME|--gitlab-token-prompt] # restart a terminal run
ploy run apply <run-id> [path] [--force] # apply a run patch locally
ploy run pull <run-id> [artifacts-path] # download final run artifacts
ploy job status <job-id> # inspect one job as JSON
ploy mig run <mig-id|name> [<namespace/repo[:ref]> ...] [--failed] [--follow] [--gitlab-token-env ENV_NAME|--gitlab-token-prompt] # execute a mig project over its repo set
ploy spec schema # print the mig JSON Schema
ploy spec validate docs/schemas/mig.example.yaml # validate a mig spec
ploy spec push [<git-folder>] # publish named specs from a clean git worktree
ploy spec ls [--archived] # list published named specs
ploy spec <selector>[@sha] (--archive|--unarchive) # archive or unarchive a named spec version
Run IDs (<run-id>) are KSUID-backed strings.
Treat them as opaque identifiers when passing them between commands or scripts.
ploy run submits a local spec file, local spec directory, or published named
spec against one repository source. Add :<step-name> to a local spec path to
submit only one named step from the expanded spec. Named spec selectors use
<name>, <namespace/repo>:<name>, or
<domain>/<namespace/repo>:<name>. Add @<sha-prefix> to select a specific
published version. Use repeatable --env:<step> KEY=VALUE flags to override
steps[].envs for exactly one named step; later values win for the same
step/key. Use --build-gate-forced <lang>@<release>[/<tool>] to override both
Build Gate phases with mode: forced; use --build-gate-forced-pre and
--build-gate-forced-post for phase-specific overrides. The global flag is
mutually exclusive with phase-specific flags. These overrides preserve
build_gate.images, set build_gate.disabled: false, and make named specs
submit a mutated anonymous spec. Local repo paths submit HEAD; remote
selectors use namespace/repo, optionally suffixed with :<branch> or
:<sha>. Use --follow to wait for the run's terminal status,
--pull[=path] to wait for success and download artifacts, or --apply to
wait for success and apply the resulting patch to a clean local worktree. Use
--gitlab-token-env ENV_NAME to submit a run-scoped ephemeral GitLab token from
an environment variable, or --gitlab-token-prompt to enter it at the prompt.
The flags are mutually exclusive. The server stores only the token's SHA-256
marker in run metadata and keeps the token in memory while the run or wave is
active. Ephemeral GitLab tokens require
PLOY_GITLAB_DOMAIN and are accepted only for HTTPS repos on that configured
host. The same token flags are available on ploy run restart to attach a
fresh token marker to the restarted attempt.
ploy mig run executes an existing mig project over its managed repo set. Use
ploy mig add --name <name> --spec <path>, ploy mig repo add, and
ploy mig spec set to manage the project before running it.
ploy spec push publishes named specs from a clean git worktree. It scans
committed .yaml files, selects roots with apiVersion: ploy.mig/v1alpha1 and
a non-empty name, prepares the spec the same way as ploy run, and records
the origin source, HEAD SHA, and commit date. Untracked or modified files
stop publishing before any spec is uploaded. ploy spec ls lists the latest
published named specs.
When follow mode is used, the CLI displays a summarized per-repo job graph that
refreshes until the run reaches a terminal state. The job graph shows step index,
job type, job ID, display name, status glyph, duration, and status for each job.
For running jobs, follow mode also shows STD[O]UT and STD[E]RR preview rows
(collapsed by default). Press o to expand/collapse stdout previews for all
currently running jobs, and e for stderr previews. Failed jobs are kept expanded.
Note: --follow does not stream container logs. Use ploy job log --follow <job-id> for container log streaming.
Use ploy job status <job-id> to print the current job row and execution fields
as JSON for investigation scripts.
For ploy mig run --follow, --cap enforces an overall time limit. If exceeded,
the CLI exits follow mode; add --cancel-on-cap to also cancel the run.
When a ploy run submission completes successfully, pass --pull[=<dir>] to
download referenced artifacts and generate <dir>/manifest.json. The manifest
lists artifacts with stage, name, cid, digest, size (bytes written),
and the local path. Filenames are sanitized and deterministic; when a content
digest is available it prefixes the name, otherwise the artifact CID is used.
Mig Projects
Mig projects are long-lived containers with a unique name, a current spec, and a managed repo set.
# Create a mig project.
ploy mig add --name my-mig --spec mig.yaml
# Update the mig's current spec.
ploy mig spec set my-mig mig.yaml
# Manage the mig's repo set.
ploy mig repo add my-mig org/repo-a:main
ploy mig repo add my-mig org/repo-b:main
ploy mig repo list my-mig
# Execute the mig project (all repos by default).
ploy mig run my-mig
# Execute only specific repos.
ploy mig run my-mig org/repo-a:main org/repo-b:main
# Re-run only repos whose last terminal state is Fail.
ploy mig run my-mig --failed
Run Commands
For the long-lived current-state reference, see docs/runs.md.
# Submit from the current git worktree.
ploy run ./mig.yaml
# Submit a selected local repo path.
ploy run ./mig.yaml ../service-a
# Submit a remote repo selector. Missing ref defaults to master.
ploy run ./mig.yaml org/service-a:main
# Follow until terminal.
ploy run ./mig.yaml org/service-a:main --follow
# Follow until success and download artifacts.
ploy run ./mig.yaml org/service-a:main --pull=./artifacts
# Apply a completed run to the current repo.
ploy run apply <run-id>
# Download artifacts for an existing run.
ploy run pull <run-id> ./artifacts
Named Spec Commands
Named specs are committed mig specs that can be published for later selection.
The publish command derives source identity from origin, so the remote must
normalize to domain/namespace/repo.
# Publish named specs from the current git worktree.
ploy spec push
# Publish named specs from another worktree.
ploy spec push ../migs
# List latest published named specs.
ploy spec ls
# List archived named specs.
ploy spec ls --archived
# Archive or unarchive a named spec version.
ploy spec github.com/acme/service:upgrade-java@01234567 --archive
ploy spec github.com/acme/service:upgrade-java@01234567 --unarchive
Publish output shows updated for newly stored specs and skipped when the
same name, source, and SHA already exists. List output shows NAME, SOURCE,
SHA, and DATE. Archive and unarchive resolve active or archived rows,
respectively, then update the resolved named spec row.
Mig Project Runs
mig run executes an existing mig project over all repos in its managed set, an
explicit subset, or repos whose last terminal state was Fail.
ploy mig add --name java17 --spec mig.yaml
ploy mig repo add java17 org/repo-a:main
ploy mig repo add java17 org/repo-b:main
# Execute all repos.
ploy mig run java17 --follow
# Execute only selected repos.
ploy mig run java17 org/repo-a:main
# Re-run only repos whose last terminal state is Fail.
ploy mig run java17 --failed
Mig Workflow Summary
| Command | Description |
|---|---|
mig add --name <name> |
Create a mig project |
mig repo add |
Add a repository to a mig project |
mig run <mig> |
Run a mig project |
run apply <run-id> |
Apply diffs for the current repo from a run |
run pull <run-id> |
Download final artifacts for a run |
mig pull [<mig>] |
Pull diffs for the current repo from a mig |
run status --follow |
Follow run status until terminal |
job status <job-id> |
Print one job status as JSON |
See docs/migs-lifecycle.md for the relationship between waves, runs, and jobs.
Pull Migs Changes Locally
After a run completes, you can apply the Migs-generated changes into your local
repository using either ploy run apply <run-id> (run-based) or ploy mig pull (mig-based).
These commands apply stored diffs from the control plane to the current worktree.
# From a repo that participated in a Migs run:
cd service-a
# Run-based apply (you know the run_id):
ploy run apply <run-id>
# Mig-based pull (default: last succeeded):
ploy mig pull <mig-id|name>
How it works:
- Derives the current repo identity from the git remote (default:
origin). - Verifies the working tree is clean (no uncommitted changes).
- Resolves the run via
POST /v1/runs/{run_id}/pull(orPOST /v1/migs/{mig_id}/pullfor mig-based pull). - Fetches repo details and verifies local
HEADmatches the run'ssource_commit_sha. - Downloads and applies all stored Migs diffs via
git apply.
Arguments:
<run-id>— Run ID (KSUID string), forploy run apply.[<mig-id|name>]— Mig ID or name (optional), forploy mig pull.
Flags:
--force— Forploy run apply, allow localHEADto differ from the run source SHA. This never bypasses dirty worktree checks.
Examples:
# Apply changes from a run ID.
ploy run apply <run-id>
# Download final run artifacts.
ploy run pull <run-id> [artifacts-path]
# Pull changes from the latest successful run for a mig.
ploy mig pull <mig-id|name>
# Pull changes from the latest failed run for a mig.
ploy mig pull --last-failed <mig-id|name>
Requirements:
- Must be run inside a git repository.
- Working tree must be clean (commit or stash changes first).
- The origin remote URL must match the
repo_urlused when the run was created. - The run must exist and have diffs available.
Interactive TUI (ploy tui)
ploy tui launches a full-screen terminal UI for browsing migrations, runs, and jobs
without chaining multiple CLI commands. It opens in alternate screen mode.
ploy tui
Screens and navigation
| Screen | Title | Description |
|---|---|---|
| Root | PLOY |
Root selector. Choose Migrations, Runs, or Jobs. |
| Migrations list | PLOY | MIGRATIONS |
Two side-by-side columns (PLOY + MIGRATIONS) with list height matched to terminal height; migrations are ordered newest-to-oldest. |
| Migration details | MIGRATION <name> |
Migration detail: repository count and run count. |
| Runs list | PLOY | RUNS |
Two side-by-side columns (PLOY + RUNS) with list height matched to terminal height; runs are ordered newest-to-oldest with DD MM HH:mm timestamp. |
| Run details | RUN |
Run detail: repository count and job count. |
| Jobs list | PLOY | JOBS |
Two side-by-side columns (PLOY + JOBS) with list height matched to terminal height; each row shows job, mig name, run id, and repo id. |
Keys:
Enter— drill into the selected item.Esc— return to the previous screen.q— quit.
The TUI uses the same PLOY_SERVER_URL and optional PLOY_AUTH_TOKEN environment
variables as all other ploy commands (see docs/envs/README.md).
Shell Completion
The CLI provides shell completion for bash, zsh, fish, and PowerShell via the completion command:
# Generate completion script for your shell
ploy completion bash > /etc/bash_completion.d/ploy # bash
ploy completion zsh > ~/.zsh/completion/_ploy # zsh
ploy completion fish > ~/.config/fish/completions/ploy.fish # fish
ploy completion powershell > ploy.ps1 # PowerShell
To load completions in your current shell session:
# bash
source <(ploy completion bash)
# zsh
source <(ploy completion zsh)
# fish
ploy completion fish | source
# PowerShell
ploy completion powershell | Out-String | Invoke-Expression
The completion command is powered by Cobra and provides:
- Command completion for all subcommands (mig, cluster, config, etc.)
- Flag completion for available options
- Context-aware suggestions based on command hierarchy
Note: Token management commands are nested under ploy cluster token:
ploy cluster token create|list|revoke— Manage API tokensploy cluster token create --role control-planerequires--username.
For persistent setup instructions specific to your shell, run:
ploy completion <shell> --help
Flags
- Mig spec files use canonical
steps[]shape for both single-step and multi-step runs. Each step supportsimage/command/envsplus Hydra file-record fields (in,out,home) for deterministic file injection via content-addressed bundles. CLI-authored specs can usesteps[].refentries such asref: ../shared/mig.yaml:deprecations; refs are expanded before submission and only the selected step is imported. A ref wrapper may also declareimageandenvs;imagereplaces the imported step image, andenvsare merged into the imported step with wrapper values winning on key conflicts. Seedocs/schemas/mig.example.yamlfor the full schema andtests/e2e/migs/README.mdfor usage examples. ploy run --pull[=path]andploy run pull <run-id> [path]download run artifacts and writemanifest.json.ploy run lslists only runs owned by the resolved token username by default;--alllists every run. The table columns areID STATUS SPEC REPO.ploy mig run --cap <duration>applies only with--follow. When the duration elapses, the CLI stops following; use--cancel-on-capto cancel the run too.
Global Environment Configuration
The ploy config env commands manage global environment variables that are automatically
injected into target components. This provides a centralized way to configure
credentials and other shared settings without embedding them in every spec file.
Key Concepts
Targets control which components receive each variable:
server— Inject into the server processnodes— Inject into node agent processesgates— Inject into gate jobs (pre_gate,post_gate)steps— Inject into step jobs (mig)
The set command uses --on selectors for convenience:
all→ server, nodes, gates, steps (all targets)jobs→ gates, steps (default)server,nodes,gates,steps→ single target
The show and unset commands use --from to specify the target when a key
exists for multiple targets. When omitted and the key is unambiguous (single target),
the target is inferred automatically.
Secrets are redacted in list/show output by default. Use --raw with show to reveal the
full value.
Precedence: Per-run env vars in spec files take precedence over global env. Existing keys in the spec are never overwritten by global config.
Commands
# List all global environment variables (secret values redacted)
ploy config env list
# Show a specific variable (use --raw to reveal secret values)
ploy config env show --key OPENAI_API_KEY --raw
# Set a variable with an inline value (default --on jobs → gates, steps)
ploy config env set --key OPENAI_API_KEY --value sk-...
# Set a non-secret variable (visible in list output)
ploy config env set --key CUSTOM_VAR --value myvalue --on gates --secret=false
# Delete a variable (use --from when key exists for multiple targets)
ploy config env unset --key OLD_VAR
Common Variables
| Variable / Field | Description | Recommended Target |
|---|---|---|
OPENAI_API_KEY |
OpenAI API key for LLM-integrated migs | jobs |
See docs/envs/README.md § "Global Env Configuration" for detailed semantics and
docs/migs-lifecycle.md for how these variables flow into job containers.
Build Gate
Build Gate runs as regular jobs in the unified jobs queue. Gate jobs are
claimed and executed by nodes through the same claim loop as other run jobs.
CLI-visible gate summaries:
Gate results are surfaced via GET /v1/runs/{id}/status:
Gate: passed duration=1234msGate: failed pre-gate duration=567ms
See docs/build-gate/README.md for current gate contract and execution details.
Job Graph And DAG State
Migs runs execute as a directed acyclic graph (DAG) of jobs. The graph structure
surfaces via ploy run status <run-id> --json and
GET /v1/runs/{id}/status. Run status includes a stages map. Each job has a
next_id for execution ordering and optional metadata identifying the job phase.
Job phases (job_type):
pre_gate— Build Gate validation before migs runmig— Main mig execution (code transformation)post_gate— Build Gate validation after migs succeed
DAG structure:
pre-gate → mig-0 → post-gate
Status inspection:
Use ploy run status <run-id> --json or GET /v1/runs/{id}/status to view
run-level state:
$ curl -sk "$PLOY_CONTROL_PLANE_URL/v1/runs/migs-abc123/status" | jq .
Run migs-abc123: running
Gate: failed pre-gate duration=567ms
Jobs:
[1000] a1b2c3d4: succeeded
[2000] e5f6g7h8: running
[2500] i9j0k1l2: pending
The [next_id] ordering reflects execution sequence.
API response:
The GET /v1/runs/{id}/status endpoint returns RunSummary with:
stages— Map of job ID (KSUID string) toStageStatus(state, next_id, attempts)metadata["gate_summary"]— Human-readable gate result
See internal/migs/api/types.go for the full schema.
Exit Codes
0— command completed successfully.1— command failed, including argument errors, unknown commands, API errors, run/job failures surfaced by follow mode, or downstream tool errors.
Development
- Build via
make build(outputs todist/ploy). - Run unit tests with
make test. - Run coverage with
make test-coverageormake coverage-all. - See
docs/schemas/mig.example.yamlfor the current mig spec shape. - Review
docs/runs.md,docs/migs-lifecycle.md, anddocs/envs/README.mdwhen changing command behavior or durable CLI documentation.
Documentation
¶
There is no documentation for this package.