Bump version #9
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |