diff --git a/README.md b/README.md index f39d69d0..23690b15 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ This leads to some minor differences in how they are used in rules_lint. | CSS/HTML | [Prettier] | | | JSON | [Prettier] | | | Markdown | [Prettier] | | -| Bash | [prettier-plugin-sh] | [shellcheck] | +| Bash | [shfmt] | [shellcheck] | | SQL | [prettier-plugin-sql] | | | Starlark (Bazel) | [Buildifier] | | | Swift | [SwiftFormat] (1) | | @@ -72,13 +72,13 @@ This leads to some minor differences in how they are used in rules_lint. [buf]: https://docs.buf.build/format/usage [ktfmt]: https://github.com/facebook/ktfmt [buildifier]: https://github.com/keith/buildifier-prebuilt -[prettier-plugin-sh]: https://github.com/un-ts/prettier [prettier-plugin-sql]: https://github.com/un-ts/prettier [gofmt]: https://pkg.go.dev/cmd/gofmt [jsonnetfmt]: https://github.com/google/go-jsonnet [scalafmt]: https://scalameta.org/scalafmt [ruff]: https://docs.astral.sh/ruff/ [shellcheck]: https://www.shellcheck.net/ +[shfmt]: https://github.com/mvdan/sh 1. Non-hermetic: requires that a swift toolchain is installed on the machine. See https://github.com/bazelbuild/rules_swift#1-install-swift diff --git a/docs/format.md b/docs/format.md index 4628572a..0d39af07 100644 --- a/docs/format.md +++ b/docs/format.md @@ -37,7 +37,7 @@ multi_formatter_binary( ## multi_formatter_binary
-multi_formatter_binary(name, go, java, javascript, jsonnet, kotlin, python, starlark, swift,
+multi_formatter_binary(name, go, java, javascript, jsonnet, kotlin, python, sh, starlark, swift,
                        terraform)
 
@@ -55,6 +55,7 @@ Produces an executable that aggregates the supplied formatter binaries | jsonnet | a binary target that runs jsonnetfmt | Label | optional | None | | kotlin | a binary target that runs ktfmt | Label | optional | None | | python | a binary target that runs ruff | Label | optional | None | +| sh | a binary target that runs shfmt | Label | optional | None | | starlark | a binary target that runs buildifier | Label | optional | None | | swift | a binary target that runs swiftformat | Label | optional | None | | terraform | a binary target that runs terraform | Label | optional | None | diff --git a/example/MODULE.bazel b/example/MODULE.bazel index 92bf9bf4..845c6757 100644 --- a/example/MODULE.bazel +++ b/example/MODULE.bazel @@ -28,7 +28,6 @@ npm.npm_translate_lock( pnpm_lock = "//:pnpm-lock.yaml", public_hoist_packages = { "@typescript-eslint/eslint-plugin": [""], - "prettier-plugin-sh": [""], "prettier-plugin-sql": [""], }, ) diff --git a/example/WORKSPACE.bazel b/example/WORKSPACE.bazel index 74770262..374ee05e 100644 --- a/example/WORKSPACE.bazel +++ b/example/WORKSPACE.bazel @@ -151,6 +151,7 @@ load( "fetch_jsonnet", "fetch_ktfmt", "fetch_pmd", + "fetch_shfmt", "fetch_swiftformat", "fetch_terraform", ) @@ -165,6 +166,8 @@ fetch_java_format() fetch_ktfmt() +fetch_shfmt() + fetch_swiftformat() load("@aspect_rules_lint//lint:ruff.bzl", "fetch_ruff") diff --git a/example/WORKSPACE.bzlmod b/example/WORKSPACE.bzlmod index 9a172123..6f846fbb 100644 --- a/example/WORKSPACE.bzlmod +++ b/example/WORKSPACE.bzlmod @@ -9,6 +9,7 @@ load( "fetch_jsonnet", "fetch_ktfmt", "fetch_pmd", + "fetch_shfmt", "fetch_swiftformat", "fetch_terraform", ) @@ -24,6 +25,8 @@ fetch_java_format() fetch_ktfmt() +fetch_shfmt() + fetch_swiftformat() fetch_ruff() diff --git a/example/lint.sh b/example/lint.sh index 700b502f..6eab05f0 100755 --- a/example/lint.sh +++ b/example/lint.sh @@ -8,8 +8,8 @@ set -o errexit -o pipefail -o nounset if [ "$#" -eq 0 ]; then - echo "usage: lint.sh [target pattern...]" - exit 1 + echo "usage: lint.sh [target pattern...]" + exit 1 fi # Produce report files @@ -17,12 +17,11 @@ fi # TODO: put back ruff after the output paths don't collide bazel build --aspects //tools:lint.bzl%eslint,//tools:lint.bzl%buf,//tools:lint.bzl%flake8,//tools:lint.bzl%pmd,//tools:lint.bzl%shellcheck --output_groups=rules_lint_report $@ - # Show the results. # `-mtime -1`: only look at files modified in the last day, to mitigate showing stale results of old bazel runs. # `-size +1c`: don't show files containing zero bytes for report in $(find $(bazel info bazel-bin) -mtime -1 -size +1c -type f -name "*.aspect_rules_lint.report"); do - echo "From ${report}:" - cat "${report}" - echo + echo "From ${report}:" + cat "${report}" + echo done diff --git a/example/package.json b/example/package.json index afab9fe0..660f6928 100644 --- a/example/package.json +++ b/example/package.json @@ -5,7 +5,6 @@ "@typescript-eslint/eslint-plugin": "*", "typescript": "4.9.5", "prettier": "^2.8.7", - "prettier-plugin-sh": "^0.12.8", "prettier-plugin-sql": "^0.14.0" }, "//FIXME(alexeagle)": "This section shouldn't be needed since these are plugins", @@ -13,7 +12,6 @@ "packageExtensions": { "prettier": { "peerDependencies": { - "prettier-plugin-sh": "*", "prettier-plugin-sql": "*" } } diff --git a/example/tools/BUILD b/example/tools/BUILD index d05d0a24..ba653b09 100644 --- a/example/tools/BUILD +++ b/example/tools/BUILD @@ -6,7 +6,6 @@ we don't want to trigger eager fetches of these for builds that don't want to ru load("@aspect_rules_lint//format:defs.bzl", "multi_formatter_binary") load("@npm//:prettier/package_json.bzl", prettier = "bin") -load("@rules_python//python/entry_points:py_console_script_binary.bzl", "py_console_script_binary") package(default_visibility = ["//:__pkg__"]) @@ -64,6 +63,16 @@ java_binary( runtime_deps = ["@ktfmt//jar"], ) +alias( + name = "shfmt", + actual = select({ + "@bazel_tools//src/conditions:linux_x86_64": "@shfmt_linux_x86_64//file:shfmt", + "@bazel_tools//src/conditions:linux_aarch64": "@shfmt_linux_aarch64//file:shfmt", + "@bazel_tools//src/conditions:darwin_arm64": "@shfmt_darwin_aarch64//file:shfmt", + "@bazel_tools//src/conditions:darwin_x86_64": "@shfmt_darwin_x86_64//file:shfmt", + }), +) + alias( name = "swiftformat", # TODO: keith says it would be okay to upstream a proper toolchain for this @@ -82,6 +91,7 @@ multi_formatter_binary( jsonnet = ":jsonnetfmt", kotlin = ":ktfmt", python = ":ruff", + sh = ":shfmt", starlark = "@buildifier_prebuilt//:buildifier", swift = ":swiftformat", terraform = ":terraform", diff --git a/format/private/format.sh b/format/private/format.sh index dab317c1..2071e4bc 100755 --- a/format/private/format.sh +++ b/format/private/format.sh @@ -50,6 +50,7 @@ case "$mode" in swiftmode="--lint" prettiermode="--check" ruffmode="format --check" + shfmtmode="-l" javamode="--set-exit-if-changed --dry-run" ktmode="--set-exit-if-changed --dry-run" gofmtmode="-l" @@ -62,6 +63,7 @@ case "$mode" in swiftmode="" prettiermode="--write" ruffmode="format" + shfmtmode="-w" javamode="--replace" ktmode="" gofmtmode="-w" @@ -85,9 +87,9 @@ if [ -n "$files" ] && [ -n "$bin" ]; then fi if [ "$#" -eq 0 ]; then - files=$(git ls-files '*.js' '*.cjs' '*.mjs' '*.sh' '*.ts' '*.tsx' '*.mts' '*.cts' '*.json' '*.css' '*.html' '*.md' '*.sql') + files=$(git ls-files '*.js' '*.cjs' '*.mjs' '*.ts' '*.tsx' '*.mts' '*.cts' '*.json' '*.css' '*.html' '*.md' '*.sql') else - files=$(find "$@" -name '*.js' -or -name '*.cjs' -or -name '*.mjs' -or -name '*.sh' -or -name '*.ts' -or -name '*.tsx' -or -name '*.mts' -or -name '*.cts' -or -name '*.json' -or -name '*.css' -or -name '*.html' -or -name '*.md' -or -name '*.sql') + files=$(find "$@" -name '*.js' -or -name '*.cjs' -or -name '*.mjs' -or -name '*.ts' -or -name '*.tsx' -or -name '*.mts' -or -name '*.cts' -or -name '*.json' -or -name '*.css' -or -name '*.html' -or -name '*.md' -or -name '*.sql') fi bin=$(rlocation {{prettier}}) if [ -n "$files" ] && [ -n "$bin" ]; then @@ -186,6 +188,17 @@ if [ -n "$files" ] && [ -n "$bin" ]; then fi fi +if [ "$#" -eq 0 ]; then + files=$(git ls-files '*.sh' '*.bash') +else + files=$(find "$@" -name '*.sh' -or -name '*.bash') +fi +bin=$(rlocation {{shfmt}}) +if [ -n "$files" ] && [ -n "$bin" ]; then + echo "Running shfmt..." + echo "$files" | tr \\n \\0 | xargs -0 $bin $shfmtmode +fi + if [ "$#" -eq 0 ]; then files=$(git ls-files '*.swift') else diff --git a/format/private/formatter_binary.bzl b/format/private/formatter_binary.bzl index cb070ce2..cbea4c53 100644 --- a/format/private/formatter_binary.bzl +++ b/format/private/formatter_binary.bzl @@ -12,6 +12,7 @@ _attrs = { "java": attr.label(doc = "a binary target that runs google-java-format", executable = True, cfg = "exec", allow_files = True), "swift": attr.label(doc = "a binary target that runs swiftformat", executable = True, cfg = "exec", allow_files = True), "go": attr.label(doc = "a binary target that runs go fmt", executable = True, cfg = "exec", allow_files = True), + "sh": attr.label(doc = "a binary target that runs shfmt", executable = True, cfg = "exec", allow_files = True), "_bin": attr.label(default = "//format/private:format.sh", allow_single_file = True), "_runfiles_lib": attr.label(default = "@bazel_tools//tools/bash/runfiles", allow_single_file = True), } @@ -29,6 +30,7 @@ def _formatter_binary_impl(ctx): "java-format": ctx.attr.java, "swiftformat": ctx.attr.swift, "gofmt": ctx.attr.go, + "shfmt": ctx.attr.sh, } for tool, attr in tools.items(): if attr: diff --git a/format/repositories.bzl b/format/repositories.bzl index bcb1ee5d..0af29086 100644 --- a/format/repositories.bzl +++ b/format/repositories.bzl @@ -3,12 +3,15 @@ Needed until Bazel 7 allows MODULE.bazel to directly call repository rules. """ -load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive", _http_jar = "http_jar") +load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive", _http_file = "http_file", _http_jar = "http_jar") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") def http_archive(name, **kwargs): maybe(_http_archive, name = name, **kwargs) +def http_file(name, **kwargs): + maybe(_http_file, name = name, **kwargs) + def http_jar(name, **kwargs): maybe(_http_jar, name = name, **kwargs) @@ -53,6 +56,42 @@ def fetch_jsonnet(): urls = ["https://github.com/google/go-jsonnet/releases/download/v{0}/go-jsonnet_{0}_Linux_arm64.tar.gz".format(jsonnet_version)], ) +# buildifier: disable=function-docstring +def fetch_shfmt(): + shfmt_version = "3.7.0" + + http_file( + name = "shfmt_darwin_x86_64", + downloaded_file_path = "shfmt", + executable = True, + sha256 = "ae1d1ab961c113fb3dc2ff1150f33c3548983550d91da889b3171a5bcfaab14f", + urls = ["https://github.com/mvdan/sh/releases/download/v{0}/shfmt_v{0}_darwin_amd64".format(shfmt_version)], + ) + + http_file( + name = "shfmt_darwin_aarch64", + downloaded_file_path = "shfmt", + executable = True, + sha256 = "ad7ff6f666adba3d801eb17365a15539f07296718d39fb62cc2fde6b527178aa", + urls = ["https://github.com/mvdan/sh/releases/download/v{0}/shfmt_v{0}_darwin_arm64".format(shfmt_version)], + ) + + http_file( + name = "shfmt_linux_x86_64", + downloaded_file_path = "shfmt", + executable = True, + sha256 = "0264c424278b18e22453fe523ec01a19805ce3b8ebf18eaf3aadc1edc23f42e3", + urls = ["https://github.com/mvdan/sh/releases/download/v{0}/shfmt_v{0}_linux_amd64".format(shfmt_version)], + ) + + http_file( + name = "shfmt_linux_aarch64", + downloaded_file_path = "shfmt", + executable = True, + sha256 = "111612560d15bd53d8e8f8f85731176ce12f3b418ec473d39a40ed6bbec772de", + urls = ["https://github.com/mvdan/sh/releases/download/v{0}/shfmt_v{0}_linux_arm64".format(shfmt_version)], + ) + def fetch_terraform(): tf_version = "1.4.0"