amari 0.19.1

Advanced mathematical computing library with geometric algebra, tropical algebra, and automatic differentiation
Documentation
name: Publish to npm and crates.io

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to publish (e.g., 0.1.0)'
        required: true
        type: string
      publish_npm:
        description: 'Publish to npm'
        required: true
        type: boolean
        default: true
      publish_crates:
        description: 'Publish to crates.io'
        required: true
        type: boolean
        default: true

env:
  CARGO_TERM_COLOR: always

jobs:
  validate:
    name: Validate and Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

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

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

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

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

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

      - name: Run integration tests
        run: ./run_all_tests.sh

  publish-crates:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    needs: validate
    if: ${{ github.event.inputs.publish_crates == 'true' || startsWith(github.ref, 'refs/tags/v') }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

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

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

      - name: Bump version if specified
        if: ${{ github.event.inputs.version != '' }}
        run: |
          echo "Bumping version to ${{ github.event.inputs.version }}..."
          chmod +x scripts/bump-version.sh
          ./scripts/bump-version.sh ${{ github.event.inputs.version }}
          echo "Version bump complete. Updated files:"
          git diff --name-only

      - name: Publish crates to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          # Publish in dependency order with delays for crates.io rate limiting
          # IMPORTANT: New crates should be added before amari-measure, amari-wasm, amari-gpu, and amari
          # See VERSION_MANAGEMENT.md for best practices
          # Dependency order is critical! amari-functional and amari-topology must come
          # before amari-gpu because amari-gpu has optional deps on them.
          CRATES=(
            "amari-core"
            "amari-tropical"
            "amari-dual"
            "amari-network"
            "amari-info-geom"
            "amari-relativistic"
            "amari-holographic"
            "amari-fusion"
            "amari-automata"
            "amari-enumerative"
            "amari-flynn-macros"
            "amari-flynn"
            "amari-measure"
            "amari-calculus"
            "amari-functional"
            "amari-topology"
            "amari-probabilistic"
            "amari-dynamics"
            "amari-gpu"
            "amari-optimization"
            "amari"
          )

          for crate in "${CRATES[@]}"; do
            echo "Publishing $crate..."
            if [ "$crate" != "amari" ]; then
              cd "$crate"
            fi

            # Get the actual version from Cargo.toml (handle workspace versions)
            if grep -q "^version.workspace = true" Cargo.toml; then
              # Get version from workspace Cargo.toml [workspace.package] section
              if [ "$crate" = "amari" ]; then
                WORKSPACE_TOML="Cargo.toml"
              else
                WORKSPACE_TOML="../Cargo.toml"
              fi
              ACTUAL_VERSION=$(awk '/^\[workspace\.package\]/{f=1} f && /^version = /{print $NF; exit}' "$WORKSPACE_TOML" | sed 's/"//g' || echo "unknown")
            else
              # Get version from local Cargo.toml
              ACTUAL_VERSION=$(grep "^version" Cargo.toml | head -1 | sed 's/.*= *"\([^"]*\)".*/\1/' || echo "unknown")
            fi
            INPUT_VERSION="${{ github.event.inputs.version }}"
            TARGET_VERSION="${INPUT_VERSION:-$ACTUAL_VERSION}"

            echo "Checking $crate version $TARGET_VERSION..."

            # Check if already published
            if cargo search "$crate" --limit 1 | grep -q "^$crate = \"$TARGET_VERSION\""; then
              echo "$crate version $TARGET_VERSION already published, skipping..."
            else
              echo "Publishing $crate version $TARGET_VERSION..."
              cargo publish --allow-dirty || echo "Failed to publish $crate, continuing..."
              echo "Waiting for crates.io indexing and rate limit reset..."
              sleep 45
            fi

            if [ "$crate" != "amari" ]; then
              cd ..
            fi
          done

  build-wasm:
    name: Build WASM Package
    runs-on: ubuntu-latest
    needs: validate
    if: ${{ github.event.inputs.publish_npm == 'true' || startsWith(github.ref, 'refs/tags/v') }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown

      - name: Install wasm-pack
        run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

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

      - name: Build WASM package
        run: |
          cd amari-wasm
          wasm-pack build --target web --out-dir pkg --scope justinelliottcobb
          wasm-pack build --target nodejs --out-dir pkg-node --scope justinelliottcobb
          wasm-pack build --target bundler --out-dir pkg-bundler --scope justinelliottcobb

      - name: Upload WASM artifacts
        uses: actions/upload-artifact@v7
        with:
          name: wasm-packages
          path: |
            amari-wasm/pkg/
            amari-wasm/pkg-node/
            amari-wasm/pkg-bundler/

  publish-npm:
    name: Publish to npm
    runs-on: ubuntu-latest
    needs: validate
    if: ${{ github.event.inputs.publish_npm == 'true' || startsWith(github.ref, 'refs/tags/v') }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: wasm32-unknown-unknown

      - name: Install wasm-pack
        run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      - name: Build WASM package for npm
        run: |
          cd amari-wasm
          wasm-pack build --target web --out-dir pkg --scope justinelliottcobb
          echo "Generated package.json before fix:"
          cat pkg/package.json
          echo "Fixing package name..."
          node -e "
            const pkg = require('./pkg/package.json');
            pkg.name = '@justinelliottcobb/amari-wasm';
            require('fs').writeFileSync('./pkg/package.json', JSON.stringify(pkg, null, 2) + '\n');
          "
          echo "Fixed package.json:"
          cat pkg/package.json
          echo "Package name: $(node -p "require('./pkg/package.json').name")"
          echo "Package version: $(node -p "require('./pkg/package.json').version")"

# Note: wasm-pack automatically sets version from Cargo.toml workspace version

      - name: Check if version exists on npm
        id: npm-version-check
        run: |
          cd amari-wasm/pkg
          PACKAGE_VERSION=$(node -p "require('./package.json').version")
          PACKAGE_NAME=$(node -p "require('./package.json').name")
          if npm view $PACKAGE_NAME@$PACKAGE_VERSION version 2>/dev/null; then
            echo "version-exists=true" >> $GITHUB_OUTPUT
            echo "Version $PACKAGE_VERSION already exists on npm"
          else
            echo "version-exists=false" >> $GITHUB_OUTPUT
            echo "Version $PACKAGE_VERSION does not exist, proceeding with publish"
          fi

      - name: Publish to npm
        if: steps.npm-version-check.outputs.version-exists == 'false'
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: |
          cd amari-wasm/pkg
          npm publish --ignore-scripts --access public

      - name: Skip npm publish (version exists)
        if: steps.npm-version-check.outputs.version-exists == 'true'
        run: echo "Skipping npm publish - version already exists"

# Examples suite updates can be done manually as needed
# Removing automatic PR creation to avoid CI/CD conflicts