You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Approval gates bind a reviewed Automax plan to a JSON approval file. Operators can create an approval after reviewing the same job, inventory, vars and selector inputs that will later be executed.
9
+
10
+
Approval files are intentionally secret-free. Create them without `--secrets`; runs can still pass `--secrets` later because approval verification compares the secret-free review surface.
11
+
12
+
## Create an approval
13
+
14
+
```bash
15
+
automax approval create \
16
+
--job job.yaml \
17
+
--inventory inventory.yaml \
18
+
--vars vars.yaml \
19
+
--approved-by change-1234 \
20
+
--reason "Approved maintenance window" \
21
+
--expires-at 2026-06-30T23:00:00Z \
22
+
--output approvals/change-1234.json
23
+
```
24
+
25
+
The approval file records:
26
+
27
+
- approver identity;
28
+
- approval timestamp;
29
+
- optional expiry timestamp;
30
+
- optional reason or ticket reference;
31
+
- SHA-256 digest of the secret-free operator review;
32
+
- short review summary.
33
+
34
+
## Verify an approval
35
+
36
+
```bash
37
+
automax approval verify \
38
+
--job job.yaml \
39
+
--inventory inventory.yaml \
40
+
--vars vars.yaml \
41
+
--approval approvals/change-1234.json
42
+
```
43
+
44
+
Verification fails when the selected plan, inventory, vars, filters or tags no longer match the reviewed content. It also fails after `expires_at`.
45
+
46
+
## Run with an approval gate
47
+
48
+
```bash
49
+
automax run \
50
+
--job job.yaml \
51
+
--inventory inventory.yaml \
52
+
--vars vars.yaml \
53
+
--secrets secrets.yaml \
54
+
--approval approvals/change-1234.json
55
+
```
56
+
57
+
The run verifies the approval file before execution. Dry runs do not require approval verification, so operators can still preview changes before asking for sign-off.
@click.option("--lock-timeout", type=float, default=0.0, show_default=True, help="Seconds to wait for locks.")
456
457
@click.option("--preflight-capabilities", is_flag=True, help="Compatibility flag; capability preflight is implicit for normal runs.")
457
458
@click.option("--sudo-password-env", help="Environment variable containing the sudo password for sudo-enabled remote substeps.")
459
+
@click.option("--approval", "approval_path", type=click.Path(exists=True, dir_okay=False), help="Approval gate JSON file to verify before execution.")
458
460
@click.option("--format", "output_format", type=click.Choice(["text", "json"]), default="text", show_default=True, help="Output format for the final run summary.")
459
461
defrun(
460
462
job_path: str,
@@ -477,6 +479,7 @@ def run(
477
479
lock_timeout: float,
478
480
preflight_capabilities: bool,
479
481
sudo_password_env: str|None,
482
+
approval_path: str|None,
480
483
output_format: str,
481
484
) ->None:
482
485
"""Run a job from external YAML definitions."""
@@ -498,6 +501,19 @@ def run(
498
501
ifnotpayload["ok"]:
499
502
raiseclick.ClickException("check-mode preview found errors")
0 commit comments