Skip to content

Bump version

Bump version #9

Workflow file for this run

name: Release
on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
dry_run:
description: "Dry run (build artifacts but don't create a release)"
type: boolean
default: true
permissions:
contents: write
packages: write
env:
CARGO_TERM_COLOR: always
jobs:
# Build frontend assets once, share with all build jobs
build-frontend:
name: Build Frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Install UI dependencies
working-directory: ui
run: pnpm install --frozen-lockfile
- name: Generate API client
working-directory: ui
run: pnpm run generate-api
- name: Build UI
working-directory: ui
run: pnpm build
- name: Build Storybook
working-directory: ui
run: pnpm storybook:build
- name: Install docs dependencies
working-directory: docs
run: pnpm install --frozen-lockfile
- name: Build docs
working-directory: docs
run: pnpm build
- name: Upload frontend assets
uses: actions/upload-artifact@v4
with:
name: frontend-assets
path: |
ui/dist/
docs/out/
retention-days: 1
# Build release binaries for each target (all feature profiles per target)
build:
name: Build (${{ matrix.target }})
needs: build-frontend
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
features: "full headless standard minimal tiny"
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
features: "standard minimal tiny"
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
features: "standard minimal tiny"
- target: aarch64-apple-darwin
os: macos-latest
features: "full headless standard minimal tiny"
- target: x86_64-pc-windows-msvc
os: windows-latest
features: "standard minimal tiny"
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install dependencies (Linux full/standard)
if: runner.os == 'Linux' && (contains(matrix.features, 'full') || contains(matrix.features, 'standard'))
run: sudo apt-get update && sudo apt-get install -y libxml2-dev libxslt1-dev libxmlsec1-dev libclang-dev libssl-dev pkg-config
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install libxml2 libxslt libxmlsec1 llvm
- name: Install musl toolchain
if: matrix.target == 'x86_64-unknown-linux-musl'
run: sudo apt-get update && sudo apt-get install -y musl-tools
- name: Install aarch64 cross toolchain
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
shared-key: release-${{ matrix.target }}
- name: Download frontend assets
uses: actions/download-artifact@v4
with:
name: frontend-assets
- name: Fetch model catalog
shell: bash
run: mkdir -p data && curl -sSL https://models.dev/api.json -o data/models-dev-catalog.json
- name: Build and package (Unix)
if: runner.os != 'Windows'
shell: bash
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
run: |
TARGET="${{ matrix.target }}"
mkdir -p staging artifacts
for feature in ${{ matrix.features }}; do
echo "::group::Building $feature for $TARGET"
cargo build --release --target "$TARGET" --no-default-features --features "$feature"
echo "::endgroup::"
cp "target/$TARGET/release/hadrian" staging/hadrian
ARCHIVE="hadrian-${TARGET}-${feature}.tar.gz"
tar czf "artifacts/$ARCHIVE" -C staging hadrian
rm staging/hadrian
(cd artifacts && sha256sum "$ARCHIVE") >> "artifacts/checksums.txt"
echo "Built $ARCHIVE"
done
- name: Build and package (Windows)
if: runner.os == 'Windows'
shell: bash
env:
TARGET: ${{ matrix.target }}
run: |
mkdir -p staging artifacts
for feature in ${{ matrix.features }}; do
echo "::group::Building $feature for $TARGET"
cargo build --release --target "$TARGET" --no-default-features --features "$feature"
echo "::endgroup::"
cp "target/$TARGET/release/hadrian.exe" staging/hadrian.exe
ARCHIVE="hadrian-${TARGET}-${feature}.zip"
cd staging && 7z a -tzip "../artifacts/$ARCHIVE" hadrian.exe && cd ..
rm staging/hadrian.exe
(cd artifacts && sha256sum "$ARCHIVE") >> "artifacts/checksums.txt"
echo "Built $ARCHIVE"
done
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.target }}
path: artifacts/
retention-days: 3
# Create GitHub Release (only on tag push, not dry-run)
release:
name: Create Release
needs: build
if: github.ref_type == 'tag' && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download all build artifacts
uses: actions/download-artifact@v4
with:
pattern: build-*
path: all-artifacts
merge-multiple: false
- name: Collect release assets
run: |
mkdir -p release
# Move all archives to release dir and merge checksums
for dir in all-artifacts/build-*/; do
if [ -d "$dir" ]; then
mv "$dir"/*.tar.gz release/ 2>/dev/null || true
mv "$dir"/*.zip release/ 2>/dev/null || true
cat "$dir/checksums.txt" >> release/hadrian-checksums-sha256.txt 2>/dev/null || true
fi
done
echo "Release assets:"
ls -lh release/
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: release/*
# Publish to crates.io (only on tag push, not dry-run)
publish-crate:
name: Publish to crates.io
if: github.ref_type == 'tag' && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
shared-key: publish
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y libxml2-dev libxslt1-dev libxmlsec1-dev libclang-dev tesseract-ocr tesseract-ocr-eng
- name: Publish to crates.io
run: cargo publish --no-default-features --features headless --allow-dirty
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
# Build per-platform Docker images on native runners (no QEMU emulation)
docker:
name: Docker (${{ matrix.suffix }})
if: github.ref_type == 'tag' && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
suffix: amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
suffix: arm64
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ matrix.suffix }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.suffix }}
cache-to: type=gha,mode=max,scope=${{ matrix.suffix }}
# Combine per-platform images into a multi-arch manifest
docker-manifest:
name: Docker Manifest
needs: docker
if: github.ref_type == 'tag' && !(github.event_name == 'workflow_dispatch' && inputs.dry_run)
runs-on: ubuntu-latest
steps:
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
type=raw,value=latest
- name: Create and push multi-arch manifests
env:
TAGS: ${{ steps.meta.outputs.tags }}
run: |
IMAGE="ghcr.io/${{ github.repository }}"
TAG="${GITHUB_REF_NAME}"
while IFS= read -r tag; do
[ -z "$tag" ] && continue
docker buildx imagetools create -t "$tag" \
"${IMAGE}:${TAG}-amd64" \
"${IMAGE}:${TAG}-arm64"
done <<< "$TAGS"
# Build Docker images without pushing (dry-run validation)
docker-dry-run:
name: Docker Dry Run (${{ matrix.suffix }})
if: github.event_name == 'workflow_dispatch' && inputs.dry_run
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
suffix: amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
suffix: arm64
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Build (no push)
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
push: false
cache-from: type=gha,scope=${{ matrix.suffix }}
cache-to: type=gha,mode=max,scope=${{ matrix.suffix }}
# Dry-run summary (manual dispatch with dry_run=true)
dry-run-summary:
name: Dry Run Summary
needs: [build, docker-dry-run]
if: github.event_name == 'workflow_dispatch' && inputs.dry_run
runs-on: ubuntu-latest
steps:
- name: Download all build artifacts
uses: actions/download-artifact@v4
with:
pattern: build-*
path: all-artifacts
merge-multiple: false
- name: Print summary
run: |
echo "## Dry Run Results" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Artifact | Size | SHA256 |" >> "$GITHUB_STEP_SUMMARY"
echo "|----------|------|--------|" >> "$GITHUB_STEP_SUMMARY"
for dir in all-artifacts/build-*/; do
if [ -f "$dir/checksums.txt" ]; then
while read -r line; do
hash="${line%% *}"
name="${line##* }"
size=$(stat --printf="%s" "$dir/$name" 2>/dev/null || echo "?")
if [ "$size" != "?" ]; then
human_size=$(numfmt --to=iec-i --suffix=B "$size")
else
human_size="?"
fi
echo "| \`$name\` | $human_size | \`${hash:0:16}...\` |" >> "$GITHUB_STEP_SUMMARY"
done < "$dir/checksums.txt"
fi
done
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "No release was created (dry run mode)." >> "$GITHUB_STEP_SUMMARY"