2023-02-24 01:44:48 +09:00
#!/usr/bin/env bash
2023-04-05 22:47:02 +09:00
# SPDX-License-Identifier: Apache-2.0 OR MIT
2021-12-30 17:33:20 +09:00
# shellcheck disable=SC2046
2023-09-29 21:03:39 +09:00
set -eEuo pipefail
2021-12-30 17:33:20 +09:00
IFS = $'\n\t'
2022-02-06 10:00:55 +09:00
cd " $( dirname " $0 " ) " /..
2021-12-30 17:33:20 +09:00
2022-12-09 22:03:48 +09:00
# shellcheck disable=SC2154
2023-06-05 12:41:49 +09:00
trap 's=$?; echo >&2 "$0: error on line "${LINENO}": ${BASH_COMMAND}"; exit ${s}' ERR
2022-12-09 22:03:48 +09:00
2021-12-30 17:33:20 +09:00
# USAGE:
# ./tools/tidy.sh
#
2022-02-17 13:06:46 +09:00
# Note: This script requires the following tools:
2021-12-30 17:33:20 +09:00
# - shfmt
# - shellcheck
2022-12-09 22:03:48 +09:00
# - npm
2024-03-03 02:40:50 +09:00
# - jq
# - python
2022-12-24 13:11:48 +09:00
# - rustup (if Rust code exists)
# - clang-format (if C/C++ code exists)
2022-12-02 03:19:40 +09:00
#
# This script is shared with other repositories, so there may also be
# checks for files not included in this repository, but they will be
# skipped if the corresponding files do not exist.
2021-12-30 17:33:20 +09:00
2022-12-02 03:19:40 +09:00
check_diff( ) {
if [ [ -n " ${ CI :- } " ] ] ; then
if ! git --no-pager diff --exit-code " $@ " ; then
should_fail = 1
fi
else
if ! git --no-pager diff --exit-code " $@ " & >/dev/null; then
should_fail = 1
fi
fi
}
2023-04-05 22:47:02 +09:00
info( ) {
echo >& 2 " info: $* "
}
2021-12-30 17:33:20 +09:00
warn( ) {
2022-12-02 03:19:40 +09:00
if [ [ -n " ${ GITHUB_ACTIONS :- } " ] ] ; then
echo " ::warning:: $* "
else
echo >& 2 " warning: $* "
fi
should_fail = 1
2021-12-30 17:33:20 +09:00
}
2023-03-30 22:39:28 +09:00
error( ) {
if [ [ -n " ${ GITHUB_ACTIONS :- } " ] ] ; then
echo " ::error:: $* "
else
echo >& 2 " error: $* "
fi
should_fail = 1
}
2021-12-30 17:33:20 +09:00
if [ [ $# -gt 0 ] ] ; then
cat <<EOF
USAGE:
2022-12-09 22:03:48 +09:00
$0
2021-12-30 17:33:20 +09:00
EOF
exit 1
fi
2022-12-24 13:11:48 +09:00
# Rust (if exists)
if [ [ -n " $( git ls-files '*.rs' ) " ] ] ; then
2023-04-13 00:04:55 +09:00
info "checking Rust code style"
2023-10-31 03:08:57 +09:00
if [ [ ! -e .rustfmt.toml ] ] ; then
warn "could not found .rustfmt.toml in the repository root"
fi
2022-12-24 13:11:48 +09:00
if type -P rustup & >/dev/null; then
# `cargo fmt` cannot recognize files not included in the current workspace and modules
# defined inside macros, so run rustfmt directly.
# We need to use nightly rustfmt because we use the unstable formatting options of rustfmt.
rustc_version = $( rustc -Vv | grep 'release: ' | sed 's/release: //' )
if [ [ " ${ rustc_version } " = = *"nightly" * ] ] || [ [ " ${ rustc_version } " = = *"dev" * ] ] ; then
rustup component add rustfmt & >/dev/null
echo "+ rustfmt \$(git ls-files '*.rs')"
rustfmt $( git ls-files '*.rs' )
else
rustup component add rustfmt --toolchain nightly & >/dev/null
echo "+ rustfmt +nightly \$(git ls-files '*.rs')"
rustfmt +nightly $( git ls-files '*.rs' )
fi
check_diff $( git ls-files '*.rs' )
else
2023-09-28 23:41:17 +09:00
warn "'rustup' is not installed; skipped Rust code style check"
2022-12-24 13:11:48 +09:00
fi
2023-03-30 22:39:28 +09:00
cast_without_turbofish = $( grep -n -E '\.cast\(\)' $( git ls-files '*.rs' ) || true )
if [ [ -n " ${ cast_without_turbofish } " ] ] ; then
error "please replace \`.cast()\` with \`.cast::<type_name>()\`:"
echo " ${ cast_without_turbofish } "
fi
2024-01-27 23:57:26 +09:00
# Sync readme and crate-level doc.
2023-04-13 00:04:55 +09:00
first = '1'
for readme in $( git ls-files '*README.md' ) ; do
if ! grep -q '^<!-- tidy:crate-doc:start -->' " ${ readme } " ; then
continue
fi
lib = " $( dirname " ${ readme } " ) /src/lib.rs "
if [ [ -n " ${ first } " ] ] ; then
first = ''
info "checking readme and crate-level doc are synchronized"
fi
if ! grep -q '^<!-- tidy:crate-doc:end -->' " ${ readme } " ; then
bail " missing '<!-- tidy:crate-doc:end -->' comment in ${ readme } "
fi
if ! grep -q '^<!-- tidy:crate-doc:start -->' " ${ lib } " ; then
bail " missing '<!-- tidy:crate-doc:start -->' comment in ${ lib } "
fi
if ! grep -q '^<!-- tidy:crate-doc:end -->' " ${ lib } " ; then
bail " missing '<!-- tidy:crate-doc:end -->' comment in ${ lib } "
fi
new = $( tr <" ${ readme } " '\n' '\a' | grep -o '<!-- tidy:crate-doc:start -->.*<!-- tidy:crate-doc:end -->' | sed 's/\&/\\\&/g; s/\\/\\\\/g' )
new = $( tr <" ${ lib } " '\n' '\a' | awk -v new = " ${ new } " 'gsub("<!-- tidy:crate-doc:start -->.*<!-- tidy:crate-doc:end -->",new)' | tr '\a' '\n' )
echo " ${ new } " >" ${ lib } "
check_diff " ${ lib } "
done
2023-08-19 15:58:26 +09:00
# Make sure that public Rust crates don't contain executables.
failed_files = ''
2023-10-19 04:25:54 +09:00
metadata = $( cargo metadata --format-version= 1 --no-deps)
2023-08-19 15:58:26 +09:00
has_public_crate = ''
for id in $( jq <<< " ${ metadata } " '.workspace_members[]' ) ; do
pkg = $( jq <<< " ${ metadata } " " .packages[] | select(.id == ${ id } ) " )
publish = $( jq <<< " ${ pkg } " -r '.publish' )
2023-10-31 03:08:57 +09:00
manifest_path = $( jq <<< " ${ pkg } " -r '.manifest_path' )
2023-10-31 22:09:23 +09:00
if ! grep -q '^\[lints\]' " ${ manifest_path } " && ! grep -q '^\[lints\.rust\]' " ${ manifest_path } " ; then
2023-10-31 03:08:57 +09:00
warn " no [lints] table in ${ manifest_path } please add '[lints]' with 'workspace = true' "
fi
2023-08-19 15:58:26 +09:00
# Publishing is unrestricted if null, and forbidden if an empty array.
if [ [ " ${ publish } " = = "[]" ] ] ; then
continue
fi
has_public_crate = '1'
done
if [ [ -n " ${ has_public_crate } " ] ] ; then
info "checking file permissions"
2023-10-31 03:08:57 +09:00
if [ [ -f Cargo.toml ] ] ; then
root_manifest = $( cargo locate-project --message-format= plain --manifest-path Cargo.toml)
root_pkg = $( jq <<< " ${ metadata } " " .packages[] | select(.manifest_path == \" ${ root_manifest } \") " )
if [ [ -n " ${ root_pkg } " ] ] ; then
publish = $( jq <<< " ${ root_pkg } " -r '.publish' )
# Publishing is unrestricted if null, and forbidden if an empty array.
if [ [ " ${ publish } " != "[]" ] ] ; then
if ! grep -Eq '^exclude = \[.*\.\*.*\]' Cargo.toml; then
error "top-level Cargo.toml of real manifest should have exclude field with \"/.*\" and \"/tools\""
elif ! grep -Eq '^exclude = \[.*/tools.*\]' Cargo.toml; then
error "top-level Cargo.toml of real manifest should have exclude field with \"/.*\" and \"/tools\""
fi
fi
2023-08-19 15:58:26 +09:00
fi
fi
for p in $( git ls-files) ; do
# Skip directories.
if [ [ -d " ${ p } " ] ] ; then
continue
fi
# Top-level hidden files/directories and tools/* are excluded from crates.io (ensured by the above check).
# TODO: fully respect exclude field in Cargo.toml.
case " ${ p } " in
.* | tools/*) continue ; ;
esac
if [ [ -x " ${ p } " ] ] ; then
failed_files += " ${ p } " $'\n'
fi
done
if [ [ -n " ${ failed_files } " ] ] ; then
error "file-permissions-check failed: executable should be in tools/ directory"
echo "======================================="
echo -n " ${ failed_files } "
echo "======================================="
fi
fi
2022-12-24 13:11:48 +09:00
fi
2022-12-02 03:19:40 +09:00
# C/C++ (if exists)
2023-10-31 03:08:57 +09:00
if [ [ -n " $( git ls-files '*.c' '*.h' '*.cpp' '*.hpp' ) " ] ] ; then
2023-04-13 00:04:55 +09:00
info "checking C/C++ code style"
2022-12-02 03:19:40 +09:00
if [ [ ! -e .clang-format ] ] ; then
2023-10-31 03:08:57 +09:00
warn "could not found .clang-format in the repository root"
2022-12-02 03:19:40 +09:00
fi
if type -P clang-format & >/dev/null; then
2023-10-31 03:08:57 +09:00
echo "+ clang-format -i \$(git ls-files '*.c' '*.h' '*.cpp' '*.hpp')"
clang-format -i $( git ls-files '*.c' '*.h' '*.cpp' '*.hpp' )
check_diff $( git ls-files '*.c' '*.h' '*.cpp' '*.hpp' )
2022-12-02 03:19:40 +09:00
else
2023-09-28 23:41:17 +09:00
warn "'clang-format' is not installed; skipped C/C++ code style check"
2022-12-02 03:19:40 +09:00
fi
fi
# YAML/JavaScript/JSON (if exists)
2023-10-31 03:08:57 +09:00
if [ [ -n " $( git ls-files '*.yml' '*.js' '*.json' ) " ] ] ; then
2023-04-13 00:04:55 +09:00
info "checking YAML/JavaScript/JSON code style"
2023-10-31 03:08:57 +09:00
if [ [ ! -e .editorconfig ] ] ; then
warn "could not found .editorconfig in the repository root"
fi
2022-12-02 03:19:40 +09:00
if type -P npm & >/dev/null; then
2023-10-31 03:08:57 +09:00
echo "+ npx -y prettier -l -w \$(git ls-files '*.yml' '*.js' '*.json')"
npx -y prettier -l -w $( git ls-files '*.yml' '*.js' '*.json' )
check_diff $( git ls-files '*.yml' '*.js' '*.json' )
2022-12-02 03:19:40 +09:00
else
2023-09-28 23:41:17 +09:00
warn "'npm' is not installed; skipped YAML/JavaScript/JSON code style check"
2022-12-02 03:19:40 +09:00
fi
2023-03-09 23:12:12 +09:00
# Check GitHub workflows.
if [ [ -d .github/workflows ] ] ; then
2023-04-13 00:04:55 +09:00
info "checking GitHub workflows"
2024-03-03 02:40:50 +09:00
if type -P jq & >/dev/null; then
if type -P python3 & >/dev/null || type -P python & >/dev/null; then
2024-03-03 02:43:01 +09:00
py_suffix = ''
2024-03-03 02:40:50 +09:00
if type -P python3 & >/dev/null; then
2024-03-03 02:43:01 +09:00
py_suffix = '3'
2024-03-03 02:40:50 +09:00
fi
if [ [ ! -d .venv ] ] ; then
2024-03-03 02:43:01 +09:00
python" ${ py_suffix } " -m venv .venv
2024-03-03 02:40:50 +09:00
fi
if [ [ ! -e .venv/bin/yq ] ] ; then
2024-03-03 02:43:01 +09:00
.venv/bin/pip" ${ py_suffix } " install yq
2023-03-09 23:12:12 +09:00
fi
2024-03-03 02:40:50 +09:00
for workflow in .github/workflows/*.yml; do
# The top-level permissions must be weak as they are referenced by all jobs.
permissions = $( .venv/bin/yq -c '.permissions' " ${ workflow } " )
case " ${ permissions } " in
'{"contents":"read"}' | '{"contents":"none"}' ) ; ;
null) error " ${ workflow } : top level permissions not found; it must be 'contents: read' or weaker permissions " ; ;
*) 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 " ; ;
esac
# Make sure the 'needs' section is not out of date.
if grep -q '# tidy:needs' " ${ workflow } " && ! grep -Eq '# *needs: \[' " ${ workflow } " ; then
# shellcheck disable=SC2207
jobs_actual = ( $( .venv/bin/yq '.jobs' " ${ workflow } " | jq -r 'keys_unsorted[]' ) )
unset 'jobs_actual[${#jobs_actual[@]}-1]'
# shellcheck disable=SC2207
jobs_expected = ( $( .venv/bin/yq -r '.jobs."ci-success".needs[]' " ${ workflow } " ) )
if [ [ " ${ jobs_actual [*] } " != " ${ jobs_expected [*]+ " ${ jobs_expected [*] } " } " ] ] ; then
printf -v jobs '%s, ' " ${ jobs_actual [@] } "
sed -i " s/needs: \[.*\] # tidy:needs/needs: [ ${ jobs %, } ] # tidy:needs/ " " ${ workflow } "
check_diff " ${ workflow } "
error " ${ workflow } : please update 'needs' section in 'ci-success' job "
fi
fi
done
else
warn "'python3' is not installed; skipped GitHub workflow check"
fi
2022-12-02 03:19:40 +09:00
else
2024-03-03 02:40:50 +09:00
warn "'jq' is not installed; skipped GitHub workflow check"
2022-12-02 03:19:40 +09:00
fi
fi
fi
if [ [ -n " $( git ls-files '*.yaml' ) " ] ] ; then
2023-03-30 22:39:28 +09:00
error "please use '.yml' instead of '.yaml' for consistency"
2022-12-02 03:19:40 +09:00
git ls-files '*.yaml'
2021-12-30 17:33:20 +09:00
fi
2024-02-23 03:08:59 +09:00
# TOML (if exists)
if [ [ -n " $( git ls-files '*.toml' ) " ] ] ; then
info "checking TOML style"
if [ [ ! -e .taplo.toml ] ] ; then
warn "could not found .taplo.toml in the repository root"
fi
if type -P npm & >/dev/null; then
echo "+ npx -y @taplo/cli fmt \$(git ls-files '*.toml')"
npx -y @taplo/cli fmt $( git ls-files '*.toml' )
check_diff $( git ls-files '*.toml' )
else
warn "'npm' is not installed; skipped TOML style check"
fi
fi
2023-09-28 23:41:17 +09:00
# Markdown (if exists)
if [ [ -n " $( git ls-files '*.md' ) " ] ] ; then
info "checking Markdown style"
2023-10-31 03:08:57 +09:00
if [ [ ! -e .markdownlint.yml ] ] ; then
warn "could not found .markdownlint.yml in the repository root"
fi
2023-09-28 23:41:17 +09:00
if type -P npm & >/dev/null; then
echo "+ npx -y markdownlint-cli2 \$(git ls-files '*.md')"
npx -y markdownlint-cli2 $( git ls-files '*.md' )
else
warn "'npm' is not installed; skipped Markdown style check"
fi
fi
if [ [ -n " $( git ls-files '*.markdown' ) " ] ] ; then
error "please use '.md' instead of '.markdown' for consistency"
git ls-files '*.markdown'
fi
2022-12-02 03:19:40 +09:00
# Shell scripts
2023-04-13 00:04:55 +09:00
info "checking Shell scripts"
2021-12-30 17:33:20 +09:00
if type -P shfmt & >/dev/null; then
2023-10-31 03:08:57 +09:00
if [ [ ! -e .editorconfig ] ] ; then
warn "could not found .editorconfig in the repository root"
fi
2022-12-24 13:11:48 +09:00
echo "+ shfmt -l -w \$(git ls-files '*.sh')"
shfmt -l -w $( git ls-files '*.sh' )
2022-12-02 03:19:40 +09:00
check_diff $( git ls-files '*.sh' )
2021-12-30 17:33:20 +09:00
else
2023-09-28 23:41:17 +09:00
warn "'shfmt' is not installed; skipped Shell scripts style check"
2021-12-30 17:33:20 +09:00
fi
if type -P shellcheck & >/dev/null; then
2023-10-31 03:08:57 +09:00
if [ [ ! -e .shellcheckrc ] ] ; then
warn "could not found .shellcheckrc in the repository root"
fi
2022-12-24 13:11:48 +09:00
echo "+ shellcheck \$(git ls-files '*.sh')"
if ! shellcheck $( git ls-files '*.sh' ) ; then
2022-12-02 03:19:40 +09:00
should_fail = 1
fi
if [ [ -n " $( git ls-files '*Dockerfile' ) " ] ] ; then
# SC2154 doesn't seem to work on dockerfile.
2022-12-24 13:11:48 +09:00
echo "+ shellcheck -e SC2148,SC2154,SC2250 \$(git ls-files '*Dockerfile')"
if ! shellcheck -e SC2148,SC2154,SC2250 $( git ls-files '*Dockerfile' ) ; then
2022-12-02 03:19:40 +09:00
should_fail = 1
fi
fi
2021-12-30 17:33:20 +09:00
else
2023-09-28 23:41:17 +09:00
warn "'shellcheck' is not installed; skipped Shell scripts style check"
2021-12-30 17:33:20 +09:00
fi
2022-12-02 03:19:40 +09:00
2023-06-05 22:58:32 +09:00
# License check
# TODO: This check is still experimental and does not track all files that should be tracked.
if [ [ -f tools/.tidy-check-license-headers ] ] ; then
info "checking license headers (experimental)"
failed_files = ''
for p in $( eval $( <tools/.tidy-check-license-headers) ) ; do
case " $( basename " ${ p } " ) " in
2023-09-16 17:56:46 +09:00
*.stderr | *.expanded.rs) continue ; ; # generated files
*.sh | *.py | *.rb | *Dockerfile) prefix = ( "# " ) ; ;
*.rs | *.c | *.h | *.cpp | *.hpp | *.s | *.S | *.js) prefix = ( "// " "/* " ) ; ;
2023-06-05 22:58:32 +09:00
*.ld | *.x) prefix = ( "/* " ) ; ;
2023-09-16 17:56:46 +09:00
# TODO: More file types?
*) continue ; ;
2023-06-05 22:58:32 +09:00
esac
# TODO: The exact line number is not actually important; it is important
# that it be part of the top-level comments of the file.
line = "1"
2023-09-16 17:56:46 +09:00
if IFS = LC_ALL = C read -rn3 -d '' shebang <" ${ p } " && [ [ " ${ shebang } " = = '#!/' ] ] ; then
line = "2"
elif [ [ " ${ p } " = = *"Dockerfile" ] ] && IFS = LC_ALL = C read -rn9 -d '' syntax <" ${ p } " && [ [ " ${ syntax } " = = '# syntax=' ] ] ; then
line = "2"
fi
2023-06-05 22:58:32 +09:00
header_found = ''
for pre in " ${ prefix [@] } " ; do
2023-09-28 23:41:17 +09:00
# TODO: check that the license is valid as SPDX and is allowed in this project.
2023-06-05 22:58:32 +09:00
if [ [ " $( grep -E -n " ${ pre } SPDX-License-Identifier: " " ${ p } " ) " = = " ${ line } : ${ pre } SPDX-License-Identifier: " * ] ] ; then
header_found = '1'
2023-09-28 23:41:17 +09:00
break
2023-06-05 22:58:32 +09:00
fi
done
if [ [ -z " ${ header_found } " ] ] ; then
failed_files += " ${ p } : ${ line } " $'\n'
fi
done
if [ [ -n " ${ failed_files } " ] ] ; then
error "license-check failed: please add SPDX-License-Identifier to the following files"
echo "======================================="
echo -n " ${ failed_files } "
echo "======================================="
fi
fi
2022-12-09 22:03:48 +09:00
# Spell check (if config exists)
if [ [ -f .cspell.json ] ] ; then
2023-04-13 00:04:55 +09:00
info "spell checking"
2023-05-31 02:55:36 +09:00
project_dictionary = .github/.cspell/project-dictionary.txt
2022-12-09 22:03:48 +09:00
if type -P npm & >/dev/null; then
2023-04-05 22:47:02 +09:00
has_rust = ''
2023-02-07 21:53:59 +09:00
if [ [ -n " $( git ls-files '*Cargo.toml' ) " ] ] ; then
2023-04-05 22:47:02 +09:00
has_rust = '1'
2022-12-09 22:03:48 +09:00
dependencies = ''
2023-02-07 21:53:59 +09:00
for manifest_path in $( git ls-files '*Cargo.toml' ) ; do
if [ [ " ${ manifest_path } " != "Cargo.toml" ] ] && ! grep -Eq '\[workspace\]' " ${ manifest_path } " ; then
continue
fi
2023-10-19 04:25:54 +09:00
metadata = $( cargo metadata --format-version= 1 --no-deps --manifest-path " ${ manifest_path } " )
2023-02-07 21:53:59 +09:00
for id in $( jq <<< " ${ metadata } " '.workspace_members[]' ) ; do
2023-04-05 22:47:02 +09:00
dependencies += " $( jq <<< " ${ metadata } " " .packages[] | select(.id == ${ id } ) " | jq -r '.dependencies[].name' ) " $'\n'
2023-02-07 21:53:59 +09:00
done
2022-12-09 22:03:48 +09:00
done
2023-02-07 21:53:59 +09:00
# shellcheck disable=SC2001
2023-03-10 00:41:57 +09:00
dependencies = $( sed <<< " ${ dependencies } " 's/[0-9_-]/\n/g' | LC_ALL = C sort -f -u)
2023-02-07 21:53:59 +09:00
fi
2023-04-05 22:47:02 +09:00
config_old = $( <.cspell.json)
2023-12-24 19:59:08 +09:00
config_new = $( grep <<< " ${ config_old } " -v '^ *//' | jq 'del(.dictionaries[] | select(index("organization-dictionary") | not))' | jq 'del(.dictionaryDefinitions[] | select(.name == "organization-dictionary" | not))' )
2023-09-28 23:41:17 +09:00
trap -- 'echo "${config_old}" >.cspell.json; echo >&2 "$0: trapped SIGINT"; exit 1' SIGINT
2023-04-05 22:47:02 +09:00
echo " ${ config_new } " >.cspell.json
if [ [ -n " ${ has_rust } " ] ] ; then
2023-09-28 23:41:17 +09:00
dependencies_words = $( npx <<< " ${ dependencies } " -y cspell stdin --no-progress --no-summary --words-only --unique || true )
2023-04-05 22:47:02 +09:00
fi
2023-09-28 23:41:17 +09:00
all_words = $( npx -y cspell --no-progress --no-summary --words-only --unique $( git ls-files | ( grep -v " ${ project_dictionary // \. / \\ . } " || true ) ) || true )
2023-04-05 22:47:02 +09:00
echo " ${ config_old } " >.cspell.json
2023-09-28 23:41:17 +09:00
trap - SIGINT
2023-02-07 21:53:59 +09:00
cat >.github/.cspell/rust-dependencies.txt <<EOF
2022-12-09 22:03:48 +09:00
// This file is @generated by $( basename " $0 " ) .
// It is not intended for manual editing.
EOF
2023-04-05 22:47:02 +09:00
if [ [ -n " ${ dependencies_words :- } " ] ] ; then
echo $'\n' " ${ dependencies_words } " >>.github/.cspell/rust-dependencies.txt
2022-12-09 22:03:48 +09:00
fi
2023-02-07 21:53:59 +09:00
check_diff .github/.cspell/rust-dependencies.txt
2023-02-09 05:17:26 +09:00
if ! grep -Eq "^\.github/\.cspell/rust-dependencies.txt linguist-generated" .gitattributes; then
echo "warning: you may want to mark .github/.cspell/rust-dependencies.txt linguist-generated"
fi
2022-12-09 22:03:48 +09:00
2023-09-28 23:41:17 +09:00
echo "+ npx -y cspell --no-progress --no-summary \$(git ls-files)"
if ! npx -y cspell --no-progress --no-summary $( git ls-files) ; then
2023-05-31 02:55:36 +09:00
error " spellcheck failed: please fix uses of above words or add to ${ project_dictionary } if correct "
2023-04-05 22:47:02 +09:00
fi
2022-12-09 22:03:48 +09:00
2023-04-05 22:47:02 +09:00
# Make sure the project-specific dictionary does not contain duplicated words.
2022-12-09 22:03:48 +09:00
for dictionary in .github/.cspell/*.txt; do
2023-05-31 02:55:36 +09:00
if [ [ " ${ dictionary } " = = " ${ project_dictionary } " ] ] ; then
2022-12-09 22:03:48 +09:00
continue
fi
2023-05-31 02:55:36 +09:00
dup = $( sed '/^$/d' " ${ project_dictionary } " " ${ dictionary } " | LC_ALL = C sort -f | uniq -d -i | ( grep -v '//.*' || true ) )
2022-12-09 22:03:48 +09:00
if [ [ -n " ${ dup } " ] ] ; then
2023-05-31 02:55:36 +09:00
error " duplicated words in dictionaries; please remove the following words from ${ project_dictionary } "
2022-12-09 22:03:48 +09:00
echo "======================================="
echo " ${ dup } "
echo "======================================="
fi
done
2023-04-05 22:47:02 +09:00
# Make sure the project-specific dictionary does not contain unused words.
unused = ''
2023-05-31 02:55:36 +09:00
for word in $( grep -v '//.*' " ${ project_dictionary } " || true ) ; do
2023-04-05 22:47:02 +09:00
if ! grep <<< " ${ all_words } " -Eq -i " ^ ${ word } $" ; then
unused += " ${ word } " $'\n'
fi
done
if [ [ -n " ${ unused } " ] ] ; then
2023-05-31 02:55:36 +09:00
error " unused words in dictionaries; please remove the following words from ${ project_dictionary } "
2023-04-05 22:47:02 +09:00
echo "======================================="
echo -n " ${ unused } "
echo "======================================="
fi
2022-12-09 22:03:48 +09:00
else
2023-09-28 23:41:17 +09:00
warn "'npm' is not installed; skipped spell check"
2022-12-09 22:03:48 +09:00
fi
fi
2022-12-02 03:19:40 +09:00
if [ [ -n " ${ should_fail :- } " ] ] ; then
exit 1
fi