From 1948995443d9b26f628cf4b228d1c69a82252f2f Mon Sep 17 00:00:00 2001 From: Jose Storopoli Date: Wed, 10 Jul 2024 14:22:36 +0000 Subject: [PATCH] ci: semver-check for non-additive cargo features Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> --- .github/workflows/semver-checks.yml | 24 +++- contrib/check-semver-feature.sh | 136 ++++++++++++++++++ .../{check-semver.sh => check-semver-pr.sh} | 0 3 files changed, 157 insertions(+), 3 deletions(-) create mode 100755 contrib/check-semver-feature.sh rename contrib/{check-semver.sh => check-semver-pr.sh} (100%) diff --git a/.github/workflows/semver-checks.yml b/.github/workflows/semver-checks.yml index eb89381c8..166200585 100644 --- a/.github/workflows/semver-checks.yml +++ b/.github/workflows/semver-checks.yml @@ -4,8 +4,8 @@ on: # yamllint disable-line rule:truthy name: Check semver breaks jobs: - API: - name: API - stable toolchain + PR: + name: PR Semver - nightly toolchain runs-on: ubuntu-latest strategy: fail-fast: false @@ -21,7 +21,7 @@ jobs: - name: "Binstall cargo-semver-checks" run: cargo binstall cargo-semver-checks --no-confirm - name: "Run semver checker script" - run: ./contrib/check-semver.sh + run: ./contrib/check-semver-pr.sh - name: Save PR number if: ${{ hashFiles('semver-break') != '' }} env: @@ -39,3 +39,21 @@ jobs: with: name: semver-break path: semver-break + + Feature: + name: Non additive cargo features - stable toolchain + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - name: "Checkout repo" + uses: actions/checkout@v4 + - name: "Install Rustup" + uses: dtolnay/rust-toolchain@stable + - name: "Install cargo-binstall" + uses: cargo-bins/cargo-binstall@main + - name: "Binstall cargo-semver-checks" + run: cargo binstall cargo-semver-checks --no-confirm + - name: "Run semver checker script" + run: ./contrib/check-semver-feature.sh + diff --git a/contrib/check-semver-feature.sh b/contrib/check-semver-feature.sh new file mode 100755 index 000000000..1bdf8ad1f --- /dev/null +++ b/contrib/check-semver-feature.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +# +# Checks semver compatibility between the `--no-features` and `all-features`. +# This is important since it tests for the presence non-additive cargo features. +# +# Under the hood uses cargo semver-checks to check for breaking changes. +# We cannot use it directly since it only supports checking against published +# crates. +# That's the intended use case for cargo semver-checks: +# you run before publishing a new version of a crate to check semver breaks. +# Here we are hacking it by first generating JSON files from cargo doc +# and then using those files to check for breaking changes with +# cargo semver-checks. + +set -euo pipefail + +# These are the hardcoded flags that cargo semver-checks uses +# under the hood to invoke rustdoc. +RUSTDOCFLAGS="-Z unstable-options --document-private-items --document-hidden-items --output-format=json --cap-lints=allow" + +main() { + # Generate JSON files for no-features and all-features + # 1. bitcoin + generate_json_files_all_features "bitcoin" + generate_json_files_no_default_features "bitcoin" + + # 2. base58ck + generate_json_files_all_features "base58ck" + generate_json_files_no_default_features "base58ck" + + # 3. bitcoin_hashes + generate_json_files_all_features "bitcoin_hashes" + generate_json_files_no_default_features "bitcoin_hashes" + + # 4. bitcoin-units + generate_json_files_all_features "bitcoin-units" + generate_json_files_no_default_features "bitcoin-units" + + # 5. bitcoin-io + generate_json_files_all_features "bitcoin-io" + generate_json_files_no_default_features "bitcoin-io" + + # Check for API semver non-addivite cargo features on all the generated JSON files above. + run_cargo_semver_check "bitcoin" + run_cargo_semver_check "base58ck" + run_cargo_semver_check "bitcoin_hashes" + run_cargo_semver_check "bitcoin-units" + run_cargo_semver_check "bitcoin-io" + + # Invoke cargo semver-checks to check for non-additive cargo features + # in all generated files. + check_for_non_additive_cargo_features +} + +# Run cargo doc with the cargo semver-checks rustdoc flags. +# We don't care about dependencies. +run_cargo_doc() { + RUSTDOCFLAGS="$RUSTDOCFLAGS" RUSTC_BOOTSTRAP=1 cargo doc --no-deps "$@" +} + +# Run cargo semver-check +run_cargo_semver_check() { + local crate="$1" + + echo "Running cargo semver-checks for $crate" + # Hack to not fail on errors. + # This is necessary since cargo semver-checks will fail if the + # semver check fails. + # We check that manually later. + set +e + cargo semver-checks -v --baseline-rustdoc "$crate-no-default-features.json" --current-rustdoc "$crate-all-features.json" > "$crate--additive-features.txt" 2>&1 + set -e +} + +# The following function uses cargo doc to generate JSON files that +# cargo semver-checks can use. +# - no-default-features: generate JSON doc files with no default features. +generate_json_files_no_default_features() { + local crate="$1" + + echo "Running cargo doc no-default-features for $crate" + run_cargo_doc --no-default-features -p "$crate" + + # replace _ for - in crate name. + # This is necessary since some crates have - in their name + # which will be converted to _ in the output file by cargo doc. + mv "target/doc/${crate//-/_}.json" "$crate-no-default-features.json" +} +# - all-features: generate JSON doc files with all features. +generate_json_files_all_features() { + local crate="$1" + + echo "Running cargo doc all-features for $crate" + run_cargo_doc --all-features -p "$crate" + + # replace _ for - in crate name. + # This is necessary since some crates have - in their name + # which will be converted to _ in the output file by cargo doc. + mv -v "target/doc/${crate//-/_}.json" "$crate-all-features.json" +} + +# Check if there are non-additive cargo features. +# We loop through all the generated files and check if there is a FAIL +# in the cargo semver-checks output. +# If we detect a fail, we create an empty file non-additive-cargo. +# If the following CI step finds this file, it will add: +# 1. a comment on the PR. +# 2. a label to the PR. +check_for_non_additive_cargo_features() { + for file in *additive-features.txt; do + echo "Checking $file" + if grep -q "FAIL" "$file"; then + echo "You have introduced non-additive cargo features" + echo "FAIL found in $file" + # flag it as a breaking change + # Handle the case where FAIL is found + touch non-additive-cargo + fi + done + if ! [ -f non-additive-cargo ]; then + echo "No non-additive cargo features found" + else + err "Non-additive cargo features found" + fi +} + +err() { + echo "$1" >&2 + exit 1 +} + +# +# Main script +# +main "$@" +exit 0 \ No newline at end of file diff --git a/contrib/check-semver.sh b/contrib/check-semver-pr.sh similarity index 100% rename from contrib/check-semver.sh rename to contrib/check-semver-pr.sh