Add a "release-gate" step to the release workflow (#18804)

The way this works is

1. `release-gate` is an environment which requires approval from another
person in the organization
2. Once approved, the release can continue
3. GitHub then requires approval for every subsequent job, which we use
the `release` environment for
4. We do not require team members to approve on the `release`
environment because we run _many_ child jobs during releases
5. The `release` environment uses a deployment protection rule which
queries a GitHub App we manage
6. The GitHub App checks if the `release-gate` job was successful in the
same workflow and approves or denies accordingly

The GitHub App's source is at
https://github.com/open-security-tools/ost-environment-gate and includes
another explanation of what's going on in this process.

We don't make the release-gate block everything, so builds can start at
least while we wait for the release-gate to be approved.
This commit is contained in:
Zanie Blue
2026-04-01 15:23:37 -05:00
committed by GitHub
parent 8fe68cf52d
commit 6b1ebc33dc
4 changed files with 16 additions and 6 deletions
-1
View File
@@ -17,7 +17,6 @@ jobs:
runs-on: ubuntu-latest
environment:
name: release
deployment: false
permissions:
contents: read
id-token: write
-1
View File
@@ -17,7 +17,6 @@ jobs:
runs-on: ubuntu-latest
environment:
name: release
deployment: false
env:
VERSION: ${{ fromJson(inputs.plan).announcement_tag }}
steps:
-2
View File
@@ -17,7 +17,6 @@ jobs:
runs-on: ubuntu-latest
environment:
name: release
deployment: false
permissions:
id-token: write # For PyPI's trusted publishing
steps:
@@ -36,7 +35,6 @@ jobs:
runs-on: ubuntu-latest
environment:
name: release
deployment: false
permissions:
id-token: write # For PyPI's trusted publishing
steps:
+16 -2
View File
@@ -52,6 +52,16 @@ env:
CARGO_DIST_CHECKSUM: "cd355dab0b4c02fb59038fef87655550021d07f45f1d82f947a34ef98560abb8"
jobs:
release-gate:
name: release-gate
if: ${{ inputs.tag != 'dry-run' }}
runs-on: ubuntu-latest
environment:
name: release-gate
deployment: false
steps:
- run: echo "Release approved"
# Run 'dist plan' (or host) to determine what tasks we need to do
plan:
runs-on: "depot-ubuntu-latest-4"
@@ -108,7 +118,8 @@ jobs:
custom-build-docker:
needs:
- plan
if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }}
- release-gate
if: ${{ always() && needs.plan.result == 'success' && (needs.release-gate.result == 'success' || needs.release-gate.result == 'skipped') && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run') }}
uses: ./.github/workflows/build-docker.yml
with:
plan: ${{ needs.plan.outputs.val }}
@@ -256,6 +267,7 @@ jobs:
needs:
- plan
- host
- release-gate
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
uses: ./.github/workflows/publish-pypi.yml
with:
@@ -270,6 +282,7 @@ jobs:
needs:
- plan
- host
- release-gate
- custom-publish-pypi # DIRTY: see #16989
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
uses: ./.github/workflows/publish-crates.yml
@@ -286,12 +299,13 @@ jobs:
needs:
- plan
- host
- release-gate
- custom-publish-pypi
- custom-publish-crates
# use "always() && ..." to allow us to wait for all publish jobs while
# still allowing individual publish jobs to skip themselves (for prereleases).
# "host" however must run to completion, no skipping allowed!
if: ${{ always() && needs.host.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') && (needs.custom-publish-crates.result == 'skipped' || needs.custom-publish-crates.result == 'success') }}
if: ${{ always() && needs.host.result == 'success' && needs.release-gate.result == 'success' && (needs.custom-publish-pypi.result == 'skipped' || needs.custom-publish-pypi.result == 'success') && (needs.custom-publish-crates.result == 'skipped' || needs.custom-publish-crates.result == 'success') }}
runs-on: "depot-ubuntu-latest-4"
environment:
name: release