Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions .github/workflows/release-tracing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
# write the public repo): crane-copy staging@digest →
# public coordinate (idempotent; digest unchanged),
# assert public digest equals the packaged constant
# publish-pypi - PyPI (gated by the `pypi` environment). Version-burn
# publish-pypi - PyPI (gated by the `tracing-pypi` environment).
# Version-burn
# rule: a candidate that failed the gate burns its
# version; never re-tag — bump and rebuild.
# finalize - always() but gated on github-release success (a
Expand All @@ -73,8 +74,12 @@
# surfaces partial publication with the
# yank/version-burn recovery.
#
# One-time GitHub setup: environments `testpypi`, `pypi`, and
# `release-promote` (the latter two with required reviewers), and
# One-time GitHub setup: environments `tracing-testpypi`,
# `tracing-pypi`, and `release-promote` (the latter two with required
# reviewers; all three restricted to tag `tracing-v*`). The names are
# tracing-PREFIXED on purpose: the root SDK's release.yml publishes
# through the shared `pypi`/`testpypi` environments on `v*` tags, and a
# `tracing-v*` restriction there would block root releases. And
# ENABLE IMMUTABLE RELEASES (Settings → General → Releases) BEFORE the
# first release: finalize verifies the setting BEFORE publishing —
# GitHub does not apply immutability retroactively, so a release
Expand All @@ -87,7 +92,8 @@
# - Owner: GoogleCloudPlatform
# - Repo: BigQuery-Agent-Analytics-SDK
# - Workflow: release-tracing.yml
# - Environment: testpypi / pypi (match the `environment:` blocks
# - Environment: tracing-testpypi / tracing-pypi (match the
# `environment:` blocks
# below). Until the project + trusted publisher are configured on
# TestPyPI/PyPI, the publish jobs will fail with a clear error —
# `build` and `github-release` still produce + attach artifacts so
Expand Down Expand Up @@ -479,7 +485,7 @@ jobs:
# publish-pypi inherits the guard transitively via needs: publish-testpypi.
needs: [verify, build, github-release]
environment:
name: testpypi
name: tracing-testpypi
url: https://test.pypi.org/p/bigquery-agent-analytics-tracing
permissions:
id-token: write
Expand Down Expand Up @@ -632,7 +638,7 @@ jobs:
timeout-minutes: 15
needs: [verify, build, promote, publish-testpypi]
environment:
name: pypi
name: tracing-pypi
url: https://pypi.org/p/bigquery-agent-analytics-tracing
permissions:
id-token: write
Expand Down
53 changes: 37 additions & 16 deletions producers/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ git push origin "tracing-v${VERSION}"

The workflow takes over from there. Automated stages run in ~10
minutes; the release then WAITS at two manual approval gates
(`release-promote`, then `pypi`) — see the gate section below.
(`release-promote`, then `tracing-pypi`) — see the gate section below.

## What CI does (issue #349 release contract)

Expand Down Expand Up @@ -98,7 +98,7 @@ minutes; the release then WAITS at two manual approval gates
coordinate (idempotent — an existing tag is accepted only with the
exact expected digest) and asserts public digest == packaged
constant. It does NOT publish the release.
7. **`publish-pypi`** — PyPI, gated by the `pypi` environment;
7. **`publish-pypi`** — PyPI, gated by the `tracing-pypi` environment;
requires `promote`.
8. **`finalize`** — runs `always()` but is gated on `github-release`
succeeding (when the rerun guard deliberately fails, its "rerun the
Expand Down Expand Up @@ -161,7 +161,8 @@ bqaa-otel teardown ... --confirm # WITH --confirm: the gate evidence must

(The staging ref + digest are in the `build-image` job output.) All
green → approve `release-promote`, then run the post-promotion smoke
WITHOUT `--image` (embedded public default), then approve `pypi`.
WITHOUT `--image` (embedded public default), then approve
`tracing-pypi`.
Post evidence on the release issue.

## Version-burn rule
Expand Down Expand Up @@ -267,32 +268,52 @@ TestPyPI) and add a publisher with:
| Owner | `GoogleCloudPlatform` |
| Repository | `BigQuery-Agent-Analytics-SDK` |
| Workflow filename | `release-tracing.yml` |
| Environment | `pypi` (or `testpypi` on TestPyPI) |
| Environment | `tracing-pypi` (or `tracing-testpypi` on TestPyPI) |

These names must match exactly — the `environment:` blocks in the
workflow are the binding contract.

GitHub-side one-time setup: create environments `testpypi`, `pypi`,
and `release-promote` in the repo settings —
GitHub-side one-time setup: create environments `tracing-testpypi`,
`tracing-pypi`, and `release-promote` in the repo settings. The
tracing-PREFIXED names are deliberate: the root SDK's `release.yml`
publishes through the shared `pypi`/`testpypi` environments on `v*`
tags, so a `tracing-v*` restriction on those would block root
releases — the tracing pipeline must never reuse them.

- restrict **all three** environments to tag `tracing-v*`
- restrict **all three** tracing environments to tag `tracing-v*`
(Settings → Environments → Deployment branches and tags → Selected →
Tag rule `tracing-v*`); the `testpypi` rule matters even without a
reviewer, because it stops a branch-modified copy of the workflow
from using the TestPyPI OIDC identity and burning a version;
- required reviewers on `pypi` and `release-promote` ONLY (approving
`release-promote` asserts the TestPyPI full-lifecycle gate passed);
`testpypi` gets no reviewer — the manual lifecycle verdict is the
later `release-promote` approval —

and **enable immutable releases BEFORE the first release**
Tag rule `tracing-v*`); the `tracing-testpypi` rule matters even
without a reviewer, because it stops a branch-modified copy of the
workflow from using the TestPyPI OIDC identity and burning a
version; the root SDK's `pypi`/`testpypi` environments keep their
own `v*`-appropriate rules;
- required reviewers on `tracing-pypi` and `release-promote` ONLY
(approving `release-promote` asserts the TestPyPI full-lifecycle
gate passed); `tracing-testpypi` gets no reviewer — the manual
lifecycle verdict is the later `release-promote` approval —

and **enable immutable releases BEFORE tagging**
(Settings → General → Releases):
`finalize` verifies the setting and refuses to publish while it is
off, because GitHub applies immutability only at publish time — a
release published while the setting is off keeps mutable assets and
`SHA256SUMS` forever, replaceable by anything holding
`contents: write`.

Immutable-releases toggle protocol (until the root SDK flow is
hardened): the repository setting is repo-wide, and the root SDK's
current `release.yml` starts from an already-published GitHub release
and has previously recovered versions by re-running a tag from a new
SHA — permanent immutability would break that recovery. For each
tracing release: (1) complete the App and publisher setup, (2) enable
immutable releases immediately before pushing the tag
(`PUT /repos/{repo}/immutable-releases`), (3) publish and verify the
tracing release reports `immutable: true`, then (4) disable the
setting again. Disabling does NOT make releases published while it
was enabled mutable again. Longer term, harden the root SDK flow so
verification/build precedes public release publication, then leave
the setting permanently enabled.

Policy-read credential (one-time): the immutable-releases check calls
`GET /repos/{repo}/immutable-releases`, which requires repository
**Administration: read** — a permission the workflow `GITHUB_TOKEN`
Expand Down
Loading