#!/bin/bash # SAL Publishing Script # This script publishes all SAL crates to crates.io in the correct dependency order # Handles path dependencies, version updates, and rate limiting set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration DRY_RUN=false WAIT_TIME=15 # Seconds to wait between publishes VERSION="" # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in --dry-run) DRY_RUN=true shift ;; --wait) WAIT_TIME="$2" shift 2 ;; --version) VERSION="$2" shift 2 ;; -h|--help) echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --dry-run Show what would be published without actually publishing" echo " --wait SECONDS Time to wait between publishes (default: 15)" echo " --version VER Set version for all crates" echo " -h, --help Show this help message" exit 0 ;; *) echo "Unknown option: $1" exit 1 ;; esac done # Crates to publish in dependency order CRATES=( "os" "process" "text" "net" "git" "vault" "kubernetes" "virt" "redisclient" "postgresclient" "zinit_client" "service_manager" "mycelium" "rhai" ) echo -e "${BLUE}===============================================${NC}" echo -e "${BLUE} SAL Publishing Script${NC}" echo -e "${BLUE}===============================================${NC}" echo "" if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}๐Ÿ” DRY RUN MODE - No actual publishing will occur${NC}" echo "" fi # Check if we're in the right directory if [ ! -f "Cargo.toml" ] || [ ! -d "os" ] || [ ! -d "git" ]; then echo -e "${RED}โŒ Error: This script must be run from the SAL repository root${NC}" exit 1 fi # Check if cargo is available if ! command -v cargo &> /dev/null; then echo -e "${RED}โŒ Error: cargo is not installed or not in PATH${NC}" exit 1 fi # Check if user is logged in to crates.io if [ "$DRY_RUN" = false ]; then if ! cargo login --help &> /dev/null; then echo -e "${RED}โŒ Error: Please run 'cargo login' first${NC}" exit 1 fi fi # Update version if specified if [ -n "$VERSION" ]; then echo -e "${YELLOW}๐Ÿ“ Updating version to $VERSION...${NC}" # Update root Cargo.toml sed -i.bak "s/^version = \".*\"/version = \"$VERSION\"/" Cargo.toml # Update each crate's Cargo.toml for crate in "${CRATES[@]}"; do if [ -f "$crate/Cargo.toml" ]; then sed -i.bak "s/^version = \".*\"/version = \"$VERSION\"/" "$crate/Cargo.toml" echo " โœ… Updated $crate to version $VERSION" fi done echo "" fi # Run tests before publishing echo -e "${YELLOW}๐Ÿงช Running tests...${NC}" if [ "$DRY_RUN" = false ]; then if ! cargo test --workspace; then echo -e "${RED}โŒ Tests failed! Aborting publish.${NC}" exit 1 fi echo -e "${GREEN}โœ… All tests passed${NC}" else echo -e "${YELLOW} (Skipped in dry-run mode)${NC}" fi echo "" # Check for uncommitted changes if [ "$DRY_RUN" = false ]; then if ! git diff --quiet; then echo -e "${YELLOW}โš ๏ธ Warning: You have uncommitted changes${NC}" read -p "Continue anyway? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo -e "${RED}โŒ Aborted by user${NC}" exit 1 fi fi fi # Function to check if a crate version is already published is_published() { local crate_name="$1" local version="$2" # Handle special case for zinit_client (directory) -> sal-zinit-client (package) local package_name="sal-$crate_name" if [ "$crate_name" = "zinit_client" ]; then package_name="sal-zinit-client" fi # Use cargo search to check if the exact version exists if cargo search "$package_name" --limit 1 | grep -q "$package_name.*$version"; then return 0 # Already published else return 1 # Not published fi } # Function to update dependencies in a crate's Cargo.toml update_dependencies() { local crate_dir="$1" local version="$2" if [ ! -f "$crate_dir/Cargo.toml" ]; then return fi # Create backup cp "$crate_dir/Cargo.toml" "$crate_dir/Cargo.toml.bak" # Update all SAL path dependencies to version dependencies sed -i.tmp "s|sal-text = { path = \"../text\" }|sal-text = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-os = { path = \"../os\" }|sal-os = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-process = { path = \"../process\" }|sal-process = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-git = { path = \"../git\" }|sal-git = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-vault = { path = \"../vault\" }|sal-vault = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-net = { path = \"../net\" }|sal-net = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-kubernetes = { path = \"../kubernetes\" }|sal-kubernetes = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-redisclient = { path = \"../redisclient\" }|sal-redisclient = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-postgresclient = { path = \"../postgresclient\" }|sal-postgresclient = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-virt = { path = \"../virt\" }|sal-virt = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-mycelium = { path = \"../mycelium\" }|sal-mycelium = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-zinit-client = { path = \"../zinit_client\" }|sal-zinit-client = \"$version\"|g" "$crate_dir/Cargo.toml" sed -i.tmp "s|sal-service-manager = { path = \"../service_manager\" }|sal-service-manager = \"$version\"|g" "$crate_dir/Cargo.toml" # Clean up temporary files rm -f "$crate_dir/Cargo.toml.tmp" } # Function to restore dependencies from backup restore_dependencies() { local crate_dir="$1" if [ -f "$crate_dir/Cargo.toml.bak" ]; then mv "$crate_dir/Cargo.toml.bak" "$crate_dir/Cargo.toml" fi } # Publish individual crates echo -e "${BLUE}๐Ÿ“ฆ Publishing individual crates...${NC}" echo "" for crate in "${CRATES[@]}"; do echo -e "${YELLOW}Publishing sal-$crate...${NC}" if [ ! -d "$crate" ]; then echo -e "${RED} โŒ Directory $crate not found${NC}" continue fi # Check if already published if [ "$DRY_RUN" = false ] && is_published "$crate" "$VERSION"; then # Handle special case for zinit_client display name display_name="sal-$crate" if [ "$crate" = "zinit_client" ]; then display_name="sal-zinit-client" fi echo -e "${GREEN} โœ… $display_name@$VERSION already published, skipping${NC}" echo "" continue fi # Update dependencies to use version numbers echo -e "${BLUE} ๐Ÿ“ Updating dependencies for sal-$crate...${NC}" update_dependencies "$crate" "$VERSION" cd "$crate" if [ "$DRY_RUN" = true ]; then echo -e "${BLUE} ๐Ÿ” Would run: cargo publish --allow-dirty${NC}" if is_published "$crate" "$VERSION"; then # Handle special case for zinit_client display name display_name="sal-$crate" if [ "$crate" = "zinit_client" ]; then display_name="sal-zinit-client" fi echo -e "${YELLOW} ๐Ÿ“ Note: $display_name@$VERSION already exists${NC}" fi else # Handle special case for zinit_client display name display_name="sal-$crate" if [ "$crate" = "zinit_client" ]; then display_name="sal-zinit-client" fi if cargo publish --allow-dirty; then echo -e "${GREEN} โœ… $display_name published successfully${NC}" else echo -e "${RED} โŒ Failed to publish $display_name${NC}" cd .. restore_dependencies "$crate" exit 1 fi fi cd .. # Restore original dependencies restore_dependencies "$crate" # Wait between publishes (except for the last one) if [ "$DRY_RUN" = false ]; then # Get the last element of the array last_crate="${CRATES[${#CRATES[@]}-1]}" if [ "$crate" != "$last_crate" ]; then echo -e "${BLUE} โณ Waiting $WAIT_TIME seconds for crates.io to process...${NC}" sleep "$WAIT_TIME" fi fi echo "" done # Publish main crate echo -e "${BLUE}๐Ÿ“ฆ Publishing main sal crate...${NC}" # Check if main crate is already published if [ "$DRY_RUN" = false ] && cargo search "sal" --limit 1 | grep -q "sal.*$VERSION"; then echo -e "${GREEN}โœ… sal@$VERSION already published, skipping${NC}" else if [ "$DRY_RUN" = true ]; then echo -e "${BLUE}๐Ÿ” Would run: cargo publish --allow-dirty${NC}" if cargo search "sal" --limit 1 | grep -q "sal.*$VERSION"; then echo -e "${YELLOW}๐Ÿ“ Note: sal@$VERSION already exists${NC}" fi else if cargo publish --allow-dirty; then echo -e "${GREEN}โœ… Main sal crate published successfully${NC}" else echo -e "${RED}โŒ Failed to publish main sal crate${NC}" exit 1 fi fi fi # Clean up any remaining backup files echo -e "${BLUE}๐Ÿงน Cleaning up backup files...${NC}" find . -name "Cargo.toml.bak" -delete 2>/dev/null || true echo "" echo -e "${GREEN}===============================================${NC}" echo -e "${GREEN} Publishing Complete!${NC}" echo -e "${GREEN}===============================================${NC}" echo "" if [ "$DRY_RUN" = true ]; then echo -e "${YELLOW}๐Ÿ” This was a dry run. No crates were actually published.${NC}" echo -e "${YELLOW} Run without --dry-run to publish for real.${NC}" else echo -e "${GREEN}๐ŸŽ‰ All SAL crates have been published to crates.io!${NC}" echo "" echo "Users can now install SAL modules with:" echo "" echo -e "${BLUE}# Individual crates${NC}" echo "cargo add sal-os sal-process sal-text" echo "" echo -e "${BLUE}# Meta-crate with features${NC}" echo "cargo add sal --features core" echo "cargo add sal --features all" echo "" echo "๐Ÿ“š See PUBLISHING.md for complete usage documentation." fi echo ""