Files
pulse/scripts/release_control
rcourtman d5604fe3df Confine release credentials to the release environment (#3303)
## What

Every job that reads a release, signing or registry credential now
declares
`environment: release`, and `scripts/check_workflow_trust.py` enforces
it.

- 20 jobs across `create-release`, `build-release-candidate`,
`publish-docker`,
  `promote-floating-tags`, `promote-private-pro-runtime`,
  `recover-release-activation`, `backfill-release-assets`,
  `retry-release-convergence`, `release-signing-preflight`,
  `signpath-test-signing` and `canonical-private-governance` declare
`environment: release`. `qualify-browser-update-release` declares
`infra` for
its tailnet credentials. `PULSE_LICENSE_PUBLIC_KEY` is a public key and
stays
  repository-wide.
- The checker reads job structure with a duplicate-key-rejecting YAML
loader
  and scans both raw text and decoded values for secret expressions
  (case-insensitive, across lines, quote-aware). It requires the literal
release environment for release credentials and a deployment environment
for demo/tailnet credentials (the demo workflow's runtime choice only in
its
own job). It refuses dynamic `secrets[...]` access and workflow-level
secret
env, and requires any push workflow that reads a secret to admit exactly
  `branches: [main]`.

## Why

Release secrets are repository-level today, so any workflow run on any
branch,
including a pushed proposal branch before review, can read them. The
release
train is switching to post-merge `release-dispatch/*` branches that only
the
release dispatcher App can create (ruleset-enforced). A `release`
environment
admitting only `main` and `release-dispatch/*` then keeps these
credentials
away from unmerged code.

## Merge order (draft until then)

Stacked on #3297. Do not merge until:
1. a beta has published through a `release-dispatch/*` dispatch, and
2. the `release` (branches `main`, `release-dispatch/*`) and `infra`
(`main`)
environments exist with branch-typed policies, so the first post-merge
run
   does not auto-create an unprotected environment.

Repository-level secret copies stay until no snapshot without these
declarations can still be dispatched or recovered.

## Validation

- Live tree passes the checker; `test_workflow_trust.py` 50 OK,
including new
cases for every rule and for bypasses found in review (lower-case names,
`}}` inside strings and YAML-escaped quotes, `release#x`, duplicate
keys,
  quoted `tags`/`inherit`, a push key hidden in a description, the demo
  expression outside its job).
- Eleven mutations, one per guard, each fail the tests.
- All 1301 `scripts/tests`, Go `scripts/installtests`, and
`release_promotion_policy_test.py` 74 OK (new proof test pins the
release
jobs' environment). Release-control discovery has 17 failures and 1
error
  that are identical on `main` and on this branch's base.
2026-10-26 14:22:52 +00:00
..
2026-03-19 10:06:39 +00:00