CLI reference
The CLI has one command: mirops scan. The inputs you give pick the checks. Every flag has an environment-variable equivalent, MIROPS_<FLAG> (dashes become underscores); flags take precedence over env vars, and a variable that doesn't parse stops the CLI with exit 2.
mirops scan --source <mirror report> [-f <change>] [-n <namespaces>] [--upgrade --upgrade-source <analysis report>] [--enforce] [--output table|json]
Flags
| Flag | Env var | Default | Purpose |
|---|---|---|---|
--source | MIROPS_SOURCE | — | The ClusterMirror report (<name>.mirops): path, file://, s3://, azure://, http(s)://. An UpgradeAnalysis report here is gated on directly, as in 0.1.0. |
-f, --file | MIROPS_FILE | — | What you're about to apply: a manifest file, a directory, - for stdin, or a Terraform plan in JSON. See Impact of a change. |
-n, --namespace | MIROPS_NAMESPACE | — | Namespaces to show the state of — and, with -f, the only ones the change is judged in: one, a comma-separated list, or all. Never blocks by itself. |
--upgrade | MIROPS_UPGRADE | false | Run the upgrade check. |
--upgrade-source | MIROPS_UPGRADE_SOURCE | — | The UpgradeAnalysis report (<name>.mirops) to gate on. Required with --upgrade. |
--enforce | MIROPS_ENFORCE | false | Exit 1 when a check blocks — the change breaks something, or the upgrade verdict blocks |
--max-mirror-age | MIROPS_MAX_MIRROR_AGE | 1h | Oldest mirror report -f accepts; older exits 2 |
--enforce-level | MIROPS_ENFORCE_LEVEL | critical | Minimum level that blocks: critical | warning |
--output | MIROPS_OUTPUT | table | Output format: table | json |
--version | — | — | Print the CLI version |
-h, --help | — | — | Help |
--api-url, --api-token and --cluster exist for a hosted mode that isn't implemented yet.
The impact check
With -f, the CLI reads the change, keeps the objects in the -n namespaces (all of them without -n), and looks each one up in the mirror's graph. With --enforce it exits 1 when the change deletes something still in use, mounts a Pending, Lost or missing volume, routes an Ingress to a missing Service, or depends on something it deletes. The full rules, the namespace filter and what it refuses are in Impact of a change.
The upgrade check
With --upgrade, the CLI:
- Reads the mirror report. If it says upgrade analysis is off in the cluster, it exits
2with the command to turn it on. - Reads the analysis report at
--upgrade-source. The target version comes from that report. - If the cluster (per the mirror) already runs the target version, reports
skippedand passes. - Otherwise prints the analysis and its verdict; with
--enforce, a blocked verdict exits1.
The CLI never looks the analysis up by itself — you give its URL.
Exit codes
| Code | Meaning |
|---|---|
0 | Passed, or nothing to check on purpose (no upgrade pending) |
1 | A check blocked — the change breaks something, or the upgrade verdict blocked — and --enforce is set |
2 | Couldn't evaluate: missing or unreadable source, unknown report kind, bad flag, variable or argument, nothing to scan, a namespace in neither the mirror nor the change, a source that must be rendered first (values.yaml, kustomization.yaml, {{ }}, .tf, binary plan), a mirror report older than --max-mirror-age, --upgrade without --upgrade-source, upgrade analysis off. Never a pass. |
Examples
export MIROPS_SOURCE=s3://bucket/prod/default.mirops
# impact of a change — fail when it breaks something in payments
helm template payments ./chart | mirops scan -f - -n payments --enforce
mirops scan -f k8s/ -n payments --enforce
terraform show -json plan.out | mirops scan -f - --enforce
# a namespace's state — never fails
mirops scan -n payments
mirops scan -n payments,checkout
mirops scan -n all # only namespaces with something at risk
# CI gate — fail on CRITICAL
mirops scan --upgrade --upgrade-source s3://bucket/prod/to-1-35.mirops --enforce
# stricter — also fail on WARNING
mirops scan --upgrade --upgrade-source s3://bucket/prod/to-1-35.mirops --enforce --enforce-level warning
# JSON for another tool
mirops scan -n all --output json | jq '.checks'
Output
table (the default) prints headed sections — the mirror, then IMPACT, NAMESPACES and the upgrade verdict, for the checks that ran. Color is used only on a real terminal: it's off when the output is piped or redirected, and when NO_COLOR is set.
JSON output
--output json prints one document:
| Field | Meaning |
|---|---|
schemaVersion | 1. Bumped only for a breaking change; new optional fields don't bump it. |
source | The --source that was read. |
mirror | The mirror's name, generatedAt, clusterVersion, and summary. |
checks.impact | summary (format, changes, evaluated, namespaces, what was skipped — otherNamespaces, noNamespace, clusterScoped, nonKubernetes, noOp, notEvaluable — and ignored files), blocking, blockers[], info[], notVerified[]. Each finding has object, namespace, message, and dependents. |
checks.namespaces[] | Each requested namespace: namespace, risk, components, atRisk, atRiskComponents. |
checks.otherHealthyNamespaces | With all: how many namespaces were left out because nothing in them is at risk. |
checks.upgrade | status (evaluated | skipped), source, blocking, allow, level, blockers, reason, targetVersion, … |
checks.upgrade.blocking says whether this verdict fails the pipeline under --enforce at the current --enforce-level.
Sources
| Scheme | Auth |
|---|---|
local / file:// | — |
http://, https:// | — (the operator's report endpoint) |
s3://bucket/key | IRSA / instance profile / env vars / static keys |
azure://account/container/blob | DefaultAzureCredential (Workload/Managed Identity, az login, service principal) |
Credentials
The CLI reads reports from the cloud (it doesn't write — that's the operator's job) and has no credential flags: each source resolves credentials for its own scheme, so the mirror and the analysis reports can live in different places. It only needs read access — s3:GetObject on the report object, or read on the blob.
In GitHub Actions, the cleanest option is a role assumed with OIDC — no stored keys:
permissions: { contents: read, id-token: write }
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::<account>:role/<read-only role>
aws-region: us-east-1
- run: mirops scan -f k8s/ --enforce
The role's trust policy must accept the repository's OIDC subject. Repositories using GitHub's immutable subject send repo:<org>@<org-id>/<repo>@<repo-id>:… instead of repo:<org>/<repo>:… — allow the one your repository uses.
Or the provider's standard environment variables:
| Source | Environment variables |
|---|---|
| S3 | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION (and AWS_SESSION_TOKEN for temporary creds) |
| Azure Blob | AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET |
Inside the cloud you can set nothing and rely on IRSA (S3) or Workload / Managed Identity (Azure); from a laptop, az login or an AWS profile works too.
export MIROPS_SOURCE="azure://myacct/reports/default.mirops"
export AZURE_CLIENT_ID=... AZURE_TENANT_ID=... AZURE_CLIENT_SECRET=...
mirops scan -n all
See Use it in CI/CD for a full pipeline example.