diff --git a/.automation/test/protobuf/README.md b/.automation/test/protobuf/README.md new file mode 100644 index 00000000..7048213f --- /dev/null +++ b/.automation/test/protobuf/README.md @@ -0,0 +1,19 @@ +# Protobuf Test Cases + +This folder holds the test cases for **Protobuf**. + +## 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/protobuf/protobuf_bad_1.proto b/.automation/test/protobuf/protobuf_bad_1.proto new file mode 100644 index 00000000..b42980ba --- /dev/null +++ b/.automation/test/protobuf/protobuf_bad_1.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; +// A broken example of the official reference +// See https://developers.google.com/protocol-buffers/docs/reference/proto3-spec#proto_file +package examplePb; + +option java_package = "com.example.foo"; + +import "other.proto"; +import public "new.proto"; + +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; + +import "myproject/other_protos.proto"; +import "myproject/main_protos.proto"; + +enum enumAllowingAlias { + option allow_alias = true; + UNKNOWN = 0; + STARTED = 1; + RUNNING = 2 [(custom_option) = "hello world"]; +} +message outer { + option (my_option).a = true; + // inner is an inner message. + message inner { // Level 2 + int64 ival = 1; + } + repeated inner inner_message = 2; + EnumAllowingAlias enum_field =3; + map my_map = 4; + string reason_for_error = 5; + string end_of_support_version= 6; + message AccountForAdmin {} + message SpecialEndOfSupport {} + required inner inner_message = 7; + group Result = 8 { + string url = 9; + } + repeated group Result = 10 { + } + repeated inner paper = 11; + repeated group Regular = 12 { + } +} +service SearchApi { + rpc search (SearchRequest) returns (SearchResponse) {}; +}; diff --git a/.automation/test/protobuf/protobuf_good_1.proto b/.automation/test/protobuf/protobuf_good_1.proto new file mode 100644 index 00000000..76022990 --- /dev/null +++ b/.automation/test/protobuf/protobuf_good_1.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +import public "other.proto"; +option java_package = "com.example.foo"; +enum EnumAllowingAlias { + option allow_alias = true; + ALLOWING_UNSPECIFIED = 0; + STARTED = 1; + RUNNING = 2 [(custom_option) = "hello world"]; +} +message Outer { + option (my_option).a = true; + message Inner { // Level 2 + int64 ival = 1; + } + inner inner_message = 2; + EnumAllowingAlias enum_field =3; + map my_map = 4; +} diff --git a/.github/linters/.protolintrc.yml b/.github/linters/.protolintrc.yml new file mode 100644 index 00000000..7bd3e0ce --- /dev/null +++ b/.github/linters/.protolintrc.yml @@ -0,0 +1,7 @@ +# Lint directives. +lint: + # Linter rules. + # Run `protolint list` to see all available rules. + rules: + # Set the default to all linters. + all_default: false diff --git a/Dockerfile b/Dockerfile index d0c3a70b..46e6a686 100644 --- a/Dockerfile +++ b/Dockerfile @@ -126,6 +126,14 @@ RUN wget -O- -nvq https://raw.githubusercontent.com/golangci/golangci-lint/maste RUN curl -Ls "$(curl -Ls https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" -o tflint.zip && unzip tflint.zip && rm tflint.zip \ && mv "tflint" /usr/bin/ +###################### +# Install protolint # +###################### +RUN curl -LsS "$(curl -Ls https://api.github.com/repos/yoheimuta/protolint/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" -o protolint.tar.gz \ + && tar -xzf protolint.tar.gz \ + && rm protolint.tar.gz \ + && mv "protolint" /usr/bin/ + ######################### # Install dotenv-linter # ######################### @@ -180,6 +188,7 @@ ENV GITHUB_SHA=${GITHUB_SHA} \ VALIDATE_KOTLIN=${VALIDATE_KOTLIN} \ VALIDATE_POWERSHELL=${VALIDATE_POWERSHELL} \ VALIDATE_OPENAPI=${VALIDATE_OPENAPI} \ + VALIDATE_PROTOBUF=${VALIDATE_PROTOBUF} \ ANSIBLE_DIRECTORY=${ANSIBLE_DIRECTORY} \ RUN_LOCAL=${RUN_LOCAL} \ TEST_CASE_RUN=${TEST_CASE_RUN} \ diff --git a/README.md b/README.md index 1b9b7e09..ab9efb85 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Developers on **GitHub** can call the **GitHub Action** to lint their code base | **ENV** | [dotenv-linter](https://github.com/dotenv-linter/dotenv-linter) | | **Kotlin** | [ktlint](https://github.com/pinterest/ktlint) | | **OpenAPI** | [spectral](https://github.com/stoplightio/spectral) | +| **Protocol Buffers** | [protolint](https://github.com/yoheimuta/protolint) | ## How to use To use this **GitHub** Action you will need to complete the following: @@ -167,6 +168,7 @@ and won't run anything unexpected. | **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. | | **VALIDATE_OPENAPI** | `true` | Flag to enable or disable the linting process of the language. | +| **VALIDATE_PROTOBUF** | `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. | | **DISABLE_ERRORS** | `false` | Flag to have the linter complete with exit code 0 even if errors were detected. | diff --git a/TEMPLATES/.protolintrc.yml b/TEMPLATES/.protolintrc.yml new file mode 100644 index 00000000..7bd3e0ce --- /dev/null +++ b/TEMPLATES/.protolintrc.yml @@ -0,0 +1,7 @@ +# Lint directives. +lint: + # Linter rules. + # Run `protolint list` to see all available rules. + rules: + # Set the default to all linters. + all_default: false diff --git a/docs/disabling-linters.md b/docs/disabling-linters.md index 408eba19..f08fd22b 100644 --- a/docs/disabling-linters.md +++ b/docs/disabling-linters.md @@ -25,6 +25,7 @@ Below are examples and documentation for each language and the various methods t - [ENV](#dotenv-linter) - [Kotlin](#kotlin) - [OpenAPI](#openapi) +- [Protocol Buffers](#protocol-buffers) @@ -632,6 +633,53 @@ import package.b.* -------------------------------------------------------------------------------- +## Protocol Buffers + +- [protolint](https://github.com/yoheimuta/protolint) + +### protolint Config file + +- `.github/linters/.protolintrc.yml` +- You can add, extend, and disable rules +- Documentation at [Rules](https://github.com/yoheimuta/protolint#rules) and [Configuring](https://github.com/yoheimuta/protolint#configuring) + +### protolint disable single line + +```protobuf +enum Foo { + // protolint:disable:next ENUM_FIELD_NAMES_UPPER_SNAKE_CASE + firstValue = 0; + second_value = 1; // protolint:disable:this ENUM_FIELD_NAMES_UPPER_SNAKE_CASE + THIRD_VALUE = 2; +} +``` + +### protolint disable code block + +```protobuf +// protolint:disable ENUM_FIELD_NAMES_UPPER_SNAKE_CASE +enum Foo { + firstValue = 0; + second_value = 1; + THIRD_VALUE = 2; +} +// protolint:enable ENUM_FIELD_NAMES_UPPER_SNAKE_CASE +``` + +### protolint disable entire file + +- You can disable entire files with the `lint.files.exclude` property in `.protolintrc.yml` + +```yaml +# Lint directives. +lint: + # Linter files to walk. + files: + # The specific files to exclude. + exclude: + - path/to/file +``` + ## 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) diff --git a/lib/linter.sh b/lib/linter.sh index 6226455c..69523ee4 100755 --- a/lib/linter.sh +++ b/lib/linter.sh @@ -57,6 +57,9 @@ CSS_LINTER_RULES="$DEFAULT_RULES_LOCATION/$CSS_FILE_NAME" # Path to th # OpenAPI Vars OPENAPI_FILE_NAME='.openapirc.yml' # Name of the file OPENAPI_LINTER_RULES="$DEFAULT_RULES_LOCATION/$OPENAPI_FILE_NAME" # Path to the OpenAPI lint rules +# Protocol Buffers Vars +PROTOBUF_FILE_NAME='.protolintrc.yml' # Name of the file +PROTOBUF_LINTER_RULES="$DEFAULT_RULES_LOCATION/$PROTOBUF_FILE_NAME" # Path to the Protocol Buffers lint rules # Clojure Vars CLOJURE_FILE_NAME='.clj-kondo/config.edn' CLOJURE_LINTER_RULES="$DEFAULT_RULES_LOCATION/$CLOJURE_FILE_NAME" @@ -67,7 +70,7 @@ CLOJURE_LINTER_RULES="$DEFAULT_RULES_LOCATION/$CLOJURE_FILE_NAME" 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" "clj-kondo" "spectral") + "stylelint" "dotenv-linter" "powershell" "ktlint" "protolint" "clj-kondo" "spectral") ############################# # Language array for prints # @@ -75,7 +78,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' - 'CSS' 'ENV' 'POWERSHELL' 'KOTLIN' 'CLOJURE' 'OPENAPI') + 'CSS' 'ENV' 'POWERSHELL' 'KOTLIN' 'PROTO' 'CLOJURE' 'OPENAPI') ################### # GitHub ENV Vars # @@ -159,6 +162,7 @@ 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 +FILE_ARRAY_PROTOBUF=() # Array of files to check FILE_ARRAY_OPENAPI=() # Array of files to check ############ @@ -187,6 +191,7 @@ 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 +ERRORS_FOUND_PROTOBUF=0 # Count of errors found ERRORS_FOUND_OPENAPI=0 # Count of errors found ################################################################################ @@ -806,6 +811,7 @@ GetValidationInfo() 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)}') + VALIDATE_PROTOBUF=$(echo "$VALIDATE_PROTOBUF" | awk '{print tolower($0)}') VALIDATE_OPENAPI=$(echo "$VALIDATE_OPENAPI" | awk '{print tolower($0)}') ################################################ @@ -834,6 +840,7 @@ GetValidationInfo() -n "$VALIDATE_CSS" || \ -n "$VALIDATE_ENV" || \ -n "$VALIDATE_CLOJURE" || \ + -n "$VALIDATE_PROTOBUF" || \ -n "$VALIDATE_OPENAPI" || \ -n "$VALIDATE_KOTLIN" ]]; then ANY_SET="true" @@ -1161,6 +1168,20 @@ GetValidationInfo() VALIDATE_OPENAPI="true" fi + ####################################### + # Validate if we should check PROTOBUF # + ####################################### + if [[ "$ANY_SET" == "true" ]]; then + # Some linter flags were set - only run those set to true + if [[ -z "$VALIDATE_PROTOBUF" ]]; then + # PROTOBUF flag was not set - default to false + VALIDATE_PROTOBUF="false" + fi + else + # No linter flags were set - default all to true + VALIDATE_PROTOBUF="true" + fi + ####################################### # Validate if we should check Clojure # ####################################### @@ -1298,6 +1319,11 @@ GetValidationInfo() else PRINT_ARRAY+=("- Excluding [OPENAPI] files in code base...") fi + if [[ "$VALIDATE_PROTOBUF" == "true" ]]; then + PRINT_ARRAY+=("- Validating [PROTOBUF] files in code base...") + else + PRINT_ARRAY+=("- Excluding [PROTOBUF] files in code base...") + fi ############################## # Validate Ansible Directory # @@ -1686,6 +1712,18 @@ BuildFileList() # Set the READ_ONLY_CHANGE_FLAG since this could be exec # ########################################################## READ_ONLY_CHANGE_FLAG=1 + ############################ + # Get the Protocol Buffers files # + ############################ + elif [ "$FILE_TYPE" == "proto" ]; then + ################################ + # Append the file to the array # + ################################ + FILE_ARRAY_PROTOBUF+=("$FILE") + ########################################################## + # Set the READ_ONLY_CHANGE_FLAG since this could be exec # + ########################################################## + READ_ONLY_CHANGE_FLAG=1 elif [ "$FILE" == "Dockerfile" ]; then ################################ # Append the file to the array # @@ -2254,6 +2292,7 @@ Footer() [ "$ERRORS_FOUND_CSS" -ne 0 ] || \ [ "$ERRORS_FOUND_ENV" -ne 0 ] || \ [ "$ERRORS_FOUND_OPENAPI" -ne 0 ] || \ + [ "$ERRORS_FOUND_PROTOBUF" -ne 0 ] || \ [ "$ERRORS_FOUND_CLOJURE" -ne 0 ] || \ [ "$ERRORS_FOUND_KOTLIN" -ne 0 ]; then # Failed exit @@ -2318,6 +2357,7 @@ RunTestCases() 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\)\$" + TestCodebase "PROTOBUF" "protolint" "protolint lint --config_path $PROTOBUF_LINTER_RULES" ".*\.\(proto\)\$" TestCodebase "OPENAPI" "spectral" "spectral lint -r $OPENAPI_LINTER_RULES" ".*\.\(ymlopenapi\|jsonopenapi\)\$" ################# @@ -2664,6 +2704,17 @@ if [ "$VALIDATE_CLOJURE" == "true" ]; then LintCodebase "CLOJURE" "clj-kondo" "clj-kondo --config $CLOJURE_LINTER_RULES --lint" ".*\.\(clj\|cljs\|cljc\|edn\)\$" "${FILE_ARRAY_CLOJURE[@]}" fi +################## +# PROTOBUF LINTING # +################## +if [ "$VALIDATE_PROTOBUF" == "true" ]; then + ####################### + # Lint the Protocol Buffers files # + ####################### + # LintCodebase "FILE_TYPE" "LINTER_NAME" "LINTER_CMD" "FILE_TYPES_REGEX" "FILE_ARRAY" + LintCodebase "PROTOBUF" "protolint" "protolint lint --config_path $PROTOBUF_LINTER_RULES" ".*\.\(proto\)\$" "${FILE_ARRAY_PROTOBUF[@]}" +fi + ###################### # POWERSHELL LINTING # ######################