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
101 changes: 101 additions & 0 deletions .github/workflows/release-finalize.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Finalize release

# Called by release-plz.yml once release-plz has published the crate and created
# the tag. `git_release_enable = false` leaves the GitHub release to this
# workflow, because the crate, SBOM, checksums, and attestations have to be
# attached before it is published.
"on":
workflow_call:
inputs:
tag:
description: Exact release tag created by release-plz
required: true
type: string
version:
description: Exact crate version released by release-plz
required: true
type: string

permissions: {}

concurrency:
group: release-finalize-${{ inputs.tag }}
cancel-in-progress: false

jobs:
finalize:
name: Attest and publish the release
runs-on: windows-latest
timeout-minutes: 45
permissions:
contents: write
id-token: write
attestations: write
artifact-metadata: write
env:
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_VERSION: ${{ inputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag }}
fetch-depth: 0
persist-credentials: false
- uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
with:
toolchain: stable
- uses: taiki-e/install-action@6a1bd70eaac3c8bdf093356838d7ee09fda951cf # v2.85.5
with:
tool: cargo-cyclonedx@0.5.9
- run: python -m pip install "reuse[charset-normalizer]==6.2.0"
- name: Verify tag, commit, and Cargo version
run: cargo xtask verify-release-tag "$env:RELEASE_TAG"
- name: Build and verify release candidate
id: candidate
run: cargo xtask release-candidate --github-output

# The attestations below cover the archive built here, so it has to be
# the archive crates.io actually serves. `cargo package` is deterministic
# for a given source tree, and release-plz published from this same tag,
# so a mismatch means something diverged and the release must not proceed.
- name: Require the built archive to match the published crate
shell: pwsh
run: |
$crate = Get-ChildItem target/release-candidate/*.crate |
Select-Object -First 1
if ($null -eq $crate) { throw 'release candidate has no .crate archive' }
$built = (Get-FileHash $crate.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
$uri = "https://crates.io/api/v1/crates/windows-spawn/$env:RELEASE_VERSION/download"
$probe = Join-Path $env:RUNNER_TEMP 'published.crate'
Invoke-WebRequest -Uri $uri -OutFile $probe -TimeoutSec 60 `
-Headers @{ 'User-Agent' = 'windows-spawn-release-finalize' }
$published = (Get-FileHash $probe -Algorithm SHA256).Hash.ToLowerInvariant()
if ($built -ne $published) {
throw "built archive $built does not match the published crate $published"
}
"built and published archives match: $built" | Write-Output

- name: Attest SLSA v1 provenance
uses: actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d # v4
with:
subject-path: |
target/release-candidate/*.crate
target/release-candidate/*.cdx.json
target/release-candidate/*.reuse.spdx
target/release-candidate/SHA256SUMS

- name: Attest CycloneDX SBOM
uses: actions/attest@508db95dd578ae2727ebd6217d5ba78e4fbda05d # v4
with:
subject-path: target/release-candidate/*.crate
sbom-path: ${{ steps.candidate.outputs.sbom }}

- name: Create draft GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: cargo xtask draft-release "$env:RELEASE_TAG" --github-output

- name: Publish GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: gh release edit $env:RELEASE_TAG --draft=false
151 changes: 151 additions & 0 deletions .github/workflows/release-plz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
---
# SPDX-FileCopyrightText: 2026 Yasunobu Sakashita
#
# SPDX-License-Identifier: MIT OR Apache-2.0

# The upstream-recommended release-plz workflow. Two deviations, both of which
# release-plz documents:
#
# * A GitHub App token instead of GITHUB_TOKEN. The default token cannot
# trigger other workflows, so CI would never run on a release pull request.
# * `environment: release` on both jobs. The App credentials are scoped to it,
# and it is where the crates.io credential lives.
#
# Windows, not the ubuntu-latest of the upstream template: `cargo publish`
# verifies the tarball by building it, and this crate does not build on Linux
# because its windows-sys dependency sits behind `cfg(windows)`.
name: Release-plz

"on":
push:
branches: [main]

permissions: {}

concurrency:
group: release-plz-${{ github.ref }}
cancel-in-progress: false

jobs:
release:
name: Release-plz release
if: github.repository == 'P4suta/windows-spawn'
runs-on: windows-2022
timeout-minutes: 45
environment:
name: release
permissions:
contents: write
id-token: write
outputs:
released: ${{ steps.run.outputs.releases_created }}
tag: ${{ steps.released.outputs.tag }}
version: ${{ steps.released.outputs.version }}
steps:
- name: Mint the release GitHub App token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.RELEASE_PLZ_APP_CLIENT_ID }}
private-key: ${{ secrets.RELEASE_PLZ_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: read
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
with:
toolchain: stable
# A trusted publisher can only be registered against a crate that already
# exists. The bootstrap token covered 0.1.0; once a trusted publisher is
# registered for this workflow the secret can be deleted and the OpenID
# Connect exchange takes over.
- name: Select the crates.io credential
id: auth-mode
env:
CRATES_IO_BOOTSTRAP_TOKEN: ${{ secrets.CRATES_IO_BOOTSTRAP_TOKEN }}
run: cargo xtask crates-io-auth-mode --github-output
- name: Authenticate with crates.io trusted publishing
id: crates-auth
if: steps.auth-mode.outputs.bootstrap == 'false'
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
- name: Run release-plz
id: run
uses: release-plz/action@2eb1d8bcb770b4c48ccfaad919734b38b51958c9 # v0.5.131
with:
command: release
version: 0.3.160
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CARGO_REGISTRY_TOKEN: >-
${{ steps.crates-auth.outputs.token
|| secrets.CRATES_IO_BOOTSTRAP_TOKEN }}
- name: Read the released tag and version
id: released
if: steps.run.outputs.releases_created == 'true'
shell: bash
env:
RELEASES: ${{ steps.run.outputs.releases }}
run: |
set -euo pipefail
jq -r '.[0] | "tag=\(.tag)", "version=\(.version)"' <<< "$RELEASES" >> "$GITHUB_OUTPUT"

# release-plz publishes the crate and creates the tag; the GitHub release is
# built here so the crate, SBOM, checksums, and attestations are attached
# before it is published.
finalize:
name: Attest and publish the release
needs: release
if: needs.release.outputs.released == 'true'
permissions:
artifact-metadata: write
attestations: write
contents: write
id-token: write
uses: ./.github/workflows/release-finalize.yml
with:
tag: ${{ needs.release.outputs.tag }}
version: ${{ needs.release.outputs.version }}

release-pr:
name: Release-plz PR
# Ordered after `release` so a release pull request is never opened for a
# version that is being published in the same run.
needs: release
if: always() && github.repository == 'P4suta/windows-spawn'
runs-on: windows-2022
timeout-minutes: 30
environment:
name: release
permissions: {}
concurrency:
group: release-plz-pr-${{ github.ref }}
cancel-in-progress: false
steps:
- name: Mint the release GitHub App token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.RELEASE_PLZ_APP_CLIENT_ID }}
private-key: ${{ secrets.RELEASE_PLZ_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
with:
toolchain: stable
# `semver_check = true` in release-plz.toml needs this on PATH.
- uses: taiki-e/install-action@6a1bd70eaac3c8bdf093356838d7ee09fda951cf # v2.85.5
with:
tool: cargo-semver-checks@0.45.0
- name: Run release-plz
uses: release-plz/action@2eb1d8bcb770b4c48ccfaad919734b38b51958c9 # v0.5.131
with:
command: release-pr
version: 0.3.160
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
121 changes: 0 additions & 121 deletions .github/workflows/release.yml

This file was deleted.

Loading