svault-ai 1.0.0

Secret access layer for cooperative AI agents — structured, policy-gated, audited credential access
Documentation
name: Release

on:
  push:
    tags: ["v*"]
  workflow_dispatch:
    inputs:
      tag:
        description: "Existing tag to build and attach binaries for (e.g. v0.2.1)"
        required: true

env:
  CARGO_TERM_COLOR: always

permissions:
  contents: write
  id-token: write # SLSA build-provenance attestation (#11)
  attestations: write

jobs:
  binaries:
    name: build ${{ matrix.target }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
          - os: macos-latest
            target: aarch64-apple-darwin
          - os: macos-latest
            target: x86_64-apple-darwin
          - os: windows-latest
            target: x86_64-pc-windows-msvc
    steps:
      - uses: actions/checkout@v5
        with:
          ref: ${{ github.event.inputs.tag || github.ref }}
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      # The official binaries ship the `yubikey` feature; on Linux its hidapi dep
      # needs libudev at build time (libudev.so.1 is present at runtime on any
      # systemd-based distro).
      - name: Install libudev (Linux, for the yubikey feature)
        if: runner.os == 'Linux'
        run: sudo apt-get update && sudo apt-get install -y libudev-dev pkg-config
      - name: Build release binary
        run: cargo build --release --locked --features yubikey --target ${{ matrix.target }}
      - name: Package tarball (unix)
        if: runner.os != 'Windows'
        shell: bash
        env:
          TAG: ${{ github.event.inputs.tag || github.ref_name }}
          TARGET: ${{ matrix.target }}
        run: |
          archive="svault-${TAG}-${TARGET}.tar.gz"
          tar -czf "$archive" -C "target/${TARGET}/release" svault
          # SHA-256 checksum so users can verify the download (finding #11).
          if command -v sha256sum >/dev/null 2>&1; then
            sha256sum "$archive" > "$archive.sha256"
          else
            shasum -a 256 "$archive" > "$archive.sha256"
          fi
          echo "ARCHIVE=$archive" >> "$GITHUB_ENV"
          echo "CHECKSUM=$archive.sha256" >> "$GITHUB_ENV"
      - name: Package zip (windows)
        if: runner.os == 'Windows'
        shell: pwsh
        env:
          TAG: ${{ github.event.inputs.tag || github.ref_name }}
          TARGET: ${{ matrix.target }}
        run: |
          $archive = "svault-$env:TAG-$env:TARGET.zip"
          Compress-Archive -Path "target/$env:TARGET/release/svault.exe" -DestinationPath $archive
          # SHA-256 checksum so users can verify the download (finding #11).
          $hash = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLower()
          "$hash  $archive" | Out-File -Encoding ascii "$archive.sha256"
          "ARCHIVE=$archive" >> $env:GITHUB_ENV
          "CHECKSUM=$archive.sha256" >> $env:GITHUB_ENV
      - name: Attest build provenance
        uses: actions/attest-build-provenance@v2
        with:
          subject-path: ${{ env.ARCHIVE }}
      - name: Attach to release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ github.event.inputs.tag || github.ref_name }}
          files: |
            ${{ env.ARCHIVE }}
            ${{ env.CHECKSUM }}

  publish:
    name: publish to crates.io
    needs: binaries
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: dtolnay/rust-toolchain@stable
      - name: cargo publish
        env:
          # Configure in Settings -> Secrets and variables -> Actions.
          # Without the secret the step is a no-op so the release still succeeds.
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          if [ -z "$CARGO_REGISTRY_TOKEN" ]; then
            echo "CARGO_REGISTRY_TOKEN not set — skipping crates.io publish."
            exit 0
          fi
          # Idempotent: if this version is already on crates.io (e.g. published
          # manually), skip instead of failing the release run.
          version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
          status=$(curl -s -o /dev/null -w '%{http_code}' \
            -H 'User-Agent: svault-release (https://github.com/nim444/Svault)' \
            "https://crates.io/api/v1/crates/svault-ai/$version")
          if [ "$status" = "200" ]; then
            echo "svault-ai $version is already on crates.io — skipping publish."
            exit 0
          fi
          cargo publish --locked