name: Publish SAL Crates on: release: types: [published] workflow_dispatch: inputs: version: description: 'Version to publish (e.g., 0.1.0)' required: true type: string dry_run: description: 'Dry run (do not actually publish)' required: false type: boolean default: false env: CARGO_TERM_COLOR: always jobs: publish: name: Publish to crates.io runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: toolchain: stable - name: Cache Cargo dependencies uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- - name: Install cargo-edit for version management run: cargo install cargo-edit - name: Set version from release tag if: github.event_name == 'release' run: | VERSION=${GITHUB_REF#refs/tags/v} echo "PUBLISH_VERSION=$VERSION" >> $GITHUB_ENV echo "Publishing version: $VERSION" - name: Set version from workflow input if: github.event_name == 'workflow_dispatch' run: | echo "PUBLISH_VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV echo "Publishing version: ${{ github.event.inputs.version }}" - name: Update version in all crates run: | echo "Updating version to $PUBLISH_VERSION" # Update root Cargo.toml cargo set-version $PUBLISH_VERSION # Update each crate CRATES=(os process text net git vault kubernetes virt redisclient postgresclient zinit_client mycelium rhai) for crate in "${CRATES[@]}"; do if [ -d "$crate" ]; then cd "$crate" cargo set-version $PUBLISH_VERSION cd .. echo "Updated $crate to version $PUBLISH_VERSION" fi done - name: Run tests run: cargo test --workspace --verbose - name: Check formatting run: cargo fmt --all -- --check - name: Run clippy run: cargo clippy --workspace --all-targets --all-features -- -D warnings - name: Dry run publish (check packages) run: | echo "Checking all packages can be published..." CRATES=(os process text net git vault kubernetes virt redisclient postgresclient zinit_client mycelium rhai) for crate in "${CRATES[@]}"; do if [ -d "$crate" ]; then echo "Checking $crate..." cd "$crate" cargo publish --dry-run cd .. fi done echo "Checking main crate..." cargo publish --dry-run - name: Publish crates (dry run) if: github.event.inputs.dry_run == 'true' run: | echo "🔍 DRY RUN MODE - Would publish the following crates:" echo "Individual crates: sal-os, sal-process, sal-text, sal-net, sal-git, sal-vault, sal-kubernetes, sal-virt, sal-redisclient, sal-postgresclient, sal-zinit-client, sal-mycelium, sal-rhai" echo "Meta-crate: sal" echo "Version: $PUBLISH_VERSION" - name: Publish individual crates if: github.event.inputs.dry_run != 'true' env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} run: | echo "Publishing individual crates..." # Crates in dependency order CRATES=(os process text net git vault kubernetes virt redisclient postgresclient zinit_client mycelium rhai) for crate in "${CRATES[@]}"; do if [ -d "$crate" ]; then echo "Publishing sal-$crate..." cd "$crate" # Retry logic for transient failures for attempt in 1 2 3; do if cargo publish --token $CARGO_REGISTRY_TOKEN; then echo "✅ sal-$crate published successfully" break else if [ $attempt -eq 3 ]; then echo "❌ Failed to publish sal-$crate after 3 attempts" exit 1 else echo "⚠️ Attempt $attempt failed, retrying in 30 seconds..." sleep 30 fi fi done cd .. # Wait for crates.io to process if [ "$crate" != "rhai" ]; then echo "⏳ Waiting 30 seconds for crates.io to process..." sleep 30 fi fi done - name: Publish main crate if: github.event.inputs.dry_run != 'true' env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} run: | echo "Publishing main sal crate..." # Wait a bit longer before publishing the meta-crate echo "⏳ Waiting 60 seconds for all individual crates to be available..." sleep 60 # Retry logic for the main crate for attempt in 1 2 3; do if cargo publish --token $CARGO_REGISTRY_TOKEN; then echo "✅ Main sal crate published successfully" break else if [ $attempt -eq 3 ]; then echo "❌ Failed to publish main sal crate after 3 attempts" exit 1 else echo "⚠️ Attempt $attempt failed, retrying in 60 seconds..." sleep 60 fi fi done - name: Create summary if: always() run: | echo "## 📦 SAL Publishing Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Version:** $PUBLISH_VERSION" >> $GITHUB_STEP_SUMMARY echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then echo "**Mode:** Dry Run" >> $GITHUB_STEP_SUMMARY else echo "**Mode:** Live Publishing" >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY echo "### Published Crates" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "- sal-os" >> $GITHUB_STEP_SUMMARY echo "- sal-process" >> $GITHUB_STEP_SUMMARY echo "- sal-text" >> $GITHUB_STEP_SUMMARY echo "- sal-net" >> $GITHUB_STEP_SUMMARY echo "- sal-git" >> $GITHUB_STEP_SUMMARY echo "- sal-vault" >> $GITHUB_STEP_SUMMARY echo "- sal-kubernetes" >> $GITHUB_STEP_SUMMARY echo "- sal-virt" >> $GITHUB_STEP_SUMMARY echo "- sal-redisclient" >> $GITHUB_STEP_SUMMARY echo "- sal-postgresclient" >> $GITHUB_STEP_SUMMARY echo "- sal-zinit-client" >> $GITHUB_STEP_SUMMARY echo "- sal-mycelium" >> $GITHUB_STEP_SUMMARY echo "- sal-rhai" >> $GITHUB_STEP_SUMMARY echo "- sal (meta-crate)" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Usage" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo '```bash' >> $GITHUB_STEP_SUMMARY echo "# Individual crates" >> $GITHUB_STEP_SUMMARY echo "cargo add sal-os sal-process sal-text" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "# Meta-crate with features" >> $GITHUB_STEP_SUMMARY echo "cargo add sal --features core" >> $GITHUB_STEP_SUMMARY echo "cargo add sal --features all" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY