Skip to content

Commit b8ce3a9

Browse files
authored
Merge pull request #223 from mhucka/mh-add-format-script
Add format-incremental script
2 parents 6c18189 + 699c540 commit b8ce3a9

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed

dev_tools/format-incremental

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#!/usr/bin/env bash
2+
# Summary: check files against style guidelines and optionally reformat them.
3+
# Run this program with the argument --help for usage information.
4+
5+
read -r -d '' usage <<-EOF
6+
Usage:
7+
8+
${0##*/} [BASE_REV] [--help] [--apply] [--all] [--no-color] [--quiet]
9+
10+
Check the format of Python source files against project style guidelines. If
11+
any changes are needed, this program prints the differences to stdout and exits
12+
with code 1; otherwise, it exits with code 0.
13+
14+
Main options
15+
~~~~~~~~~~~~
16+
17+
If the option '--apply' is supplied as an argument, then instead of printing
18+
differences, this program reformats the files and exits with code 0 if
19+
successful or 1 if an error occurs.
20+
21+
By default, this program examines only those files that git reports to have
22+
changed in relation to the git revision (see next paragraph). With option
23+
'--all', this program will examine all files instead of only the changed files.
24+
25+
File changes are considered relative to the base git revision in the repository
26+
unless a different git revision is given as an argument to this program. The
27+
revision can be given as a SHA value or a name such as 'origin/main' or
28+
'HEAD~1'. If no git revision is provided as an argument, this program tries the
29+
following defaults, in order, until one is found to exist:
30+
31+
1. upstream/main (or upstream/master)
32+
2. origin/main (or origin/master)
33+
3. main (or master)
34+
35+
If none of them exists, the program will fail and return exit code 1.
36+
37+
Additional options
38+
~~~~~~~~~~~~~~~~~~
39+
40+
Informative messages are printed to stdout unless option '--quiet' is given.
41+
(Error messages are always printed.)
42+
43+
Color is used to enhance the output unless the option '--no-color' is given.
44+
45+
Running this program with the option '--help' will make it print this help text
46+
and exit with exit code 0 without doing anything else.
47+
48+
If an error occurs in Black itself, this program will return the non-zero error
49+
code returned by Black.
50+
EOF
51+
52+
# Change the working directory of this script to the root of the repo.
53+
thisdir="$(dirname "${BASH_SOURCE[0]}")" || exit $?
54+
cd "$(git -C "${thisdir}" rev-parse --show-toplevel)" || exit $?
55+
56+
# Set default values.
57+
declare only_print=true
58+
declare only_changed=true
59+
declare no_color=false
60+
declare be_quiet=false
61+
62+
function print() {
63+
local type="$1" msg="$2"
64+
local red="" green="" reset=""
65+
$no_color || red="\033[31;1m"
66+
$no_color || green="\033[32;1m"
67+
$no_color || reset="\033[0m"
68+
case $type in
69+
error) echo -e "${reset}${red}Error: $msg${reset}" >&2;;
70+
info) $be_quiet || echo -e "${reset}${green}$msg${reset}";;
71+
*) echo "$msg";;
72+
esac
73+
}
74+
75+
declare rev=""
76+
77+
# Parse the command line.
78+
# Don't be fussy about whether options are written upper case or lower case.
79+
shopt -s nocasematch
80+
while (( $# > 0 )); do
81+
case $1 in
82+
-h | --help)
83+
echo "$usage"
84+
exit 0
85+
;;
86+
--apply)
87+
only_print=false
88+
shift
89+
;;
90+
--all)
91+
only_changed=false
92+
shift
93+
;;
94+
--no-color)
95+
no_color=true
96+
shift
97+
;;
98+
--quiet)
99+
be_quiet=true
100+
shift
101+
;;
102+
-*)
103+
print error "Unrecognized option $1."
104+
echo "$usage"
105+
exit 1
106+
;;
107+
*)
108+
if [[ -n "$rev" ]]; then
109+
print error "Too many arguments."
110+
echo "$usage"
111+
exit 1
112+
fi
113+
if ! git rev-parse -q --verify --no-revs "$1^{commit}"; then
114+
print error "Cannot find revision $1."
115+
exit 1
116+
fi
117+
rev="$1"
118+
shift
119+
;;
120+
esac
121+
done
122+
shopt -u nocasematch
123+
124+
# Gather a list of Python files that have been modified, added, or moved.
125+
declare -a modified_files=("")
126+
if $only_changed; then
127+
# Figure out which branch to compare against.
128+
if [[ -z "$rev" ]]; then
129+
declare -r -a try=("upstream/main" "origin/main" "main"
130+
"upstream/master" "origin/master" "master")
131+
for name in "${try[@]}"; do
132+
if [[ "$(git cat-file -t "$name" 2> /dev/null)" == "commit" ]]; then
133+
rev="$name"
134+
break
135+
fi
136+
done
137+
if [[ -z "$rev" ]]; then
138+
print error "None of the defaults (${try[*]}) were found and no" \
139+
" git revision was provided as argument. Argument #1 must" \
140+
" be what to diff against (e.g., 'origin/main' or 'HEAD~1')."
141+
exit 1
142+
fi
143+
fi
144+
declare base base_info
145+
base="$(git merge-base "$rev" HEAD)"
146+
if [[ "$(git rev-parse "$rev")" != "$base" ]]; then
147+
rev="$base"
148+
base_info=" (merge base $base)"
149+
fi
150+
print info "Comparing files to revision '$rev'$base_info."
151+
152+
# Get the list of changed files.
153+
IFS=$'\n' read -r -d '' -a modified_files < \
154+
<(git diff --name-only --diff-filter=MAR "$rev" -- '*.py')
155+
else
156+
# The user asked for all files.
157+
print info "Formatting all Python files."
158+
IFS=$'\n' read -r -d '' -a modified_files < <(git ls-files '*.py')
159+
fi
160+
161+
if (( ${#modified_files[@]} == 0 )); then
162+
print info "No modified files found – no changes needed."
163+
exit 0
164+
fi
165+
166+
declare black_version
167+
black_version="$(black --version)"
168+
black_version=${black_version//[$'\n']/ } # Remove annoying embedded newline.
169+
black_version=${black_version#black, } # Remove leading "black, "
170+
print info "Running Black (version $black_version) ..."
171+
172+
declare -a black_args
173+
$only_print && black_args+=("--check" "--diff")
174+
$be_quiet && black_args+=("--quiet")
175+
$no_color && black_args+=("--no-color")
176+
177+
black "${black_args[@]}" "${modified_files[@]}"

0 commit comments

Comments
 (0)