slotstrike 1.0.0

Low-latency Solana slotstrike runtime for event-driven token execution
Documentation
name: Release

on:
  push:
    branches:
      - main

concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false

permissions:
  contents: write

jobs:
  resolve-tag:
    name: Resolve Release Tag
    runs-on: ubuntu-latest
    outputs:
      release_tag: ${{ steps.resolve.outputs.release_tag }}
      clean_version: ${{ steps.resolve.outputs.clean_version }}
      base_version: ${{ steps.resolve.outputs.base_version }}
      is_prerelease: ${{ steps.resolve.outputs.is_prerelease }}
      tag_exists: ${{ steps.resolve.outputs.tag_exists }}
      release_exists: ${{ steps.resolve.outputs.release_exists }}
      release_draft: ${{ steps.resolve.outputs.release_draft }}
      should_release: ${{ steps.resolve.outputs.should_release }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - id: resolve
        shell: bash
        env:
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          CLEAN_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n 1)"
          if [[ -z "${CLEAN_VERSION}" ]]; then
            echo "Failed to read Cargo.toml version."
            exit 1
          fi

          BASE_VERSION="${CLEAN_VERSION%%-*}"
          if [[ "${CLEAN_VERSION}" == *"-"* ]]; then
            IS_PRERELEASE="true"
          else
            IS_PRERELEASE="false"
          fi

          TAG="v${CLEAN_VERSION}"
          if git rev-parse --verify "refs/tags/${TAG}" >/dev/null 2>&1; then
            TAG_EXISTS="true"
          else
            TAG_EXISTS="false"
          fi

          RELEASE_EXISTS="false"
          RELEASE_DRAFT="false"
          RELEASE_URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${TAG}"
          RELEASE_JSON="$(mktemp)"
          if curl --fail --silent \
            --header "Authorization: Bearer ${GITHUB_TOKEN}" \
            --header "Accept: application/vnd.github+json" \
            "${RELEASE_URL}" > "${RELEASE_JSON}"; then
            RELEASE_EXISTS="true"
            if grep -q '"draft":[[:space:]]*true' "${RELEASE_JSON}"; then
              RELEASE_DRAFT="true"
            fi
          fi
          rm -f "${RELEASE_JSON}"

          if [[ "${RELEASE_EXISTS}" == "true" && "${RELEASE_DRAFT}" != "true" ]]; then
            SHOULD_RELEASE="false"
          else
            SHOULD_RELEASE="true"
          fi

          echo "release_tag=${TAG}" >> "${GITHUB_OUTPUT}"
          echo "clean_version=${CLEAN_VERSION}" >> "${GITHUB_OUTPUT}"
          echo "base_version=${BASE_VERSION}" >> "${GITHUB_OUTPUT}"
          echo "is_prerelease=${IS_PRERELEASE}" >> "${GITHUB_OUTPUT}"
          echo "tag_exists=${TAG_EXISTS}" >> "${GITHUB_OUTPUT}"
          echo "release_exists=${RELEASE_EXISTS}" >> "${GITHUB_OUTPUT}"
          echo "release_draft=${RELEASE_DRAFT}" >> "${GITHUB_OUTPUT}"
          echo "should_release=${SHOULD_RELEASE}" >> "${GITHUB_OUTPUT}"

          {
            echo "Detected version: ${CLEAN_VERSION}"
            echo "Resolved release tag: ${TAG}"
            if [[ "${SHOULD_RELEASE}" == "false" ]]; then
              echo "Published release already exists for ${TAG}. Skipping release pipeline."
            elif [[ "${RELEASE_EXISTS}" == "true" && "${RELEASE_DRAFT}" == "true" ]]; then
              echo "Draft release already exists for ${TAG}. Continuing release pipeline."
            elif [[ "${TAG_EXISTS}" == "true" ]]; then
              echo "Tag exists but GitHub release is missing. Continuing release pipeline."
            fi
          } >> "${GITHUB_STEP_SUMMARY}"

  prepare-release:
    name: Prepare GitHub Release
    needs: [resolve-tag, validate]
    if: needs.resolve-tag.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Ensure Draft Release Exists
        uses: ncipollo/release-action@v1
        with:
          tag: ${{ needs.resolve-tag.outputs.release_tag }}
          commit: ${{ github.sha }}
          name: Release ${{ needs.resolve-tag.outputs.release_tag }}
          draft: true
          allowUpdates: true
          prerelease: ${{ needs.resolve-tag.outputs.is_prerelease == 'true' }}

  validate:
    name: Validate Release
    needs: resolve-tag
    if: needs.resolve-tag.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    env:
      CARGO_NET_OFFLINE: "false"
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.sha }}

      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt

      - uses: Swatinem/rust-cache@v2

      - name: Install cargo-make
        uses: davidB/rust-cargo-make@v1

      - name: Install cargo-nextest
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-nextest

      - name: Install cargo-audit
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-audit

      - name: Install cargo-deny
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-deny

      - name: Verify Changelog Section (if CHANGELOG.md exists)
        shell: bash
        run: |
          BASE_VERSION="${{ needs.resolve-tag.outputs.base_version }}"
          if [[ -f CHANGELOG.md ]]; then
            if ! grep -qE "^##[[:space:]]+${BASE_VERSION}$" CHANGELOG.md; then
              echo "Missing CHANGELOG.md section for ${BASE_VERSION}."
              exit 1
            fi
          else
            echo "CHANGELOG.md not found. Skipping changelog section validation."
          fi

      - name: Release Info
        shell: bash
        run: |
          TAG="${{ needs.resolve-tag.outputs.release_tag }}"
          IS_PRERELEASE="${{ needs.resolve-tag.outputs.is_prerelease }}"

          if [[ "${IS_PRERELEASE}" == "true" ]]; then
            {
              echo "Detected pre-release tag: ${TAG}"
              echo "Crates.io publish is skipped for prerelease versions."
            } >> "${GITHUB_STEP_SUMMARY}"
          else
            {
              echo "Detected release tag: ${TAG}"
              echo "Crates.io publish is enabled when CARGO_REGISTRY_TOKEN is available."
            } >> "${GITHUB_STEP_SUMMARY}"
          fi

      - name: Audit Dependencies
        run: cargo make audit

      - name: Deny Check
        run: cargo make deny

      - name: Run Tests
        run: cargo make test

      - name: Lint
        run: cargo make clippy

      - name: Format Check
        run: cargo make format-check

      - name: Verify Publish Readiness
        run: cargo publish --dry-run --locked

  publish-crates:
    name: Publish to Crates.io
    needs: [resolve-tag, validate, prepare-release]
    if: needs.resolve-tag.outputs.should_release == 'true' && needs.resolve-tag.outputs.is_prerelease == 'false'
    runs-on: ubuntu-latest
    env:
      CARGO_NET_OFFLINE: "false"
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.sha }}

      - uses: dtolnay/rust-toolchain@stable

      - uses: Swatinem/rust-cache@v2

      - name: Check CARGO_REGISTRY_TOKEN
        id: token
        shell: bash
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          if [[ -z "${CARGO_REGISTRY_TOKEN}" ]]; then
            echo "present=false" >> "${GITHUB_OUTPUT}"
            echo "CARGO_REGISTRY_TOKEN is not configured. Skipping crates.io publish." >> "${GITHUB_STEP_SUMMARY}"
          else
            echo "present=true" >> "${GITHUB_OUTPUT}"
          fi

      - name: Check Crates.io Version
        id: crate
        shell: bash
        run: |
          VERSION="${{ needs.resolve-tag.outputs.clean_version }}"
          CRATE="slotstrike"
          META_URL="https://crates.io/api/v1/crates/${CRATE}"

          if curl --fail --silent "${META_URL}" | grep -q "\"num\":\"${VERSION}\""; then
            echo "exists=true" >> "${GITHUB_OUTPUT}"
            echo "${CRATE} ${VERSION} already exists on crates.io. Skipping publish." >> "${GITHUB_STEP_SUMMARY}"
          else
            echo "exists=false" >> "${GITHUB_OUTPUT}"
          fi

      - name: Publish to Cargo
        if: steps.token.outputs.present == 'true' && steps.crate.outputs.exists != 'true'
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: cargo publish --locked

  build-binaries:
    name: Build Binaries
    needs: [resolve-tag, validate, prepare-release]
    if: needs.resolve-tag.outputs.should_release == 'true'
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            suffix: linux-x86_64
            archive: tar.gz
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            suffix: windows-x86_64
            archive: zip
          - os: macos-15-intel
            target: x86_64-apple-darwin
            suffix: macos-x86_64
            archive: tar.gz
          - os: macos-14
            target: aarch64-apple-darwin
            suffix: macos-arm64
            archive: tar.gz
    runs-on: ${{ matrix.os }}
    env:
      CARGO_NET_OFFLINE: "false"
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.sha }}

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - uses: Swatinem/rust-cache@v2

      - name: Build Release
        run: cargo build --release --target ${{ matrix.target }} --locked

      - name: Package Assets
        shell: bash
        run: |
          TAG="${{ needs.resolve-tag.outputs.release_tag }}"
          BIN_NAME="slotstrike"
          if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
            BIN_NAME="slotstrike.exe"
          fi

          mkdir -p dist
          cp "target/${{ matrix.target }}/release/${BIN_NAME}" dist/
          cp README.md LICENSE slotstrike.example.toml dist/ 2>/dev/null || :

          cd dist
          if [[ "${{ matrix.archive }}" == "tar.gz" ]]; then
            tar -czf "../slotstrike-${TAG}-${{ matrix.suffix }}.tar.gz" *
          else
            7z a "../slotstrike-${TAG}-${{ matrix.suffix }}.zip" *
          fi

      - name: Upload Build Artifact
        uses: actions/upload-artifact@v4
        with:
          name: slotstrike-${{ matrix.suffix }}
          path: slotstrike-${{ needs.resolve-tag.outputs.release_tag }}-${{ matrix.suffix }}.${{ matrix.archive }}
          if-no-files-found: error

  release:
    name: Create GitHub Release
    needs: [resolve-tag, prepare-release, build-binaries, publish-crates]
    if: >
      always() &&
      needs.resolve-tag.outputs.should_release == 'true' &&
      needs.prepare-release.result == 'success' &&
      needs.build-binaries.result == 'success' &&
      (needs.publish-crates.result == 'success' || needs.publish-crates.result == 'skipped')
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.sha }}

      - name: Download Build Artifacts
        uses: actions/download-artifact@v4
        with:
          path: release-artifacts
          merge-multiple: true

      - name: Build Release Notes
        id: notes
        shell: bash
        run: |
          TAG="${{ needs.resolve-tag.outputs.release_tag }}"
          BASE_VERSION="${{ needs.resolve-tag.outputs.base_version }}"
          NOTES_FILE="${RUNNER_TEMP}/release-notes.md"

          {
            echo "### What's New"
            echo
            if [[ -f CHANGELOG.md ]] && grep -qE "^##[[:space:]]+${BASE_VERSION}$" CHANGELOG.md; then
              awk -v version="${BASE_VERSION}" '
                $0 ~ "^##[[:space:]]+" version "$" { in_section=1; next }
                in_section && $0 ~ "^##[[:space:]]+" { exit }
                in_section { print }
              ' CHANGELOG.md
            else
              echo "- Automated release for ${TAG}."
              echo "- See commit history for details."
            fi
            echo
            echo "### Installation"
            echo '```bash'
            echo 'cargo install slotstrike'
            echo '```'
          } > "${NOTES_FILE}"

          echo "path=${NOTES_FILE}" >> "${GITHUB_OUTPUT}"

      - name: Create GitHub Release
        uses: ncipollo/release-action@v1
        with:
          tag: ${{ needs.resolve-tag.outputs.release_tag }}
          commit: ${{ github.sha }}
          name: Release ${{ needs.resolve-tag.outputs.release_tag }}
          bodyFile: ${{ steps.notes.outputs.path }}
          artifacts: release-artifacts/*
          allowUpdates: true
          draft: false
          prerelease: ${{ needs.resolve-tag.outputs.is_prerelease == 'true' }}
          makeLatest: ${{ needs.resolve-tag.outputs.is_prerelease == 'false' }}