From 1f4cd3f38959958be30a3c163f7ee52a9fec8453 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 31 May 2023 09:52:42 -0400 Subject: [PATCH] New script to run cargo-semver-checks with appropriate options. The options are rather complicated; because we do not want to subject our experimental features to semver, we need to run generate JSON rustdoc on our own and then pass that JSON to cargo-semver-checks. This in turn requires us to use the same options that cargo-semver-checks uses, including "RUSTC_BOOTSTRAP". I've left some TODOs here in places where we will likely want to improve our code in the future. See #711. --- maint/semver-checks | 121 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100755 maint/semver-checks diff --git a/maint/semver-checks b/maint/semver-checks new file mode 100755 index 000000000..1d5bcb8a0 --- /dev/null +++ b/maint/semver-checks @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# +# Run "cargo-semver-checks" on each of our crates, with the "full" features +# set, comparing against a provided arti version. +# +# We can't just run "cargo-semver-checks" directly; it runs with +# --all-features unconditionally, and therefore would complain about +# breakage in our experimental features. +# +# Note that cargo-semver-checks has a fairly high false negative rate. +# If it tells you that something is broken, you probably need to bump +# a major or minor version; but if it _doesn't_ tell you something is +# broken, you are not in the clear. + +set -euo pipefail + +if [ -z "${1-}" ]; then + echo "Usage: $0 [git-tag]" + echo "Script will run cargo-semver-checks on changes since [git-tag]." + exit 1 +fi +LAST_VERSION="$1" + +if test "$(command -v cargo-semver-checks 2>/dev/null)" = ""; then + echo "cargo-semver-checks appears not to be installed." + echo "Try 'cargo install cargo-semver-checks.'" + exit 1 +fi + +TEMPDIR=$(mktemp -d -t arti_semver_checks."XX""XX""XX") +trap 'rm -rf "$TEMPDIR"' 0 + +MAINT_PATH="$(dirname "$0")" + +# Setting this envvar is extremely naughty, but it is what +# cargo-semver-checks does. It lets us use unstable options on stable +# rust. +# +# Using nightly is not a reliable option; cargo-semver-checks does not +# always accept the json rustdoc format that nightly produces. +export RUSTC_BOOTSTRAP=1 + +# These are the same flags that cargo-semver-checks uses. +export RUSTDOCFLAGS="\ + -Z unstable-options \ + --document-private-items \ + --document-hidden-items \ + --output-format=json \ + --cap-lints allow" + +# This is the crate that we document in order ot document all of our +# other crates. +# +# Note that this won't give quite correct results with arti <=1.1.4; +# "full" in one crate didn't imply "full" in all crates until 1.1.5 +# when fixup-features was merged and applied. +TOPLEVEL_CRATE=arti + +run_rustdoc () { + # TODO: This is not a great way to do this. It leaves the + # worktree around after the script is done. + if test ! -d sc_tmp; then + git worktree add sc_tmp "$LAST_VERSION" + fi + + # Note: We document a top-level crate directly, whereas + # cargo-semver-checks instead creates a temporary crate and documents + # that crate instead. + # + # It says that it does so in order to work around issues like + # https://github.com/obi1kenobi/cargo-semver-checks/issues/167#issuecomment-1382367128 + cargo doc --package "${TOPLEVEL_CRATE}" --features=full + + cd sc_tmp + git checkout "$LAST_VERSION" + cargo doc --package "${TOPLEVEL_CRATE}" --features=full + cd .. +} + +run_semver_checks () { + BREAKING=() + + for package in $("$MAINT_PATH"/list_crates); do + echo "==== $package" 1>&2 + if grep "^publish *= *false" "crates/$package/Cargo.toml" >/dev/null; then + echo "...publish=false; skipping." 1>&2 + continue + fi + fname="${package//-/_}" + if ! test -f "target/doc/$fname.json" ; then + echo "...no documentation found; skipping." 1>&2 + continue + fi + tmpfile="$TEMPDIR/output.txt" + rm -f "$tmpfile" + x=ok + # We use "script" here to capture the output without + # overriding the terminal settings, as would happen if we used + # tee. + script -efq "$tmpfile" -c "cargo semver-checks check-release \ + -p \"$package\" \ + --baseline-rustdoc \"sc_tmp/target/doc/$fname.json\" \ + --current-rustdoc \"target/doc/$fname.json\"" || x=failed + if test "$x" = "failed"; then + ISSUE=$(grep "Final.*semver requires " "$tmpfile" || true) + ISSUE=${ISSUE/*:/} + BREAKING+=("$(printf "%-24s %s" "$package" "$ISSUE")") + fi + done + + if test "${#BREAKING[@]}" -ne "0"; then + echo "semver-checks reported errors:" + for br in "${BREAKING[@]}"; do + echo " $br" + done + fi +} + +# TODO: Add a flag to skip the run_rustdoc step: it can be kinda slow. +run_rustdoc +run_semver_checks