diff --git a/README.md b/README.md index 2e0141e7..461ea52f 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,12 @@ Features: New tools are being added frequently, so check this page again! +Linters which are not language-specific: + +- [keep-sorted] + | Language | Formatter | Linter(s) | -| ---------------------- | --------------------- |----------------------------------| +| ---------------------- | --------------------- | -------------------------------- | | C / C++ | [clang-format] | [clang-tidy] | | Cuda | [clang-format] | | | CSS, Less, Sass | [Prettier] | [Stylelint] | @@ -59,6 +63,7 @@ New tools are being added frequently, so check this page again! [swiftformat]: https://github.com/nicklockwood/SwiftFormat [terraform]: https://github.com/hashicorp/terraform [buf]: https://docs.buf.build/format/usage +[keep-sorted]: https://github.com/google/keep-sorted [ktfmt]: https://github.com/facebook/ktfmt [ktlint]: https://github.com/pinterest/ktlint [buildifier]: https://github.com/keith/buildifier-prebuilt diff --git a/docs/BUILD.bazel b/docs/BUILD.bazel index 0c0d296f..0911157e 100644 --- a/docs/BUILD.bazel +++ b/docs/BUILD.bazel @@ -22,6 +22,11 @@ stardoc_with_diff_test( bzl_library_target = "//lint:flake8", ) +stardoc_with_diff_test( + name = "keep_sorted", + bzl_library_target = "//lint:keep_sorted", +) + stardoc_with_diff_test( name = "pmd", bzl_library_target = "//lint:pmd", diff --git a/docs/keep_sorted.md b/docs/keep_sorted.md new file mode 100644 index 00000000..e857e9bd --- /dev/null +++ b/docs/keep_sorted.md @@ -0,0 +1,105 @@ + + +API for declaring a keep-sorted lint aspect that visits all source files. + +Typical usage: + +First, fetch the keep-sorted dependency via gazelle. We provide a convenient go.mod file. +To keep it isolated from your other go dependencies, we recommend adding to .bazelrc: + + common --experimental_isolated_extension_usages + +Next add to MODULE.bazel: + + keep_sorted_deps = use_extension("@gazelle//:extensions.bzl", "go_deps", isolate = True) + keep_sorted_deps.from_file(go_mod = "@aspect_rules_lint//lint/keep-sorted:go.mod") + use_repo(keep_sorted_deps, "com_github_google_keep_sorted") + +Finally, create the linter aspect, typically in `tools/lint/linters.bzl`: + +```starlark +load("@aspect_rules_lint//lint:keep_sorted.bzl", "lint_keep_sorted_aspect") + +keep_sorted = lint_keep_sorted_aspect( + binary = "@com_github_google_keep_sorted//:keep-sorted", +) +``` + +Now you can add `// keep-sorted start` / `// keep-sorted end` lines to your library sources, +following the documentation at https://github.com/google/keep-sorted#usage. + + + +## keep_sorted_action + +
+load("@aspect_rules_lint//lint:keep_sorted.bzl", "keep_sorted_action")
+
+keep_sorted_action(ctx, executable, srcs, stdout, exit_code, options)
+
+ +Run keep-sorted as an action under Bazel. + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| ctx | Bazel Rule or Aspect evaluation context | none | +| executable | label of the the keep-sorted program | none | +| srcs | files to be linted | none | +| stdout | output file containing stdout | none | +| exit_code | output file containing exit code If None, then fail the build when program exits non-zero. | `None` | +| options | additional command-line options | `[]` | + + + + +## keep_sorted_fix + +
+load("@aspect_rules_lint//lint:keep_sorted.bzl", "keep_sorted_fix")
+
+keep_sorted_fix(ctx, executable, srcs, patch, stdout, exit_code, options)
+
+ + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| ctx |

-

| none | +| executable |

-

| none | +| srcs |

-

| none | +| patch |

-

| none | +| stdout |

-

| none | +| exit_code |

-

| `None` | +| options |

-

| `[]` | + + + + +## lint_keep_sorted_aspect + +
+load("@aspect_rules_lint//lint:keep_sorted.bzl", "lint_keep_sorted_aspect")
+
+lint_keep_sorted_aspect(binary)
+
+ +A factory function to create a linter aspect. + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| binary | a keep-sorted executable | none | + +**RETURNS** + +An aspect definition for keep-sorted + + diff --git a/example/.aspect/cli/config.yaml b/example/.aspect/cli/config.yaml index a13ca939..91352919 100644 --- a/example/.aspect/cli/config.yaml +++ b/example/.aspect/cli/config.yaml @@ -11,3 +11,4 @@ lint: - //tools/lint:linters.bzl%checkstyle - //tools/lint:linters.bzl%clang_tidy - //tools/lint:linters.bzl%spotbugs + - //tools/lint:linters.bzl%keep_sorted diff --git a/example/.bazelrc b/example/.bazelrc index 7201ba71..4efb737e 100644 --- a/example/.bazelrc +++ b/example/.bazelrc @@ -1,6 +1,9 @@ # Automatically apply --config=linux, --config=windows etc common --enable_platform_specific_config +# Permit MODULE.bazel to use_extension("@gazelle//:extensions.bzl", "go_deps", isolate = True) +common --experimental_isolated_extension_usages + # Aspect recommended Bazel flags when using rules_java and rules_jvm_external # Pin java versions to desired language level diff --git a/example/MODULE.bazel b/example/MODULE.bazel index eb27adaf..8d677d24 100644 --- a/example/MODULE.bazel +++ b/example/MODULE.bazel @@ -11,7 +11,7 @@ bazel_dep(name = "toolchains_llvm", version = "0.10.3") bazel_dep(name = "toolchains_protoc", version = "0.3.0") bazel_dep(name = "rules_java", version = "8.5.0") bazel_dep(name = "rules_jvm_external", version = "6.5") -bazel_dep(name = "rules_go", version = "0.42.0", repo_name = "io_bazel_rules_go") +bazel_dep(name = "rules_go", version = "0.52.0", repo_name = "io_bazel_rules_go") bazel_dep(name = "rules_proto", version = "6.0.0") bazel_dep(name = "rules_python", version = "0.26.0") bazel_dep(name = "rules_rust", version = "0.50.1") @@ -19,6 +19,7 @@ bazel_dep(name = "buildifier_prebuilt", version = "6.3.3") bazel_dep(name = "platforms", version = "0.0.8") bazel_dep(name = "rules_kotlin", version = "1.9.0") bazel_dep(name = "rules_cc", version = "0.0.9") +bazel_dep(name = "gazelle", version = "0.41.0") local_path_override( module_name = "aspect_rules_lint", @@ -74,10 +75,14 @@ use_repo(pip, "pip") go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk") go_sdk.download( name = "go_sdk", - version = "1.20.3", + version = "1.23.5", ) use_repo(go_sdk, "go_sdk") +keep_sorted_deps = use_extension("@gazelle//:extensions.bzl", "go_deps", isolate = True) +keep_sorted_deps.from_file(go_mod = "@aspect_rules_lint//lint/keep-sorted:go.mod") +use_repo(keep_sorted_deps, "com_github_google_keep_sorted") + # Java and other JVM languages: # https://github.com/bazelbuild/rules_jvm_external/blob/master/examples/bzlmod/MODULE.bazel # https://github.com/bazelbuild/rules_jvm_external#pinning-artifacts-and-integration-with-bazels-downloader diff --git a/example/lint.sh b/example/lint.sh index e552d63d..d66d98e6 100755 --- a/example/lint.sh +++ b/example/lint.sh @@ -37,7 +37,7 @@ if [ $machine == "Windows" ]; then # avoid missing linters on windows platform args=("--aspects=$(echo //tools/lint:linters.bzl%{flake8,pmd,ruff,vale,clang_tidy} | tr ' ' ',')") else - args=("--aspects=$(echo //tools/lint:linters.bzl%{buf,eslint,flake8,ktlint,pmd,ruff,shellcheck,stylelint,vale,clang_tidy,spotbugs} | tr ' ' ',')") + args=("--aspects=$(echo //tools/lint:linters.bzl%{buf,eslint,flake8,keep_sorted,ktlint,pmd,ruff,shellcheck,stylelint,vale,clang_tidy,spotbugs} | tr ' ' ',')") fi # NB: perhaps --remote_download_toplevel is needed as well with remote execution? diff --git a/example/src/Bar.java b/example/src/Bar.java index 64077315..b1ea6f4a 100644 --- a/example/src/Bar.java +++ b/example/src/Bar.java @@ -5,6 +5,16 @@ import java.io.BufferedInputStream; public class Bar { + + enum MyEnum { + // keep-sorted start + B(), + A(), + D(), + C(), + // keep-sorted end + } + // Max line length set to 20, so this should raise issue. protected void finalize(int a) {} } diff --git a/example/tools/lint/linters.bzl b/example/tools/lint/linters.bzl index a0cbd1c4..0e064715 100644 --- a/example/tools/lint/linters.bzl +++ b/example/tools/lint/linters.bzl @@ -5,6 +5,7 @@ load("@aspect_rules_lint//lint:checkstyle.bzl", "lint_checkstyle_aspect") load("@aspect_rules_lint//lint:clang_tidy.bzl", "lint_clang_tidy_aspect") load("@aspect_rules_lint//lint:eslint.bzl", "lint_eslint_aspect") load("@aspect_rules_lint//lint:flake8.bzl", "lint_flake8_aspect") +load("@aspect_rules_lint//lint:keep_sorted.bzl", "lint_keep_sorted_aspect") load("@aspect_rules_lint//lint:ktlint.bzl", "lint_ktlint_aspect") load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") load("@aspect_rules_lint//lint:pmd.bzl", "lint_pmd_aspect") @@ -15,45 +16,45 @@ load("@aspect_rules_lint//lint:stylelint.bzl", "lint_stylelint_aspect") load("@aspect_rules_lint//lint:vale.bzl", "lint_vale_aspect") buf = lint_buf_aspect( - config = "@@//:buf.yaml", + config = Label("@//:buf.yaml"), ) eslint = lint_eslint_aspect( - binary = "@@//tools/lint:eslint", + binary = Label("@//tools/lint:eslint"), # ESLint will resolve the configuration file by looking in the working directory first. # See https://eslint.org/docs/latest/use/configure/configuration-files#configuration-file-resolution # We must also include any other config files we expect eslint to be able to locate, e.g. tsconfigs configs = [ - "@@//:eslintrc", - "@@//src:tsconfig", + Label("@//:eslintrc"), + Label("@//src:tsconfig"), ], ) eslint_test = lint_test(aspect = eslint) stylelint = lint_stylelint_aspect( - binary = "@@//tools/lint:stylelint", - config = "@@//:stylelintrc", + binary = Label("@//tools/lint:stylelint"), + config = Label("@//:stylelintrc"), ) flake8 = lint_flake8_aspect( - binary = "@@//tools/lint:flake8", - config = "@@//:.flake8", + binary = Label("@//tools/lint:flake8"), + config = Label("@//:.flake8"), ) flake8_test = lint_test(aspect = flake8) pmd = lint_pmd_aspect( - binary = "@@//tools/lint:pmd", - rulesets = ["@@//:pmd.xml"], + binary = Label("@//tools/lint:pmd"), + rulesets = [Label("@//:pmd.xml")], ) pmd_test = lint_test(aspect = pmd) checkstyle = lint_checkstyle_aspect( - binary = "@@//tools/lint:checkstyle", - config = "@@//:checkstyle.xml", - data = ["@@//:checkstyle-suppressions.xml"], + binary = Label("@//tools/lint:checkstyle"), + config = Label("@//:checkstyle.xml"), + data = [Label("@//:checkstyle-suppressions.xml")], ) checkstyle_test = lint_test(aspect = checkstyle) @@ -61,8 +62,8 @@ checkstyle_test = lint_test(aspect = checkstyle) ruff = lint_ruff_aspect( binary = "@multitool//tools/ruff", configs = [ - "@@//:.ruff.toml", - "@@//src/subdir:ruff.toml", + Label("@//:.ruff.toml"), + Label("@//src/subdir:ruff.toml"), ], ) @@ -70,21 +71,21 @@ ruff_test = lint_test(aspect = ruff) shellcheck = lint_shellcheck_aspect( binary = "@multitool//tools/shellcheck", - config = "@@//:.shellcheckrc", + config = Label("@//:.shellcheckrc"), ) shellcheck_test = lint_test(aspect = shellcheck) vale = lint_vale_aspect( - binary = "@@//tools/lint:vale", - config = "@@//:.vale.ini", - styles = "@@//tools/lint:vale_styles", + binary = Label("@//tools/lint:vale"), + config = Label("@//:.vale.ini"), + styles = Label("@//tools/lint:vale_styles"), ) ktlint = lint_ktlint_aspect( - binary = "@@com_github_pinterest_ktlint//file", - editorconfig = "@@//:.editorconfig", - baseline_file = "@@//:ktlint-baseline.xml", + binary = Label("@com_github_pinterest_ktlint//file"), + editorconfig = Label("@//:.editorconfig"), + baseline_file = Label("@//:ktlint-baseline.xml"), ) ktlint_test = lint_test(aspect = ktlint) @@ -113,8 +114,14 @@ clang_tidy_global_config = lint_clang_tidy_aspect( ) spotbugs = lint_spotbugs_aspect( - binary = "@@//tools/lint:spotbugs", - exclude_filter = "@@//:spotbugs-exclude.xml", + binary = Label("@//tools/lint:spotbugs"), + exclude_filter = Label("@//:spotbugs-exclude.xml"), ) spotbugs_test = lint_test(aspect = spotbugs) + +keep_sorted = lint_keep_sorted_aspect( + binary = Label("@com_github_google_keep_sorted//:keep-sorted"), +) + +keep_sorted_test = lint_test(aspect = keep_sorted) diff --git a/lint/BUILD.bazel b/lint/BUILD.bazel index 61e15f91..88ed6751 100644 --- a/lint/BUILD.bazel +++ b/lint/BUILD.bazel @@ -161,6 +161,13 @@ bzl_library( deps = ["//lint/private:lint_aspect"], ) +bzl_library( + name = "keep_sorted", + srcs = ["keep_sorted.bzl"], + visibility = ["//visibility:public"], + deps = ["//lint/private:lint_aspect"], +) + bzl_library( name = "pmd", srcs = ["pmd.bzl"], diff --git a/lint/keep-sorted/BUILD.bazel b/lint/keep-sorted/BUILD.bazel new file mode 100644 index 00000000..aead3791 --- /dev/null +++ b/lint/keep-sorted/BUILD.bazel @@ -0,0 +1 @@ +# Empty package diff --git a/lint/keep-sorted/go.mod b/lint/keep-sorted/go.mod new file mode 100644 index 00000000..5cd5b7b1 --- /dev/null +++ b/lint/keep-sorted/go.mod @@ -0,0 +1,15 @@ +module github.com/aspect-build/rules_lint/lint/keep-sorted + +go 1.23.5 + +require github.com/google/keep-sorted v0.6.0 + +require ( + github.com/Workiva/go-datastructures v1.0.53 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/rs/zerolog v1.31.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/sys v0.15.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/lint/keep-sorted/go.sum b/lint/keep-sorted/go.sum new file mode 100644 index 00000000..dae70103 --- /dev/null +++ b/lint/keep-sorted/go.sum @@ -0,0 +1,62 @@ +github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig= +github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/keep-sorted v0.6.0 h1:mrDMFnTUxsLatnYBBIJA/1j/Z3ZXTub/pCzbKvA2Ga8= +github.com/google/keep-sorted v0.6.0/go.mod h1:JYy9vljs7P8b3QdPOQkywA+4u36FUHwsNITZIpJyPkE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lint/keep-sorted/tools.go b/lint/keep-sorted/tools.go new file mode 100644 index 00000000..f04af7c4 --- /dev/null +++ b/lint/keep-sorted/tools.go @@ -0,0 +1,9 @@ +// See https://github.com/bazel-contrib/rules_go/blob/7f6a9bf5870f2b5ffbba1615658676dcabf9edc7/docs/go/core/bzlmod.md#depending-on-tools +//go:build tools +// +build tools + +package linting_tools + +import ( + _ "github.com/google/keep-sorted" +) diff --git a/lint/keep_sorted.bzl b/lint/keep_sorted.bzl new file mode 100644 index 00000000..8a713a59 --- /dev/null +++ b/lint/keep_sorted.bzl @@ -0,0 +1,161 @@ +"""API for declaring a keep-sorted lint aspect that visits all source files. + +Typical usage: + +First, fetch the keep-sorted dependency via gazelle. We provide a convenient go.mod file. +To keep it isolated from your other go dependencies, we recommend adding to .bazelrc: + + common --experimental_isolated_extension_usages + +Next add to MODULE.bazel: + + keep_sorted_deps = use_extension("@gazelle//:extensions.bzl", "go_deps", isolate = True) + keep_sorted_deps.from_file(go_mod = "@aspect_rules_lint//lint/keep-sorted:go.mod") + use_repo(keep_sorted_deps, "com_github_google_keep_sorted") + +Finally, create the linter aspect, typically in `tools/lint/linters.bzl`: + +```starlark +load("@aspect_rules_lint//lint:keep_sorted.bzl", "lint_keep_sorted_aspect") + +keep_sorted = lint_keep_sorted_aspect( + binary = "@com_github_google_keep_sorted//:keep-sorted", +) +``` + +Now you can add `// keep-sorted start` / `// keep-sorted end` lines to your library sources, +following the documentation at https://github.com/google/keep-sorted#usage. +""" + +load("//lint/private:lint_aspect.bzl", "LintOptionsInfo", "filter_srcs", "noop_lint_action", "output_files", "patch_and_output_files") + +_MNEMONIC = "AspectRulesLintKeepSorted" + +def keep_sorted_action(ctx, executable, srcs, stdout, exit_code = None, options = []): + """Run keep-sorted as an action under Bazel. + + Args: + ctx: Bazel Rule or Aspect evaluation context + executable: label of the the keep-sorted program + srcs: files to be linted + stdout: output file containing stdout + exit_code: output file containing exit code + If None, then fail the build when program exits non-zero. + options: additional command-line options + """ + inputs = srcs + outputs = [stdout] + + # Wire command-line options, see + # Flags: + # --color string Whether to color debug output. One of "always", "never", or "auto" (default "auto") + # --default-options options The options keep-sorted will use to sort. Per-block overrides apply on top of these options. Note: list options like prefix_order are not merged with per-block overrides. They are completely overridden. (default allow_yaml_lists=yes case=yes group=yes remove_duplicates=yes sticky_comments=yes) + # --lines line_ranges Line ranges of the form "start:end". Only processes keep-sorted blocks that overlap with the given line ranges. Can only be used when fixing a single file. This flag can either be a comma-separated list of line ranges, or it can be specified multiple times on the command line to specify multiple line ranges. (default []) + # --mode mode Determines what mode to run this tool in. One of ["fix" "lint"] (default fix) + # -v, --verbose count Log more verbosely + # --version Report the keep-sorted version. + args = ctx.actions.args() + args.add_all(options) + args.add("--mode=lint") + args.add_all(srcs) + + if exit_code: + command = "{keep_sorted} $@ >{stdout}; echo $? > " + exit_code.path + outputs.append(exit_code) + else: + # Create empty stdout file on success, as Bazel expects one + command = "{keep_sorted} $@ && touch {stdout}" + + ctx.actions.run_shell( + inputs = inputs, + outputs = outputs, + tools = [executable], + command = command.format(keep_sorted = executable.path, stdout = stdout.path), + arguments = [args], + mnemonic = _MNEMONIC, + progress_message = "Linting %{label} with KeepSorted", + ) + +def keep_sorted_fix(ctx, executable, srcs, patch, stdout, exit_code = None, options = []): + patch_cfg = ctx.actions.declare_file("_{}.keep-sorted.patch_cfg".format(ctx.label.name)) + + ctx.actions.write( + output = patch_cfg, + content = json.encode({ + "linter": executable._keep_sorted.path, + "args": ["--mode=fix"] + options + [s.path for s in srcs], + "files_to_diff": [s.path for s in srcs], + "output": patch.path, + }), + ) + + ctx.actions.run( + inputs = srcs + [patch_cfg], + outputs = [patch, exit_code, stdout], + executable = executable._patcher, + arguments = [patch_cfg.path], + env = { + "BAZEL_BINDIR": ".", + "JS_BINARY__EXIT_CODE_OUTPUT_FILE": exit_code.path, + "JS_BINARY__STDOUT_OUTPUT_FILE": stdout.path, + "JS_BINARY__SILENT_ON_SUCCESS": "1", + }, + tools = [executable._keep_sorted], + mnemonic = _MNEMONIC, + progress_message = "Fixing %{label} with KeepSorted", + ) + +def _keep_sorted_aspect_impl(target, ctx): + if ctx.attr._options[LintOptionsInfo].fix: + outputs, info = patch_and_output_files(_MNEMONIC, target, ctx) + else: + outputs, info = output_files(_MNEMONIC, target, ctx) + + if not hasattr(ctx.rule.attr, "srcs"): + noop_lint_action(ctx, outputs) + return [info] + + files_to_lint = filter_srcs(ctx.rule) + + if len(files_to_lint) == 0: + noop_lint_action(ctx, outputs) + return [info] + + color_options = ["--color=always"] if ctx.attr._options[LintOptionsInfo].color else [] + if hasattr(outputs, "patch"): + keep_sorted_fix(ctx, ctx.executable, files_to_lint, outputs.patch, outputs.human.out, outputs.human.exit_code, color_options) + else: + keep_sorted_action(ctx, ctx.executable._keep_sorted, files_to_lint, outputs.human.out, outputs.human.exit_code, color_options) + keep_sorted_action(ctx, ctx.executable._keep_sorted, files_to_lint, outputs.machine.out, outputs.machine.exit_code) + return [info] + +def lint_keep_sorted_aspect(binary): + """A factory function to create a linter aspect. + + Args: + binary: a keep-sorted executable + + Returns: + An aspect definition for keep-sorted + """ + return aspect( + implementation = _keep_sorted_aspect_impl, + attrs = { + "_options": attr.label( + default = "//lint:options", + providers = [LintOptionsInfo], + ), + "_keep_sorted": attr.label( + default = binary, + executable = True, + cfg = "exec", + ), + "_patcher": attr.label( + default = "@aspect_rules_lint//lint/private:patcher", + executable = True, + cfg = "exec", + ), + }, + toolchains = [ + ], + ) diff --git a/lint/ruff.bzl b/lint/ruff.bzl index cd05e37f..80e03bc3 100644 --- a/lint/ruff.bzl +++ b/lint/ruff.bzl @@ -149,7 +149,7 @@ def ruff_fix(ctx, executable, srcs, config, patch, stdout, exit_code, env = {}): }), tools = [executable._ruff], mnemonic = _MNEMONIC, - progress_message = "Linting %{label} with Ruff", + progress_message = "Fixing %{label} with Ruff", ) # buildifier: disable=function-docstring