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")"
- 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"