Add GitHub Action to test master rustfmt formatting vs a feature branch

This new action is intended to help us maintainers determine when feature
branches cause breaking formatting changes by running rustfmt (master)
and the feature branch on various rust repositories.

Over time I expect the list of checked projects to increase.

With this action in place we can more easily test that a new feature or
bug fix doesn't introduce breaking changes. Although this action needs to
be manually triggered right now, we might consider adding it to our CI
runs in the future.
This commit is contained in:
Yacin Tmimi 2022-08-01 00:29:32 -04:00 committed by Caleb Cartwright
parent ea017d7f84
commit 949da529d7
2 changed files with 232 additions and 0 deletions

33
.github/workflows/check_diff.yml vendored Normal file
View file

@ -0,0 +1,33 @@
name: Diff Check
on:
workflow_dispatch:
inputs:
clone_url:
description: 'Git url of a rustfmt fork to compare against the latest master rustfmt'
required: true
branch_name:
description: 'Name of the feature branch on the forked repo'
required: true
commit_hash:
description: 'Optional commit hash from the feature branch'
required: false
rustfmt_configs:
description: 'Optional comma separated list of rustfmt config options to pass when running the feature branch'
required: false
jobs:
diff_check:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
- name: install rustup
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh
sh rustup-init.sh -y --default-toolchain none
rustup target add x86_64-unknown-linux-gnu
- name: check diff
run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash }} ${{ github.event.inputs.rustfmt_configs }}

199
ci/check_diff.sh Executable file
View file

@ -0,0 +1,199 @@
#!/bin/bash
function print_usage() {
echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]"
}
if [ $# -le 1 ]; then
print_usage
exit 1
fi
REMOTE_REPO=$1
FEATURE_BRANCH=$2
OPTIONAL_COMMIT_HASH=$3
OPTIONAL_RUSTFMT_CONFIGS=$4
# OUTPUT array used to collect all the status of running diffs on various repos
STATUSES=()
# Clone a git repository and cd into it.
#
# Parameters:
# $1: git clone url
# $2: directory where the repo should be cloned
function clone_repo() {
GIT_TERMINAL_PROMPT=0 git clone --quiet $1 --depth 1 $2 && cd $2
}
# Initialize Git submoduels for the repo.
#
# Parameters
# $1: list of directories to initialize
function init_submodules() {
git submodule update --init $1
}
# Run rusfmt with the --check flag to see if a diff is produced.
#
# Parameters:
# $1: Path to a rustfmt binary
# $2: Output file path for the diff
# $3: Any additional configuration options to pass to rustfmt
#
# Globlas:
# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4
function create_diff() {
local config;
if [ -z "$3" ]; then
config="--config=error_on_line_overflow=false,error_on_unformatted=false"
else
config="--config=error_on_line_overflow=false,error_on_unformatted=false,$OPTIONAL_RUSTFMT_CONFIGS"
fi
for i in `find . | grep "\.rs$"`
do
$1 --unstable-features --skip-children --check --color=always $config $i >> $2 2>/dev/null
done
}
# Run the master rustfmt binary and the feature branch binary in the current directory and compare the diffs
#
# Parameters
# $1: Name of the repository (used for logging)
#
# Globlas:
# $RUSFMT_BIN: Path to the rustfmt master binary. Created when running `compile_rustfmt`
# $FEATURE_BIN: Path to the rustfmt feature binary. Created when running `compile_rustfmt`
# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4
function check_diff() {
echo "running rustfmt (master) on $1"
create_diff $RUSFMT_BIN rustfmt_diff.txt
echo "running rustfmt (feature) on $1"
create_diff $FEATURE_BIN feature_diff.txt $OPTIONAL_RUSTFMT_CONFIGS
echo "checking diff"
local diff;
# we don't add color to the diff since we added color when running rustfmt --check.
# tail -n + 6 removes the git diff header info
# cut -c 2- removes the leading diff characters("+","-"," ") from running git diff.
# Again, the diff output we care about was already added when we ran rustfmt --check
diff=$(
git --no-pager diff --color=never \
--unified=0 --no-index rustfmt_diff.txt feature_diff.txt 2>&1 | tail -n +6 | cut -c 2-
)
if [ -z "$diff" ]; then
echo "no diff detected between rustfmt and the feture branch"
return 0
else
echo "$diff"
return 1
fi
}
# Compiles and produces two rustfmt binaries.
# One for the current master, and another for the feature branch
#
# Parameters:
# $1: Directory where rustfmt will be cloned
#
# Globlas:
# $REMOTE_REPO: Clone URL to the rustfmt fork that we want to test
# $FEATURE_BRANCH: Name of the feature branch
# $OPTIONAL_COMMIT_HASH: Optional commit hash that will be checked out if provided
function compile_rustfmt() {
RUSTFMT_REPO="https://github.com/rust-lang/rustfmt.git"
clone_repo $RUSTFMT_REPO $1
git remote add feature $REMOTE_REPO
git fetch feature $FEATURE_BRANCH
cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt
if [ -z "$OPTIONAL_COMMIT_HASH" ]; then
git switch $FEATURE_BRANCH
else
git switch $OPTIONAL_COMMIT_HASH --detach
fi
cargo build --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt
RUSFMT_BIN=$1/rustfmt
FEATURE_BIN=$1/feature_rustfmt
}
# Check the diff for running rustfmt and the feature branch on all the .rs files in the repo.
#
# Parameters
# $1: Clone URL for the repo
# $2: Name of the repo (mostly used for logging)
# $3: Path to any submodules that should be initialized
function check_repo() {
WORKDIR=$(pwd)
REPO_URL=$1
REPO_NAME=$2
SUBMODULES=$3
local tmp_dir;
tmp_dir=$(mktemp -d -t $REPO_NAME-XXXXXXXX)
clone_repo $REPO_URL $tmp_dir
if [ ! -z "$SUBMODULES" ]; then
init_submodules $SUBMODULES
fi
check_diff $REPO_NAME
# append the status of running `check_diff` to the STATUSES array
STATUSES+=($?)
echo "removing tmp_dir $tmp_dir"
rm -rf $tmp_dir
cd $WORKDIR
}
function main() {
tmp_dir=$(mktemp -d -t rustfmt-XXXXXXXX)
echo Created tmp_dir $tmp_dir
compile_rustfmt $tmp_dir
# run checks
check_repo "https://github.com/rust-lang/rust.git" rust-lang-rust
check_repo "https://github.com/rust-lang/cargo.git" cargo
check_repo "https://github.com/rust-lang/miri.git" miri
check_repo "https://github.com/rust-lang/rust-analyzer.git" rust-analyzer
check_repo "https://github.com/bitflags/bitflags.git" bitflags
check_repo "https://github.com/rust-lang/log.git" log
check_repo "https://github.com/rust-lang/mdBook.git" mdBook
check_repo "https://github.com/rust-lang/packed_simd.git" packed_simd
check_repo "https://github.com/rust-lang/rust-semverver.git" check_repo
check_repo "https://github.com/Stebalien/tempfile.git" tempfile
check_repo "https://github.com/rust-lang/futures-rs.git" futures-rs
check_repo "https://github.com/dtolnay/anyhow.git" anyhow
check_repo "https://github.com/dtolnay/thiserror.git" thiserror
check_repo "https://github.com/dtolnay/syn.git" syn
check_repo "https://github.com/serde-rs/serde.git" serde
check_repo "https://github.com/rust-lang/rustlings.git" rustlings
check_repo "https://github.com/rust-lang/rustup.git" rustup
check_repo "https://github.com/SergioBenitez/Rocket.git" Rocket
check_repo "https://github.com/rustls/rustls.git" rustls
check_repo "https://github.com/rust-lang/rust-bindgen.git" rust-bindgen
check_repo "https://github.com/hyperium/hyper.git" hyper
check_repo "https://github.com/actix/actix.git" actix
check_repo "https://github.com/denoland/deno.git" denoland_deno
# cleanup temp dir
echo removing tmp_dir $tmp_dir
rm -rf $tmp_dir
# figure out the exit code
for status in ${STATUSES[@]}
do
if [ $status -eq 1 ]; then
echo "formatting diff found 💔"
return 1
fi
done
echo "no diff found 😊"
}
main