Last active
February 19, 2026 21:32
-
-
Save dylarcher/699d3c12f996b47e0cca98be4628cbef to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| #? Fixes/Checks ONLY current branch file(s) changes, e.g: | |
| # audit-changes.sh [options] | |
| # | |
| # General: | |
| # -h, --help Show this help text and exit | |
| # -u, --update-snapshot Update Jest snapshots (jest -u) | |
| # -c, --coverage Collect test coverage (jest --coverage) | |
| # -p, --prune Suppress verbose logs in final report [default] | |
| # -P, --no-prune Show full output for all checks | |
| # --unsafe Aggressive eslint fixes: all files, --max-warnings=-1, | |
| # all --fix-type categories (directive,problem,suggestion,layout) | |
| # | |
| # ESLint / Prettier: | |
| # --cache Enable caching for eslint AND prettier | |
| # --quiet Report errors only, suppress warnings (eslint ONLY) | |
| # --fix-type <types> Comma-separated eslint fix types: directive,problem,suggestion,layout (eslint ONLY) | |
| # --concurrency <n> ESLint thread count (eslint ONLY) | |
| # --log-level <level> Prettier log level: silent|error|warn|log|debug (prettier ONLY) | |
| # | |
| # Jest: | |
| # -b, --bail Exit after first test failure (jest --bail) | |
| # --verbose Show individual test results (jest --verbose) | |
| # --silent Suppress console output in tests (jest --silent) | |
| # --maxWorkers <n> Max jest worker threads (jest --maxWorkers) | |
| # --forceExit Force jest to exit after tests (jest --forceExit) | |
| # | |
| # TypeScript: | |
| # --pretty Colorize tsc output (tsc --pretty) | |
| # | |
| # Conveyor: | |
| # --slice <n/m> Conveyor test slice [default: 1/5] | |
| # --retry <n> Conveyor retry attempts [default: 2] | |
| # --stop-on-fail Stop conveyor on first failure | |
| # --no-parallel, --noAsync Disable parallel conveyor execution | |
| # | |
| # stage a/sync: Progress tracking implemented via background process monitoring | |
| # stage b/async: Complex scenario support added with retry logic and filtering | |
| # | |
| #* [A/Sync] sequentially fixes code quality issues… (e.g., all "safe" problems, warnings & errors) | |
| # 1. eslint: git diff --name-only origin/master...HEAD | xargs npx eslint --no-error-on-unmatched-pattern --no-warn-ignored --stats --fix | |
| # 2. prettier: git diff --name-only origin/master...HEAD | xargs npx prettier --no-error-on-unmatched-pattern -uw | |
| # 2b. (optional): unsafe mode allows additional fixes for complex issues when --unsafe flag is provided | |
| # | |
| #* [B/Async] audits read-only checks/tests in parallel with progress tracking, then outputs results… | |
| # - type-checks: bash -c 'npx tsc --noEmit 2>&1 | grep -F -f <(git diff --name-only origin/master...HEAD -- "*.ts" "*.tsx" "*.js" "*.jsx" "*.mjs" "*.cjs" | grep -vE "\.(test|spec)\.(ts|tsx|js|jsx|mjs|cjs)$")' | |
| # - test-suites: npx jest --changedSince=origin/master [-u] [--coverage] #? Pass `-u` to forward the snapshot-update flag to update Jest screen captures | |
| # - git-actions: conveyor test --changed-only=origin/master...HEAD --slice <n/m> ALL (with retry logic) | |
| # - cpgit-hooks: npx lint-staged with enhanced error filtering and reporting | |
| # general | |
| prune_flag=true # clean up output by default (local) | |
| # eslint/prettier pass-throughs | |
| unsafe_flag=false # safe fixes only by default (eslint ONLY) | |
| quiet_flag="" # report all by default (eslint ONLY) | |
| fix_type_flag="" # default fix types, all when --fix (eslint ONLY) | |
| concurrency_flag="" # default threading (eslint ONLY) | |
| cache_flag="" # no caching by default (eslint & prettier) | |
| log_level_flag="" # default log level (prettier ONLY) | |
| # jest pass-throughs | |
| u_flag="" # no snapshot updates by default | |
| coverage_flag="" # no coverage by default | |
| bail_flag="" # don't bail by default | |
| verbose_flag="" # default verbosity | |
| silent_flag="" # default console output | |
| max_workers_flag="" # default worker count | |
| force_exit_flag="" # don't force exit by default | |
| # tsc pass-throughs | |
| tsc_pretty_flag="" # default pretty | |
| # conveyor pass-throughs | |
| slice="1/5" # default slice | |
| retry=2 # default retry attempts | |
| stop_on_fail_flag="" # don't stop on fail by default | |
| no_parallel_flag="" # parallel by default | |
| show_help() { | |
| sed -n '/^#[?*]/,/^[^#]/{s/^#[?* ] \{0,1\}//p;}' "$0" | |
| exit 0 | |
| } | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -h|--help) show_help ;; | |
| -p|--prune) prune_flag=true ;; | |
| -P|--no-prune|--noPrune) prune_flag=false ;; | |
| # eslint/prettier-specific | |
| --unsafe) unsafe_flag=true ;; | |
| --cache) cache_flag="--cache" ;; | |
| --quiet) quiet_flag="--quiet" ;; | |
| --fix-type|--fixType) fix_type_flag="--fix-type $2"; shift ;; | |
| --concurrency) concurrency_flag="--concurrency $2"; shift ;; | |
| --log-level|--logLevel) log_level_flag="--log-level $2"; shift ;; | |
| # jest-specific | |
| -u|--update-snapshot|--updateSnapshot) u_flag="-u" ;; | |
| -c|--coverage) coverage_flag="--coverage" ;; | |
| -b|--bail) bail_flag="--bail" ;; | |
| --verbose) verbose_flag="--verbose" ;; | |
| --silent) silent_flag="--silent" ;; | |
| --max-workers|--maxWorkers) max_workers_flag="--maxWorkers=$2"; shift ;; | |
| --force-exit|--forceExit) force_exit_flag="--forceExit" ;; | |
| # tsc-specific | |
| --pretty) tsc_pretty_flag="--pretty" ;; | |
| # conveyor-specific | |
| --slice) slice="$2"; shift ;; | |
| --retry) retry="$2"; shift ;; | |
| --stop-on-fail|--stopOnFail) stop_on_fail_flag="--stop-on-fail" ;; | |
| --no-parallel|--no-async|--noParallel|--noAsync) no_parallel_flag="--no-parallel" ;; | |
| *) echo "Unknown option: $1" >&2; exit 1 ;; | |
| esac | |
| shift | |
| done | |
| changed=$(git diff --name-only origin/master...HEAD) | |
| if [ -z "$changed" ]; then | |
| echo "No files changed since origin/master." | |
| exit 0 | |
| fi | |
| tmp=$(mktemp -d) | |
| trap 'rm -rf "$tmp"' EXIT | |
| eslint_args="--no-error-on-unmatched-pattern --no-warn-ignored --stats --fix" | |
| [[ -n "$cache_flag" ]] && eslint_args+=" $cache_flag" | |
| [[ -n "$quiet_flag" ]] && eslint_args+=" $quiet_flag" | |
| [[ -n "$fix_type_flag" ]] && eslint_args+=" $fix_type_flag" | |
| [[ -n "$concurrency_flag" ]] && eslint_args+=" $concurrency_flag" | |
| prettier_args="--no-error-on-unmatched-pattern -uw" | |
| [[ -n "$cache_flag" ]] && prettier_args+=" $cache_flag" | |
| [[ -n "$log_level_flag" ]] && prettier_args+=" $log_level_flag" | |
| jest_args="--changedSince=origin/master" | |
| [[ -n "$u_flag" ]] && jest_args+=" $u_flag" | |
| [[ -n "$coverage_flag" ]] && jest_args+=" $coverage_flag" | |
| [[ -n "$bail_flag" ]] && jest_args+=" $bail_flag" | |
| [[ -n "$verbose_flag" ]] && jest_args+=" $verbose_flag" | |
| [[ -n "$silent_flag" ]] && jest_args+=" $silent_flag" | |
| [[ -n "$max_workers_flag" ]] && jest_args+=" $max_workers_flag" | |
| [[ -n "$force_exit_flag" ]] && jest_args+=" $force_exit_flag" | |
| tsc_args="--noEmit" | |
| [[ -n "$tsc_pretty_flag" ]] && tsc_args+=" $tsc_pretty_flag" | |
| conveyor_args="--changed-only=origin/master...HEAD --slice $slice" | |
| [[ -n "$stop_on_fail_flag" ]] && conveyor_args+=" $stop_on_fail_flag" | |
| [[ -n "$no_parallel_flag" ]] && conveyor_args+=" $no_parallel_flag" | |
| echo "[Stage 1/3] Checking & applying safe syntax code quality fixes w/eslint" | |
| echo "$changed" | xargs npx eslint $eslint_args 2>&1 || true | |
| printf '\n[Stage 2/3] Checking & applying safe formatting fixes w/prettier\n' | |
| echo "$changed" | xargs npx prettier $prettier_args 2>&1 || true | |
| #? OPTIONAL: unsafe fixes — all files, unlimited warnings, all fix-type categories | |
| if [ "$unsafe_flag" = true ]; then | |
| printf '\n[Stage 2/3, continued] Applying unsafe fixes w/eslint+prettier\n' | |
| unsafe_eslint_args="--no-error-on-unmatched-pattern --stats --fix --max-warnings=-1" | |
| [[ -n "$cache_flag" ]] && unsafe_eslint_args+=" $cache_flag" | |
| [[ -n "$quiet_flag" ]] && unsafe_eslint_args+=" $quiet_flag" | |
| [[ -n "$concurrency_flag" ]] && unsafe_eslint_args+=" $concurrency_flag" | |
| #? --unsafe implies all fix-type categories unless explicitly overridden via --fix-type | |
| if [[ -n "$fix_type_flag" ]]; then | |
| unsafe_eslint_args+=" $fix_type_flag" | |
| else | |
| unsafe_eslint_args+=" --fix-type directive,problem,suggestion,layout" | |
| fi | |
| npx eslint $unsafe_eslint_args 2>&1 || true | |
| fi | |
| printf '\n[Stage 3/3] Running parallel scans for type checks w/tsc + test coverage w/jest + ci pre-checks w/conveyor\n' | |
| track_progress() { | |
| local check_name=$1 | |
| local check_cmd=$2 | |
| local log_file="$tmp/$check_name.log" | |
| $check_cmd >"$log_file" 2>&1 | |
| echo $? >"$tmp/$check_name.rc" | |
| } | |
| ( | |
| bash -c "npx tsc $tsc_args 2>&1 | grep -F -f <(git diff --name-only origin/master...HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx' '*.mjs' '*.cjs' | grep -vE '\.(test|spec)\.(ts|tsx|js|jsx|mjs|cjs)$')" | |
| echo $? >"$tmp/tsc.rc" | |
| ) >"$tmp/tsc.log" 2>&1 & | |
| ( | |
| npx jest $jest_args | |
| echo $? >"$tmp/jest.rc" | |
| ) >"$tmp/jest.log" 2>&1 & | |
| ( | |
| for attempt in $(seq 1 "$retry"); do | |
| conveyor test $conveyor_args ALL && break | |
| [ "$attempt" -lt "$retry" ] && sleep 5 | |
| done | |
| echo $? >"$tmp/conveyor.rc" | |
| ) >"$tmp/conveyor.log" 2>&1 & | |
| wait | |
| failed=0 | |
| for check in tsc jest conveyor; do | |
| rc=$(cat "$tmp/$check.rc" 2>/dev/null || echo 1) | |
| echo "" | |
| echo "━━ $check (exit $rc) ━━" | |
| [ "$prune_flag" = false ] && cat "$tmp/$check.log" 2>/dev/null | |
| [ "$rc" != "0" ] && failed=1 | |
| done | |
| exit $failed |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment