Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

789
vendor/ruvector/scripts/deploy/deploy.sh vendored Executable file
View File

@@ -0,0 +1,789 @@
#!/bin/bash
################################################################################
# RuVector Comprehensive Deployment Script
#
# This script orchestrates the complete deployment process for ruvector:
# - Version management and synchronization
# - Pre-deployment checks (tests, linting, formatting)
# - WASM package builds
# - Crate publishing to crates.io
# - NPM package publishing
# - GitHub Actions trigger for cross-platform native builds
#
# Usage:
# ./scripts/deploy.sh [OPTIONS]
#
# Options:
# --dry-run Run without actually publishing
# --skip-tests Skip test suite execution
# --skip-crates Skip crates.io publishing
# --skip-npm Skip NPM publishing
# --skip-checks Skip pre-deployment checks
# --force Skip confirmation prompts
# --version VERSION Set explicit version (otherwise read from Cargo.toml)
#
# Environment Variables:
# CRATES_API_KEY API key for crates.io (required for crate publishing)
# NPM_TOKEN NPM authentication token (required for npm publishing)
# GITHUB_TOKEN GitHub token for Actions API (optional)
#
################################################################################
set -euo pipefail
# Color codes for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly BOLD='\033[1m'
readonly NC='\033[0m' # No Color
# Configuration (can be overridden by command-line flags)
DRY_RUN=${DRY_RUN:-false}
SKIP_TESTS=${SKIP_TESTS:-false}
SKIP_CHECKS=${SKIP_CHECKS:-false}
PUBLISH_CRATES=${PUBLISH_CRATES:-true}
PUBLISH_NPM=${PUBLISH_NPM:-true}
FORCE=${FORCE:-false}
VERSION=""
# Project root
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# Log files
readonly LOG_DIR="$PROJECT_ROOT/logs/deployment"
readonly LOG_FILE="$LOG_DIR/deploy-$(date +%Y%m%d-%H%M%S).log"
################################################################################
# Logging Functions
################################################################################
setup_logging() {
mkdir -p "$LOG_DIR"
exec 1> >(tee -a "$LOG_FILE")
exec 2>&1
echo -e "${CYAN}Logging to: $LOG_FILE${NC}"
}
log_info() {
echo -e "${BLUE}[INFO]${NC} $*"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $*"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $*"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $*" >&2
}
log_step() {
echo ""
echo -e "${BOLD}${CYAN}========================================${NC}"
echo -e "${BOLD}${CYAN}$*${NC}"
echo -e "${BOLD}${CYAN}========================================${NC}"
}
################################################################################
# Utility Functions
################################################################################
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
log_warning "DRY RUN MODE: No actual publishing will occur"
shift
;;
--skip-tests)
SKIP_TESTS=true
log_warning "Skipping test suite"
shift
;;
--skip-crates)
PUBLISH_CRATES=false
log_info "Skipping crates.io publishing"
shift
;;
--skip-npm)
PUBLISH_NPM=false
log_info "Skipping NPM publishing"
shift
;;
--skip-checks)
SKIP_CHECKS=true
log_warning "Skipping pre-deployment checks"
shift
;;
--force)
FORCE=true
log_warning "Force mode: Skipping confirmation prompts"
shift
;;
--version)
VERSION="$2"
log_info "Using explicit version: $VERSION"
shift 2
;;
--help|-h)
show_help
exit 0
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
}
show_help() {
cat << EOF
RuVector Deployment Script
Usage: $0 [OPTIONS]
Options:
--dry-run Run without actually publishing
--skip-tests Skip test suite execution
--skip-crates Skip crates.io publishing
--skip-npm Skip NPM publishing
--skip-checks Skip pre-deployment checks
--force Skip confirmation prompts
--version VERSION Set explicit version
-h, --help Show this help message
Environment Variables:
CRATES_API_KEY API key for crates.io (required for crate publishing)
NPM_TOKEN NPM authentication token (required for npm publishing)
GITHUB_TOKEN GitHub token for Actions API (optional)
Examples:
# Full deployment with all checks
$0
# Dry run to test the process
$0 --dry-run
# Publish only to crates.io
$0 --skip-npm
# Quick deployment skipping tests (not recommended for production)
$0 --skip-tests --force
EOF
}
confirm_action() {
local message="$1"
if [[ "$FORCE" == "true" ]]; then
return 0
fi
echo -e "${YELLOW}$message${NC}"
read -p "Continue? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_error "Deployment cancelled by user"
exit 1
fi
}
################################################################################
# Prerequisites Check
################################################################################
check_prerequisites() {
log_step "Checking Prerequisites"
local missing_tools=()
# Check required tools
command -v cargo >/dev/null 2>&1 || missing_tools+=("cargo")
command -v rustc >/dev/null 2>&1 || missing_tools+=("rustc")
command -v npm >/dev/null 2>&1 || missing_tools+=("npm")
command -v node >/dev/null 2>&1 || missing_tools+=("node")
command -v wasm-pack >/dev/null 2>&1 || missing_tools+=("wasm-pack")
command -v jq >/dev/null 2>&1 || missing_tools+=("jq")
if [[ ${#missing_tools[@]} -gt 0 ]]; then
log_error "Missing required tools: ${missing_tools[*]}"
log_error "Please install them and try again"
exit 1
fi
log_success "All required tools found"
# Check environment variables for publishing
if [[ "$PUBLISH_CRATES" == "true" ]] && [[ -z "${CRATES_API_KEY:-}" ]]; then
log_error "CRATES_API_KEY environment variable not set"
log_error "Either set it or use --skip-crates flag"
exit 1
fi
if [[ "$PUBLISH_NPM" == "true" ]] && [[ -z "${NPM_TOKEN:-}" ]]; then
log_error "NPM_TOKEN environment variable not set"
log_error "Either set it or use --skip-npm flag"
exit 1
fi
# Display versions
log_info "Rust version: $(rustc --version)"
log_info "Cargo version: $(cargo --version)"
log_info "Node version: $(node --version)"
log_info "NPM version: $(npm --version)"
log_info "wasm-pack version: $(wasm-pack --version)"
}
################################################################################
# Version Management
################################################################################
get_workspace_version() {
log_step "Reading Workspace Version"
cd "$PROJECT_ROOT"
if [[ -n "$VERSION" ]]; then
log_info "Using explicit version: $VERSION"
return
fi
# Extract version from workspace Cargo.toml
VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
if [[ -z "$VERSION" ]]; then
log_error "Could not determine version from Cargo.toml"
exit 1
fi
log_success "Workspace version: $VERSION"
}
sync_package_versions() {
log_step "Synchronizing Package Versions"
cd "$PROJECT_ROOT"
# Update root package.json
if [[ -f "package.json" ]]; then
log_info "Updating root package.json to version $VERSION"
local temp_file=$(mktemp)
jq --arg version "$VERSION" '.version = $version' package.json > "$temp_file"
mv "$temp_file" package.json
log_success "Root package.json updated"
fi
# Update NPM package versions
local npm_packages=(
"crates/ruvector-node"
"crates/ruvector-wasm"
"crates/ruvector-gnn-node"
"crates/ruvector-gnn-wasm"
"crates/ruvector-graph-node"
"crates/ruvector-graph-wasm"
"crates/ruvector-tiny-dancer-node"
"crates/ruvector-tiny-dancer-wasm"
)
for pkg in "${npm_packages[@]}"; do
if [[ -f "$pkg/package.json" ]]; then
log_info "Updating $pkg/package.json to version $VERSION"
local temp_file=$(mktemp)
jq --arg version "$VERSION" '.version = $version' "$pkg/package.json" > "$temp_file"
mv "$temp_file" "$pkg/package.json"
fi
done
log_success "All package versions synchronized to $VERSION"
}
################################################################################
# Pre-Deployment Checks
################################################################################
run_tests() {
if [[ "$SKIP_TESTS" == "true" ]]; then
log_warning "Skipping tests (--skip-tests flag set)"
return
fi
log_step "Running Test Suite"
cd "$PROJECT_ROOT"
log_info "Running cargo test --all..."
if ! cargo test --all --verbose; then
log_error "Tests failed"
exit 1
fi
log_success "All tests passed"
}
run_clippy() {
if [[ "$SKIP_CHECKS" == "true" ]]; then
log_warning "Skipping clippy checks"
return
fi
log_step "Running Clippy Linter"
cd "$PROJECT_ROOT"
log_info "Running cargo clippy --all-targets..."
if ! cargo clippy --all-targets --all-features -- -D warnings; then
log_error "Clippy found issues"
exit 1
fi
log_success "Clippy checks passed"
}
check_formatting() {
if [[ "$SKIP_CHECKS" == "true" ]]; then
log_warning "Skipping formatting check"
return
fi
log_step "Checking Code Formatting"
cd "$PROJECT_ROOT"
log_info "Running cargo fmt --check..."
if ! cargo fmt --all -- --check; then
log_error "Code formatting issues found"
log_error "Run 'cargo fmt --all' to fix"
exit 1
fi
log_success "Code formatting is correct"
}
build_wasm_packages() {
log_step "Building WASM Packages"
cd "$PROJECT_ROOT"
local wasm_packages=(
"crates/ruvector-wasm"
"crates/ruvector-gnn-wasm"
"crates/ruvector-graph-wasm"
"crates/ruvector-tiny-dancer-wasm"
)
for pkg in "${wasm_packages[@]}"; do
if [[ -d "$pkg" ]]; then
log_info "Building WASM package: $pkg"
cd "$PROJECT_ROOT/$pkg"
if [[ -f "build.sh" ]]; then
log_info "Using build script for $pkg"
bash build.sh
elif [[ -f "package.json" ]] && grep -q '"build"' package.json; then
log_info "Using npm build for $pkg"
npm run build
else
log_info "Using wasm-pack for $pkg"
wasm-pack build --target web --release
fi
log_success "Built WASM package: $pkg"
fi
done
cd "$PROJECT_ROOT"
log_success "All WASM packages built"
}
################################################################################
# Crate Publishing
################################################################################
publish_crates() {
if [[ "$PUBLISH_CRATES" != "true" ]]; then
log_warning "Skipping crates.io publishing"
return
fi
log_step "Publishing Crates to crates.io"
cd "$PROJECT_ROOT"
# Configure cargo authentication
log_info "Configuring cargo authentication..."
if [[ "$DRY_RUN" != "true" ]]; then
cargo login "$CRATES_API_KEY"
fi
# Crates in dependency order
local crates=(
# Core crates (no dependencies)
"crates/ruvector-core"
"crates/ruvector-metrics"
"crates/ruvector-filter"
# Cluster and replication (depend on core)
"crates/ruvector-collections"
"crates/ruvector-snapshot"
"crates/ruvector-raft"
"crates/ruvector-cluster"
"crates/ruvector-replication"
# Graph and GNN (depend on core)
"crates/ruvector-graph"
"crates/ruvector-gnn"
# Router (depend on core)
"crates/ruvector-router-core"
"crates/ruvector-router-ffi"
"crates/ruvector-router-wasm"
"crates/ruvector-router-cli"
# Tiny Dancer (depend on core)
"crates/ruvector-tiny-dancer-core"
"crates/ruvector-tiny-dancer-wasm"
"crates/ruvector-tiny-dancer-node"
# Bindings (depend on core)
"crates/ruvector-node"
"crates/ruvector-wasm"
"crates/ruvector-gnn-node"
"crates/ruvector-gnn-wasm"
"crates/ruvector-graph-node"
"crates/ruvector-graph-wasm"
# CLI and server (depend on everything)
"crates/ruvector-cli"
"crates/ruvector-server"
"crates/ruvector-bench"
)
local success_count=0
local failed_crates=()
local skipped_crates=()
for crate in "${crates[@]}"; do
if [[ ! -d "$crate" ]]; then
log_warning "Crate directory not found: $crate (skipping)"
skipped_crates+=("$crate")
continue
fi
local crate_name=$(basename "$crate")
log_info "Publishing $crate_name..."
cd "$PROJECT_ROOT/$crate"
# Check if already published
if cargo search "$crate_name" --limit 1 | grep -q "^$crate_name = \"$VERSION\""; then
log_warning "$crate_name v$VERSION already published (skipping)"
((success_count++))
skipped_crates+=("$crate_name")
continue
fi
# Verify package
log_info "Verifying package: $crate_name"
if ! cargo package --allow-dirty; then
log_error "Package verification failed: $crate_name"
failed_crates+=("$crate_name")
continue
fi
# Publish
if [[ "$DRY_RUN" == "true" ]]; then
log_info "DRY RUN: Would publish $crate_name"
((success_count++))
else
log_info "Publishing $crate_name to crates.io..."
if cargo publish --allow-dirty; then
log_success "Published $crate_name v$VERSION"
((success_count++))
# Wait for crates.io to index
log_info "Waiting 30 seconds for crates.io indexing..."
sleep 30
else
log_error "Failed to publish $crate_name"
failed_crates+=("$crate_name")
fi
fi
done
cd "$PROJECT_ROOT"
# Summary
log_step "Crates Publishing Summary"
log_info "Total crates: ${#crates[@]}"
log_success "Successfully published: $success_count"
log_warning "Skipped: ${#skipped_crates[@]}"
if [[ ${#failed_crates[@]} -gt 0 ]]; then
log_error "Failed to publish: ${#failed_crates[@]}"
for crate in "${failed_crates[@]}"; do
log_error " - $crate"
done
exit 1
fi
log_success "All crates published successfully!"
}
################################################################################
# NPM Publishing
################################################################################
build_native_modules() {
log_step "Building Native Modules for Current Platform"
cd "$PROJECT_ROOT"
local native_packages=(
"crates/ruvector-node"
"crates/ruvector-gnn-node"
"crates/ruvector-graph-node"
"crates/ruvector-tiny-dancer-node"
)
for pkg in "${native_packages[@]}"; do
if [[ -d "$pkg" ]]; then
log_info "Building native module: $pkg"
cd "$PROJECT_ROOT/$pkg"
# Install dependencies
if [[ ! -d "node_modules" ]]; then
log_info "Installing npm dependencies for $pkg"
npm install
fi
# Build
log_info "Building native module with napi"
npm run build
log_success "Built native module: $pkg"
fi
done
cd "$PROJECT_ROOT"
}
publish_npm() {
if [[ "$PUBLISH_NPM" != "true" ]]; then
log_warning "Skipping NPM publishing"
return
fi
log_step "Publishing NPM Packages"
cd "$PROJECT_ROOT"
# Configure npm authentication
log_info "Configuring npm authentication..."
if [[ "$DRY_RUN" != "true" ]]; then
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
fi
local npm_packages=(
"crates/ruvector-node"
"crates/ruvector-wasm"
"crates/ruvector-gnn-node"
"crates/ruvector-gnn-wasm"
"crates/ruvector-graph-node"
"crates/ruvector-graph-wasm"
"crates/ruvector-tiny-dancer-node"
"crates/ruvector-tiny-dancer-wasm"
)
local success_count=0
local failed_packages=()
for pkg in "${npm_packages[@]}"; do
if [[ ! -d "$pkg" ]] || [[ ! -f "$pkg/package.json" ]]; then
log_warning "Package not found: $pkg (skipping)"
continue
fi
local pkg_name=$(jq -r '.name' "$pkg/package.json")
log_info "Publishing $pkg_name..."
cd "$PROJECT_ROOT/$pkg"
# Check if already published
if npm view "$pkg_name@$VERSION" version >/dev/null 2>&1; then
log_warning "$pkg_name@$VERSION already published (skipping)"
((success_count++))
continue
fi
# Publish
if [[ "$DRY_RUN" == "true" ]]; then
log_info "DRY RUN: Would publish $pkg_name"
((success_count++))
else
log_info "Publishing $pkg_name to npm..."
if npm publish --access public; then
log_success "Published $pkg_name@$VERSION"
((success_count++))
else
log_error "Failed to publish $pkg_name"
failed_packages+=("$pkg_name")
fi
fi
done
cd "$PROJECT_ROOT"
# Summary
log_step "NPM Publishing Summary"
log_success "Successfully published: $success_count/${#npm_packages[@]}"
if [[ ${#failed_packages[@]} -gt 0 ]]; then
log_error "Failed to publish: ${#failed_packages[@]}"
for pkg in "${failed_packages[@]}"; do
log_error " - $pkg"
done
exit 1
fi
log_success "All NPM packages published successfully!"
}
################################################################################
# GitHub Actions Integration
################################################################################
trigger_github_builds() {
log_step "Triggering GitHub Actions for Cross-Platform Builds"
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
log_warning "GITHUB_TOKEN not set, skipping GitHub Actions trigger"
log_info "You can manually trigger the workflow from GitHub Actions UI"
return
fi
if [[ "$DRY_RUN" == "true" ]]; then
log_info "DRY RUN: Would trigger GitHub Actions workflow"
return
fi
local repo_owner="ruvnet"
local repo_name="ruvector"
local workflow_name="native-builds.yml"
log_info "Triggering workflow: $workflow_name"
log_info "Repository: $repo_owner/$repo_name"
log_info "Version tag: v$VERSION"
# Create GitHub API request
local response=$(curl -s -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/$repo_owner/$repo_name/actions/workflows/$workflow_name/dispatches" \
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$VERSION\"}}")
if [[ -z "$response" ]]; then
log_success "GitHub Actions workflow triggered successfully"
log_info "Check status at: https://github.com/$repo_owner/$repo_name/actions"
else
log_error "Failed to trigger GitHub Actions workflow"
log_error "Response: $response"
fi
}
################################################################################
# Deployment Summary
################################################################################
print_deployment_summary() {
log_step "Deployment Summary"
echo ""
echo -e "${BOLD}Version:${NC} $VERSION"
echo -e "${BOLD}Dry Run:${NC} $DRY_RUN"
echo ""
if [[ "$PUBLISH_CRATES" == "true" ]]; then
echo -e "${GREEN}${NC} Crates published to crates.io"
echo -e " View at: ${CYAN}https://crates.io/crates/ruvector-core${NC}"
fi
if [[ "$PUBLISH_NPM" == "true" ]]; then
echo -e "${GREEN}${NC} NPM packages published"
echo -e " View at: ${CYAN}https://www.npmjs.com/package/@ruvector/node${NC}"
fi
echo ""
echo -e "${BOLD}${GREEN}Deployment completed successfully!${NC}"
echo ""
if [[ "$DRY_RUN" == "true" ]]; then
echo -e "${YELLOW}NOTE: This was a dry run. No actual publishing occurred.${NC}"
echo -e "${YELLOW}Run without --dry-run to perform actual deployment.${NC}"
fi
}
################################################################################
# Main Deployment Flow
################################################################################
main() {
echo -e "${BOLD}${CYAN}"
cat << "EOF"
╔═══════════════════════════════════════════════════════════════╗
║ ║
║ RuVector Comprehensive Deployment Script ║
║ ║
╚═══════════════════════════════════════════════════════════════╝
EOF
echo -e "${NC}"
# Setup
setup_logging
parse_args "$@"
# Prerequisites
check_prerequisites
# Version management
get_workspace_version
sync_package_versions
# Confirmation
confirm_action "Ready to deploy version $VERSION. This will:
- Run tests and quality checks
- Build WASM packages
- Publish $([ "$PUBLISH_CRATES" == "true" ] && echo "crates.io" || echo "")$([ "$PUBLISH_CRATES" == "true" ] && [ "$PUBLISH_NPM" == "true" ] && echo " and ")$([ "$PUBLISH_NPM" == "true" ] && echo "NPM packages" || echo "")"
# Pre-deployment checks
run_tests
run_clippy
check_formatting
build_wasm_packages
# Publishing
publish_crates
build_native_modules
publish_npm
# GitHub Actions
trigger_github_builds
# Summary
print_deployment_summary
log_info "Deployment log saved to: $LOG_FILE"
}
# Run main function
main "$@"