Skip to content

Release

Release #64

Workflow file for this run

name: Release
on: # zizmor: ignore[dangerous-triggers] -- Exact SHA/API-verified same-repository main pushes only; no PR artifacts/caches consumed.
workflow_run:
workflows: [CI]
branches: [main]
types: [completed]
# crates.io Trusted Publishing refuses OIDC tokens from workflow_run events, so
# the verified workflow_run dispatches this same workflow for the publish job.
# Every input is re-verified through the API before anything is published.
workflow_dispatch:
inputs:
sha:
description: Merged release-plz commit on main to publish (40 hex)
required: true
type: string
ci_run_id:
description: Successful ci.yml push-to-main run for that commit
required: true
type: string
permissions: {}
concurrency:
group: release-main
cancel-in-progress: false
queue: max
defaults:
run:
shell: bash
jobs:
verify:
if: >-
github.event_name == 'workflow_dispatch' || (
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_repository.full_name == 'PerryTS/turnloop' &&
github.event.workflow_run.conclusion == 'success')
runs-on: ubuntu-24.04
permissions:
contents: read
actions: read
pull-requests: read
timeout-minutes: 10
outputs:
release: ${{ steps.ci.outputs.release }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# A dispatch runs main's verifier (github.sha), never the input commit's
# scripts; the verifier proves inputs.sha is a merged release commit.
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.14'
- name: Verify the exact main commit's ci-gate through the API
id: ci
env:
GH_TOKEN: ${{ github.token }}
RELEASE_SHA: ${{ github.event.workflow_run.head_sha || inputs.sha }}
CI_RUN_ID: ${{ github.event.workflow_run.id || inputs.ci_run_id }}
run: python3 scripts/ci/check-ci.py --release-pr
release-pr:
needs: verify
if: github.event_name == 'workflow_run'
runs-on: ubuntu-24.04
permissions:
contents: read
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: main
fetch-depth: 0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.14'
- name: Install pinned Rust
run: rustup toolchain install nightly-2026-08-20 --profile minimal --component rustfmt,clippy
- name: Install release PR tools
run: python3 scripts/ci/install-tools.py release-plz cargo-semver-checks
- name: Short-lived GitHub App token so release PRs trigger normal CI
id: app
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ vars.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
owner: PerryTS
repositories: turnloop
permission-contents: write
permission-pull-requests: write
- name: Prepare version bumps and changelogs
env:
GIT_TOKEN: ${{ steps.app.outputs.token }}
RUSTUP_TOOLCHAIN: nightly-2026-08-20
# release-plz resolves each crate against the registry, where our own
# siblings are hours old, so the seven-day publish-age soak rejects
# them ("turnloop-io = ^0.1.0-alpha.3 is too new"). The soak exists for
# third-party supply chain, and this resolution produces no artifact:
# every lockfile the release PR carries is still gated by
# scripts/ci/soak.py in required CI, which no exception here can skip.
CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE: allow
run: release-plz release-pr --repo-url https://github.com/PerryTS/turnloop --config release-plz.toml
dispatch-publish:
needs: verify
if: github.event_name == 'workflow_run' && needs.verify.outputs.release == 'true'
runs-on: ubuntu-24.04
permissions:
actions: write
timeout-minutes: 5
steps:
- name: Dispatch the Trusted Publishing run for the verified release commit
env:
GH_TOKEN: ${{ github.token }}
RELEASE_SHA: ${{ github.event.workflow_run.head_sha }}
CI_RUN_ID: ${{ github.event.workflow_run.id }}
run: gh workflow run release.yml --repo PerryTS/turnloop --ref main -f "sha=$RELEASE_SHA" -f "ci_run_id=$CI_RUN_ID"
publish:
needs: verify
if: github.event_name == 'workflow_dispatch' && needs.verify.outputs.release == 'true'
runs-on: ubuntu-24.04
# The owner must configure required reviewers and main-only deployments.
environment: crates-io
permissions:
contents: write
actions: read
id-token: write
timeout-minutes: 60
env:
RELEASE_SHA: ${{ inputs.sha }}
CI_RUN_ID: ${{ inputs.ci_run_id }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ inputs.sha }}
fetch-depth: 0
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.14'
- name: Install pinned Rust
run: rustup toolchain install nightly-2026-08-20 --profile minimal --component rustfmt,clippy
- name: Install release checks and audit toolchain
run: |
python3 scripts/ci/install-tools.py cargo-semver-checks
rustup toolchain install nightly-2026-09-07 --profile minimal
- name: Supply-chain and all-target runtime gates
run: |
python3 scripts/ci/soak.py
bash scripts/ci/no-tokio.sh
- name: Semver and dry-run for every publishable crate before any upload
run: python3 scripts/ci/release.py preflight
- name: Verify ci-gate again after environment approval
env:
GH_TOKEN: ${{ github.token }}
run: python3 scripts/ci/check-ci.py
- name: Obtain a short-lived crates.io token through OIDC
id: crates
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
- name: Publish in dependency order, then create crate tags and GitHub Releases
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates.outputs.token }}
GH_TOKEN: ${{ github.token }}
run: python3 scripts/ci/release.py publish