From 2258d140638387c5df38bf608499414e01d94db3 Mon Sep 17 00:00:00 2001 From: Joshua Temple Date: Mon, 29 Jun 2026 16:14:16 -0400 Subject: [PATCH] docs(branch-protection): document --apply mode and admin-token caveat branch-protection now has two modes: the unchanged default that emits the protection JSON for an operator to apply, and an opt-in --apply that PUTs the body to GitHub directly with a caller-supplied scoped token. Document the new --apply, --token, --repo, and --api-url flags with their env fallbacks, note that applying requires repo-admin (Administration: write) which the workflow GITHUB_TOKEN lacks, and prefer the env var over the flag to keep the token out of process args. Cross-reference the apply mode from the hardening guide. Signed-off-by: Joshua Temple --- docs/src/content/docs/cli-reference.md | 31 ++++++++++++++++++--- docs/src/content/docs/security/hardening.md | 2 +- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/docs/src/content/docs/cli-reference.md b/docs/src/content/docs/cli-reference.md index 54202dd6..63ca4a31 100644 --- a/docs/src/content/docs/cli-reference.md +++ b/docs/src/content/docs/cli-reference.md @@ -300,7 +300,7 @@ cascade plan ### branch-protection -Emit the JSON body an operator applies to GitHub's branch-protection API for a cascade-managed trunk. cascade emits the file; the operator applies it. cascade never calls the GitHub API. +Produce the branch-protection settings for a cascade-managed trunk. The command has two modes. By default it emits the JSON body for an operator to apply and makes no API call. Pass `--apply` and cascade PUTs the body to GitHub's branch-protection API for you using a caller-supplied scoped token. ```bash cascade branch-protection @@ -318,13 +318,32 @@ cascade branch-protection | jq .protection | \ gh api -X PUT repos/my-org/my-app/branches/main/protection --input - ``` +#### Applying directly with `--apply` + +Instead of piping the JSON to `gh`, pass `--apply` and cascade sends the PUT itself. It transmits only the `.protection` object; the `operator_todo` guidance is never part of the request. The default emit behavior is unchanged: without `--apply` cascade still only prints or writes the JSON. + +```bash +cascade branch-protection --apply --repo my-org/my-app --branch main +``` + +With `--apply`, `--branch` is the real protection target rather than just a label on the guidance note. cascade resolves the target repository from `--repo`, falling back to the `GITHUB_REPOSITORY` environment variable, and the REST API base from `--api-url`, falling back to `GITHUB_API_URL` and then `https://api.github.com`. A missing token or repository is reported before any network call, and a non-2xx response from GitHub (for example a `403` from an under-scoped token) is surfaced with GitHub's own rejection message. + +Applying branch protection requires a token with repo-admin authority (the `Administration: write` permission). The workflow `GITHUB_TOKEN` does not carry that authority, so `--apply` needs a scoped personal access token supplied through `--token` or the `GITHUB_TOKEN` environment variable. Prefer the environment variable over the flag so the token stays out of process arguments and shell history. + +```bash +GITHUB_TOKEN=ghp_your_admin_pat \ + cascade branch-protection --apply --repo my-org/my-app --branch main +``` + +You can also pass `--output` alongside `--apply` to keep the emitted JSON on disk for your records; cascade writes the file first and then applies. + #### What ends up required, and why it is safe The required status checks contain only the cascade-controlled `Setup` and `Finalize` jobs. These are the orchestrate workflow's two steps jobs; cascade knows their exact check-run names and both run on every pipeline run. Because of that, `.protection` applied as-is never creates a required check that can never report, so it never blocks a pull request on its own. The reusable-workflow caller jobs (validate, build, deploy) are deliberately left out of the required contexts. cascade knows each caller's display-name prefix (for example `Build (my-app)`) but not the inner job name that GitHub appends to form the real check-run context, which is ` / `. That inner job lives in your reusable workflow, which cascade does not author. Requiring a bare prefix would never match and would block every pull request, so cascade lists those prefixes under `operator_todo.complete_these_contexts` as ` / ` placeholders instead. Replace `` with the job name inside each reusable workflow, then add the completed strings to `required_status_checks.contexts` when you want them required. -The `--branch` flag only labels the guidance note. The required contexts are the same across branches and environments because they are the orchestrate-workflow steps jobs, so `--env` would not change them and is not offered. +In the default emit mode the `--branch` flag only labels the guidance note (with `--apply` it is the real apply target, as described above). Either way the required contexts are the same across branches and environments because they are the orchestrate-workflow steps jobs, so `--env` would not change them and is not offered. This command complements the hotfix branch-protection advisory (see [Hotfix workflow](/workflows/#hotfix-workflow)): the advisory prints ready-to-run `gh` commands for env branches, while `branch-protection` emits the full PUT body for the trunk. @@ -334,8 +353,12 @@ This command complements the hotfix branch-protection advisory (see [Hotfix work |------|------|---------|-------------| | `--config`, `-c` | string | auto-detect | Path to manifest file | | `--manifest-key` | string | `ci` | Top-level key inside the manifest | -| `--branch` | string | `main` | Branch the protection targets (labels the guidance note only; does not change the required contexts) | -| `--output`, `-o` | string | stdout | Write to this path instead of stdout (`-` also means stdout) | +| `--branch` | string | `main` | Branch the protection targets; with `--apply` this is the real apply target, otherwise it labels the guidance note only and does not change the required contexts | +| `--output`, `-o` | string | stdout | Write to this path instead of stdout (`-` also means stdout); honored alongside `--apply` to keep the JSON for your records | +| `--apply` | bool | `false` | PUT the `.protection` body to GitHub instead of emitting JSON (requires a repo-admin token) | +| `--token` | string | `GITHUB_TOKEN` | Scoped repo-admin token used for `--apply`; falls back to the `GITHUB_TOKEN` environment variable | +| `--repo` | string | `GITHUB_REPOSITORY` | `owner/repo` the apply targets; falls back to the `GITHUB_REPOSITORY` environment variable | +| `--api-url` | string | `GITHUB_API_URL` | REST API base for `--apply`; falls back to `GITHUB_API_URL`, then `https://api.github.com` | ### environments diff --git a/docs/src/content/docs/security/hardening.md b/docs/src/content/docs/security/hardening.md index d62ee72a..551d42b4 100644 --- a/docs/src/content/docs/security/hardening.md +++ b/docs/src/content/docs/security/hardening.md @@ -40,7 +40,7 @@ Treat these as future work rather than current guarantees: GitHub and your cloud own these controls; cascade cannot set them for you: -- **Branch protection** on your trunk: require reviews and status checks, restrict who can push, and require signed commits where appropriate. +- **Branch protection** on your trunk: require reviews and status checks, restrict who can push, and require signed commits where appropriate. The [`branch-protection`](/cli-reference/#branch-protection) command emits the matching settings, or applies them for you with `--apply` given a repo-admin token (the workflow `GITHUB_TOKEN` cannot). - **Tag protection or rulesets** so release and version tags cannot be moved or forged. - **Environment protection rules** with required reviewers, wait timers, and deployment branch or tag policies on production environments. For reusable deploys, place this gate in the called workflow. - **CODEOWNERS on workflow files** (`.github/workflows/**`) so changes to the pipeline itself require owner review.