Skip to content

fix: make five upstream-parity resolution rules stricter or more exac… #21

fix: make five upstream-parity resolution rules stricter or more exac…

fix: make five upstream-parity resolution rules stricter or more exac… #21

Workflow file for this run

# Same-run Rust release: release-please owns version, tag, and a draft Release;
# verify and build legs run downstream in this run; the archives are attested in
# the no-compile upload-assets job; the terminal job verifies every remote asset
# and flips the draft. Requires .github/scripts/resolve-release-gate.sh and
# .github/scripts/publish-release-draft.sh from the shared asset set.
name: Release
on:
push:
branches: [main]
workflow_dispatch:
inputs:
tag_name:
description: "Existing draft release tag to rebuild, for example v1.2.3"
required: true
type: string
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
BINARY_NAME: codegraph
CRATE_NAME: codegraph-rs
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: sccache
jobs:
release-please:
name: Release Please
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: write
pull-requests: write
outputs:
build_artifacts: ${{ steps.gate.outputs.build_artifacts }}
prerelease: ${{ steps.gate.outputs.prerelease }}
release_sha: ${{ steps.gate.outputs.release_sha }}
tag_name: ${{ steps.gate.outputs.tag_name }}
version: ${{ steps.gate.outputs.version }}
steps:
- name: Run release-please
id: release
if: github.event_name == 'push'
uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
- name: Checkout release tooling
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Resolve release gate
id: gate
shell: bash
env:
DEFAULT_BRANCH: main
EVENT_NAME: ${{ github.event_name }}
GH_TOKEN: ${{ github.token }}
INPUT_TAG: ${{ inputs.tag_name }}
REF_PROTECTED: ${{ github.ref_protected }}
RELEASE_CREATED: ${{ steps.release.outputs.release_created }}
RELEASE_SHA: ${{ steps.release.outputs.sha }}
RELEASE_TAG: ${{ steps.release.outputs.tag_name }}
RELEASE_VERSION: ${{ steps.release.outputs.version }}
REPO: ${{ github.repository }}
RUN_REF: ${{ github.ref }}
run: bash .github/scripts/resolve-release-gate.sh
source-gate:
name: Release Source Gate
needs: release-please
if: needs.release-please.outputs.build_artifacts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
persist-credentials: false
- name: Require the tag to resolve to the release commit
shell: bash
env:
RELEASE_SHA: ${{ needs.release-please.outputs.release_sha }}
run: |
set -euo pipefail
if [ "$(git rev-parse HEAD)" != "$RELEASE_SHA" ]; then
echo "::error title=Release source::checked-out tag differs from the release commit"
exit 1
fi
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # master 2026-08-21
with:
toolchain: 1.98.0
- name: Require the version gate's parser
run: command -v jq >/dev/null 2>&1 || { echo "jq is required"; exit 1; }
# The first Cargo subprocess in a release is the non-mutating version gate.
- name: Verify workspace version surfaces
run: bash scripts/check-workspace-versions.sh
verify-ci:
name: Verify CI Passed
needs: release-please
if: needs.release-please.outputs.build_artifacts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 35
permissions:
actions: read
contents: read
steps:
- name: Checkout release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
fetch-depth: 0
persist-credentials: false
- name: Require the tag to resolve to the release commit
shell: bash
env:
RELEASE_SHA: ${{ needs.release-please.outputs.release_sha }}
run: |
set -euo pipefail
if [ "$(git rev-parse HEAD)" != "$RELEASE_SHA" ]; then
echo "::error title=Release CI::checked-out tag differs from the release commit"
exit 1
fi
- name: Wait for exact-SHA CI Success
shell: bash
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ needs.release-please.outputs.release_sha }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
run_id=""
for _ in $(seq 1 120); do
run_id=$(gh api "repos/${REPO}/actions/runs?head_sha=${HEAD_SHA}&event=push&branch=main&per_page=100" \
--jq '[.workflow_runs[] | select(.name == "CI")] | sort_by(.created_at) | last | .id // empty')
[ -z "$run_id" ] || break
sleep 15
done
if [ -z "$run_id" ]; then
echo "::error title=Release CI::no CI push run found for ${HEAD_SHA}"
exit 1
fi
for _ in $(seq 1 120); do
job=$(gh api "repos/${REPO}/actions/runs/${run_id}/jobs?per_page=100" \
--jq '[.jobs[] | select(.name == "CI Success")] | last // {}')
status=$(jq -r '.status // empty' <<<"$job")
conclusion=$(jq -r '.conclusion // empty' <<<"$job")
if [ "$status" != completed ]; then
sleep 15
continue
fi
if [ "$conclusion" = success ]; then
echo "CI Success passed for exact release SHA ${HEAD_SHA} (run ${run_id})."
exit 0
fi
echo "::error title=Release CI::CI Success concluded ${conclusion:-<missing>} for ${HEAD_SHA}"
exit 1
done
echo "::error title=Release CI::timed out waiting for CI Success in run ${run_id}"
exit 1
verify:
name: Verify release tag
needs: [release-please, source-gate]
if: needs.release-please.outputs.build_artifacts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
persist-credentials: false
- name: Require the tag to resolve to the release commit
shell: bash
env:
RELEASE_SHA: ${{ needs.release-please.outputs.release_sha }}
run: |
set -euo pipefail
if [ "$(git rev-parse HEAD)" != "$RELEASE_SHA" ]; then
echo "::error title=Release verify::checked-out tag differs from the release commit"
exit 1
fi
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # master 2026-08-21
with:
toolchain: 1.98.0
components: rustfmt,clippy
- name: Verify workspace version surfaces before Cargo/cache
run: bash scripts/check-workspace-versions.sh
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
shared-key: cargo-home-${{ runner.os }}-${{ runner.arch }}
cache-targets: false
- name: Configure compiler cache
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
with:
version: "v0.16.0"
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
- name: Install verification tools
run: npm install --global oxfmt@0.64.0
- name: Install pinned actionlint
run: bash .github/scripts/install-actionlint.sh
- name: Require fixture parsers and shell linter
run: |
set -euo pipefail
command -v shellcheck >/dev/null 2>&1 || { echo "shellcheck is required"; exit 1; }
python3 -c 'import yaml' \
|| python3 -m pip install --break-system-packages PyYAML==6.0.3 \
|| python3 -m pip install PyYAML==6.0.3
- name: Check
run: make check
- name: Install cargo-audit
uses: taiki-e/install-action@a2a5f6e99e1a31540baa0468acfa302cff0f359f # v2.86.4
with:
tool: cargo-audit@0.22.2
- name: Audit dependencies
run: cargo audit
build-binaries:
name: Build ${{ matrix.target }}
needs: [release-please, source-gate]
if: needs.release-please.outputs.build_artifacts == 'true'
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
# Build legs execute build scripts and dependency code, so they hold no
# id-token or attestations permission; upload-assets attests the archives.
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-musl
runner: ubuntu-24.04
platform: linux
use_zigbuild: true
archive: tar.gz
- target: aarch64-unknown-linux-musl
runner: ubuntu-24.04-arm
platform: linux
use_zigbuild: true
archive: tar.gz
- target: x86_64-apple-darwin
runner: macos-15
platform: macos
execution_arch: x86_64
use_zigbuild: false
archive: tar.gz
- target: aarch64-apple-darwin
runner: macos-15
platform: macos
execution_arch: arm64
use_zigbuild: false
archive: tar.gz
- target: x86_64-pc-windows-msvc
runner: windows-2025
platform: windows
use_zigbuild: false
archive: zip
- target: aarch64-pc-windows-msvc
runner: windows-11-arm
platform: windows
use_zigbuild: false
archive: zip
steps:
- name: Checkout release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.release-please.outputs.tag_name }}
persist-credentials: false
- name: Require the tag to resolve to the release commit
shell: bash
env:
RELEASE_SHA: ${{ needs.release-please.outputs.release_sha }}
run: |
set -euo pipefail
if [ "$(git rev-parse HEAD)" != "$RELEASE_SHA" ]; then
echo "::error title=Release build::checked-out tag differs from the release commit"
exit 1
fi
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # master 2026-08-21
with:
toolchain: 1.98.0
targets: ${{ matrix.target }}
- name: Verify workspace version surfaces before Cargo/cache
shell: bash
run: bash scripts/check-workspace-versions.sh
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
shared-key: cargo-home-${{ runner.os }}-${{ runner.arch }}
key: ${{ matrix.target }}
cache-targets: false
cache-workspace-crates: false
- name: Configure compiler cache
uses: mozilla-actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
with:
version: "v0.16.0"
- name: Install Zig
if: matrix.use_zigbuild
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
with:
version: 0.15.1
- name: Install cargo-zigbuild
if: matrix.use_zigbuild
uses: taiki-e/install-action@a2a5f6e99e1a31540baa0468acfa302cff0f359f # v2.86.4
with:
tool: cargo-zigbuild@0.23.0
- name: Populate the Cargo registry
run: cargo fetch --locked
- name: Compute the archive name once
id: names
shell: bash
env:
ARCHIVE_KIND: ${{ matrix.archive }}
TARGET: ${{ matrix.target }}
VERSION: ${{ needs.release-please.outputs.version }}
run: |
set -euo pipefail
echo "archive=${BINARY_NAME}-${VERSION}-${TARGET}.${ARCHIVE_KIND}" >>"$GITHUB_OUTPUT"
- name: Build with cargo-zigbuild
if: matrix.use_zigbuild
shell: bash
env:
TARGET: ${{ matrix.target }}
run: cargo zigbuild --locked --release --timings -p "$CRATE_NAME" --target "$TARGET"
- name: Build natively
if: ${{ !matrix.use_zigbuild }}
shell: bash
env:
TARGET: ${{ matrix.target }}
run: cargo build --locked --release --timings -p "$CRATE_NAME" --target "$TARGET"
- name: Package and unpack tar.gz
if: matrix.archive == 'tar.gz'
shell: bash
env:
ARCHIVE: ${{ steps.names.outputs.archive }}
TARGET: ${{ matrix.target }}
run: |
set -euo pipefail
mkdir -p dist unpacked
tar -czf "dist/${ARCHIVE}" -C "target/${TARGET}/release" "$BINARY_NAME"
tar -xzf "dist/${ARCHIVE}" -C unpacked
chmod +x "unpacked/$BINARY_NAME"
- name: Package and unpack zip
if: matrix.archive == 'zip'
shell: pwsh
env:
ARCHIVE: ${{ steps.names.outputs.archive }}
TARGET: ${{ matrix.target }}
run: |
New-Item -ItemType Directory -Force -Path dist,unpacked | Out-Null
Compress-Archive `
-Path "target/${env:TARGET}/release/${env:BINARY_NAME}.exe" `
-DestinationPath "dist/${env:ARCHIVE}"
Expand-Archive -Path "dist/${env:ARCHIVE}" -DestinationPath unpacked -Force
- name: Smoke packaged artifact on Linux
if: matrix.platform == 'linux'
shell: bash
run: '"unpacked/$BINARY_NAME" --version'
- name: Smoke packaged artifact on macOS
if: matrix.platform == 'macos'
shell: bash
env:
EXECUTION_ARCH: ${{ matrix.execution_arch }}
run: |
set -euo pipefail
/usr/bin/lipo "unpacked/$BINARY_NAME" -verify_arch "$EXECUTION_ARCH"
/usr/bin/arch "-${EXECUTION_ARCH}" "unpacked/$BINARY_NAME" --version
- name: Smoke packaged artifact on Windows
if: matrix.platform == 'windows'
shell: pwsh
run: '& "unpacked/${env:BINARY_NAME}.exe" --version'
- name: Upload smoked build artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist-${{ matrix.target }}
path: dist/*
if-no-files-found: error
retention-days: 1
- name: Collect compiler-cache statistics
if: always()
shell: bash
run: |
mkdir -p diagnostics
sccache --show-stats --stats-format=json >diagnostics/sccache-stats.json || true
- name: Upload build diagnostics
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: diagnostics-${{ matrix.target }}
path: |
target/cargo-timings/*.html
diagnostics/
if-no-files-found: warn
retention-days: 3
upload-assets:
name: Attach release assets
needs: [release-please, source-gate, verify-ci, verify, build-binaries]
if: needs.release-please.outputs.build_artifacts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 15
# Downloads, hashes, attests, and uploads; executes no project code, so it
# is the only job holding id-token: write.
permissions:
attestations: write
contents: write
id-token: write
steps:
- name: Checkout release tooling
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Download build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: dist-*
path: dist
merge-multiple: true
- name: Generate SHA256SUMS
shell: bash
working-directory: dist
run: |
set -euo pipefail
shopt -s nullglob
archives=(*.tar.gz *.zip)
((${#archives[@]} > 0)) || { echo "::error::no release archives found"; exit 1; }
printf '%s\n' "${archives[@]}" | LC_ALL=C sort | xargs sha256sum >SHA256SUMS
- name: Attest release archives
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-checksums: dist/SHA256SUMS
- name: Attach assets to draft
shell: bash
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
set -euo pipefail
mapfile -t files < <(awk '{ print "dist/" $2 }' dist/SHA256SUMS)
gh release upload "$TAG" "${files[@]}" dist/SHA256SUMS \
--repo "$GITHUB_REPOSITORY" \
--clobber
- name: Verify remote asset bytes
shell: bash
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
bash .github/scripts/publish-release-draft.sh --verify-only \
--checksums dist/SHA256SUMS --asset dist/SHA256SUMS --exact
- name: Upload checksums for the publish job
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: checksums
path: dist/SHA256SUMS
if-no-files-found: error
retention-days: 1
publish-release:
name: Publish Release
needs: [release-please, upload-assets]
if: needs.release-please.outputs.build_artifacts == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 10
environment: release
permissions:
contents: write
steps:
- name: Checkout release tooling
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Download checksums
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: checksums
path: dist
- name: Verify remote assets and publish the draft
shell: bash
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
bash .github/scripts/publish-release-draft.sh --publish \
--checksums dist/SHA256SUMS --asset dist/SHA256SUMS --exact