nsip 0.4.0

NSIP Search API client for nsipsearch.nsip.org/api
Documentation
---
name: Release

"on":
  push:
    tags:
      - "v*.*.*"
  workflow_dispatch:

permissions:
  contents: write
  id-token: write
  attestations: write

env:
  CARGO_TERM_COLOR: always

jobs:
  create-release:
    name: Create Release
    runs-on: ubuntu-latest
    timeout-minutes: 15
    environment: copilot
    outputs:
      upload_url: ${{ steps.create_release.outputs.upload_url }}
      version: ${{ steps.get_version.outputs.version }}
    steps:
      - name: Checkout repository
        # yamllint disable-line rule:line-length
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
        with:
          fetch-depth: 0

      - name: Get version from tag
        id: get_version
        # yamllint disable-line rule:line-length
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Generate changelog
        id: changelog
        # yamllint disable-line rule:line-length
        uses: orhun/git-cliff-action@c93ef52f3d0ddcdcc9bd5447d98d458a11cd4f72  # v4.5.0
        with:
          config: cliff.toml
          args: --latest --strip header

      - name: Create GitHub Release
        id: create_release
        # yamllint disable-line rule:line-length
        uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b  # v2.0.3
        with:
          tag_name: ${{ github.ref }}
          name: Release ${{ steps.get_version.outputs.version }}
          body: ${{ steps.changelog.outputs.content }}
          draft: false
          # yamllint disable-line rule:line-length
          prerelease: ${{ contains(steps.get_version.outputs.version, '-alpha') || contains(steps.get_version.outputs.version, '-beta') || contains(steps.get_version.outputs.version, '-rc') }}
        env:
          # Use PAT so release event propagates to
          # downstream workflows (homebrew, sbom)
          GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}

  generate-extras:
    name: Generate completions & man pages
    needs: create-release
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - name: Checkout repository
        # yamllint disable-line rule:line-length
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2

      - name: Setup Rust with caching
        uses: ./.github/actions/setup-rust-cached
        with:
          toolchain: stable
          cache-key: release

      - name: Build release binary
        run: cargo build --release

      - name: Generate shell completions
        run: |
          mkdir -p completions
          ./target/release/nsip completions bash \
            > completions/nsip.bash
          ./target/release/nsip completions zsh \
            > completions/_nsip
          ./target/release/nsip completions fish \
            > completions/nsip.fish
          ./target/release/nsip completions powershell \
            > completions/_nsip.ps1

      - name: Generate man pages
        run: |
          mkdir -p man
          ./target/release/nsip man-pages --out-dir man

      - name: Package completions
        run: >-
          tar czf nsip-completions.tar.gz
          -C completions .

      - name: Package man pages
        run: >-
          tar czf nsip-man-pages.tar.gz -C man .

      - name: Attest completions archive
        id: attest_completions
        # yamllint disable-line rule:line-length
        uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32  # v4.1.0
        with:
          subject-path: nsip-completions.tar.gz

      - name: Attest man pages archive
        id: attest_man
        # yamllint disable-line rule:line-length
        uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32  # v4.1.0
        with:
          subject-path: nsip-man-pages.tar.gz

      - name: Rename attestation bundles
        env:
          COMP_BUNDLE: ${{ steps.attest_completions.outputs.bundle-path }}
          MAN_BUNDLE: ${{ steps.attest_man.outputs.bundle-path }}
        run: |
          cp "$COMP_BUNDLE" \
            nsip-completions.tar.gz.sigstore.json
          cp "$MAN_BUNDLE" \
            nsip-man-pages.tar.gz.sigstore.json

      - name: Upload to release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload "${{ github.ref_name }}" \
            nsip-completions.tar.gz \
            nsip-man-pages.tar.gz \
            nsip-completions.tar.gz.sigstore.json \
            nsip-man-pages.tar.gz.sigstore.json \
            --clobber

  generate-sbom:
    name: Generate SBOM
    needs: create-release
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout repository
        # yamllint disable-line rule:line-length
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2

      - name: Setup Rust with caching
        uses: ./.github/actions/setup-rust-cached
        with:
          toolchain: stable
          cache-key: sbom

      - name: Install cargo-sbom
        # yamllint disable-line rule:line-length
        uses: ./.github/actions/install-cargo-tool
        with:
          tool: cargo-sbom

      - name: Generate SBOM (SPDX format)
        run: >-
          cargo sbom --output-format spdx_json_2_3
          > nsip-sbom-spdx.json

      - name: Attest SBOM provenance
        id: attest_sbom
        # yamllint disable-line rule:line-length
        uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32  # v4.1.0
        with:
          subject-path: nsip-sbom-spdx.json

      - name: Rename attestation bundle
        env:
          SBOM_BUNDLE: ${{ steps.attest_sbom.outputs.bundle-path }}
        run: >-
          cp "$SBOM_BUNDLE"
          nsip-sbom-spdx.json.sigstore.json

      - name: Upload SBOM to release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload "${{ github.ref_name }}" \
            nsip-sbom-spdx.json \
            nsip-sbom-spdx.json.sigstore.json \
            --clobber

      - name: Upload SBOM artifact
        # yamllint disable-line rule:line-length
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f  # v7.0.0
        with:
          name: nsip-sbom-spdx
          path: nsip-sbom-spdx.json
          retention-days: 90

  build-binaries:
    name: Build ${{ matrix.target }}
    needs: create-release
    runs-on: ${{ matrix.os }}
    timeout-minutes: 45
    strategy:
      fail-fast: false
      matrix:
        include:
          # Linux x86_64
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            artifact_name: nsip
            asset_name: nsip-linux-amd64
          # Linux aarch64
          - os: ubuntu-latest
            target: aarch64-unknown-linux-gnu
            artifact_name: nsip
            asset_name: nsip-linux-arm64
          # macOS x86_64
          - os: macos-latest
            target: x86_64-apple-darwin
            artifact_name: nsip
            asset_name: nsip-macos-amd64
          # macOS aarch64
          - os: macos-latest
            target: aarch64-apple-darwin
            artifact_name: nsip
            asset_name: nsip-macos-arm64
          # Windows x86_64
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            artifact_name: nsip.exe
            asset_name: nsip-windows-amd64.exe

    steps:
      - name: Checkout repository
        # yamllint disable-line rule:line-length
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2

      - name: Setup Rust with caching
        uses: ./.github/actions/setup-rust-cached
        with:
          toolchain: stable
          targets: ${{ matrix.target }}
          cache-key: release-${{ matrix.target }}

      - name: Install cross-compilation tools
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu

      - name: Build release binary
        run: >-
          cargo build --release
          --target ${{ matrix.target }}
        env:
          # yamllint disable-line rule:line-length
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc

      - name: Strip binary (Unix)
        if: runner.os != 'Windows'
        run: |
          if [ "${{ matrix.target }}" = \
               "aarch64-unknown-linux-gnu" ]; then
            aarch64-linux-gnu-strip \
              "target/${{ matrix.target }}/release/${{ matrix.artifact_name }}"
          else
            strip \
              "target/${{ matrix.target }}/release/${{ matrix.artifact_name }}"
          fi

      - name: Rename binary for release
        shell: bash
        run: |
          cp "target/${{ matrix.target }}/release/${{ matrix.artifact_name }}" \
             "${{ matrix.asset_name }}"

      - name: Attest binary provenance
        id: attest_binary
        # yamllint disable-line rule:line-length
        uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32  # v4.1.0
        with:
          subject-path: ${{ matrix.asset_name }}

      - name: Rename attestation bundle
        shell: bash
        env:
          BUNDLE: ${{ steps.attest_binary.outputs.bundle-path }}
          ASSET: ${{ matrix.asset_name }}
        run: >-
          cp "$BUNDLE" "${ASSET}.sigstore.json"

      - name: Upload release asset
        shell: bash
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload "${{ github.ref_name }}" \
            "${{ matrix.asset_name }}" \
            "${{ matrix.asset_name }}.sigstore.json" \
            --clobber

      - name: Upload artifact
        # yamllint disable-line rule:line-length
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f  # v7.0.0
        with:
          name: ${{ matrix.asset_name }}
          path: ${{ matrix.asset_name }}

  package-mcpb:
    name: Package MCPB Bundle
    needs: [create-release, build-binaries]
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout repository
        # yamllint disable-line rule:line-length
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2

      - name: Setup Node.js
        # yamllint disable-line rule:line-length
        uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f  # v6.3.0
        with:
          node-version: "22"

      - name: Install mcpb CLI
        run: npm install -g @anthropic-ai/mcpb

      - name: Download platform binaries
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mkdir -p server
          gh release download "${{ github.ref_name }}" \
            --pattern "nsip-linux-amd64" \
            --pattern "nsip-linux-arm64" \
            --pattern "nsip-macos-amd64" \
            --pattern "nsip-macos-arm64" \
            --pattern "nsip-windows-amd64.exe" \
            --dir server

      - name: Set binary permissions
        run: chmod +x server/nsip-*

      - name: Inject version into manifest
        env:
          VERSION: >-
            ${{ needs.create-release.outputs.version }}
        run: >-
          sed -i
          "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/"
          manifest.json

      - name: Validate manifest
        run: mcpb validate .

      - name: Pack bundle
        run: mcpb pack . nsip.mcpb

      - name: Inspect bundle
        run: mcpb info nsip.mcpb

      - name: Attest bundle provenance
        id: attest_mcpb
        # yamllint disable-line rule:line-length
        uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32  # v4.1.0
        with:
          subject-path: nsip.mcpb

      - name: Rename attestation bundle
        env:
          MCPB_BUNDLE: >-
            ${{ steps.attest_mcpb.outputs.bundle-path }}
        run: >-
          cp "$MCPB_BUNDLE"
          nsip.mcpb.sigstore.json

      - name: Upload to release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload "${{ github.ref_name }}" \
            nsip.mcpb \
            nsip.mcpb.sigstore.json \
            --clobber