text-to-cypher 0.1.17

A library and REST API for translating natural language text to Cypher queries using AI models
name: Release on Tag

on:
  push:
    tags:
      - 'v*'  # Triggers on version tags like v1.0.0, v2.1.0, etc.

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always

jobs:
  publish-crates:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # stable
        with:
          toolchain: stable

      - name: Publish to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: cargo publish

  release:
    name: Build and release binaries
    runs-on: ubuntu-latest
    needs: publish-crates
    steps:
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # stable
        with:
          toolchain: stable

      - name: Cache cargo dependencies
        uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-cross-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-cross-
            ${{ runner.os }}-cargo-

      - name: Install cross-compilation tools
        run: |
          # Install musl tools for static linking
          sudo apt-get update
          sudo apt-get install -y musl-tools musl-dev
          
          # Install cross compilation tool (use specific stable version)
          cargo install cross --version 0.2.5
          
          # Add required targets
          rustup target add x86_64-unknown-linux-gnu
          rustup target add x86_64-unknown-linux-musl
          rustup target add aarch64-unknown-linux-gnu

      - name: Build release binaries
        env:
          CROSS_CONTAINER_IN_CONTAINER: true
          HOSTNAME: github-actions
          USER: runner
          HOME: /home/runner
        run: |
          mkdir -p release-artifacts
          
          # Show available targets and tools
          echo "Available Rust targets:"
          rustup target list --installed
          echo "Cross version:"
          cross --version
          echo "Environment variables:"
          echo "HOSTNAME: $HOSTNAME"
          echo "USER: $USER"
          echo "HOME: $HOME"
          
          # Build for different platforms
          echo "Building for x86_64-unknown-linux-gnu..."
          cargo build --release --target x86_64-unknown-linux-gnu
          
          echo "Building for x86_64-unknown-linux-musl..."
          # Try using cargo directly for musl with system tools
          CC_x86_64_unknown_linux_musl=musl-gcc cargo build --release --target x86_64-unknown-linux-musl
          
          echo "Building for aarch64-unknown-linux-gnu..."
          # Try with more environment variables and verbose output
          if ! HOSTNAME=github-actions USER=runner HOME=/home/runner cross build --release --target aarch64-unknown-linux-gnu --verbose; then
            echo "⚠️ ARM64 build failed, continuing with x86_64 builds only"
            echo "This is not critical as x86_64 covers most use cases"
          fi
          
          # Copy binaries (check if they exist first)
          cp target/x86_64-unknown-linux-gnu/release/text-to-cypher release-artifacts/text-to-cypher-linux-x86_64
          cp target/x86_64-unknown-linux-musl/release/text-to-cypher release-artifacts/text-to-cypher-linux-x86_64-musl
          
          # Copy ARM64 binary only if it exists
          if [ -f target/aarch64-unknown-linux-gnu/release/text-to-cypher ]; then
            cp target/aarch64-unknown-linux-gnu/release/text-to-cypher release-artifacts/text-to-cypher-linux-aarch64
            echo "✅ ARM64 binary copied successfully"
          else
            echo "⚠️ ARM64 binary not found, skipping"
          fi
          
          # Copy templates directory
          cp -r templates release-artifacts/
          
          # Create complete packages with templates
          cd release-artifacts
          
          # Create tar.gz packages that include templates
          mkdir -p packages
          for binary in text-to-cypher-linux-*; do
            package_name="${binary}.tar.gz"
            tar -czf "packages/${package_name}" "$binary" templates/
          done
          
          # Create checksums for packages only
          sha256sum packages/*.tar.gz > checksums.txt

      - name: Create installation script
        run: |
          cat > release-artifacts/install.sh << 'EOF'
          #!/bin/bash
          set -e
          
          # Text-to-Cypher Installation Script
          REPO="FalkorDB/text-to-cypher"
          BINARY_NAME="text-to-cypher"
          
          # Parse arguments
          INSTALL_SYSTEM=false
          
          for arg in "$@"; do
            case $arg in
              --install)
                INSTALL_SYSTEM=true
                ;;
              *)
                ;;
            esac
          done
          
          # Detect architecture
          ARCH=$(uname -m)
          OS=$(uname -s)
          
          case "$OS" in
            Linux*)
              case "$ARCH" in
                x86_64)  PLATFORM="linux-x86_64" ;;
                aarch64) PLATFORM="linux-aarch64" ;;
                arm64)   PLATFORM="linux-aarch64" ;;
                *) echo "Unsupported architecture: $ARCH"; exit 1 ;;
              esac
              ;;
            Darwin*)
              case "$ARCH" in
                x86_64) PLATFORM="darwin-x86_64" ;;
                arm64)  PLATFORM="darwin-aarch64" ;;
                *) echo "Unsupported architecture: $ARCH"; exit 1 ;;
              esac
              ;;
            *) echo "Unsupported OS: $OS"; exit 1 ;;
          esac
          
          # Get latest release
          LATEST_RELEASE=$(curl -s "https://api.github.com/repos/$REPO/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
          
          # Download complete package with templates (always includes templates)
          DOWNLOAD_URL="https://github.com/$REPO/releases/download/$LATEST_RELEASE/packages/$BINARY_NAME-$PLATFORM.tar.gz"
          echo "Downloading $BINARY_NAME $LATEST_RELEASE (with templates) for $PLATFORM..."
          
          # Download and extract package
          if command -v curl >/dev/null 2>&1; then
            curl -L "$DOWNLOAD_URL" -o "$BINARY_NAME.tar.gz"
          elif command -v wget >/dev/null 2>&1; then
            wget "$DOWNLOAD_URL" -O "$BINARY_NAME.tar.gz"
          else
            echo "Error: curl or wget is required"
            exit 1
          fi
          
          # Extract the package
          tar -xzf "$BINARY_NAME.tar.gz"
          rm "$BINARY_NAME.tar.gz"
          
          echo "Downloaded $BINARY_NAME with templates!"
          echo "Files extracted:"
          echo "  - $BINARY_NAME (executable)"
          echo "  - templates/ (directory)"
          
          # Install to system path (optional)
          if [ "$INSTALL_SYSTEM" = "true" ]; then
            sudo mv "$BINARY_NAME" /usr/local/bin/
            sudo mkdir -p /usr/local/share/text-to-cypher
            sudo cp -r templates /usr/local/share/text-to-cypher/
            echo "$BINARY_NAME installed to /usr/local/bin/"
            echo "Templates installed to /usr/local/share/text-to-cypher/templates/"
          else
            echo "$BINARY_NAME downloaded successfully!"
            echo "Run with: ./$BINARY_NAME"
            echo "Templates available in: ./templates/"
            echo ""
            echo "Usage options:"
            echo "  $0 --install    # Install system-wide"
          fi
          EOF
          chmod +x release-artifacts/install.sh

          chmod +x release-artifacts/install.sh

      - name: Extract tag info
        id: tag_info
        run: |
          TAG=${GITHUB_REF#refs/tags/}
          echo "tag=$TAG" >> $GITHUB_OUTPUT
          
          # Check if it's a prerelease (contains alpha, beta, rc)
          if [[ $TAG =~ (alpha|beta|rc) ]]; then
            echo "prerelease=true" >> $GITHUB_OUTPUT
          else
            echo "prerelease=false" >> $GITHUB_OUTPUT
          fi

      - name: Create GitHub Release
        uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
        with:
          tag_name: ${{ steps.tag_info.outputs.tag }}
          name: Release ${{ steps.tag_info.outputs.tag }}
          prerelease: ${{ steps.tag_info.outputs.prerelease }}
          body: |
            ## 🚀 Text-to-Cypher ${{ steps.tag_info.outputs.tag }}
            
            **Built from commit:** ${{ github.sha }}
            **Release Type:** ${{ steps.tag_info.outputs.prerelease == 'true' && 'Pre-release' || 'Stable' }}
            
            ### 📦 Quick Installation:
            ```bash
            # One-line install (Linux/macOS)
            curl -sSL https://github.com/${{ github.repository }}/releases/download/${{ steps.tag_info.outputs.tag }}/install.sh | bash
            
            # Or install system-wide
            curl -sSL https://github.com/${{ github.repository }}/releases/download/${{ steps.tag_info.outputs.tag }}/install.sh | bash -s -- --install
            ```
            
            ### 📋 Manual Downloads:
            - **Complete Packages** (includes binary + templates):
              - `packages/text-to-cypher-linux-x86_64.tar.gz`
              - `packages/text-to-cypher-linux-x86_64-musl.tar.gz`
              - `packages/text-to-cypher-linux-aarch64.tar.gz`
            - **Checksums**: `checksums.txt`
            
            ### � Use in Your Dockerfiles:
            ```dockerfile
            # Download and use in your Dockerfile
            FROM alpine:latest
            RUN apk add --no-cache wget tar && \
                wget https://github.com/${{ github.repository }}/releases/download/${{ steps.tag_info.outputs.tag }}/packages/text-to-cypher-linux-x86_64-musl.tar.gz && \
                tar -xzf text-to-cypher-linux-x86_64-musl.tar.gz && \
                mv text-to-cypher-linux-x86_64-musl /usr/local/bin/text-to-cypher && \
                mv templates /usr/local/share/text-to-cypher-templates && \
                rm text-to-cypher-linux-x86_64-musl.tar.gz
            ENV TEMPLATES_DIR=/usr/local/share/text-to-cypher-templates
            CMD ["text-to-cypher"]
            ```
          files: |
            release-artifacts/*
          draft: false

  cleanup-old-releases:
    runs-on: ubuntu-latest
    needs: release
    steps:
      - name: Cleanup old releases
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        with:
          script: |
            const { data: releases } = await github.rest.repos.listReleases({
              owner: context.repo.owner,
              repo: context.repo.repo,
              per_page: 100
            });
            
            // Sort releases by creation date (newest first)
            const sortedReleases = releases.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
            
            // Keep only the latest 10 releases
            const releasesToDelete = sortedReleases.slice(10);
            
            console.log(`Found ${releases.length} total releases`);
            console.log(`Keeping latest 10 releases`);
            console.log(`Deleting ${releasesToDelete.length} old releases`);
            
            for (const release of releasesToDelete) {
              try {
                console.log(`Deleting release: ${release.tag_name} (${release.name})`);
                await github.rest.repos.deleteRelease({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  release_id: release.id
                });
                
                // Also delete the associated tag
                try {
                  await github.rest.git.deleteRef({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    ref: `tags/${release.tag_name}`
                  });
                  console.log(`Deleted tag: ${release.tag_name}`);
                } catch (tagError) {
                  console.log(`Failed to delete tag ${release.tag_name}: ${tagError.message}`);
                }
              } catch (error) {
                console.log(`Failed to delete release ${release.tag_name}: ${error.message}`);
              }
            }
            
            console.log('Cleanup completed');