diff --git a/.automation/test/clojure/README.md b/.automation/test/clojure/README.md new file mode 100644 index 00000000..6606ef5b --- /dev/null +++ b/.automation/test/clojure/README.md @@ -0,0 +1,13 @@ +# Clojure Test Cases +This folder holds the test cases for **Clojure**. + +## Additional Docs +No Additional information is needed for this test case. + +## Good Test Cases +The test cases denoted: `LANGUAGE_good_FILE.EXTENSION` are all valid, and should pass successfully when linted. +- **Note:** They are linted utilizing the default linter rules. + +## Bad Test Cases +The test cases denoted: `LANGUAGE_bad_FILE.EXTENSION` are **NOT** valid, and should trigger errors when linted. +- **Note:** They are linted utilizing the default linter rules. diff --git a/.automation/test/clojure/clojure_bad_1.clj b/.automation/test/clojure/clojure_bad_1.clj new file mode 100644 index 00000000..c8366066 --- /dev/null +++ b/.automation/test/clojure/clojure_bad_1.clj @@ -0,0 +1,64 @@ +(ns foo + (:require + [clojure.string :as str] + ;; We're never using this namespace. Also, the namespaces aren't sorted. + [clojure.set :as set])) + +;; Here we made a typo, so the symbol is unresolved: +(but-last [1 2 3]) + +;; Clj-kondo knows about arities of clojure namespaces, but you can also teach +;; it about your libraries or own namespaces +(str/join) + +;; foo has an arity of 2, but we're not actually using y +(defn foo-fn [x y] + ;; this do is redundant: + (do + ;; this is handy for debugging, but please remove it before pushing your code: + (def tmp_x x) + (let [y (fn [] (inc x))] + ;; the next let can be squashed together with the previous: + (let [z y] + ;; whoopsy, calling a local function with an incorrect number of args: + (y x) + ;; also wrong: + (recur))))) + +(letfn + [(f [] (h 1)) + (h [] (f 1))]) + +(defn- private-fn []) +;; redefining it... +(defn- private-fn []) + +(defn foo [] :foo) +;; Type error, because foo doesn't return a number! +(inc (foo)) + +;; I'm tired now, let's sleep... +;; Oops, not happening because of wrong amount of args: +(Thread/sleep 1000 1 2) + +;; Here we switch to another namespace and require the previous: +(ns bar (:require [foo :as f])) + +;; Wrong arity when calling a function from the previous namespace: +(f/foo-fn) + +;; private: +(f/private-fn) + +;; this won't pass the reader: +{:a 1 :a 2} +;; and neither will this: +#{1 1} +;; nor this: +{:a 1 :b} + +(ns bar-test (:require [clojure.test :as t])) + +(t/deftest my-tests + ;; you're not actually testing something here: + (odd? (inc 1))) \ No newline at end of file diff --git a/.automation/test/clojure/clojure_good_1.clj b/.automation/test/clojure/clojure_good_1.clj new file mode 100644 index 00000000..391f4688 --- /dev/null +++ b/.automation/test/clojure/clojure_good_1.clj @@ -0,0 +1,34 @@ +(ns foo + (:require + [clojure.string :as str])) + +(butlast [1 2 3]) + +(str/join "" "") + +(defn foo-fn [x] + (let [y (fn [] (inc x))] + (y))) + +(letfn + [(f [g] (h g)) + (h [i] (f i))]) + +(defn foo [] 1) +(inc (foo)) + +(Thread/sleep 1000 1) + +;; Here we switch to another namespace and require the previous: +(ns bar (:require [foo :as f])) + +(f/foo-fn 1) + +{:a 1 :b 2} +#{1 2} +{:a 1 :b 2} + +(ns bar-test (:require [clojure.test :as t])) + +(t/deftest my-tests + (t/is (odd? (inc 1)))) \ No newline at end of file diff --git a/.github/linters/.clj-kondo/config.edn b/.github/linters/.clj-kondo/config.edn new file mode 100644 index 00000000..30cae4c6 --- /dev/null +++ b/.github/linters/.clj-kondo/config.edn @@ -0,0 +1,2 @@ +{:linters {:unresolved-symbol {:exclude [(compojure.api.sweet/defroutes)]} + :refer-all {:exclude [clj-time.jdbc]}}} diff --git a/.gitignore b/.gitignore index 0c70da66..e529ffb7 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,6 @@ typings/ # next.js build output .next + +# clj-kondo cache +.cache \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 49a5dbb1..4dec22a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -131,6 +131,15 @@ RUN curl -Ls "$(curl -Ls https://api.github.com/repos/terraform-linters/tflint/r RUN wget "https://github.com/dotenv-linter/dotenv-linter/releases/latest/download/dotenv-linter-alpine-x86_64.tar.gz" -O - -q | tar -xzf - \ && mv "dotenv-linter" /usr/bin +##################### +# Install clj-kondo # +##################### +ARG CLJ_KONDO_VERSION='2020.06.12' +RUN curl -sLO https://github.com/borkdude/clj-kondo/releases/download/v${CLJ_KONDO_VERSION}/clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip \ + && unzip clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip \ + && rm clj-kondo-${CLJ_KONDO_VERSION}-linux-static-amd64.zip \ + && mv clj-kondo /usr/bin/ + ################## # Install ktlint # ################## @@ -166,6 +175,7 @@ ENV GITHUB_SHA=${GITHUB_SHA} \ VALIDATE_TERRAFORM=${VALIDATE_TERRAFORM} \ VALIDATE_CSS=${VALIDATE_CSS} \ VALIDATE_ENV=${VALIDATE_ENV} \ + VALIDATE_CLOJURE=${VALIDATE_CLOJURE} \ VALIDATE_KOTLIN=${VALIDATE_KOTLIN} \ VALIDATE_POWERSHELL=${VALIDATE_POWERSHELL} \ ANSIBLE_DIRECTORY=${ANSIBLE_DIRECTORY} \ diff --git a/README.md b/README.md index d21b666b..de10ad11 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Developers on **GitHub** can call the **GitHub Action** to lint their code base | --- | --- | | **Ansible** | [ansible-lint](https://github.com/ansible/ansible-lint) | | **CSS** | [stylelint](https://stylelint.io/) | +| **Clojure** | [clj-kondo](https://github.com/borkdude/clj-kondo) | | **CoffeeScript** | [coffeelint](https://coffeelint.github.io/) | | **Dockerfile** | [dockerfilelint](https://github.com/replicatedhq/dockerfilelint.git) | | **Golang** | [golangci-lint](https://github.com/golangci/golangci-lint) | @@ -159,6 +160,7 @@ and won't run anything unexpected. | **VALIDATE_TERRAFORM** | `true` | Flag to enable or disable the linting process of the language. | | **VALIDATE_CSS** | `true` | Flag to enable or disable the linting process of the language. | | **VALIDATE_ENV** | `true` | Flag to enable or disable the linting process of the language. | +| **VALIDATE_CLOJURE** | `true` | Flag to enable or disable the linting process of the language. | | **VALIDATE_KOTLIN** | `true` | Flag to enable or disable the linting process of the language. | | **ANSIBLE_DIRECTORY** | `/ansible` | Flag to set the root directory for Ansible file location(s). | | **ACTIONS_RUNNER_DEBUG** | `false` | Flag to enable additional information about the linter, versions, and additional output. | diff --git a/TEMPLATES/.clj-kondo/config.edn b/TEMPLATES/.clj-kondo/config.edn new file mode 100644 index 00000000..30cae4c6 --- /dev/null +++ b/TEMPLATES/.clj-kondo/config.edn @@ -0,0 +1,2 @@ +{:linters {:unresolved-symbol {:exclude [(compojure.api.sweet/defroutes)]} + :refer-all {:exclude [clj-time.jdbc]}}} diff --git a/docs/disabling-linters.md b/docs/disabling-linters.md index 9c6ecf0a..cf5bd831 100644 --- a/docs/disabling-linters.md +++ b/docs/disabling-linters.md @@ -607,3 +607,23 @@ import package.b.* ### ktlint disable entire file - There is currently **No** way to disable rules inline of the file(s) + +-------------------------------------------------------------------------------- + +## Clojure +- [clj-kondo](https://github.com/borkdude/clj-kondo) +- Since clj-kondo approaches static analysis in a very Clojure way, it is advised to read the [configuration docs](https://github.com/borkdude/clj-kondo/blob/master/doc/config.md) + +### clj-kondo standard Config file +- `.github/linters/.clj-kondo/config.edn` + +### clj-kondo disable single line +- There is currently **No** way to disable rules in a single line + +### clj-kondo disable code block +- There is currently **No** way to disable rules in a code block + +### clj-kondo disable entire file +```clojure +{:output {:exclude-files ["path/to/file"]}} +``` diff --git a/lib/linter.sh b/lib/linter.sh index e422ad9d..5957ba15 100755 --- a/lib/linter.sh +++ b/lib/linter.sh @@ -54,6 +54,9 @@ POWERSHELL_LINTER_RULES="$DEFAULT_RULES_LOCATION/$POWERSHELL_FILE_NAME" # Pat # CSS Vars CSS_FILE_NAME='.stylelintrc.json' # Name of the file CSS_LINTER_RULES="$DEFAULT_RULES_LOCATION/$CSS_FILE_NAME" # Path to the CSS lint rules +# Clojure Vars +CLOJURE_FILE_NAME='.clj-kondo/config.edn' +CLOJURE_LINTER_RULES="$DEFAULT_RULES_LOCATION/$CLOJURE_FILE_NAME" ####################################### # Linter array for information prints # @@ -61,7 +64,8 @@ CSS_LINTER_RULES="$DEFAULT_RULES_LOCATION/$CSS_FILE_NAME" # Path to th LINTER_ARRAY=("jsonlint" "yamllint" "xmllint" "markdownlint" "shellcheck" "pylint" "perl" "rubocop" "coffeelint" "eslint" "standard" "ansible-lint" "/dockerfilelint/bin/dockerfilelint" "golangci-lint" "tflint" - "stylelint" "dotenv-linter" "powershell" "ktlint") + "stylelint" "dotenv-linter" "powershell" "ktlint" "clj-kondo") + ############################# # Language array for prints # @@ -69,7 +73,7 @@ LINTER_ARRAY=("jsonlint" "yamllint" "xmllint" "markdownlint" "shellcheck" LANGUAGE_ARRAY=('YML' 'JSON' 'XML' 'MARKDOWN' 'BASH' 'PERL' 'PHP' 'RUBY' 'PYTHON' 'COFFEESCRIPT' 'ANSIBLE' 'JAVASCRIPT_STANDARD' 'JAVASCRIPT_ES' 'TYPESCRIPT_STANDARD' 'TYPESCRIPT_ES' 'DOCKER' 'GO' 'TERRAFORM' - 'ENV' 'POWERSHELL' 'KOTLIN') + 'CSS' 'ENV' 'POWERSHELL' 'KOTLIN' 'CLOJURE') ################### # GitHub ENV Vars # @@ -99,6 +103,7 @@ VALIDATE_DOCKER="${VALIDATE_DOCKER}" # Boolean to validate lang VALIDATE_GO="${VALIDATE_GO}" # Boolean to validate language VALIDATE_CSS="${VALIDATE_CSS}" # Boolean to validate language VALIDATE_ENV="${VALIDATE_ENV}" # Boolean to validate language +VALIDATE_CLOJURE="${VALIDATE_CLOJURE}" # Boolean to validate language VALIDATE_TERRAFORM="${VALIDATE_TERRAFORM}" # Boolean to validate language VALIDATE_POWERSHELL="${VALIDATE_POWERSHELL}" # Boolean to validate language VALIDATE_KOTLIN="${VALIDATE_KOTLIN}" # Boolean to validate language @@ -148,6 +153,7 @@ FILE_ARRAY_TERRAFORM=() # Array of files to check FILE_ARRAY_POWERSHELL=() # Array of files to check FILE_ARRAY_CSS=() # Array of files to check FILE_ARRAY_ENV=() # Array of files to check +FILE_ARRAY_CLOJURE=() # Array of files to check FILE_ARRAY_KOTLIN=() # Array of files to check ############ @@ -174,6 +180,7 @@ ERRORS_FOUND_TERRAFORM=0 # Count of errors found ERRORS_FOUND_POWERSHELL=0 # Count of errors found ERRORS_FOUND_CSS=0 # Count of errors found ERRORS_FOUND_ENV=0 # Count of errors found +ERRORS_FOUND_CLOJURE=0 # Count of errors found ERRORS_FOUND_KOTLIN=0 # Count of errors found ################################################################################ @@ -755,6 +762,7 @@ GetValidationInfo() VALIDATE_POWERSHELL=$(echo "$VALIDATE_POWERSHELL" | awk '{print tolower($0)}') VALIDATE_CSS=$(echo "$VALIDATE_CSS" | awk '{print tolower($0)}') VALIDATE_ENV=$(echo "$VALIDATE_ENV" | awk '{print tolower($0)}') + VALIDATE_CLOJURE=$(echo "$VALIDATE_CLOJURE" | awk '{print tolower($0)') VALIDATE_KOTLIN=$(echo "$VALIDATE_KOTLIN" | awk '{print tolower($0)}') ################################################ @@ -782,6 +790,7 @@ GetValidationInfo() -n "$VALIDATE_POWERSHELL" || \ -n "$VALIDATE_CSS" || \ -n "$VALIDATE_ENV" || \ + -n "$VALIDATE_CLOJURE" || \ -n "$VALIDATE_KOTLIN" ]]; then ANY_SET="true" fi @@ -1095,6 +1104,20 @@ GetValidationInfo() fi + ####################################### + # Validate if we should check Clojure # + ####################################### + if [[ "$ANY_SET" == "true" ]]; then + # Some linter flags were set - only run those set to true + if [[ -z "$VALIDATE_CLOJURE" ]]; then + # Clojure flag was not set - default to false + VALIDATE_CLOJURE="false" + fi + else + # No linter flags were set - default all to true + VALIDATE_CLOJURE="true" + fi + ####################################### # Print which linters we are enabling # ####################################### @@ -1198,6 +1221,11 @@ GetValidationInfo() else PRINT_ARRAY+=("- Excluding [CSS] files in code base...") fi + if [[ "$VALIDATE_CLOJURE" == "true" ]]; then + PRINT_ARRAY+=("- Validating [CLOJURE] files in code base...") + else + PRINT_ARRAY+=("- Excluding [CLOJURE] files in code base...") + fi if [[ "$VALIDATE_ENV" == "true" ]]; then PRINT_ARRAY+=("- Validating [ENV] files in code base...") else @@ -1593,6 +1621,15 @@ BuildFileList() # Set the READ_ONLY_CHANGE_FLAG since this could be exec # ########################################################## READ_ONLY_CHANGE_FLAG=1 + elif [ "$FILE" == "clj" ] || [ "$FILE" == "cljs" ] || [ "$FILE" == "cljc" ] || [ "$FILE" == "edn" ]; then + ################################ + # Append the file to the array # + ################################ + FILE_ARRAY_CLOJURE+=("$FILE") + ########################################################## + # Set the READ_ONLY_CHANGE_FLAG since this could be exec # + ########################################################## + READ_ONLY_CHANGE_FLAG=1 else ############################################## # Use file to see if we can parse what it is # @@ -2122,6 +2159,7 @@ Footer() [ "$ERRORS_FOUND_RUBY" -ne 0 ] || \ [ "$ERRORS_FOUND_CSS" -ne 0 ] || \ [ "$ERRORS_FOUND_ENV" -ne 0 ] || \ + [ "$ERRORS_FOUND_CLOJURE" -ne 0 ] || \ [ "$ERRORS_FOUND_KOTLIN" -ne 0 ]; then # Failed exit echo "Exiting with errors found!" @@ -2183,6 +2221,7 @@ RunTestCases() TestCodebase "POWERSHELL" "pwsh" "pwsh -c Invoke-ScriptAnalyzer -EnableExit -Settings $POWERSHELL_LINTER_RULES -Path" ".*\.\(ps1\|psm1\|psd1\|ps1xml\|pssc\|psrc\|cdxml\)\$" TestCodebase "CSS" "stylelint" "stylelint --config $CSS_LINTER_RULES" ".*\.\(css\)\$" TestCodebase "ENV" "dotenv-linter" "dotenv-linter" ".*\.\(env\)\$" + TestCodebase "CLOJURE" "clj-kondo" "clj-kondo --config $CLOJURE_LINTER_RULES --lint" ".*\.\(clj\|cljs\|cljc\|edn\)\$" TestCodebase "KOTLIN" "ktlint" "ktlint" ".*\.\(kt\|kts\)\$" ################# @@ -2515,6 +2554,20 @@ if [ "$VALIDATE_DOCKER" == "true" ]; then LintCodebase "DOCKER" "/dockerfilelint/bin/dockerfilelint" "/dockerfilelint/bin/dockerfilelint" ".*\(Dockerfile\)\$" "${FILE_ARRAY_DOCKER[@]}" fi +################### +# CLOJURE LINTING # +################### +if [ "$VALIDATE_CLOJURE" == "true" ]; then + ################################# + # Get Clojure standard rules # + ################################# + GetStandardRules "clj-kondo" + ######################### + # Lint the Clojure files # + ######################### + LintCodebase "CLOJURE" "clj-kondo" "clj-kondo --config $CLOJURE_LINTER_RULES --lint" ".*\.\(clj\|cljs\|cljc\|edn\)\$" "${FILE_ARRAY_CLOJURE[@]}" +fi + ###################### # POWERSHELL LINTING # ######################