From c3ea4027cacd608f0b835e8cbbb2373b84e2021e Mon Sep 17 00:00:00 2001 From: Joshua Temple Date: Mon, 20 Jul 2026 01:12:35 -0400 Subject: [PATCH 1/2] ci(fleet): add retries and dryrun example repos to the roster Signed-off-by: Joshua Temple --- .github/workflows/fleet-e2e.yaml | 4 ++-- .github/workflows/suite-rc-repin.yaml | 2 +- docs/src/content/docs/internals/coverage-matrix.md | 4 +++- docs/src/content/docs/internals/testing.md | 2 ++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/fleet-e2e.yaml b/.github/workflows/fleet-e2e.yaml index b3b92b06..4db031e3 100644 --- a/.github/workflows/fleet-e2e.yaml +++ b/.github/workflows/fleet-e2e.yaml @@ -254,7 +254,7 @@ jobs: PRIMARY_REPO="primary" DEPENDENTS="artifact-a artifact-b" HEAVY_REPO="4env" - REMAINDER="3env 2env single-env release-only no-env callbacks rollback-dispatch monorepo" + REMAINDER="3env 2env single-env release-only no-env callbacks rollback-dispatch monorepo retries dryrun" # ------------------------------------------------------------------- RAW="${REPOS_INPUT:-all}" @@ -529,7 +529,7 @@ jobs: echo "| primary | $R_PRIMARY |" echo "| dependents (artifact-a, artifact-b) | $R_DEPENDENTS |" echo "| 4env (heavy, alone) | $R_HEAVY |" - echo "| remainder (3env, 2env, single-env, release-only, no-env, callbacks, rollback-dispatch, monorepo) | $R_REMAINDER |" + echo "| remainder (3env, 2env, single-env, release-only, no-env, callbacks, rollback-dispatch, monorepo, retries, dryrun) | $R_REMAINDER |" echo "" echo "> rc gate: this conclusion is the fleet validation signal for" echo "> the rc tag. The repin job re-stamped every example repo onto" diff --git a/.github/workflows/suite-rc-repin.yaml b/.github/workflows/suite-rc-repin.yaml index c2c9d1f6..88757f4d 100644 --- a/.github/workflows/suite-rc-repin.yaml +++ b/.github/workflows/suite-rc-repin.yaml @@ -161,7 +161,7 @@ jobs: # push only if something changed. Every hand-written suite feature is # preserved: regeneration only rewrites the generated workflows, and # nothing else is touched. - REPOS="primary artifact-a artifact-b 4env 3env 2env single-env release-only no-env callbacks rollback-dispatch monorepo" + REPOS="primary artifact-a artifact-b 4env 3env 2env single-env release-only no-env callbacks rollback-dispatch monorepo retries dryrun" # Apply the repin mutation to the checkout in the current directory: # point cli_version at the rc, rewrite any other in-repo rc refs, then diff --git a/docs/src/content/docs/internals/coverage-matrix.md b/docs/src/content/docs/internals/coverage-matrix.md index 603c7dce..44676b5c 100644 --- a/docs/src/content/docs/internals/coverage-matrix.md +++ b/docs/src/content/docs/internals/coverage-matrix.md @@ -108,7 +108,7 @@ only under real installation tokens on the fleet, never in the token-free harnes | Least-privilege permissions | `orchestrate/least-privilege-permissions`, `09-single-env-repo` | least-priv posture (callbacks); gen-time wiring (3env); single-env release posture (single-env) | `internal/generate` | Permissions are scoped to the job that needs them, not the top level | | OIDC id-token propagation | `orchestrate/callback-permissions-oidc` | OIDC posture at callee (callbacks); gen-time `id-token: write` scoped (3env) | `internal/generate` | `id-token: write` propagates to the caller job without leaking workflow-wide | | Callback dependency ordering (`depends_on`) | `11-job-timeouts-and-optional-deps` | needs ordering (callbacks); base to app order (3env gen-time) | `internal/generate` | A dependent callback starts only after its prerequisite concludes | -| Callback retry wrapper | `73-deploy-retries` | retry-wrapper jobs present (callbacks); retry shim jobs (3env gen-time) | `internal/generate` | The retry jobs are emitted and wired for `retries: N`, including a dependent deploy's `needs:` carrying its dependency's retry shims | +| Callback retry wrapper | `73-deploy-retries` | retry rescue and dependent-of-a-retried-deploy gating (retries); retry-wrapper jobs present (callbacks); retry shim jobs (3env gen-time) | `internal/generate` | The retry jobs are emitted and wired for `retries: N`, including a dependent deploy's `needs:` carrying its dependency's retry shims | | Signed auto-commit identity (`auto_commits`) | `03-three-env-repo` | auto_commits author and message (3env) | `internal/promote/auto_commit_sha*.go` | The state commit carries the configured author and message | See [the `auto_commits` field](/cascade/reference/manifest/) in the manifest reference for what a callback must do to trigger this capture. @@ -148,6 +148,8 @@ from a general case. | Primary plus artifact satellites (cross-repo graph) | `cascade-example-primary`, `cascade-example-artifact-a`, `cascade-example-artifact-b` | `multi-repo/*`, `21-cross-repo-callback` | | Per-callback secrets, permissions, and OIDC posture across reusable and inline callbacks | `cascade-example-callbacks` | `orchestrate/secrets-opt-in`, `orchestrate/callback-permissions-oidc` | | External rollback entry point (`repository_dispatch`) | `cascade-example-rollback-dispatch` | `rollback/*` | +| Deploy retry rescue and a dependent deploy gated on a retried dependency | `cascade-example-retries` | `73-deploy-retries` | +| Dry-run promote and rollback, and the native-deployment dry-run guard | `cascade-example-dryrun` | `orchestrate/dry-run-input-expression`, `31-native-deployments` | The no-environment library shape is covered in the act plus gitea harness; a live `cascade-example-no-env` suite also asserts that orchestrate goes straight from a diff --git a/docs/src/content/docs/internals/testing.md b/docs/src/content/docs/internals/testing.md index 82fbda22..404be18f 100644 --- a/docs/src/content/docs/internals/testing.md +++ b/docs/src/content/docs/internals/testing.md @@ -61,6 +61,8 @@ The example repositories span the supported pipeline shapes, so each topology is | `cascade-example-artifact-a`, `cascade-example-artifact-b` | Satellite repositories in an artifact-dependency graph that notify the primary | | `cascade-example-rollback-dispatch` | The automated rollback entry point, where a real `repository_dispatch` payload drives a rollback and the reverted state is read back | | `cascade-example-monorepo` | One repository with several components, each versioned, promoted, hotfixed, and rolled back independently in its own namespace | +| `cascade-example-retries` | Deploy retry rescue, and a dependent deploy gated on a retried dependency's retry shims | +| `cascade-example-dryrun` | Dry-run promote and rollback, and the native-deployment dry-run guard that skips real deploys | The `no-environment` library shape is covered today in the act plus gitea harness; the other topologies above are validated in both layers. From af11b3201365ff886a3fbc352a0a7879b3a43fb1 Mon Sep 17 00:00:00 2001 From: Joshua Temple Date: Mon, 20 Jul 2026 01:17:49 -0400 Subject: [PATCH 2/2] ci(fleet): add retries/dryrun to the bootstrap-pin roster Signed-off-by: Joshua Temple --- .github/workflows/suite-bootstrap-pin.yaml | 6 +++--- docs/src/content/docs/internals/release-orchestration.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/suite-bootstrap-pin.yaml b/.github/workflows/suite-bootstrap-pin.yaml index 6beca68b..752f189f 100644 --- a/.github/workflows/suite-bootstrap-pin.yaml +++ b/.github/workflows/suite-bootstrap-pin.yaml @@ -36,7 +36,7 @@ permissions: concurrency: # One bump at a time, and do not cancel an in-flight bump: each targets a - # specific release and pushes to twelve external repos, so a later release + # specific release and pushes to fourteen external repos, so a later release # queues behind an earlier one rather than interrupting it mid-fan-out. group: suite-bootstrap-pin cancel-in-progress: false @@ -137,10 +137,10 @@ jobs: run: | set -euo pipefail - # The full roster of twelve example repos. Every repo whose suite + # The full roster of fourteen example repos. Every repo whose suite # bootstraps a pinned cascade CLI belongs here; keep it in sync with # the fleet repin roster in fleet-e2e.yaml. - REPOS="primary artifact-a artifact-b 4env 3env 2env single-env release-only no-env callbacks rollback-dispatch monorepo" + REPOS="primary artifact-a artifact-b 4env 3env 2env single-env release-only no-env callbacks rollback-dispatch monorepo retries dryrun" # Repos whose main rejects a direct push (branch protection requires a # PR). These take the PR path up front. Any OTHER repo that diff --git a/docs/src/content/docs/internals/release-orchestration.md b/docs/src/content/docs/internals/release-orchestration.md index 19f968ea..506055ed 100644 --- a/docs/src/content/docs/internals/release-orchestration.md +++ b/docs/src/content/docs/internals/release-orchestration.md @@ -20,10 +20,10 @@ Auto-promote publishes the final version, but only when the entire fleet is gree The fleet ([`.github/workflows/fleet-e2e.yaml`](https://github.com/stablekernel/cascade/blob/main/.github/workflows/fleet-e2e.yaml)) revalidates the downstream `cascade-example-*` fleet on live GitHub. Every example repository dispatches its own `scenario-suite.yaml` under one shared fleet token. A -green run means this cascade version validated across all twelve example repositories, +green run means this cascade version validated across all fourteen example repositories, each running its own scenario suite in its own repository context. -Dispatching all twelve repositories at once tripped transient GitHub API failures +Dispatching all fourteen repositories at once tripped transient GitHub API failures (401, 403, and 500 responses) on a rotating repository each run, because they all draw on the same token. The fan-out is therefore split into sequenced lanes that hold peak live concurrency near two repositories at a time. A `gh()` transient-retry @@ -49,7 +49,7 @@ flowchart LR | `primary` | Runs first and must pass before its dependents start. | | `dependents` | `artifact-a` and `artifact-b` mutate the primary's shared external state, so they run only after the primary is green. The two run together, which is the lane that defines the fleet's peak of about two repositories. | | `heavy` | `4env` is the heaviest and most fragile repository, so it runs alone in its own job, sequenced after the dependents lane so the two never stack. | -| `remainder` | The light repositories (`3env`, `2env`, `single-env`, `release-only`, `no-env`, `callbacks`, `rollback-dispatch`, `monorepo`) run in a matrix capped at two in flight via `max-parallel`, sequenced after the heavy lane. | +| `remainder` | The light repositories (`3env`, `2env`, `single-env`, `release-only`, `no-env`, `callbacks`, `rollback-dispatch`, `monorepo`, `retries`, `dryrun`) run in a matrix capped at two in flight via `max-parallel`, sequenced after the heavy lane. | | `aggregate` | The Fleet gate. It needs every lane, so a green gate means every selected repository passed. Auto-promote keys off this conclusion. | The fleet triggers on completion of the Release workflow for a release-candidate or