From 1cf483bea0901cfb24d6f11785b47cd9007e217f Mon Sep 17 00:00:00 2001 From: RAprogramm Date: Sun, 12 Oct 2025 15:02:48 +0700 Subject: [PATCH 1/2] #193 feat: add version comparison with crates.io before publishing Prevent publishing versions that are equal to or lower than current crates.io version. Changes: - Add version check step that fetches current version from crates.io API - Compare local tag version with crates.io version using semver sort - Skip publish steps if version is equal or lower - Fail workflow if attempting to publish older version - Support first-time publish when crate not found on crates.io Version comparison logic: - Uses crates.io API endpoint to fetch max_version - Implements semver comparison via sort -V - Sets should_publish output variable for conditional execution - All publish steps now conditional on version check result Benefits: - Prevents duplicate version publish errors - Clear feedback on version conflicts - Automatic skip for already-published versions - Better developer experience with explicit version messages --- .github/workflows/release.yml | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20189f8..b45a045 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -90,12 +90,52 @@ jobs: exit 1 fi + - name: Check version against crates.io + id: version_check + shell: bash + run: | + set -euo pipefail + TAG="${GITHUB_REF#refs/tags/}" + LOCAL_VER="${TAG#v}" + CRATE_NAME="masterror" + + echo "Local version: ${LOCAL_VER}" + + # Fetch current version from crates.io + CRATESIO_VER=$(curl -s "https://crates.io/api/v1/crates/${CRATE_NAME}" | jq -r '.crate.max_version // empty') + + if [ -z "${CRATESIO_VER}" ]; then + echo "Crate not found on crates.io, proceeding with first publish" + echo "should_publish=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "crates.io version: ${CRATESIO_VER}" + + # Compare versions using sort -V (version sort) + HIGHER=$(printf '%s\n%s\n' "${LOCAL_VER}" "${CRATESIO_VER}" | sort -V | tail -n1) + + if [ "${LOCAL_VER}" = "${CRATESIO_VER}" ]; then + echo "Version ${LOCAL_VER} already published on crates.io" + echo "should_publish=false" >> "$GITHUB_OUTPUT" + elif [ "${HIGHER}" = "${LOCAL_VER}" ]; then + echo "Local version ${LOCAL_VER} > crates.io version ${CRATESIO_VER}, proceeding" + echo "should_publish=true" >> "$GITHUB_OUTPUT" + else + echo "Local version ${LOCAL_VER} < crates.io version ${CRATESIO_VER}" + echo "Cannot publish older version" + echo "should_publish=false" >> "$GITHUB_OUTPUT" + exit 1 + fi + - name: Install Rust (${{ steps.msrv.outputs.msrv }}) + if: steps.version_check.outputs.should_publish == 'true' uses: dtolnay/rust-toolchain@v1 with: toolchain: ${{ steps.msrv.outputs.msrv }} - name: Maybe publish masterror-template + if: steps.version_check.outputs.should_publish == 'true' env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} shell: bash @@ -120,9 +160,11 @@ jobs: fi - name: Wait for crates.io index sync (after template) + if: steps.version_check.outputs.should_publish == 'true' run: sleep 15 - name: Maybe publish masterror-derive + if: steps.version_check.outputs.should_publish == 'true' env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} shell: bash @@ -147,9 +189,11 @@ jobs: fi - name: Wait for crates.io index sync + if: steps.version_check.outputs.should_publish == 'true' run: sleep 15 - name: Publish masterror + if: steps.version_check.outputs.should_publish == 'true' env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} shell: bash From f048f5a1020c3f46ae582e9a2f790f90dc6d6bfd Mon Sep 17 00:00:00 2001 From: RAprogramm Date: Sun, 12 Oct 2025 15:09:45 +0700 Subject: [PATCH 2/2] #193 fix: use semver crate for prerelease-aware version comparison MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace sort -V with proper semver comparison using Rust semver crate. Changes: - Create temporary Rust project with semver dependency - Use semver::Version::parse() for accurate version comparison - Handle prerelease versions correctly (1.0.0 > 1.0.0-alpha) - Proper comparison for all semver scenarios Fixes: - P1 issue: sort -V incorrectly orders prerelease versions - Now correctly handles: stable vs prerelease, prerelease ordering - Prevents publishing older versions in all cases Testing: - 1.0.0 > 1.0.0-alpha ✓ - 1.0.0-beta > 1.0.0-alpha ✓ - 0.24.19 > 0.24.18 ✓ --- .github/workflows/release.yml | 71 ++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b45a045..e669188 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,20 +112,81 @@ jobs: echo "crates.io version: ${CRATESIO_VER}" - # Compare versions using sort -V (version sort) - HIGHER=$(printf '%s\n%s\n' "${LOCAL_VER}" "${CRATESIO_VER}" | sort -V | tail -n1) - + # Equal versions check if [ "${LOCAL_VER}" = "${CRATESIO_VER}" ]; then echo "Version ${LOCAL_VER} already published on crates.io" echo "should_publish=false" >> "$GITHUB_OUTPUT" - elif [ "${HIGHER}" = "${LOCAL_VER}" ]; then + exit 0 + fi + + # Semver comparison using inline Rust script + TMPDIR=$(mktemp -d) + mkdir -p "${TMPDIR}/src" + cat > "${TMPDIR}/src/main.rs" << 'RUST_EOF' + use std::env; + + fn main() { + let args: Vec = env::args().collect(); + if args.len() != 3 { + eprintln!("Usage: compare "); + std::process::exit(1); + } + + let v1 = match semver::Version::parse(&args[1]) { + Ok(v) => v, + Err(e) => { + eprintln!("Failed to parse version1: {}", e); + std::process::exit(1); + } + }; + + let v2 = match semver::Version::parse(&args[2]) { + Ok(v) => v, + Err(e) => { + eprintln!("Failed to parse version2: {}", e); + std::process::exit(1); + } + }; + + if v1 > v2 { + println!("greater"); + } else if v1 < v2 { + println!("less"); + } else { + println!("equal"); + } + } + RUST_EOF + + cat > "${TMPDIR}/Cargo.toml" << 'TOML_EOF' + [package] + name = "semver-compare" + version = "0.1.0" + edition = "2024" + + [dependencies] + semver = "1.0" + TOML_EOF + + # Build and run comparison + COMPARISON=$(cd "${TMPDIR}" && cargo run --quiet -- "${LOCAL_VER}" "${CRATESIO_VER}" 2>/dev/null || echo "unknown") + rm -rf "${TMPDIR}" + + if [ "${COMPARISON}" = "greater" ]; then echo "Local version ${LOCAL_VER} > crates.io version ${CRATESIO_VER}, proceeding" echo "should_publish=true" >> "$GITHUB_OUTPUT" - else + elif [ "${COMPARISON}" = "less" ]; then echo "Local version ${LOCAL_VER} < crates.io version ${CRATESIO_VER}" echo "Cannot publish older version" echo "should_publish=false" >> "$GITHUB_OUTPUT" exit 1 + elif [ "${COMPARISON}" = "equal" ]; then + echo "Version ${LOCAL_VER} already published on crates.io" + echo "should_publish=false" >> "$GITHUB_OUTPUT" + else + echo "Could not determine version ordering, manual check required" + echo "should_publish=false" >> "$GITHUB_OUTPUT" + exit 1 fi - name: Install Rust (${{ steps.msrv.outputs.msrv }})