Skip to content

Commit ab1c50c

Browse files
committed
tools: Update tidy.sh
1 parent bdd1708 commit ab1c50c

1 file changed

Lines changed: 85 additions & 86 deletions

File tree

tools/tidy.sh

Lines changed: 85 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ trap 's=$?; echo >&2 "$0: error on line "${LINENO}": ${BASH_COMMAND}"; exit ${s}
1616
# - shellcheck
1717
# - npm
1818
# - jq
19-
# - python
19+
# - python 3
2020
# - rustup (if Rust code exists)
2121
# - clang-format (if C/C++ code exists)
2222
#
@@ -40,6 +40,19 @@ check_config() {
4040
error "could not found $1 in the repository root"
4141
fi
4242
}
43+
check_install() {
44+
for tool in "$@"; do
45+
if ! type -P "${tool}" &>/dev/null; then
46+
if [[ "${tool}" == "python3" ]]; then
47+
if type -P python &>/dev/null; then
48+
continue
49+
fi
50+
fi
51+
error "'${tool}' is required to run this check"
52+
return 1
53+
fi
54+
done
55+
}
4356
info() {
4457
echo >&2 "info: $*"
4558
}
@@ -56,6 +69,27 @@ venv() {
5669
shift
5770
"${venv_bin}/${bin}${exe}" "$@"
5871
}
72+
venv_install_yq() {
73+
local py_suffix=''
74+
if type -P python3 &>/dev/null; then
75+
py_suffix='3'
76+
fi
77+
exe=''
78+
venv_bin='.venv/bin'
79+
case "$(uname -s)" in
80+
MINGW* | MSYS* | CYGWIN* | Windows_NT)
81+
exe='.exe'
82+
venv_bin='.venv/Scripts'
83+
;;
84+
esac
85+
if [[ ! -d .venv ]]; then
86+
"python${py_suffix}" -m venv .venv
87+
fi
88+
if [[ ! -e "${venv_bin}/yq${exe}" ]]; then
89+
info "installing yq to ./.venv using pip"
90+
venv "pip${py_suffix}" install yq
91+
fi
92+
}
5993

6094
if [[ $# -gt 0 ]]; then
6195
cat <<EOF
@@ -68,24 +102,23 @@ fi
68102
# Rust (if exists)
69103
if [[ -n "$(git ls-files '*.rs')" ]]; then
70104
info "checking Rust code style"
105+
check_install cargo jq python3
71106
check_config .rustfmt.toml
72-
if type -P rustup &>/dev/null; then
107+
if check_install rustup; then
73108
# `cargo fmt` cannot recognize files not included in the current workspace and modules
74109
# defined inside macros, so run rustfmt directly.
75110
# We need to use nightly rustfmt because we use the unstable formatting options of rustfmt.
76111
rustc_version=$(rustc -vV | grep '^release:' | cut -d' ' -f2)
77112
if [[ "${rustc_version}" == *"nightly"* ]] || [[ "${rustc_version}" == *"dev"* ]]; then
78113
rustup component add rustfmt &>/dev/null
79-
echo "+ rustfmt \$(git ls-files '*.rs')"
114+
info "running \`rustfmt \$(git ls-files '*.rs')\`"
80115
rustfmt $(git ls-files '*.rs')
81116
else
82117
rustup component add rustfmt --toolchain nightly &>/dev/null
83-
echo "+ rustfmt +nightly \$(git ls-files '*.rs')"
118+
info "running \`rustfmt +nightly \$(git ls-files '*.rs')\`"
84119
rustfmt +nightly $(git ls-files '*.rs')
85120
fi
86121
check_diff $(git ls-files '*.rs')
87-
else
88-
error "'rustup' is not installed; skipped Rust code style check"
89122
fi
90123
cast_without_turbofish=$(grep -n -E '\.cast\(\)' $(git ls-files '*.rs') || true)
91124
if [[ -n "${cast_without_turbofish}" ]]; then
@@ -122,11 +155,12 @@ if [[ -n "$(git ls-files '*.rs')" ]]; then
122155
binaries=''
123156
metadata=$(cargo metadata --format-version=1 --no-deps)
124157
has_public_crate=''
158+
venv_install_yq
125159
for id in $(jq <<<"${metadata}" '.workspace_members[]'); do
126160
pkg=$(jq <<<"${metadata}" ".packages[] | select(.id == ${id})")
127161
publish=$(jq <<<"${pkg}" -r '.publish')
128162
manifest_path=$(jq <<<"${pkg}" -r '.manifest_path')
129-
if ! grep -q '^\[lints\]' "${manifest_path}" && ! grep -q '^\[lints\.rust\]' "${manifest_path}"; then
163+
if [[ "$(venv tomlq -c '.lints' "${manifest_path}")" == "null" ]]; then
130164
error "no [lints] table in ${manifest_path} please add '[lints]' with 'workspace = true'"
131165
fi
132166
# Publishing is unrestricted if null, and forbidden if an empty array.
@@ -144,13 +178,14 @@ if [[ -n "$(git ls-files '*.rs')" ]]; then
144178
publish=$(jq <<<"${root_pkg}" -r '.publish')
145179
# Publishing is unrestricted if null, and forbidden if an empty array.
146180
if [[ "${publish}" != "[]" ]]; then
147-
if ! grep -Eq '^exclude = \[.*"/\.\*".*\]' Cargo.toml; then
181+
exclude=$(venv tomlq -r '.package.exclude[]' Cargo.toml)
182+
if ! grep <<<"${exclude}" -Eq '^/\.\*$'; then
148183
error "top-level Cargo.toml of non-virtual workspace should have 'exclude' field with \"/.*\""
149184
fi
150-
if [[ -e tools ]] && ! grep -Eq '^exclude = \[.*"/tools".*\]' Cargo.toml; then
185+
if [[ -e tools ]] && ! grep <<<"${exclude}" -Eq '^/tools$'; then
151186
error "top-level Cargo.toml of non-virtual workspace should have 'exclude' field with \"/tools\" if it exists"
152187
fi
153-
if [[ -e target-specs ]] && ! grep -Eq '^exclude = \[.*"/target-specs".*\]' Cargo.toml; then
188+
if [[ -e target-specs ]] && ! grep <<<"${exclude}" -Eq '^/target-specs$'; then
154189
error "top-level Cargo.toml of non-virtual workspace should have 'exclude' field with \"/target-specs\" if it exists"
155190
fi
156191
fi
@@ -196,12 +231,10 @@ fi
196231
if [[ -n "$(git ls-files '*.c' '*.h' '*.cpp' '*.hpp')" ]]; then
197232
info "checking C/C++ code style"
198233
check_config .clang-format
199-
if type -P clang-format &>/dev/null; then
200-
echo "+ clang-format -i \$(git ls-files '*.c' '*.h' '*.cpp' '*.hpp')"
234+
if check_install clang-format; then
235+
info "running \`clang-format -i \$(git ls-files '*.c' '*.h' '*.cpp' '*.hpp')\`"
201236
clang-format -i $(git ls-files '*.c' '*.h' '*.cpp' '*.hpp')
202237
check_diff $(git ls-files '*.c' '*.h' '*.cpp' '*.hpp')
203-
else
204-
error "'clang-format' is not installed; skipped C/C++ code style check"
205238
fi
206239
elif [[ -e .clang-format ]]; then
207240
error ".clang-format is unused"
@@ -211,64 +244,39 @@ fi
211244
if [[ -n "$(git ls-files '*.yml' '*.yaml' '*.js' '*.json')" ]]; then
212245
info "checking YAML/JavaScript/JSON code style"
213246
check_config .editorconfig
214-
if type -P npm &>/dev/null; then
215-
echo "+ npx -y prettier -l -w \$(git ls-files '*.yml' '*.yaml' '*.js' '*.json')"
247+
if check_install npm; then
248+
info "running \`npx -y prettier -l -w \$(git ls-files '*.yml' '*.yaml' '*.js' '*.json')\`"
216249
npx -y prettier -l -w $(git ls-files '*.yml' '*.yaml' '*.js' '*.json')
217250
check_diff $(git ls-files '*.yml' '*.yaml' '*.js' '*.json')
218-
else
219-
error "'npm' is not installed; skipped YAML/JavaScript/JSON code style check"
220251
fi
221252
# Check GitHub workflows.
222253
if [[ -d .github/workflows ]]; then
223254
info "checking GitHub workflows"
224-
if type -P jq &>/dev/null; then
225-
if type -P python3 &>/dev/null || type -P python &>/dev/null; then
226-
py_suffix=''
227-
if type -P python3 &>/dev/null; then
228-
py_suffix='3'
229-
fi
230-
exe=''
231-
venv_bin='.venv/bin'
232-
case "$(uname -s)" in
233-
MINGW* | MSYS* | CYGWIN* | Windows_NT)
234-
exe='.exe'
235-
venv_bin='.venv/Scripts'
236-
;;
255+
if check_install jq python3; then
256+
venv_install_yq
257+
for workflow in .github/workflows/*.yml; do
258+
# The top-level permissions must be weak as they are referenced by all jobs.
259+
permissions=$(venv yq -c '.permissions' "${workflow}")
260+
case "${permissions}" in
261+
'{"contents":"read"}' | '{"contents":"none"}') ;;
262+
null) error "${workflow}: top level permissions not found; it must be 'contents: read' or weaker permissions" ;;
263+
*) error "${workflow}: only 'contents: read' and weaker permissions are allowed at top level; if you want to use stronger permissions, please set job-level permissions" ;;
237264
esac
238-
if [[ ! -d .venv ]]; then
239-
"python${py_suffix}" -m venv .venv
240-
fi
241-
if [[ ! -e "${venv_bin}/yq${exe}" ]]; then
242-
venv "pip${py_suffix}" install yq
243-
fi
244-
for workflow in .github/workflows/*.yml; do
245-
# The top-level permissions must be weak as they are referenced by all jobs.
246-
permissions=$(venv yq -c '.permissions' "${workflow}")
247-
case "${permissions}" in
248-
'{"contents":"read"}' | '{"contents":"none"}') ;;
249-
null) error "${workflow}: top level permissions not found; it must be 'contents: read' or weaker permissions" ;;
250-
*) error "${workflow}: only 'contents: read' and weaker permissions are allowed at top level; if you want to use stronger permissions, please set job-level permissions" ;;
251-
esac
252-
# Make sure the 'needs' section is not out of date.
253-
if grep -q '# tidy:needs' "${workflow}" && ! grep -Eq '# *needs: \[' "${workflow}"; then
254-
# shellcheck disable=SC2207
255-
jobs_actual=($(venv yq '.jobs' "${workflow}" | jq -r 'keys_unsorted[]'))
256-
unset 'jobs_actual[${#jobs_actual[@]}-1]'
257-
# shellcheck disable=SC2207
258-
jobs_expected=($(venv yq -r '.jobs."ci-success".needs[]' "${workflow}"))
259-
if [[ "${jobs_actual[*]}" != "${jobs_expected[*]+"${jobs_expected[*]}"}" ]]; then
260-
printf -v jobs '%s, ' "${jobs_actual[@]}"
261-
sed -i "s/needs: \[.*\] # tidy:needs/needs: [${jobs%, }] # tidy:needs/" "${workflow}"
262-
check_diff "${workflow}"
263-
error "${workflow}: please update 'needs' section in 'ci-success' job"
264-
fi
265+
# Make sure the 'needs' section is not out of date.
266+
if grep -q '# tidy:needs' "${workflow}" && ! grep -Eq '# *needs: \[' "${workflow}"; then
267+
# shellcheck disable=SC2207
268+
jobs_actual=($(venv yq '.jobs' "${workflow}" | jq -r 'keys_unsorted[]'))
269+
unset 'jobs_actual[${#jobs_actual[@]}-1]'
270+
# shellcheck disable=SC2207
271+
jobs_expected=($(venv yq -r '.jobs."ci-success".needs[]' "${workflow}"))
272+
if [[ "${jobs_actual[*]}" != "${jobs_expected[*]+"${jobs_expected[*]}"}" ]]; then
273+
printf -v jobs '%s, ' "${jobs_actual[@]}"
274+
sed -i "s/needs: \[.*\] # tidy:needs/needs: [${jobs%, }] # tidy:needs/" "${workflow}"
275+
check_diff "${workflow}"
276+
error "${workflow}: please update 'needs' section in 'ci-success' job"
265277
fi
266-
done
267-
else
268-
error "'python3' is not installed; skipped GitHub workflow check"
269-
fi
270-
else
271-
error "'jq' is not installed; skipped GitHub workflow check"
278+
fi
279+
done
272280
fi
273281
fi
274282
fi
@@ -281,12 +289,10 @@ fi
281289
if [[ -n "$(git ls-files '*.toml' | (grep -v .taplo.toml || true))" ]]; then
282290
info "checking TOML style"
283291
check_config .taplo.toml
284-
if type -P npm &>/dev/null; then
285-
echo "+ npx -y @taplo/cli fmt \$(git ls-files '*.toml')"
292+
if check_install npm; then
293+
info "running \`npx -y @taplo/cli fmt \$(git ls-files '*.toml')\`"
286294
RUST_LOG=warn npx -y @taplo/cli fmt $(git ls-files '*.toml')
287295
check_diff $(git ls-files '*.toml')
288-
else
289-
error "'npm' is not installed; skipped TOML style check"
290296
fi
291297
elif [[ -e .taplo.toml ]]; then
292298
error ".taplo.toml is unused"
@@ -296,11 +302,9 @@ fi
296302
if [[ -n "$(git ls-files '*.md')" ]]; then
297303
info "checking Markdown style"
298304
check_config .markdownlint-cli2.yaml
299-
if type -P npm &>/dev/null; then
300-
echo "+ npx -y markdownlint-cli2 \$(git ls-files '*.md')"
305+
if check_install npm; then
306+
info "running \`npx -y markdownlint-cli2 \$(git ls-files '*.md')\`"
301307
npx -y markdownlint-cli2 $(git ls-files '*.md')
302-
else
303-
error "'npm' is not installed; skipped Markdown style check"
304308
fi
305309
elif [[ -e .markdownlint-cli2.yaml ]]; then
306310
error ".markdownlint-cli2.yaml is unused"
@@ -312,29 +316,25 @@ fi
312316

313317
# Shell scripts
314318
info "checking Shell scripts"
315-
if type -P shfmt &>/dev/null; then
319+
if check_install shfmt; then
316320
check_config .editorconfig
317-
echo "+ shfmt -l -w \$(git ls-files '*.sh')"
321+
info "running \`shfmt -l -w \$(git ls-files '*.sh')\`"
318322
shfmt -l -w $(git ls-files '*.sh')
319323
check_diff $(git ls-files '*.sh')
320-
else
321-
error "'shfmt' is not installed; skipped Shell scripts style check"
322324
fi
323-
if type -P shellcheck &>/dev/null; then
325+
if check_install shellcheck; then
324326
check_config .shellcheckrc
325-
echo "+ shellcheck \$(git ls-files '*.sh')"
327+
info "running \`shellcheck \$(git ls-files '*.sh')\`"
326328
if ! shellcheck $(git ls-files '*.sh'); then
327329
should_fail=1
328330
fi
329331
if [[ -n "$(git ls-files '*Dockerfile')" ]]; then
330332
# SC2154 doesn't seem to work on dockerfile.
331-
echo "+ shellcheck -e SC2148,SC2154,SC2250 \$(git ls-files '*Dockerfile')"
333+
info "running \`shellcheck -e SC2148,SC2154,SC2250 \$(git ls-files '*Dockerfile')\`"
332334
if ! shellcheck -e SC2148,SC2154,SC2250 $(git ls-files '*Dockerfile'); then
333335
should_fail=1
334336
fi
335337
fi
336-
else
337-
error "'shellcheck' is not installed; skipped Shell scripts style check"
338338
fi
339339

340340
# License check
@@ -383,13 +383,14 @@ fi
383383
if [[ -f .cspell.json ]]; then
384384
info "spell checking"
385385
project_dictionary=.github/.cspell/project-dictionary.txt
386-
if type -P npm &>/dev/null; then
386+
if check_install npm jq python3; then
387387
has_rust=''
388388
if [[ -n "$(git ls-files '*Cargo.toml')" ]]; then
389+
venv_install_yq
389390
has_rust='1'
390391
dependencies=''
391392
for manifest_path in $(git ls-files '*Cargo.toml'); do
392-
if [[ "${manifest_path}" != "Cargo.toml" ]] && ! grep -Eq '\[workspace\]' "${manifest_path}"; then
393+
if [[ "${manifest_path}" != "Cargo.toml" ]] && [[ "$(venv tomlq -c '.workspace' "${manifest_path}")" == "null" ]]; then
393394
continue
394395
fi
395396
metadata=$(cargo metadata --format-version=1 --no-deps --manifest-path "${manifest_path}")
@@ -422,7 +423,7 @@ EOF
422423
error "you may want to mark .github/.cspell/rust-dependencies.txt linguist-generated"
423424
fi
424425

425-
echo "+ npx -y cspell --no-progress --no-summary \$(git ls-files)"
426+
info "running \`npx -y cspell --no-progress --no-summary \$(git ls-files)\`"
426427
if ! npx -y cspell --no-progress --no-summary $(git ls-files); then
427428
error "spellcheck failed: please fix uses of above words or add to ${project_dictionary} if correct"
428429
fi
@@ -454,8 +455,6 @@ EOF
454455
echo -n "${unused}"
455456
echo "======================================="
456457
fi
457-
else
458-
error "'npm' is not installed; skipped spell check"
459458
fi
460459
fi
461460

0 commit comments

Comments
 (0)