-
Notifications
You must be signed in to change notification settings - Fork 201
Expand file tree
/
Copy pathformat-incremental
More file actions
executable file
·160 lines (148 loc) · 5.39 KB
/
format-incremental
File metadata and controls
executable file
·160 lines (148 loc) · 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env bash
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################
# Formats Python files that have been modified.
#
# Usage:
# check/format-incremental [BASE_REVISION] [--apply] [--all]
#
# By default, the script analyzes Python files that have changed relative to the
# base revision and determines whether they need to be formatted. If any changes
# are needed, it prints the diff and exits with code 1, otherwise it exits with
# code 0.
#
# With '--apply', reformats the files instead of printing the diff and exits
# with code 0.
#
# With '--all', analyzes all files, instead of only changed files.
#
# You can specify a base git revision to compare against (i.e. to use when
# determining whether or not a file is considered to have "changed"). For
# example, you can compare against 'origin/main' or 'HEAD~1'.
#
# If you don't specify a base revision, the following defaults will be tried, in
# order, until one exists:
#
# 1. upstream/main
# 2. origin/main
# 3. main
#
# If none exists, the script fails.
#
# This script is based on the Cirq script of the same name:
# https://github.com/quantumlib/Cirq/blob/main/check/format-incremental
################################################################################
# Get the working directory to the repo root.
thisdir=$(dirname "${BASH_SOURCE[0]:?}") || exit $?
repo_dir=$(git -C "${thisdir}" rev-parse --show-toplevel) || exit $?
cd "${repo_dir}" || exit $?
# Parse arguments.
only_print=1
only_changed=1
rev=""
for arg in "$@"; do
if [[ "${arg}" == "--apply" ]]; then
only_print=0
elif [[ "${arg}" == "--all" ]]; then
only_changed=0
elif [ -z "${rev}" ]; then
if ! git rev-parse --verify --quiet --no-revs "${arg}^{commit}"; then
echo -e "\033[31mNo revision '${arg}'.\033[0m" >&2
exit 1
fi
rev="${arg}"
else
echo -e "\033[31mToo many arguments." \
"Expected [revision] [--apply] [--all].\033[0m" >&2
exit 1
fi
done
declare -a format_files
if (( only_changed == 1 )); then
# Figure out which branch to compare against.
if [ -z "${rev}" ]; then
if [ "$(git cat-file -t upstream/main 2> /dev/null)" == "commit" ]; then
rev=upstream/main
elif [ "$(git cat-file -t origin/main 2> /dev/null)" == "commit" ]; then
rev=origin/main
elif [ "$(git cat-file -t main 2> /dev/null)" == "commit" ]; then
rev=main
else
echo -e "\033[31mNo default revision found to compare against."\
"Argument #1 must be what to diff against" \
"(e.g. 'origin/main' or 'HEAD~1').\033[0m" >&2
exit 1
fi
fi
base=$(git merge-base "${rev}" HEAD)
if [ "$(git rev-parse "${rev}")" == "${base}" ]; then
echo -e "Comparing against revision '${rev}'." >&2
else
echo -e "Comparing against revision '${rev}' (merge base ${base})." >&2
rev="${base}"
fi
# Get the modified, added, and moved files.
IFS=$'\n' read -r -d '' -a format_files < \
<(git diff --name-only --diff-filter=MAR "${rev}" -- '*.py') || true
else
echo -e "Formatting all Python files." >&2
IFS=$'\n' read -r -d '' -a format_files < \
<(git ls-files '*.py')
fi
if (( ${#format_files[@]} == 0 )); then
echo -e "\033[32mNo files to format.\033[0m"
exit 0
fi
# Color the output if it goes to a terminal or GitHub Actions log.
arg_color=()
if [[ -t 1 || "${CI}" == true ]]; then
# Check if ruff supports --color (it might be missing in older/some versions).
if ruff --help 2>&1 | grep -q -- '--color'; then
arg_color=("--color" "always")
fi
fi
# Run ruff for import sorting.
# We do this first so that if it changes files, the formatting step will pick them up.
echo "Running ruff check (isort)..."
check_args=("check" "--select" "I" "${arg_color[@]}")
if (( only_print == 0 )); then
check_args+=("--fix")
else
# In check mode, we want to see diffs if possible, but ruff check usually just prints violations.
# To match previous behavior of "checking" without modifying, we don't add --fix.
# If we want diffs for imports, --diff can be used.
check_args+=("--diff")
fi
RUFF_CHECK_STATUS=0
if (( "${#format_files[@]}" )); then
# We pass all files to ruff; it handles excluding those that shouldn't be touched if configured,
# but here we are passing a specific list of files.
ruff "${check_args[@]}" "${format_files[@]}"
RUFF_CHECK_STATUS=$?
fi
echo "Running ruff format..."
format_args=("format" "${arg_color[@]}")
if (( only_print == 1 )); then
format_args+=("--check" "--diff")
fi
RUFF_FORMAT_STATUS=0
if (( "${#format_files[@]}" )); then
ruff "${format_args[@]}" "${format_files[@]}"
RUFF_FORMAT_STATUS=$?
fi
if (( RUFF_CHECK_STATUS || RUFF_FORMAT_STATUS )); then
exit 1
fi
exit 0