Skip to content

Add --format json to CLI commands - #388

Open
dhruv8sh wants to merge 9 commits into
mainfrom
spec/383-cli-format-json
Open

dhruv8sh wants to merge 9 commits into
mainfrom
spec/383-cli-format-json

Conversation

@dhruv8sh

Copy link
Copy Markdown

Summary

  • CI scripts that drive EdgeZero have had to scrape key=value log lines that were never a stable contract. Nine commands now accept --format json: active-version, auth status, build, config gc, config validate, deploy, healthcheck, provision and rollback. Each one writes a single versioned envelope, { schema_version, command, ok, result, error }, to stdout, on success and on failure.
  • stdout stays clean because, under JSON, logs and every child process's stdout (cargo, fastly, wrangler, spin, manifest commands) go to stderr. A new clippy disallowed-methods lint means future code can't bypass this. --format text is the default, and its output is byte-identical to main.
  • Follows the spec that Add --json output across the CLI #383 requires, included in this PR as docs/superpowers/specs/2026-09-25-cli-format-json-design.md. config diff --format json is unchanged.

Changes

Crate / File Change
edgezero-adapter (registry.rs) Adapter::execute returns ActionOutcome, provision returns ProvisionReport, gc_config_entries returns GcReport, instead of () or prose lines. A negative result that was still measured (unhealthy probe, unauthenticated session, partly failed gc) is an Ok outcome carrying a failure message.
edgezero-adapter (process.rs, new) Process-wide child-stdout policy plus process::status, the one sanctioned inheriting spawn.
edgezero-adapter (cli_support.rs) native_auth_status. run_native_cli goes through process::status.
edgezero-adapter-{fastly,cloudflare,spin,axum} Return typed outcomes and reports. Fastly no longer prints the version= / healthy= / status-code= / rolled-back-to= data lines; the CLI prints the same bytes. All inheriting spawns go through process::status.
edgezero-cli (output.rs, new) OutputScope (routes stdout while alive), Failure / Outcome, the envelope, and the serde wire schema, kept separate from the adapter types so internal refactors can't change the JSON.
edgezero-cli (args.rs) New OutputFormat { Text, Json } and a --format flag on the nine commands. DiffFormat is unchanged.
edgezero-cli (lib.rs, auth.rs, provision.rs, config.rs, adapter.rs) Each run_* keeps its public signature and emits the envelope itself, so CLIs already generated from the template get JSON without regenerating main.rs. The logger's info output moves to stderr under JSON.
clippy.toml disallowed-methods for Command::status / Command::spawn. Piped spawns carry a documented #[expect].
edgezero-cli/tests/format_json.rs (new) End-to-end tests against the real binary.
docs/guide/cli-reference.md --format on each command, plus a new "Machine-readable output" section covering the envelope, streams, exit codes, compatibility policy, per-command schemas and changelog.
docs/superpowers/specs/…-cli-format-json-design.md The spec, revision 2 (§13 notes what changed during implementation).

Closes

Closes #383

Test plan

  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • cargo check --workspace --all-targets --features "fastly cloudflare spin"
  • WASM builds: wasm32-wasip1 (Fastly) / wasm32-wasip2 (Spin) / wasm32-unknown-unknown (Cloudflare), via the full format.yml wasm clippy matrix and cargo check -p edgezero-adapter-spin --target wasm32-wasip2 --features spin
  • examples/app-demo workspace: cd examples/app-demo && cargo test --workspace --all-targets (--locked), plus its fmt and clippy
  • Docs build: cd docs && npm run lint && npm run format && npm run build
  • Manual testing via edgezero serve --adapter axum (not applicable: serve is out of scope)
  • Other:
    • Text mode compared byte-for-byte against main: both binaries ran 21 hermetic scenarios (success and failure paths for every in-scope command, a usage error, the bundled stub), with stdout, stderr and exit codes identical in all of them.
    • cargo test -p edgezero-cli --test generated_project_builds -- --ignored, cargo test -p edgezero-adapter-fastly --features cli, and the check_no_nested_app_config steps.
    • All checks were run on the pinned toolchain 1.95.0.

Checklist

  • Changes follow CLAUDE.md conventions
  • No Tokio deps added to core or adapter crates
  • Route params use {id} syntax (not :id)
  • Types imported from edgezero_core (not http crate)
  • Store wiring goes through KvRegistry / ConfigRegistry / SecretRegistry (not the legacy single-handle setters) — see spec §6.6
  • New code has tests
  • No secrets or credentials committed

Spec for #383: settles the open questions (shared OutputFormat enum, `text` default, stdout/stderr discipline, error envelopes, schema versioning, bundled stubs) and defines the per-command JSON schema. Also corrects the docs/.prettierignore comment: design docs are tracked in git.
active-version, auth status, build, config gc, config validate, deploy, healthcheck, provision and rollback accept --format text|json. JSON mode writes one versioned envelope to stdout and routes logs and child-process stdout to stderr; text mode is byte-identical to before.

Adapters now return typed outcomes (ActionOutcome, ProvisionReport, GcReport) instead of printing their results, and every inheriting child spawn goes through edgezero_adapter::process::status, enforced by a clippy disallowed-methods lint.

Closes #383
@dhruv8sh dhruv8sh self-assigned this Sep 25, 2026

@aram356 aram356 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Summary

Adds --format json to nine commands: one versioned envelope on stdout, with logs and child stdout moved to stderr, and typed adapter outcomes instead of printed lines. The design holds up, and the text path is unchanged (see CI Status). Three JSON results are wrong and should be fixed before schema v1 ships: version_verified on an unhealthy probe, build silently dropping a trailing --format json, and config gc --yes reporting deleted: null when there was nothing to reclaim.

Findings

Blocking

  • 🔧 version_verified: true on an unhealthy probe: the after-probe check only runs when the probe is healthy (crates/edgezero-adapter-fastly/src/cli.rs:5641)
  • 🔧 build swallows a trailing --format json: exits 0 with text on stdout (crates/edgezero-cli/src/args.rs:251)
  • 🔧 config gc --yes with nothing to reclaim reports deleted: null: the docs say null means a dry run (crates/edgezero-adapter-fastly/src/cli.rs:2497)
  • ❓ Spec approval: the spec header says "Status: Approved", and #383 asks for the spec to be approved before implementation starts. I couldn't find the approval on #383 or on this PR (the spec and the implementation arrived in the same push). Was it approved somewhere else? If so, a link in the spec header would help.

Non-blocking

  • 🤔 Raw config validate reports app_config: null although it reads and requires that file (crates/edgezero-cli/src/config.rs:235)
  • 🤔 Provision dry-run actions don't pair with real-run actions (crates/edgezero-adapter-spin/src/cli.rs:249, crates/edgezero-adapter-fastly/src/cli.rs:522)
  • 🤔 A deploy that went live but couldn't resolve its version reports result: null (crates/edgezero-cli/src/lib.rs:382)
  • 🤔 deploy.service_id reflects only the flag (crates/edgezero-cli/src/lib.rs:216)
  • ♻️ Outcome types can hold contradictory values (crates/edgezero-adapter/src/registry.rs:116)
  • ⛏ Small items: the AuthSub::Status variant (args.rs:237), OutputFormat compared with == (output.rs:75), a leftover bare block (edgezero-adapter-fastly/src/cli.rs:3958), and an unreachable --require-active promise in the spec (spec L152)
  • 🤔 Docs: nullability and edge cases
    • The compatibility policy says making a non-null key nullable is breaking, but the Results section lists key names only, so readers can't tell which keys may be null. Spec §10 called for one schema table per command. Nullable today: active-version.version, build.artifact, deploy.service_id, deploy.version, healthcheck.staging_ip, healthcheck.status_code, rollback.rolled_back_to, provision.entries[].store, provision.entries[].store.logical, config gc older_than_secs / deleted / store.id, and config validate.app_config.
    • Exit code 2 means three things in a generated CLI: a command failure, a clap usage error, and an unsupported config diff. The "empty stdout means no envelope" rule in Streams is what tells them apart; worth repeating next to Exit codes.
    • --format json --help (and --version) print clap's text to stdout and exit 0, which contradicts "stdout holds exactly one JSON document".
    • JSON routing depends on init_cli_logger() being the installed logger. A downstream CLI that installs its own logger (e.g. simple_logger, which writes every level to stdout) would put log lines on stdout. One sentence in the docs would cover it.
  • 🤔 Test gaps
    • No test produces version_verified: true (the existing healthcheck test removes the token).
    • The config gc mapping from failure_diagnostic to a partial result (config.rs:467-469), and how failed / stranded / uncertain / deleted are filled, have no JSON-level test.
    • deploy, rollback and active-version have no tests of their result mapping, or of the version= / rolled-back-to= / "no active version yet" lines that moved from the adapter into the CLI.
    • config validate has no failure (null result) or typed-mode test.
    • run_shell_tee (the deploy capture path) isn't exercised under JSON.
    • The end-to-end tests cover 5 of the 9 commands, and the 21-scenario byte comparison from the PR description isn't committed. Even a reduced version committed as a test would keep the text contract from drifting.
  • 🌱 Lint gaps: disallowed-methods fires on every status / spawn form I tried (checked in a scratch crate using this clippy.toml). It does not catch .stdout(Stdio::inherit()).output() (which really does leak at runtime), CommandExt::exec, or writeln!(io::stdout(), …) (print_stdout only covers the macros). None of these is used today. Adding std::io::stdout to the list, with #[expect] at the three existing call sites (output.rs:537, adapter.rs:431, config.rs:1370), would close the last gap. The generated project's templates/root/clippy.toml.hbs doesn't carry the lint, so a downstream CLI's own subcommands are unguarded.
  • 🌱 A closed stderr loses the envelope: CliLogger writes routed info with eprintln!, which panics on a broken pipe. Under JSON all info goes to stderr, so if the stderr reader exits first, the command panics at its first log line (exit 101) and writes no envelope. Text mode has the same failure on stdout, so this isn't new, but JSON makes stderr the busy stream. let _ = writeln!(io::stderr(), "{}", record.args()); avoids it.

📌 Out of Scope

  • wrangler whoami exit code when logged out (unverified; wrangler wasn't available to test): some wrangler versions print "You are not authenticated" and still exit 0. If current ones do, auth status --adapter cloudflare --format json reports state: authenticated. Text mode already exited 0 in that case, so this predates the PR, but the JSON value now states it outright. Worth checking, and if so parsing the whoami output rather than trusting the exit code.

CI Status

  • fmt: PASS
  • clippy: PASS
  • tests: PASS (cargo test --workspace --all-targets: 1459 passed, 0 failed)
  • cargo check --workspace --all-targets --features "fastly cloudflare spin": PASS
  • cargo check -p edgezero-adapter-spin --target wasm32-wasip2 --features spin: PASS
  • cargo test -p edgezero-adapter-fastly --features cli: PASS (288 passed)
  • Text mode vs main: 130 paired hermetic runs across the 9 commands (fake curl / fastly / wrangler / spin / cargo) gave byte-identical stdout, stderr and exit codes. The only difference is the usage-error text for --format yaml.

Comment thread crates/edgezero-adapter-fastly/src/cli.rs Outdated
Comment thread crates/edgezero-cli/src/args.rs
Comment thread crates/edgezero-adapter-fastly/src/cli.rs Outdated
Comment thread crates/edgezero-cli/src/config.rs Outdated
Comment thread crates/edgezero-adapter-spin/src/cli.rs Outdated
Comment thread crates/edgezero-adapter/src/registry.rs
Comment thread crates/edgezero-cli/src/args.rs
Comment thread crates/edgezero-cli/src/output.rs Outdated
Comment thread crates/edgezero-adapter-fastly/src/cli.rs Outdated
Comment thread docs/superpowers/specs/2026-09-25-cli-format-json-design.md Outdated
Revision 3 records the review's contract fixes: version_verified only when both active checks ran, config gc deleted: 0 for an empty real run, build rejecting a late --format, config validate always reporting app_config, deploy keeping its result and resolved service id, and Spin reporting created. Drops the unreachable active-version --require-active promise and documents --help/--version and the exit-code-2 overlap.
Maps each review comment to the change that addresses it, with the tests and verification that back it.
- healthcheck: version_verified is true only when the after-probe check ran
- config gc: a real run with nothing to reclaim reports deleted: 0, not null
- build: reject a --format that follows passthrough args instead of forwarding it
- config validate: always report app_config; the mode is passed explicitly
- deploy: report the adapter-resolved service id, and keep the result when the deploy went live but its version could not be resolved
- spin provision: an added label reports created, pairing with would_create
- Outcome types store each fact once: HealthcheckOutcome::healthy(), AuthState::Unauthenticated { reason }, GcFailure
- Lint direct std::io::stdout writers (also in the generated project's clippy.toml) and never panic the logger on a closed stderr
- Exhaustive OutputFormat matches, #[non_exhaustive] AuthSub::Status, flatten a leftover block
- Docs: per-command result tables with nullability, --help/--version, exit code 2, logger dependency, provision action pairing
- Tests: token healthcheck, gc report fields, deploy tee and partial result, active-version, rollback, validate failure, late --format
@dhruv8sh
dhruv8sh marked this pull request as ready for review October 5, 2026 09:15

@prk-Jr prk-Jr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review (re-review at cc6f1ac)

Summary

The fix commit addresses aram356's blocking items, and the new tests fail against the old code: version_verified only when both checks ran, deleted: 0 on an empty real gc run, and the late---format guard. Almost every non-blocking item is closed too.

I also built this branch and main and ran them with fake fastly / curl / wrangler / spin / cargo:

  • Under --format json, stdout held exactly one valid envelope in every scenario tried. That covered all 9 commands on success and failure paths, more than 1 MB of output from a child process, a killed child, RUST_LOG=trace, a closed stderr, and Unicode/escaping in paths and messages.
  • In text mode, 49 of 54 scenarios were byte-identical to main. All 5 differences come from the new flag, and one is the build -- --format case below.

What remains is the unanswered spec-approval question, two doc errors in the output contract, and some test gaps.

😃 Praise

  • One gate for child stdout: every child process that inherits our stdout goes through edgezero_adapter::process::status, the disallowed-methods lint enforces it, and the exceptions carry #[expect] with reasons. "stdout stays clean" can't quietly regress.
  • Routing can't get stuck: OutputScope saves and restores the routing flags in Drop, so a ?, an early return or a panic always restores routing.
  • Each fact stored once: AuthState::Unauthenticated { reason }, HealthcheckOutcome::healthy() and GcFailure rule out the contradictory states.
  • Tests that check behaviour: the fake-curl API call log proves both version checks ran, rather than just trusting the flag. sole_json_document and assert_envelope_invariants make the one-envelope rule hard to break.
  • Docs match the code: the per-command result tables match the serde wire structs field by field, including nullability and enum spellings.

Findings

Blocking

  • ❓ Spec approval still unanswered (spec:4)
  • 🔧 Docs promise a --version flag that doesn't exist (cli-reference.md:662)
  • 🔧 Empty-stdout list misses the late---format rejection, which contradicts the exit-1 guarantee (cli-reference.md:657)

Non-blocking

  • 🤔 build rejects -- --format json, deploy forwards it. This also changes text mode for build (lib.rs:176).
  • 🤔 auth status decides authenticated from the exit code alone. Real wrangler exits 0 when not logged in (cli_support.rs:112).
  • 🤔 A broken stdout pipe hides the command's real error (output.rs:545).
  • 🤔 The plan doc's status is wrong, and it includes a line specific to one machine (plan:5).
  • 🤔 Spec §7.2 types and §9.3 test list are stale (spec:331).
  • 🤔 OutputScope doesn't support overlapping runs on separate threads. It needs a doc note (output.rs:68).
  • 🤔 Test gaps:
    • config gc is the only one of the 9 commands with no end-to-end test. The CLI-side mapping (dry_run = args.dry_run || !args.yes, older_than_secs, and not printing text_lines on failure) is untested. The fake_fastly_gc helper in the Fastly adapter tests could be reused.
    • auth status has end-to-end tests only for the unauthenticated case. There's no test of the authenticated path or of a missing native CLI (result: null per the docs).
    • Staging deploy / rollback aren't tested. That includes the version= line that moved from the adapter to the CLI, and the resolved service_id (flag, then FASTLY_SERVICE_ID).
    • The generated project's new clippy.toml.hbs lines aren't asserted. The generator test only checks allow-expect-in-tests, so adding assert!(clippy.contains("std::io::stdout")) would cover it.
    • The text-mode checks for build, rollback and deploy use starts_with / ends_with, so lines added in the middle would pass. auth, provision, validate and gc have no text-mode check. The byte comparison against main from spec §9.2 is still not committed.
    • The test harness only clears FASTLY_API_TOKEN / FASTLY_SERVICE_ID. An exported EDGEZERO__* or app-overlay variable on the machine running the tests can change provision and validate results.
  • ♻️ Fastly staged deploy works out the service id a second time with .ok(). The production Deploy returns Deploy{None,None} while other adapters return Empty (fastly cli.rs:434).
  • ♻️ Result<Result<u64, String>, String> would read better as a small enum. deploy() is about 140 lines (lib.rs:368).
  • ⛏ Small items:
    • healthcheck reports status_code: 0 when curl prints 000. The docs say null when no probe got a response.
    • A production Fastly deploy with only FASTLY_SERVICE_ID set reports service_id: null, although fastly used that id. Worth one sentence in the deploy table.
    • The #[expect] reason at config.rs:1374 says "config push has no --format", but config diff calls the same function.
    • GcResult::new clones the whole GcFailure, diagnostic string included, just to read three lists. Destructure it by reference instead.
    • auth status --help describes --format with shorter wording than the other eight commands (args.rs:236).
    • The process tests restore the global setting by hand. If expect("spawn") panics, the setting stays true and POLICY_LOCK is poisoned. Use a drop guard.
    • native_auth_status_maps_exit_status_to_state runs true / false but isn't #[cfg(unix)], unlike the other fake-binary tests.
  • 🌱 Lint gaps:
    • disallowed-methods doesn't catch std::os::unix::process::CommandExt::exec or .stdout(Stdio::inherit()).output(). Neither is used today.
    • The public outcome structs (HealthcheckOutcome, GcReport, RollbackOutcome, ProvisionEntry, …) aren't #[non_exhaustive], so adding a field later breaks adapters built outside this repo.

📌 Out of Scope

These all predate this PR. Worth tracking as follow-ups:

  • provision failing partway: the result is null and neither stream says what was already created.
  • Deploy output parsing: in the tee, a non-UTF-8 line stops the version parsing, so a later SUCCESS … version 12 line is never seen. With more than 64 KB after the bad byte, the child gets SIGPIPE.
  • Built-in Fastly production deploy: this path doesn't capture fastly compute deploy's output. Without a token it fails with "no version=<N> line in the deploy output" even when Fastly printed the version.
  • Empty token: FASTLY_API_TOKEN= (empty) is treated as set.
  • Rollback to the same version: rollback --version 7 --rollback-to 7 is accepted.

CI Status

  • fmt: PASS
  • clippy (--workspace --all-targets --all-features -D warnings): PASS
  • tests (cargo test --workspace --all-targets): PASS, 0 failures
  • cargo check --workspace --all-targets --features "fastly cloudflare spin": PASS
  • cargo check -p edgezero-adapter-spin --target wasm32-wasip2 --features spin: PASS
  • Black-box JSON contract: every scenario tried produced exactly one valid envelope. Text mode vs main: 49/54 byte-identical; the 5 differences are expected, and one is the build -- --format case above.

Comment thread docs/superpowers/specs/2026-09-25-cli-format-json-design.md Outdated
Comment thread docs/guide/cli-reference.md Outdated
Comment thread docs/guide/cli-reference.md
Comment thread crates/edgezero-cli/src/lib.rs
Comment thread crates/edgezero-adapter/src/cli_support.rs
Comment thread docs/superpowers/plans/2026-10-05-pr-388-review-fixes.md Outdated
Comment thread docs/superpowers/specs/2026-09-25-cli-format-json-design.md Outdated
Comment thread crates/edgezero-cli/src/output.rs
Comment thread crates/edgezero-adapter-fastly/src/cli.rs Outdated
Comment thread crates/edgezero-cli/src/lib.rs Outdated

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

Reviewed all 24 changed files and traced adapter outcomes, CLI consumers, subprocess routing, and Fastly action parsers.

Head: cc6f1ac99aa09de8025bc26847e424dd7b02fba9
Base: 98930917d96cb665c7255f36ca8bbd61fc7539e1

Safety proof

  1. JSON remains isolated from logs and child stdout on tested paths. Executed and proven for covered cases: all 18 binary-level format tests passed, including inherited and captured child output, failure envelopes, and token-backed health verification.
  2. GC preserves partial-failure information without continuing a damaged generation. Proven through the binary with a fake provider: an injected second-delete failure produced exit 1, deleted: 1, and exact failed/stranded keys. The third delete was not attempted. A rerun retained the incomplete generation; an unknown root blocked deletion.

Findings

No meaningful new findings beyond existing PR feedback. This approval reflects the new-issues-only scope of this review; existing review concerns remain unresolved.

Validation and review context

Passed locally at the locked head:

  • cargo test --workspace --all-targets --locked
  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
  • cargo check --workspace --all-targets --features "fastly cloudflare spin" --locked
  • cargo check -p edgezero-adapter-spin --target wasm32-wasip2 --features spin --locked
  • Focused JSON, Fastly CLI, and adapter tests.
  • Binary-level GC interruption and rerun checks.

All currently reported GitHub checks passed. Existing reviews, inline threads, and issue comments were inspected to avoid duplicate findings.

No live-provider operations were attempted. The generated-project compilation test remained ignored locally. Head/base were unchanged at completion, and the worktree remained clean. No repository files changed; no delegation.

prk-Jr and others added 2 commits October 7, 2026 19:00
Mark the spec as proposed and awaiting maintainer approval, and update its
type listings, testing section and revision notes to match the code. Remove
the retroactive plan document.

Keep the command's own error when writing the JSON envelope fails. Report
the staged Fastly deploy's service id from the deploy itself, return Empty
for the production deploy, and replace the nested Result with a
ProductionDeploy enum. Treat curl's 000 as no response, not status 0.

Document that build never forwards --format, that auth status reports the
probe's exit status, and that only one command may run per process.

Cover config gc, auth status, staged deploy and staged rollback end to end,
pin the text output of all nine commands, and clear EDGEZERO__ overlays in
the test harness. Disallow CommandExt::exec, and give the lifecycle fixture
its own clippy.toml so the CLI stdout guard does not apply to it.
@dhruv8sh

dhruv8sh commented Oct 7, 2026

Copy link
Copy Markdown
Author

Addressed in e6a0b12, beyond the inline threads:

  • Test gaps: format_json.rs went from 18 to 28 tests.
    • config gc end to end: a default dry run (dry_run: true, deleted: null, older_than_secs: null) and --yes --older-than 1h with nothing to reclaim (deleted: 0, older_than_secs: 3600).
    • auth status: the authenticated path, and a missing native CLI (result: null).
    • A staged deploy resolving its service from FASTLY_SERVICE_ID, and a staged rollback.
    • Exact text bytes for all nine commands, replacing the starts_with/ends_with checks. This is the committed stand-in for the byte comparison against main.
    • The harness also clears EDGEZERO__* / DEMO_APP__*.
    • The generator test asserts each disallowed-methods path in the generated clippy.toml.
    • Still not covered end to end: the GcFailure to partial-result path. Building a failing chunked generation needs the Fastly crate's private chunker. That mapping is covered by gc_failure_fills_the_partial_result in output.rs and the adapter's gc tests.
  • Small items:
    • healthcheck treats curl's 000 as no response rather than status 0: status_code keeps the last real HTTP status, or null if no probe got one.
    • The deploy table notes that a production deploy relying on FASTLY_SERVICE_ID reports service_id: null.
    • The #[expect] reason in config.rs now mentions config diff.
    • GcResult::new destructures GcFailure by reference instead of cloning it.
    • auth status --help uses the same --format wording as the other commands.
    • The process tests use a drop guard and recover a poisoned lock.
    • native_auth_status_maps_exit_status_to_state is #[cfg(unix)].
  • Lint gaps:
    • std::os::unix::process::CommandExt::exec is now disallowed (with allow-invalid, for non-unix targets), in the workspace and in the generated project.
    • I didn't ban Stdio::inherit: .stderr(Stdio::inherit()) is legitimate, and the lifecycle fixture harness uses it.
    • I didn't add #[non_exhaustive] to the outcome structs. Adapter crates build them with struct literals, and #[non_exhaustive] forbids that outside edgezero-adapter, so every adapter would need constructors or builders. Today all adapters are in this workspace (publish = false).
  • CI: the cargo fmt job was red on the merge head (082168c). The lifecycle fixture from Add opt-in reusable application lifecycles #379 has no clippy.toml of its own, so it picked up the root disallowed-methods guard. It now has its own clippy.toml with just the test exemptions.
  • Out of scope: the five pre-existing items (partial provision, the non-UTF-8 tee, the uncaptured built-in production deploy, an empty token, rolling back to the same version) are left for follow-up issues.

@dhruv8sh
dhruv8sh requested a review from prk-Jr October 7, 2026 19:29
Bring in #381 (Fastly stores bound per deployment environment), which
moved deploy into the Adapter::preflight_deploy / deploy / finalize_deploy
hooks and made Fastly staging an adapter-managed release deploy.

Resolution keeps the --format json deploy result on the new hooks:
- Adapter::deploy and Adapter::finalize_deploy return ActionOutcome, like
  execute. Fastly reports DeployOutcome from the managed deploy plan and
  from finalize_deploy's resolved production version.
- The CLI's adapter::deploy returns DeployFailure, separating a failed
  deploy from a live deploy whose finalization failed; the latter keeps its
  JSON result with version: null.
- Fastly provision takes main's logical/physical alias handling with the
  typed ProvisionReport; the removed runtime-env provisioning and the old
  CLI-side staging and version-resolution code are dropped.
- emit_active_version_for returns ActiveVersionOutcome; finalize_deploy
  logs version=<N>, so text output is unchanged.
- Tests follow main's version-list schema and the --application-release
  requirement for staged deploys. Spec revision 5 records the merge.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add --json output across the CLI

4 participants