name: 'cargo-semver-checks' description: 'Ensure the public API in your Rust crate follows semantic versioning' inputs: crate-name: description: 'The crate whose API to check for semver' required: false default: '' crate-target: description: 'By default, check the library target of the crate. To check a different target (e.g. a binary target), set this to `--bin `' required: false default: '--lib' version-tag-prefix: description: 'The prefix to use for the git tag for a version; the default "v" creates tags like "v1.0.0"' required: false default: 'v' runs: using: "composite" steps: - name: Install rust uses: actions-rs/toolchain@v1 with: toolchain: nightly profile: minimal - name: Build rustdoc and check it shell: bash run: | set -euxo pipefail # Colorize output, since GitHub Actions terminals support color. export CARGO_TERM_COLOR=always # Record the current git sha, so we can come back to it after generating the baseline. export CURRENT_GIT_SHA="$(git rev-parse HEAD)" # We add `--all-features` to semver-check every part of the crate. # In principle, semver can be broken by moving public API code into a feature. # Checking this requires rebuilding rustdoc multiple times with different sets of features, # which can get expensive and is therefore left to individual maintainers' discretion. # # We add `--document-private-items` because for some reason, rustdoc seems to not always # generate all implemented trait information without it: # https://github.com/obi1kenobi/cargo-semver-check/issues/32 export RUSTDOC_EARLY_FLAGS="${{ inputs.crate-target }} --all-features" export RUSTDOC_LATE_FLAGS="--document-private-items -Zunstable-options --output-format json" export PACKAGE_NAME="${{ inputs.crate-name }}" if [[ "$PACKAGE_NAME" == '' ]]; then export PACKAGE_NAME="$("$GITHUB_ACTION_PATH/find_workspace_crates.sh")" else # cargo rustdoc uses the exact package name, not the "underscores" version. export RUSTDOC_EARLY_FLAGS="--package $PACKAGE_NAME $RUSTDOC_EARLY_FLAGS" fi export PACKAGE_NAME_WITH_UNDERSCORES="$(echo "$PACKAGE_NAME" | tr '-' '_')" # Switch to the tag for the correct baseline version, # then build rustdoc JSON. # # We *do not* want to record and reuse the target directory path # across different git commits, since it may be at a different location # in different commits. export COMPARISON_TAG="${{ inputs.version-tag-prefix }}$("$GITHUB_ACTION_PATH/find_comparison_version.sh" "$PACKAGE_NAME")" git fetch --depth=1 origin "+refs/tags/$COMPARISON_TAG:refs/tags/$COMPARISON_TAG" git checkout "$COMPARISON_TAG" cargo +nightly rustdoc $RUSTDOC_EARLY_FLAGS -- $RUSTDOC_LATE_FLAGS mv "$(cargo metadata --format-version 1 | jq -r .target_directory)/doc/$PACKAGE_NAME_WITH_UNDERSCORES.json" /tmp/baseline.json # Return to the original git sha. git checkout "$CURRENT_GIT_SHA" # Build rustdoc JSON for the current version, and move it to /tmp/ # so it doesn't get overwritten by the baseline build. cargo +nightly rustdoc $RUSTDOC_EARLY_FLAGS -- $RUSTDOC_LATE_FLAGS mv "$(cargo metadata --format-version 1 | jq -r .target_directory)/doc/$PACKAGE_NAME_WITH_UNDERSCORES.json" /tmp/current.json # Check for semver violations. cargo install cargo-semver-checks cargo semver-checks check-release --current /tmp/current.json --baseline /tmp/baseline.json