fix(ci): skip crates-publish when version already published on crates.io #14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: crates-publish | |
| on: | |
| workflow_call: | |
| inputs: | |
| version: | |
| required: true | |
| type: string | |
| push: | |
| branches: | |
| - master | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | |
| permissions: | |
| contents: read | |
| id-token: write | |
| checks: write | |
| defaults: | |
| run: | |
| shell: bash | |
| working-directory: rust | |
| jobs: | |
| version: | |
| name: resolve version | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| skip: ${{ steps.skip.outputs.skip }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Read version | |
| id: version | |
| working-directory: rust | |
| run: | | |
| if [ -n "${{ inputs.version }}" ]; then | |
| version="${{ inputs.version }}" | |
| else | |
| version=$(node -p "require('./package.json').version") | |
| fi | |
| echo "version=${version}" >> "$GITHUB_OUTPUT" | |
| - name: Check if already published | |
| id: skip | |
| working-directory: rust | |
| run: | | |
| version="${{ steps.version.outputs.version }}" | |
| if cargo info "jscpd@${version}" >/dev/null 2>&1; then | |
| echo "jscpd@${version} is already published on crates.io; skipping" | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| publish: | |
| name: publish crates.io packages | |
| needs: version | |
| if: needs.version.outputs.skip != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: "1.93" | |
| - name: Cache Cargo registry and target | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: "rust -> rust/target" | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Install Node | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: "22" | |
| - name: Sync version from package.json | |
| run: node scripts/sync-version.mjs | |
| - name: Install cargo-nextest | |
| uses: taiki-e/install-action@nextest | |
| - name: Test | |
| run: cargo nextest run --workspace --locked --profile ci | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: nextest-results-crates-publish | |
| path: rust/target/nextest/ci/nextest.xml | |
| if-no-files-found: warn | |
| - name: Test Report | |
| if: always() | |
| uses: dorny/test-reporter@v1 | |
| with: | |
| name: Rust Tests (crates-publish) | |
| path: rust/target/nextest/ci/nextest.xml | |
| reporter: java-junit | |
| fail-on-error: false | |
| - name: Remove publish = false from all crates | |
| run: | | |
| find crates -name Cargo.toml -exec sed -i '/^publish = false$/d' {} \; | |
| sed -i '/^publish = false$/d' Cargo.toml | |
| echo "Removed publish = false from all crates" | |
| - name: Smoke test (build and version check) | |
| run: | | |
| cargo build --release --locked -p jscpd | |
| BUILT_VERSION=$(./target/release/cpd --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "NOT_FOUND") | |
| echo "Built version: ${BUILT_VERSION}" | |
| echo "Expected version: ${{ needs.version.outputs.version }}" | |
| [ "$BUILT_VERSION" = "${{ needs.version.outputs.version }}" ] || { echo "Version mismatch!"; exit 1; } | |
| - name: Authenticate to crates.io (Trusted Publishing) | |
| id: crates-io-auth | |
| uses: rust-lang/crates-io-auth-action@v1.0.4 | |
| - name: Publish cpd-core | |
| run: | | |
| version="${{ needs.version.outputs.version }}" | |
| if cargo info "cpd-core@${version}" >/dev/null 2>&1; then | |
| echo "cpd-core@${version} already published; skipping" | |
| else | |
| cargo publish --locked -p cpd-core --allow-dirty | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token || secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Wait for cpd-core index propagation | |
| run: | | |
| echo "Waiting for crates.io index to update for cpd-core..." | |
| for i in $(seq 1 30); do | |
| if cargo info "cpd-core@${{ needs.version.outputs.version }}" >/dev/null 2>&1; then | |
| echo "cpd-core@${{ needs.version.outputs.version }} is available on crates.io" | |
| break | |
| fi | |
| echo "Attempt $i/30: waiting for index..." | |
| sleep 10 | |
| done | |
| - name: Publish cpd-tokenizer | |
| run: | | |
| version="${{ needs.version.outputs.version }}" | |
| if cargo info "cpd-tokenizer@${version}" >/dev/null 2>&1; then | |
| echo "cpd-tokenizer@${version} already published; skipping" | |
| else | |
| cargo publish --locked -p cpd-tokenizer --allow-dirty | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token || secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Publish cpd-reporter | |
| run: | | |
| version="${{ needs.version.outputs.version }}" | |
| if cargo info "cpd-reporter@${version}" >/dev/null 2>&1; then | |
| echo "cpd-reporter@${version} already published; skipping" | |
| else | |
| cargo publish --locked -p cpd-reporter --allow-dirty | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token || secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Wait for cpd-tokenizer and cpd-reporter index propagation | |
| run: | | |
| echo "Waiting for crates.io index to update..." | |
| for crate in cpd-tokenizer cpd-reporter; do | |
| for i in $(seq 1 30); do | |
| if cargo info "${crate}@${{ needs.version.outputs.version }}" >/dev/null 2>&1; then | |
| echo "${crate}@${{ needs.version.outputs.version }} is available on crates.io" | |
| break | |
| fi | |
| echo "Attempt $i/30 for ${crate}: waiting..." | |
| sleep 10 | |
| done | |
| done | |
| - name: Publish cpd-finder | |
| run: | | |
| version="${{ needs.version.outputs.version }}" | |
| if cargo info "cpd-finder@${version}" >/dev/null 2>&1; then | |
| echo "cpd-finder@${version} already published; skipping" | |
| else | |
| cargo publish --locked -p cpd-finder --allow-dirty | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token || secrets.CARGO_REGISTRY_TOKEN }} | |
| - name: Wait for cpd-finder index propagation | |
| run: | | |
| echo "Waiting for crates.io index to update for cpd-finder..." | |
| for i in $(seq 1 30); do | |
| if cargo info "cpd-finder@${{ needs.version.outputs.version }}" >/dev/null 2>&1; then | |
| echo "cpd-finder@${{ needs.version.outputs.version }} is available on crates.io" | |
| break | |
| fi | |
| echo "Attempt $i/30: waiting..." | |
| sleep 10 | |
| done | |
| - name: Publish jscpd | |
| run: | | |
| version="${{ needs.version.outputs.version }}" | |
| if cargo info "jscpd@${version}" >/dev/null 2>&1; then | |
| echo "jscpd@${version} already published; skipping" | |
| else | |
| cargo publish --locked -p jscpd --allow-dirty | |
| fi | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.crates-io-auth.outputs.token || secrets.CARGO_REGISTRY_TOKEN }} |