name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: read
jobs:
build:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
archive: tar.gz
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
archive: tar.gz
cross: true
- target: x86_64-apple-darwin
os: macos-latest
archive: tar.gz
- target: aarch64-apple-darwin
os: macos-latest
archive: tar.gz
- target: x86_64-pc-windows-msvc
os: windows-latest
archive: zip
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross (for cross-compilation)
if: matrix.cross
run: cargo install cross --locked
- name: Build
run: |
if [ "${{ matrix.cross }}" = "true" ]; then
cross build --release --target ${{ matrix.target }}
else
cargo build --release --target ${{ matrix.target }}
fi
shell: bash
- name: Package (Unix)
if: matrix.archive == 'tar.gz'
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../rust-doctor-${{ matrix.target }}.tar.gz rust-doctor
shell: bash
- name: Package (Windows)
if: matrix.archive == 'zip'
run: |
cd target/${{ matrix.target }}/release
7z a ../../../rust-doctor-${{ matrix.target }}.zip rust-doctor.exe
shell: bash
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: rust-doctor-${{ matrix.target }}
path: rust-doctor-${{ matrix.target }}.*
release:
name: Create Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Generate install script
run: |
cat > artifacts/install.sh << 'INSTALLER'
#!/bin/bash
set -euo pipefail
REPO="ArthurDEV44/rust-doctor"
BINARY="rust-doctor"
# Detect platform
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "${OS}-${ARCH}" in
linux-x86_64) TARGET="x86_64-unknown-linux-gnu" ;;
linux-aarch64) TARGET="aarch64-unknown-linux-gnu" ;;
darwin-x86_64) TARGET="x86_64-apple-darwin" ;;
darwin-arm64) TARGET="aarch64-apple-darwin" ;;
*) echo "Unsupported platform: ${OS}-${ARCH}"; exit 1 ;;
esac
# Get latest release tag
TAG=$(curl -sL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | cut -d'"' -f4)
URL="https://github.com/${REPO}/releases/download/${TAG}/${BINARY}-${TARGET}.tar.gz"
echo "Installing ${BINARY} ${TAG} for ${TARGET}..."
curl -sL "${URL}" | tar xz -C /usr/local/bin/
echo "${BINARY} installed to /usr/local/bin/${BINARY}"
INSTALLER
chmod +x artifacts/install.sh
- name: Generate PowerShell install script
run: |
cat > artifacts/install.ps1 << 'INSTALLER'
$ErrorActionPreference = "Stop"
$repo = "ArthurDEV44/rust-doctor"
$binary = "rust-doctor"
$release = Invoke-RestMethod "https://api.github.com/repos/$repo/releases/latest"
$tag = $release.tag_name
$url = "https://github.com/$repo/releases/download/$tag/$binary-x86_64-pc-windows-msvc.zip"
$tmp = New-TemporaryFile | Rename-Item -NewName { $_.Name + ".zip" } -PassThru
Invoke-WebRequest $url -OutFile $tmp
$dest = "$env:USERPROFILE\.cargo\bin"
Expand-Archive $tmp -DestinationPath $dest -Force
Remove-Item $tmp
Write-Host "$binary installed to $dest\$binary.exe"
INSTALLER
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |
artifacts/*.tar.gz
artifacts/*.zip
artifacts/install.sh
artifacts/install.ps1
publish-npm:
name: Publish npm packages
needs: release
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- name: Upgrade npm for Trusted Publishing
run: npm install -g npm@latest
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Verify tag matches crate version
run: |
TAG="${GITHUB_REF_NAME#v}"
CRATE="$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"(.*)".*/\1/')"
if [ "$TAG" != "$CRATE" ]; then
echo "::error::Tag v$TAG does not match crate version $CRATE in Cargo.toml"
exit 1
fi
echo "Tag matches crate version: $CRATE"
- name: Publish npm packages (Trusted Publishing + provenance)
run: bash npm/publish.sh "${{ steps.version.outputs.version }}" artifacts
publish-crate:
name: Publish to crates.io
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Check if version already published
id: check
run: |
VERSION=$(cargo metadata --no-deps --format-version=1 | jq -r '.packages[0].version')
if cargo search rust-doctor --limit 1 | grep -q "rust-doctor = \"${VERSION}\""; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "v${VERSION} already on crates.io — skipping publish"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Publish to crates.io
if: steps.check.outputs.skip != 'true'
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish