deslicer-cli 0.1.0

Deslicer CLI — vendor-neutral CI client for planning, approving, and shipping Splunk changes via DAP.
Documentation
# Release pipeline for deslicer-cli.
# Triggered by semver tags (v*.*.*). Builds five platform targets, signs
# artifacts with cosign keyless (Sigstore OIDC), attaches SLSA provenance,
# publishes a GitHub Release, and moves the floating v1 tag.

name: Release

on:
  push:
    tags: ['v*.*.*']

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  BINARY_NAME: deslicer

jobs:
  build:
    name: Build (${{ matrix.target }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-musl
            os: ubuntu-latest
            archive: tar.gz
            cross: false
          - target: aarch64-unknown-linux-musl
            os: ubuntu-latest
            archive: tar.gz
            cross: true
          - target: x86_64-apple-darwin
            os: macos-latest
            archive: tar.gz
            cross: false
          - target: aarch64-apple-darwin
            os: macos-latest
            archive: tar.gz
            cross: false
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            archive: zip
            cross: false
    steps:
      - name: Checkout
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable

      - name: Cache cargo
        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
        with:
          key: release-${{ matrix.target }}

      - name: Install Linux musl dependencies
        if: matrix.os == 'ubuntu-latest' && !matrix.cross
        run: |
          sudo apt-get update
          sudo apt-get install -y musl-tools

      - name: Install cross (aarch64 Linux musl)
        if: matrix.cross
        run: cargo install cross --locked --version 0.2.5

      - name: Add Rust target
        run: rustup target add ${{ matrix.target }}

      - name: Build release binary
        shell: bash
        run: |
          set -euo pipefail
          if [ "${{ matrix.cross }}" = "true" ]; then
            cross build --release --target "${{ matrix.target }}"
          else
            cargo build --release --target "${{ matrix.target }}"
          fi

      - name: Package archive (Unix)
        if: matrix.archive == 'tar.gz'
        shell: bash
        run: |
          set -euo pipefail
          TARGET="${{ matrix.target }}"
          ARTIFACT="deslicer-${TARGET}.tar.gz"
          BIN="target/${TARGET}/release/${{ env.BINARY_NAME }}"
          install -d "dist/${TARGET}"
          cp "${BIN}" "dist/${TARGET}/${{ env.BINARY_NAME }}"
          tar -C "dist/${TARGET}" -czf "${ARTIFACT}" "${{ env.BINARY_NAME }}"
          sha256sum "${ARTIFACT}" | awk '{print $1}' > "${ARTIFACT}.sha256"
          echo "artifact=${ARTIFACT}" >> "$GITHUB_ENV"

      - name: Package archive (Windows)
        if: matrix.archive == 'zip'
        shell: pwsh
        run: |
          $target = "${{ matrix.target }}"
          $artifact = "deslicer-$target.zip"
          $bin = "target/$target/release/${{ env.BINARY_NAME }}.exe"
          New-Item -ItemType Directory -Force -Path "dist/$target" | Out-Null
          Copy-Item $bin "dist/$target/${{ env.BINARY_NAME }}.exe"
          Compress-Archive -Path "dist/$target/${{ env.BINARY_NAME }}.exe" -DestinationPath $artifact -Force
          $hash = (Get-FileHash $artifact -Algorithm SHA256).Hash.ToLower()
          Set-Content -NoNewline -Path "$artifact.sha256" -Value $hash
          "artifact=$artifact" | Out-File -FilePath $env:GITHUB_ENV -Append

      - name: Upload build artifact
        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
        with:
          name: deslicer-${{ matrix.target }}
          path: |
            deslicer-${{ matrix.target }}.*
          if-no-files-found: error

  publish:
    name: Sign, provenance, and release
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: write
      id-token: write
      actions: read
    steps:
      - name: Checkout
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
        with:
          fetch-depth: 0

      - name: Download all build artifacts
        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
        with:
          path: release-assets
          merge-multiple: true

      - name: Install cosign
        uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
        with:
          cosign-release: v2.6.3

      - name: Sign release artifacts (keyless)
        shell: bash
        run: |
          set -euo pipefail
          cd release-assets
          shopt -s nullglob
          for file in deslicer-*.{tar.gz,zip}; do
            echo "Signing ${file}"
            cosign sign-blob --yes "${file}" \
              --output-signature "${file}.sig" \
              --output-certificate "${file}.cert"
          done

      - name: Create GitHub Release
        uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v2.0.4
        with:
          tag_name: ${{ github.ref_name }}
          name: ${{ github.ref_name }}
          generate_release_notes: true
          fail_on_unmatched_files: false
          files: |
            release-assets/deslicer-*.tar.gz
            release-assets/deslicer-*.zip
            release-assets/deslicer-*.sha256
            release-assets/deslicer-*.sig
            release-assets/deslicer-*.cert
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Move floating v1 tag
        shell: bash
        run: |
          set -euo pipefail
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git tag -f v1 "${GITHUB_SHA}"
          git push origin refs/tags/v1 --force

  provenance:
    name: SLSA provenance
    needs: build
    permissions:
      actions: read
      id-token: write
      contents: write
    # Reusable workflow pinned to slsa-github-generator v2.0.0.
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@5a775b367a56d5bd118a224a811bba288150a563 # v2.0.0
    with:
      base64-subjects: ""
      upload-assets: true