Skip to content

Layered EdgeZero deploy actions + Fastly staging lifecycle (design + impl, supersedes #303)#316

Draft
aram356 wants to merge 15 commits into
mainfrom
feature/edgezero-deploy-actions
Draft

Layered EdgeZero deploy actions + Fastly staging lifecycle (design + impl, supersedes #303)#316
aram356 wants to merge 15 commits into
mainfrom
feature/edgezero-deploy-actions

Conversation

@aram356

@aram356 aram356 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Layered, adapter-independent GitHub Actions for deploying EdgeZero apps —
design + implementation — superseding the Fastly-only monolith in #303.
Based off main.

Actions:

  • build-cli — compiles the CLI package the application provides (a crate
    in the app's own workspace), from the app checkout, isolated CARGO_TARGET_DIR
    • --locked; publishes a self-describing tar (cli-meta.json).
  • deploy-core — adapter-independent shared engine scripts sourced by
    wrappers; provider creds/flags only via provider-env (deploy-step-scoped),
    provider-env-clear, deploy-flags, deploy-args.
  • deploy-fastly — minimal wrapper; installs the pinned Fastly CLI; optional
    stage: true; outputs fastly-version.
  • healthcheck-fastly / rollback-fastly — Fastly staging lifecycle (parity
    with stackpop/trusted-server-actions), driven by the app CLI over the Fastly
    API.

CLI scaffolding (edgezero-adapter-fastly + downstream template): --stage,
healthcheck/rollback subcommands, deployment-version output, --version.

Design docs

  • docs/specs/edgezero-deploy-github-action.md — spec
  • docs/specs/edgezero-deploy-action-implementation-plan.md — plan (+ Add Fastly deploy action with config push #303 port map)
  • docs/specs/edgezero-deploy-adoption-guide.md — adoption guide (any app repo)

Cross-cutting

  • Git root vs Cargo workspace root for monorepo caching; no Python
    (actionlint/zizmor pinned binaries); third-party actions on readable tags;
    explicit Fastly build-in-deploy credential caveat; provider-env scoped to the
    deploy step only.

Notes

Supersedes #303 (and the earlier stacked docs PR #315). #303's unrelated changes
(KV timing logs, dep bumps) are not carried here.

🚧 Implementation in progress — phases tracked in the plan doc.

Design docs (spec + implementation plan + adoption guide) for GitHub Actions
that deploy EdgeZero apps, superseding the Fastly-only monolith from #303.

Architecture:
- build-cli compiles the CLI package the *application* provides (a crate in the
  app's own workspace), from the app checkout, isolated CARGO_TARGET_DIR +
  --locked, self-describing tar (cli-meta.json).
- deploy-core: adapter-independent shared engine scripts sourced by wrappers;
  provider creds/flags only via provider-env (deploy-step-scoped),
  provider-env-clear, deploy-flags, deploy-args.
- deploy-fastly: minimal wrapper; optional stage: true.
- Fastly staging lifecycle (parity with trusted-server-actions): deploy-fastly
  stage mode + healthcheck-fastly + rollback-fastly, scaffolded into the CLI's
  Fastly adapter and exposed via the app CLI; fastly-version output.

Cross-cutting: Git root vs Cargo workspace root for monorepo caching; no Python
(actionlint/zizmor pinned binaries); third-party actions on readable tags;
explicit Fastly build-in-deploy credential caveat. Plan includes a porting map
from the #303 reference scripts.

Based off main; supersedes #303.
aram356 added 3 commits July 9, 2026 09:59
provider-env is no longer listed among the engine's globally-passed parameters.
It is bound only to the deploy step's own env: and parsed only there; setup/build
steps receive only non-secret parameters plus provider-env-clear. Mirrors spec
§5.2/§10 so the plan no longer reintroduces the secret-blob leak.
…te target

- healthcheck-fastly / rollback-fastly now pass --service-id <id> (and step-
  scoped FASTLY_API_TOKEN) in their app-CLI invocations; without it the CLI
  can't resolve staging IPs or activate/deactivate versions.
- Make provider CLI install an explicit wrapper responsibility: deploy-fastly
  installs the pinned Fastly CLI onto PATH; the engine assumes it is present and
  never learns provider tools. healthcheck/rollback need no Fastly CLI (Fastly
  API only).
- target is wrapper-provided concrete (Fastly -> wasm32-wasip1); the engine no
  longer maps adapter -> target, keeping it provider-neutral.
- Qualify the follow-up list: additional staging/health/rollback lifecycles are
  'beyond Fastly' (Fastly's is in scope).
… guide creds

Gaps found in self-review + review:
- §13 error handling: add rows for staged-deploy failure, missing fastly-version,
  unhealthy-after-retries, rollback failure.
- Pin healthcheck-fastly exit semantics: exits non-zero on unhealthy so callers
  can gate rollback on if: failure() (the composing example relied on this
  implicitly).
- §5.4.3: deploy-fastly stage command now shows --service-id (matches §5.4.1).
- §15 testing + §17 acceptance: cover the staging lifecycle (were absent).
- §15.3 / plan smoke test: fake the app CLI + Fastly API/curl for
  healthcheck/rollback (they call the API, not the fastly CLI), not fake fastly
  binaries.
- Adoption guide §6.3: healthcheck/rollback steps now pass fastly-api-token +
  fastly-service-id (required by the CLI --service-id path).
@aram356 aram356 changed the title Design: layered EdgeZero deploy actions + Fastly staging lifecycle (supersedes #303) Layered EdgeZero deploy actions + Fastly staging lifecycle (design + impl, supersedes #303) Jul 9, 2026
aram356 added 11 commits July 9, 2026 12:33
- build-cli: action.yml + build-cli.sh (resolve app cli-package via cargo
  metadata --locked, isolated CARGO_TARGET_DIR build, cli-meta.json, tar upload).
- deploy-core shared scripts: common, validate-inputs (provider-neutral allowlist
  + JSON→NUL parsing), install-rust (wrapper-provided target), download-cli
  (extract tar, read cli-meta.json, PATH-scope), resolve-project (Git root vs
  Cargo workspace root, cache key), cleanup, write-summary.

Wrappers (deploy-fastly, healthcheck/rollback), run-cli, CI, and tests follow.
All scripts shellcheck-clean; validate-inputs functionally tested.
Port install-fastly.sh (official release + SHA-256 checksum, action-owned PATH
dir) and versions.json (Fastly 15.1.0) into the deploy-fastly wrapper. The
wrapper action.yml and the shared run-cli.sh follow once the CLI staging
contract is finalized.
…back wrappers

- deploy-core/run-cli.sh: provider-neutral CLI runner; typed deploy-flags before
  --, caller passthrough after --; build-mode clears wrapper-named aliases.
- deploy-fastly/action.yml: full orchestration (validate -> download+extract CLI
  -> resolve -> cache -> install rust + Fastly CLI -> optional build -> deploy),
  credential scoping via step-level env:, stage input -> --stage, captures
  fastly-version from the CLI's version=<N> line.
- healthcheck-fastly / rollback-fastly: thin wrappers over <cli> healthcheck /
  rollback (Fastly API); healthcheck exits non-zero on unhealthy while still
  emitting healthy/status-code outputs.

All action.yml parse; deploy-core scripts shellcheck-clean.
Apply Bash best-practices structure: wrap logic in main() with explicit local
parameters and single-responsibility helpers; route the progress line to stderr;
portable NUL-array collection (no bash 4.3 namerefs); a small named assertion
harness (assert_succeeds/assert_fails/assert_equals) in the test runner. Kept
coreutils short flags for macOS/BSD portability. All shellcheck-clean; 10/10
contract tests pass.
…st-toolchain

- Apply the main()/helper structure and Bash best-practices across all engine
  scripts (validate-inputs, resolve-project, download-cli, install-fastly,
  cleanup, write-summary); route diagnostics to stderr; local scoping throughout.
- Replace the custom deploy-core install-rust.sh with the maintained
  actions-rust-lang/setup-rust-toolchain@v1 (readable tag) in deploy-fastly,
  feeding the resolved toolchain + wasm32-wasip1 target; cache: false so our
  exact-key target/ cache stays authoritative. build-cli keeps rustup for
  dynamic (app-resolved) toolchain install.
- Add .github/workflows/deploy-action.yml: no Python — actionlint from a pinned
  release binary, zizmor via cargo install (no pip), shellcheck, Bash contract
  tests, check-action-pins.sh (flags floating @main/@master refs), docs
  validation, and a build-cli -> deploy-fastly composite smoke test.
- Add check-action-pins.sh; all third-party actions pinned to readable tags.
…thcheck, rollback)

Add the CLI capability the deploy actions drive (spec §5.4):
- args.rs: --service-id / --stage on DeployArgs; new HealthcheckArgs, RollbackArgs;
  Healthcheck/Rollback Command variants (+ arg-parse tests).
- edgezero-adapter-fastly/cli.rs: deploy_staged (compute update --autoclone +
  service-version stage), emit_active_version, healthcheck (staging-IP resolution
  via Fastly API + curl), rollback (activate previous / deactivate staged); token
  piped via curl --config stdin so it never hits argv (+ 30 unit tests).
- adapter registry + edgezero-cli adapter/lib/main dispatch wiring; other adapters
  return a clear 'unsupported' error, keeping WASM builds unaffected.
- downstream CLI template: Healthcheck/Rollback arms + #[command(version)].
- Version output contract: a parseable 'version=<N>' line on stdout for deploy
  and staged deploy; 'rolled-back-to=<N>' / 'healthy=' / 'status-code=' for the
  lifecycle commands.

All gated behind fastly/cli features. (Implemented by subagent; tests/clippy/fmt
verified.)
… smoke fixture

- cli.rs tests: suffix numeric literals (default_numeric_fallback) and rename
  single-char closure params (min_ident_chars); bind+assert the ignored result
  (let_underscore_must_use). These fire under --all-targets, which the earlier
  clippy run omitted. Fastly tests: 100 pass; workspace clippy: 0 errors.
- deploy-action.yml: scope actionlint to this workflow (no-arg actionlint tripped
  on pre-existing SC2086 in other repo workflows).
- Extract the inline 'Create fixture app' block into
  deploy-core/tests/make-smoke-fixture.sh (shellcheck-linted) and add an empty
  [workspace] table so the fixture is standalone (fixes 'believes it's in a
  workspace').
- Set the git exec bit (100755) on run-cli.sh, deploy-fastly/common.sh, and
  install-fastly.sh (rewritten via editor, lost +x) so the composite actions can
  invoke them directly (fixes 'Permission denied' exit 126 in the smoke test).
- Keep the readable @v1 tag on setup-rust-toolchain (design principle #9) and
  add an inline 'zizmor: ignore[unpinned-uses]' with justification, instead of an
  opaque SHA pin.
- Give the smoke fixture a minimal fastly.toml so the CLI's Fastly deploy path
  reaches the fake fastly binary; assert the deploy reached 'fastly compute'.
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.

1 participant