check.bash
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bash
#
# This script will run the same checks as Encore's CI pipeline and report the same static analysis errors
# as the pipeline by default. It can be used to check for what errors might be reported by the pipeline
# before you commit and open a PR.
#
# Usage:
# ./check.bash [options]
#
# Options:
# --base <ref> The merge base to compare against (default: origin/main)
# --diff Show the diff against base instead of running the checks
# --filter-mode <mode> The filter mode to use for reviewdog; added, file, diff_context, nofilter (default: file)
# --all Alias for `--filter-mode nofilter` (runs checks against all files in the working directory)
#
# Examples:
#
# # Run the checks against files changed since branching from origin/main
# # (This is the default behavior and what our CI process does)
# ./check.bash
#
# # Show the diff between the current working directory and origin/main
# ./check.bash --diff
#
# # Run the checks against the entire working directory (regardless of changes made)
# ./check.bash --all
##############################################################################################################################
# Step 0: Setup the script with basic error handling #
##############################################################################################################################
set -euo pipefail
# nosemgrep
IFS=$'\n\t'
function errHandler() {
echo "Exiting due to an error line $1" >&2
echo "" >&2
awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">> ":""),$0 }' L="$1" "$0" >&2
}
trap 'errHandler $LINENO' ERR
##############################################################################################################################
# Step 1: Configure the script with the parameters the use wants #
##############################################################################################################################
# Parameters
WORK_DIR=$( dirname "${BASH_SOURCE[0]}" ) # Get the directory this script is in
BASE_REF="origin/main" # The merge base to compare against
DIFF_ONLY="false" # If true, show the diff instead of running the checks
FILTER_MODE="file" # The filter mode to use for reviewdog (added, file, diff_context, nofilter)
# Parse the command line arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--base)
BASE_REF="$2"
shift 2
;;
--diff)
DIFF_ONLY="true"
shift 1
;;
--filter-mode)
FILTER_MODE="$2"
shift 2
;;
--all)
FILTER_MODE="nofilter"
shift 1
;;
*)
echo "Unknown argument: $1"
exit 1
;;
esac
done
##############################################################################################################################
# Step 2: Check for required tools and error out if anything is missing which we can't install for the user #
##############################################################################################################################
# Check for tools we can't install using go
command -v go >/dev/null 2>&1 || { echo >&2 "go is required but not installed. Aborting."; exit 1; }
command -v git >/dev/null 2>&1 || { echo >&2 "git is required but not installed. Aborting."; exit 1; }
command -v sed >/dev/null 2>&1 || { echo >&2 "sed is required but not installed. Aborting."; exit 1; }
command -v semgrep >/dev/null 2>&1 || { echo >&2 "semgrep is required but not installed. Aborting."; exit 1; }
# Now install all missing tools
command -v reviewdog >/dev/null 2>&1 || go install github.com/reviewdog/reviewdog/cmd/reviewdog@latest || { echo >&2 "Unable to install reviewdog. Aborting."; exit 1; }
command -v staticcheck >/dev/null 2>&1 || go install honnef.co/go/tools/cmd/staticcheck@latest || { echo >&2 "Unable to install staticcheck. Aborting."; exit 1; }
command -v errcheck >/dev/null 2>&1 || go install github.com/kisielk/errcheck@latest || { echo >&2 "Unable to install errcheck. Aborting."; exit 1; }
command -v ineffassign >/dev/null 2>&1 || go install github.com/gordonklaus/ineffassign@latest || { echo >&2 "Unable to install ineffassign. Aborting."; exit 1; }
##############################################################################################################################
# Step 3: Create a diff of the changes in the working directory against the common ancestor of the current branch and main #
# This will be used to run static analysis checks on only the files that have changed. This diff should mimic the #
# diff that would be created by GitHub when all current changes are committed and pushed into a PR on GitHub. #
##############################################################################################################################
# Don't generate the diff if we don't need it to filter!
if [[ "$FILTER_MODE" != "nofilter" ]]; then
# Create a temp directory to store the common ancestor commit
TMP_DIR=$(mktemp -d)
if [[ ! "$TMP_DIR" || ! -d "$TMP_DIR" ]]; then
echo "Could not create temp dir"
exit 1
fi
# Create a temp file to store the diff we need
DIFF_FILE=$(mktemp)
if [[ ! "$DIFF_FILE" || ! -f "$DIFF_FILE" ]]; then
echo "Could not create temp diff file"
exit 1
fi
# Create a blank file to use as a comparison when a file is missing because either it's new or been deleted
BLANK_FILE=$(mktemp)
if [[ ! "$BLANK_FILE" || ! -f "$BLANK_FILE" ]]; then
echo "Could not create blank file"
exit 1
fi
# Clean up on exit and delete all the temp files we just created
function cleanup() {
rm -rf "$TMP_DIR"
rm -f "$DIFF_FILE"
rm -f "$BLANK_FILE"
}
trap cleanup EXIT
# Clone the repo into the temp directory
git clone -q "$WORK_DIR" "$TMP_DIR"
# Change our temp directory to be a clean copy of the common ancestor commit
pushd "$TMP_DIR" > /dev/null
git reset -q --hard HEAD
git checkout -q "$(git merge-base "$BASE_REF" HEAD)"
TRACKED_FILES_FROM_MAIN=$(git ls-files)
popd > /dev/null
# Create a list of files that we care about
MODIFICATIONS_IN_WORKING_DIR=$(git status --short | awk '{print $2}')
TRACKED_FILES_IN_WORKING_DIR=$(git ls-files)
ALL_FILES=$(echo "$TRACKED_FILES_IN_WORKING_DIR $MODIFICATIONS_IN_WORKING_DIR $TRACKED_FILES_FROM_MAIN" | tr ' ' '\n' | sort -u)
# Create a diff of the changes in the working directory against the common ancestor of the current branch and main
for file in $ALL_FILES; do
# If the original file doesn't exist, use a blank file instead
# (This means it was a new file that was added in the current version of the code base)
ORIGINAL_FILE="$TMP_DIR/$file"
if [[ ! -f "$ORIGINAL_FILE" ]]; then
ORIGINAL_FILE="$BLANK_FILE"
fi
# If the updated file doesn't exist, use a blank file instead
# (This means the file was deleted in the current version of the code base)
UPDATED_FILE="$WORK_DIR/$file"
if [[ ! -f "$UPDATED_FILE" ]]; then
UPDATED_FILE="$BLANK_FILE"
fi
# Run git diff between the original file and the updated file
# Replace the file paths in the diff to match the relative path in the working directory
# Then write the diff into our diff file
git diff "$ORIGINAL_FILE" "$UPDATED_FILE" | sed "s|$ORIGINAL_FILE|/$file|g" | sed "s|$UPDATED_FILE|/$file|g" >> "$DIFF_FILE" || true # Suppress the exit code
done
if [[ "$DIFF_ONLY" == "true" ]]; then
cat "$DIFF_FILE"
exit 0
fi
fi
##############################################################################################################################
# Step 4: Run review dog using the diff we just created, allowing reviewdog to only show errors from changes we've made #
##############################################################################################################################
if [[ "$FILTER_MODE" == "nofilter" ]]; then
reviewdog -filter-mode=nofilter
else
reviewdog -filter-mode="$FILTER_MODE" -diff="cat $DIFF_FILE"
fi