opa fmt: Format and Lint Rego in CI
opa fmt rewrites Rego files into a canonical format; with --fail it exits non-zero when any file is not already formatted.
A formatting check keeps Rego diffs clean and reviewable. opa fmt --fail --list is the gofmt-style gate: it names files that need formatting and fails the build so contributors run it locally.
What it does
opa fmt applies OPA canonical formatting to Rego source. By default it prints the formatted result to stdout; -w rewrites files in place. --list names files that differ from canonical form and --fail makes any difference an error, which is how you enforce formatting in CI.
Common usage
# rewrite files in place
opa fmt -w policy/
# CI gate: list unformatted files and fail
opa fmt --list --fail policy/
# show what would change without writing
opa fmt --diff policy/deny.regoOptions
| Flag | What it does |
|---|---|
| -w, --write | Rewrite files in place with the formatted output |
| -l, --list | List files whose formatting differs from canonical |
| -d, --diff | Print a diff instead of the formatted file |
| --fail | Exit non-zero if any file is not already formatted |
In CI
Pair --list --fail so the job both fails and tells the author which files to run opa fmt -w on. Keep it as a fast, standalone check separate from opa test so a formatting nit does not mask a real test failure.
Common errors in CI
With --fail the command exits 1 and prints the paths of unformatted files (nothing else), which reads as a bare list in the log; run opa fmt -w on those paths to fix. A "rego_parse_error" here means the file will not parse at all, so it cannot be formatted; fix the syntax first.
Using this in CI
A runner has no kubeconfig, no cached context, and no interactive auth. Every kubectl invocation in CI needs the context supplied explicitly, and most confusing CI failures here are the command running against the wrong cluster or no cluster at all.
# never rely on the ambient context on a runner
kubectl --context "$KUBE_CONTEXT" -n "$NAMESPACE" get pods
# confirm what you are actually connected to before mutating anything
kubectl config current-context
kubectl cluster-info
# fail fast instead of hanging on an unreachable API server
kubectl --request-timeout=30s get nodes