name: Code Buster Release
on:
workflow_dispatch:
inputs:
version:
description: Cargo package version already prepared on main, without the v prefix
required: true
type: string
concurrency:
group: code-buster-release
cancel-in-progress: false
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.release.outputs.tag }}
steps:
- name: Require the main branch
if: github.ref != 'refs/heads/main'
run: |
echo 'Releases must be dispatched from main.' >&2
exit 1
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@360a4e93673de6630fac2478bc9e35244b01a759 with:
components: clippy,rustfmt
- name: Validate release version
id: release
env:
REQUESTED_VERSION: ${{ inputs.version }}
run: |
if ! [[ "$REQUESTED_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?(\+[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
echo "invalid semantic version: $REQUESTED_VERSION" >&2
exit 1
fi
PACKAGE_VERSION="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "code-buster") | .version')"
if [[ "$PACKAGE_VERSION" != "$REQUESTED_VERSION" ]]; then
echo "Cargo version $PACKAGE_VERSION does not match requested version $REQUESTED_VERSION" >&2
exit 1
fi
if ! grep -Fqx "## $REQUESTED_VERSION" CHANGELOG.md; then
echo "CHANGELOG.md has no release heading for $REQUESTED_VERSION" >&2
exit 1
fi
if ! grep -Fqx 'cargo install code-buster --locked' README.md; then
echo 'README.md does not contain the crates.io install command' >&2
exit 1
fi
if ! grep -Fq -- "cargo install code-buster --version $REQUESTED_VERSION --locked" integrations/github/code-buster.yml; then
echo "GitHub integration does not pin crates.io version $REQUESTED_VERSION" >&2
exit 1
fi
echo "tag=v$REQUESTED_VERSION" >> "$GITHUB_OUTPUT"
- name: Verify reviewed changelog release notes
env:
RELEASE_VERSION: ${{ inputs.version }}
run: |
python3 -B -m unittest tool/test_release_notes.py
python3 tool/release_notes.py "v$RELEASE_VERSION" "$RUNNER_TEMP/release-notes.md"
- run: cargo fmt --all -- --check
- run: cargo clippy --all-targets --all-features --locked -- -D warnings
- run: cargo test --all-targets --all-features --locked
- run: cargo package --locked
resolve-tag:
needs: verify
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with:
fetch-depth: 0
- name: Resolve release tag
env:
RELEASE_TAG: ${{ needs.verify.outputs.tag }}
run: |
if git show-ref --verify --quiet "refs/tags/$RELEASE_TAG"; then
TAG_REVISION="$(git rev-list -n 1 "$RELEASE_TAG")"
if [[ "$TAG_REVISION" != "$GITHUB_SHA" ]]; then
echo "$RELEASE_TAG already points to $TAG_REVISION instead of $GITHUB_SHA" >&2
exit 1
fi
echo "Using existing tag $RELEASE_TAG."
else
git tag "$RELEASE_TAG" "$GITHUB_SHA"
git push origin "$RELEASE_TAG"
fi
artifacts:
needs: [verify, resolve-tag]
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
platform: linux-x64
executable: target/release/cb
archive: code-buster-linux-x64.tar.gz
- os: macos-15
platform: macos-arm64
executable: target/release/cb
archive: code-buster-macos-arm64.tar.gz
- os: windows-latest
platform: windows-x64
executable: target/release/cb.exe
archive: code-buster-windows-x64.zip
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with:
ref: ${{ needs.verify.outputs.tag }}
- uses: dtolnay/rust-toolchain@360a4e93673de6630fac2478bc9e35244b01a759 - run: cargo build --release --locked
- name: Smoke release binary
shell: bash
run: |
'${{ matrix.executable }}' version
'${{ matrix.executable }}' config --files --root tests/fixtures/preview --format json > files.json
'${{ matrix.executable }}' summary --root tests/fixtures/preview --format json --advisory --allow-empty > summary.json
- name: Archive Unix artifact
if: runner.os != 'Windows'
shell: bash
run: |
cp '${{ matrix.executable }}' cb
tar -czf '${{ matrix.archive }}' cb LICENSE README.md
- name: Archive Windows artifact
if: runner.os == 'Windows'
shell: pwsh
run: Compress-Archive -Path '${{ matrix.executable }}',LICENSE,README.md -DestinationPath '${{ matrix.archive }}'
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with:
name: ${{ matrix.platform }}
path: ${{ matrix.archive }}
if-no-files-found: error
publish-crate:
needs: [verify, resolve-tag, artifacts]
runs-on: ubuntu-latest
environment: release
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with:
ref: ${{ needs.verify.outputs.tag }}
- uses: dtolnay/rust-toolchain@360a4e93673de6630fac2478bc9e35244b01a759 - name: Authenticate with crates.io
id: crates-auth
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 - name: Publish crate
run: cargo publish --locked
env:
CARGO_REGISTRY_TOKEN: ${{ steps.crates-auth.outputs.token }}
publish-github-release:
needs: [verify, artifacts, publish-crate]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 with:
ref: ${{ needs.verify.outputs.tag }}
- name: Extract reviewed changelog notes
run: python3 tool/release_notes.py '${{ needs.verify.outputs.tag }}' release-notes.md
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 with:
path: dist
merge-multiple: true
- name: Create checksums
run: cd dist && sha256sum -- * > SHA256SUMS
- name: Publish GitHub release with changelog notes
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 with:
tag_name: ${{ needs.verify.outputs.tag }}
body_path: release-notes.md
files: |
dist/*