threatflux-cache 0.1.8

A flexible async cache library for Rust with pluggable backends and serialization
Documentation
name: Release

on:
  push:
    tags:
      - 'v*.*.*'
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to release (e.g., v1.0.0)'
        required: true
        type: string

env:
  CARGO_TERM_COLOR: always

permissions:
  contents: write
  packages: write

jobs:
  validate-tag:
    name: Validate Tag
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
      is_prerelease: ${{ steps.version.outputs.is_prerelease }}

    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Extract version from tag or input
      id: version
      run: |
        if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
          VERSION="${{ inputs.version }}"
        else
          VERSION=${GITHUB_REF#refs/tags/}
        fi
        
        # Remove 'v' prefix if present
        VERSION=${VERSION#v}
        
        # Validate version format
        if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
          echo "❌ Invalid version format: $VERSION"
          exit 1
        fi
        
        # Check if it's a prerelease
        if [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
          IS_PRERELEASE=false
        else
          IS_PRERELEASE=true
        fi
        
        echo "version=$VERSION" >> $GITHUB_OUTPUT
        echo "is_prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT
        echo "✅ Version: $VERSION (prerelease: $IS_PRERELEASE)"

  test-before-release:
    name: Test Before Release
    runs-on: ubuntu-latest
    needs: validate-tag

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

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

    - name: Cache cargo
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target
        key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}

    - name: Check code formatting
      run: cargo fmt -- --check

    - name: Run clippy
      run: cargo clippy --all-targets --all-features -- -D warnings

    - name: Run tests
      run: cargo test --all-features --verbose

    - name: Verify version in Cargo.toml
      run: |
        CARGO_VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
        if [ "$CARGO_VERSION" != "${{ needs.validate-tag.outputs.version }}" ]; then
          echo "❌ Version mismatch: Cargo.toml has $CARGO_VERSION, tag has ${{ needs.validate-tag.outputs.version }}"
          exit 1
        fi
        echo "✅ Version matches: $CARGO_VERSION"

  build-artifacts:
    name: Build Artifacts
    runs-on: ${{ matrix.os }}
    needs: [validate-tag, test-before-release]
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            artifact_name: threatflux-cache-linux-x64
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            artifact_name: threatflux-cache-windows-x64
          - os: macos-latest
            target: x86_64-apple-darwin
            artifact_name: threatflux-cache-macos-x64
          - os: macos-latest
            target: aarch64-apple-darwin
            artifact_name: threatflux-cache-macos-arm64

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

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

    - name: Cache cargo
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
          target
        key: ${{ runner.os }}-${{ matrix.target }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}

    - name: Build library
      run: cargo build --release --all-features --target ${{ matrix.target }}

    - name: Package artifact (Unix)
      if: matrix.os != 'windows-latest'
      run: |
        mkdir -p artifacts
        cd target/${{ matrix.target }}/release
        tar -czf ../../../artifacts/${{ matrix.artifact_name }}.tar.gz *.so *.dylib 2>/dev/null || true
        cd ../../..

    - name: Package artifact (Windows)
      if: matrix.os == 'windows-latest'
      run: |
        mkdir artifacts
        cd target/${{ matrix.target }}/release
        if (Test-Path "*.dll") {
          Compress-Archive -Path "*.dll" -DestinationPath "../../../artifacts/${{ matrix.artifact_name }}.zip"
        }
        cd ../../..

    - name: Upload artifact
      uses: actions/upload-artifact@v4
      with:
        name: ${{ matrix.artifact_name }}
        path: artifacts/*
        if-no-files-found: ignore

  generate-changelog:
    name: Generate Changelog
    runs-on: ubuntu-latest
    needs: validate-tag
    outputs:
      changelog: ${{ steps.changelog.outputs.changelog }}

    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Generate changelog
      id: changelog
      run: |
        # Get the previous tag
        PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -v "^v${{ needs.validate-tag.outputs.version }}$" | head -1 || echo "")
        
        if [ -z "$PREVIOUS_TAG" ]; then
          # First release
          CHANGELOG="## What's New\n\nThis is the initial release of ThreatFlux Cache v${{ needs.validate-tag.outputs.version }}.\n\n"
          CHANGELOG="$CHANGELOG### Features\n"
          CHANGELOG="$CHANGELOG- Flexible async cache library for Rust\n"
          CHANGELOG="$CHANGELOG- Pluggable backends (memory, filesystem)\n"
          CHANGELOG="$CHANGELOG- Multiple serialization formats (JSON, bincode)\n"
          CHANGELOG="$CHANGELOG- LRU eviction support\n"
          CHANGELOG="$CHANGELOG- Compression support with flate2\n"
          CHANGELOG="$CHANGELOG- Metrics and tracing integration\n"
          CHANGELOG="$CHANGELOG- OpenAPI documentation support\n"
        else
          # Generate changelog from git commits
          CHANGELOG="## Changes since $PREVIOUS_TAG\n\n"
          
          # Get commits since last tag
          COMMITS=$(git log --pretty=format:"- %s" $PREVIOUS_TAG..HEAD | grep -E "^- (feat|fix|perf|docs|style|refactor|test|chore)" || true)
          
          if [ -n "$COMMITS" ]; then
            CHANGELOG="$CHANGELOG### Commits\n$COMMITS\n\n"
          else
            CHANGELOG="$CHANGELOG### Changes\n- Various improvements and bug fixes\n\n"
          fi
        fi
        
        CHANGELOG="$CHANGELOG### Documentation\n"
        CHANGELOG="$CHANGELOG- [API Documentation](https://docs.rs/threatflux-cache/${{ needs.validate-tag.outputs.version }})\n"
        CHANGELOG="$CHANGELOG- [Crates.io](https://crates.io/crates/threatflux-cache)\n"
        
        # Escape newlines for GitHub Actions
        CHANGELOG="${CHANGELOG//'%'/'%25'}"
        CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
        CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"
        
        echo "changelog=$CHANGELOG" >> $GITHUB_OUTPUT

  publish-crates-io:
    name: Publish to Crates.io
    runs-on: ubuntu-latest
    needs: [validate-tag, test-before-release]
    if: needs.validate-tag.outputs.is_prerelease == 'false'

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

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

    - name: Cache cargo
      uses: actions/cache@v4
      with:
        path: |
          ~/.cargo/registry/index/
          ~/.cargo/registry/cache/
          ~/.cargo/git/db/
        key: ${{ runner.os }}-cargo-publish-${{ hashFiles('**/Cargo.lock') }}

    - name: Login to crates.io
      run: cargo login ${{ secrets.CARGO_REGISTRY_TOKEN }}

    - name: Publish to crates.io
      run: |
        # Dry run first
        cargo publish --dry-run --all-features
        
        # Actually publish
        cargo publish --all-features

  create-github-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    needs: [validate-tag, generate-changelog, build-artifacts]
    if: always() && needs.validate-tag.result == 'success' && needs.generate-changelog.result == 'success'

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Download all artifacts
      uses: actions/download-artifact@v4
      with:
        path: release-artifacts

    - name: Create GitHub Release
      uses: softprops/action-gh-release@v2
      with:
        tag_name: v${{ needs.validate-tag.outputs.version }}
        name: ThreatFlux Cache v${{ needs.validate-tag.outputs.version }}
        body: ${{ needs.generate-changelog.outputs.changelog }}
        prerelease: ${{ needs.validate-tag.outputs.is_prerelease == 'true' }}
        files: |
          release-artifacts/**/*
        generate_release_notes: true
        make_latest: ${{ needs.validate-tag.outputs.is_prerelease == 'false' }}
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  notify-release:
    name: Post-Release Notifications
    runs-on: ubuntu-latest
    needs: [validate-tag, create-github-release, publish-crates-io]
    if: always() && needs.create-github-release.result == 'success'

    steps:
    - name: Release Summary
      run: |
        echo "🚀 ThreatFlux Cache v${{ needs.validate-tag.outputs.version }} Released!"
        echo ""
        echo "📦 Crates.io: https://crates.io/crates/threatflux-cache"
        echo "📚 Documentation: https://docs.rs/threatflux-cache/${{ needs.validate-tag.outputs.version }}"
        echo "🔖 GitHub Release: https://github.com/ThreatFlux/threatflux-cache/releases/tag/v${{ needs.validate-tag.outputs.version }}"
        echo ""
        if [ "${{ needs.validate-tag.outputs.is_prerelease }}" = "true" ]; then
          echo "⚠️  This is a pre-release version"
        else
          echo "✅ This is a stable release"
        fi