diff --git a/.github/linters/.eslintrc.yml b/.github/linters/.eslintrc.yml new file mode 100644 index 00000000..31282eb8 --- /dev/null +++ b/.github/linters/.eslintrc.yml @@ -0,0 +1,44 @@ +--- +env: + browser: true + es6: true + jest: true + node: true + +extends: + - "eslint:recommended" + +ignorePatterns: + - "!.*" + - "**/node_modules/.*" + +parser: '@typescript-eslint/parser' + +plugins: + - '@typescript-eslint' + +# Don't set the jsonSyntax parser option for JSON, JSON5, and JSONC +# so we can use eslint-plugin-jsonc to automatically fix issues +# in tests, otherwise ESLint reports parsing errors and stops +overrides: + - files: + - "*.json" + extends: + - plugin:jsonc/recommended-with-json + parser: jsonc-eslint-parser + - files: + - "*.jsonc" + extends: + - plugin:jsonc/recommended-with-jsonc + parser: jsonc-eslint-parser + - files: + - "*.json5" + extends: + - plugin:jsonc/recommended-with-json5 + parser: jsonc-eslint-parser + - files: + - "*.jsx" + - "*.tsx" + extends: + - plugin:react/recommended +... diff --git a/.github/linters/.golangci.yml b/.github/linters/.golangci.yml new file mode 100644 index 00000000..3843b1cb --- /dev/null +++ b/.github/linters/.golangci.yml @@ -0,0 +1,7 @@ +--- +# This file is only used in tests +# TODO: move in a dedicated directory in test/linters-config +linters: + enable: + - gofmt +... diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json index 3999d690..be12186d 100644 --- a/.github/linters/.jscpd.json +++ b/.github/linters/.jscpd.json @@ -38,6 +38,7 @@ "**/test/linters/typescript_es/**", "**/test/linters/typescript_prettier/**", "**/test/linters/typescript_standard/**", + "**/test/linters-config/**", "**/github_conf/**", "**/workflows/cd.yml", "**/workflows/ci.yml" diff --git a/Makefile b/Makefile index 87a265a2..d29a2a42 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all: info docker test ## Run all targets. .PHONY: test -test: info validate-container-image-labels docker-build-check docker-dev-container-build-check test-lib inspec lint-codebase fix-codebase test-default-config-files test-actions-runner-debug test-actions-steps-debug test-runner-debug test-find lint-subset-files test-custom-ssl-cert test-non-default-workdir test-git-flags test-non-default-home-directory test-git-initial-commit test-git-merge-commit-push test-log-level test-use-find-and-ignore-gitignored-files test-linters-expect-failure-log-level-notice test-bash-exec-library-expect-success test-bash-exec-library-expect-failure test-save-super-linter-output test-save-super-linter-output-custom-path test-save-super-linter-custom-summary test-linters test-linters-fix-mode-expect-success ## Run the test suite +test: info validate-container-image-labels docker-build-check docker-dev-container-build-check test-lib inspec lint-codebase fix-codebase test-default-config-files test-actions-runner-debug test-actions-steps-debug test-runner-debug test-find lint-subset-files test-custom-ssl-cert test-non-default-workdir test-git-flags test-non-default-home-directory test-git-initial-commit test-git-merge-commit-push test-log-level test-use-find-and-ignore-gitignored-files test-linters-expect-failure-log-level-notice test-bash-exec-library-expect-success test-bash-exec-library-expect-failure test-save-super-linter-output test-save-super-linter-output-custom-path test-save-super-linter-custom-summary test-linters test-linters-fix-mode ## Run the test suite # if this session isn't interactive, then we don't want to allocate a # TTY, which would fail, but if it is interactive, we do want to attach @@ -318,6 +318,14 @@ test-globals-languages: ## Test globals/languages.sh --entrypoint /tmp/lint/test/lib/globalsLanguagesTest.sh \ $(SUPER_LINTER_TEST_CONTAINER_URL) +.PHONY: test-globals-linter-command-options +test-globals-linter-command-options: ## Test globals/LinterCommandsOptions.sh + docker run \ + -v "$(CURDIR):/tmp/lint" \ + -w /tmp/lint \ + --entrypoint /tmp/lint/test/lib/globalsLinterCommandsOptionsTest.sh \ + $(SUPER_LINTER_TEST_CONTAINER_URL) + .PHONY: test-linter-rules test-linter-rules: ## Test linterRules.sh docker run \ @@ -416,14 +424,13 @@ test-non-default-home-directory: ## Test a non-default HOME directory "run_test_cases_non_default_home" \ "$(IMAGE)" -.PHONY: test-linters-fix-mode-expect-success -test-linters-fix-mode-expect-success: ## Run the linters test suite (fix mode) expecting successes +.PHONY: test-linters-fix-mode +test-linters-fix-mode: ## Run the linters test suite (fix mode) $(CURDIR)/test/run-super-linter-tests.sh \ $(SUPER_LINTER_TEST_CONTAINER_URL) \ "run_test_case_fix_mode" \ "$(IMAGE)" - .PHONY: test-linters test-linters: test-linters-expect-success test-linters-expect-failure ## Run the linters test suite diff --git a/README.md b/README.md index 0ac0ac9e..a37b8181 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,7 @@ You can configure Super-linter using the following environment variables: | **FIX_RUST_2015** | `false` | Option to enable fix mode for `RUST_2015`. | | **FIX_RUST_2018** | `false` | Option to enable fix mode for `RUST_2018`. | | **FIX_RUST_2021** | `false` | Option to enable fix mode for `RUST_2021`. | -| **FIX_RUST_CLIPPY** | `false` | Option to enable fix mode for `RUST_CLIPPY`. | +| **FIX_RUST_CLIPPY** | `false` | Option to enable fix mode for `RUST_CLIPPY`. When `FIX_RUST_CLIPPY` is `true`, Clippy is allowed to fix issues in the workspace even if there are unstaged and staged changes in the workspace. | | **FIX_SCALAFMT** | `false` | Option to enable fix mode for `SCALAFMT`. | | **FIX_SHELL_SHFMT** | `false` | Option to enable fix mode for `SHELL_SHFMT`. | | **FIX_SNAKEMAKE_SNAKEFMT** | `false` | Option to enable fix mode for `SNAKEMAKE_SNAKEFMT`. | @@ -421,6 +421,20 @@ Super-linter supports the following locations to deliver fixes: - If you're running Super-linter locally, you can commit the changes as you would with any other change in your working directory. +### Fix mode for ansible-lint + +ansible-lint requires that the `yaml` rule is enabled to for the ansible-lint +fix mode to work. The default ansible-lint configuration that Super-linter ships +disables the `yaml` rule because it might not be compatible with yamllint. If +you need to enable the ansible-lint fix mode, provide an ansible-lint +configuration that doesn't ignore the `yaml` rule. + +### Fix mode file and directory ownership + +When fix mode is enabled, some linters and formatters don't maintain the +original file or directory ownership, and use the user that Super-linter uses +to run the linter or formatter. + ## Configure linters Super-linter provides default configurations for some linters in the [`TEMPLATES/`](TEMPLATES/) diff --git a/docs/add-new-linter.md b/docs/add-new-linter.md index bfb8a3dc..be2d8c2c 100644 --- a/docs/add-new-linter.md +++ b/docs/add-new-linter.md @@ -7,11 +7,16 @@ new tool, it should include: - `README.md` - Provide test cases: - 1. Create the `test/linters/` directory. + 1. Create the `test/linters/` directory. 2. Provide at least one test case with a file that is supposed to pass validation, - with the right file extension if needed: `test/linters//-good` + with the right file extension if needed: `test/linters//-good` 3. Provide at least one test case with a file that is supposed to fail validation, - with the right file extension if needed: `test/linters//-bad` + with the right file extension if needed: `test/linters//-bad`. + If the linter supports fix mode, the test case supposed to fail validation + should only contain violations that the fix mode can automatically fix. + Avoid test cases that fail only because of syntax errors, when possible. + 4. If the linter supports check-only mode or fix mode, add the `` + to the `LANGUAGES_WITH_FIX_MODE` array in `test/testUtils.sh` - Update the test suite to check for installed packages, the commands that your new tool needs in the `PATH`, and the expected version command: diff --git a/lib/functions/validation.sh b/lib/functions/validation.sh index 1b6932e2..79b9c152 100755 --- a/lib/functions/validation.sh +++ b/lib/functions/validation.sh @@ -7,6 +7,7 @@ function ValidateBooleanConfigurationVariables() { ValidateBooleanVariable "DISABLE_ERRORS" "${DISABLE_ERRORS}" ValidateBooleanVariable "ENABLE_GITHUB_ACTIONS_GROUP_TITLE" "${ENABLE_GITHUB_ACTIONS_GROUP_TITLE}" ValidateBooleanVariable "ENABLE_GITHUB_ACTIONS_STEP_SUMMARY" "${ENABLE_GITHUB_ACTIONS_STEP_SUMMARY}" + ValidateBooleanVariable "FIX_MODE_TEST_CASE_RUN" "${FIX_MODE_TEST_CASE_RUN}" ValidateBooleanVariable "IGNORE_GENERATED_FILES" "${IGNORE_GENERATED_FILES}" ValidateBooleanVariable "IGNORE_GITIGNORED_FILES" "${IGNORE_GITIGNORED_FILES}" ValidateBooleanVariable "LOG_DEBUG" "${LOG_DEBUG}" diff --git a/lib/functions/worker.sh b/lib/functions/worker.sh index f43443d4..e730f91a 100755 --- a/lib/functions/worker.sh +++ b/lib/functions/worker.sh @@ -15,7 +15,12 @@ function LintCodebase() { unset -n VALIDATE_LANGUAGE return 0 else - fatal "Don't disable any validation when running in test mode. VALIDATE_${FILE_TYPE} is set to: ${VALIDATE_LANGUAGE}. Set it to: true" + if [[ "${FIX_MODE_TEST_CASE_RUN}" == "true" ]]; then + debug "Don't fail the test even if VALIDATE_${FILE_TYPE} is set to ${VALIDATE_LANGUAGE} because ${FILE_TYPE} might not support fix mode" + return 0 + else + fatal "Don't disable any validation when running in test mode. VALIDATE_${FILE_TYPE} is set to: ${VALIDATE_LANGUAGE}. Set it to: true" + fi fi fi diff --git a/lib/globals/linterCommandsOptions.sh b/lib/globals/linterCommandsOptions.sh index 5572a5bc..d92204b3 100755 --- a/lib/globals/linterCommandsOptions.sh +++ b/lib/globals/linterCommandsOptions.sh @@ -35,10 +35,12 @@ STANDARD_FIX_MODE_OPTIONS=(--fix) # Define configuration options to enable "fix mode". # Not all linters and formatters support this. ANSIBLE_FIX_MODE_OPTIONS=(--fix) +CLANG_FORMAT_FIX_MODE_OPTIONS=(-i) CSS_FIX_MODE_OPTIONS=(--fix) -ENV_FIX_MODE_OPTIONS=(fix) +ENV_FIX_MODE_OPTIONS=(fix --no-backup) GO_FIX_MODE_OPTIONS=("${GOLANGCI_LINT_FIX_MODE_OPTIONS[@]}") GO_MODULES_FIX_MODE_OPTIONS=("${GOLANGCI_LINT_FIX_MODE_OPTIONS[@]}") +GOOGLE_JAVA_FORMAT_FIX_MODE_OPTIONS=(--replace) GROOVY_FIX_MODE_OPTIONS=(--fix) JAVASCRIPT_ES_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}") JAVASCRIPT_PRETTIER_FIX_MODE_OPTIONS=("${PRETTIER_FIX_MODE_OPTIONS[@]}") @@ -47,11 +49,13 @@ JSON_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}") JSONC_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}") JSX_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}") MARKDOWN_FIX_MODE_OPTIONS=(--fix) +NATURAL_LANGUAGE_FIX_MODE_OPTIONS=(--fix) POWERSHELL_FIX_MODE_OPTIONS=(-Fix) PROTOBUF_FIX_MODE_OPTIONS=(-fix) +PYTHON_ISORT_FIX_MODE_OPTIONS=(--overwrite-in-place) PYTHON_RUFF_FIX_MODE_OPTIONS=(--fix) RUBY_FIX_MODE_OPTIONS=(--autocorrect) -RUST_CLIPPY_FIX_MODE_OPTIONS=(--fix) +RUST_CLIPPY_FIX_MODE_OPTIONS=(--fix --allow-dirty --allow-staged) SHELL_SHFMT_FIX_MODE_OPTIONS=(--write) SQLFLUFF_FIX_MODE_OPTIONS=(fix) TSX_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}") diff --git a/lib/linter.sh b/lib/linter.sh index 899a63c3..a62ade59 100755 --- a/lib/linter.sh +++ b/lib/linter.sh @@ -113,6 +113,10 @@ declare -l TEST_CASE_RUN TEST_CASE_RUN="${TEST_CASE_RUN:-"false"}" export TEST_CASE_RUN +declare -l FIX_MODE_TEST_CASE_RUN +FIX_MODE_TEST_CASE_RUN="${FIX_MODE_TEST_CASE_RUN:-"false"}" +export FIX_MODE_TEST_CASE_RUN + # We want a lowercase value declare -l USE_FIND_ALGORITHM USE_FIND_ALGORITHM="${USE_FIND_ALGORITHM:-false}" diff --git a/test/lib/globalsLinterCommandsOptionsTest.sh b/test/lib/globalsLinterCommandsOptionsTest.sh new file mode 100755 index 00000000..ed0decb6 --- /dev/null +++ b/test/lib/globalsLinterCommandsOptionsTest.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +# Default log level +# shellcheck disable=SC2034 +LOG_LEVEL="DEBUG" + +# shellcheck source=/dev/null +source "lib/functions/log.sh" + +# shellcheck source=/dev/null +source "test/testUtils.sh" + +# The sqlfluff command needs this, but we don't want to make this test +# dependant on other files +# shellcheck disable=SC2034 +SQLFLUFF_LINTER_RULES="SQLFLUFF_LINTER_RULES" + +# shellcheck source=/dev/null +source "lib/globals/linterCommandsOptions.sh" + +LanguagesWithFixModeTest() { + local FUNCTION_NAME + FUNCTION_NAME="${FUNCNAME[0]}" + info "${FUNCTION_NAME} start" + + for LANGUAGE in "${LANGUAGES_WITH_FIX_MODE[@]}"; do + local FIX_MODE_OPTIONS_VARIABLE_NAME="${LANGUAGE}_FIX_MODE_OPTIONS" + local CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME="${LANGUAGE}_CHECK_ONLY_MODE_OPTIONS" + if [[ -v "${FIX_MODE_OPTIONS_VARIABLE_NAME}" ]] || + [[ -v "${CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME}" ]]; then + debug "${LANGUAGE} has check-only mode or fix mode options as expected" + else + fatal "${LANGUAGE} is in the list of languages that support fix mode, but neither check-only mode, nor fix mode options were found" + fi + done + + notice "${FUNCTION_NAME} PASS" +} + +LanguagesWithFixModeTest diff --git a/test/lib/linterCommandsTest.sh b/test/lib/linterCommandsTest.sh index 24b450b0..22807af6 100755 --- a/test/lib/linterCommandsTest.sh +++ b/test/lib/linterCommandsTest.sh @@ -32,13 +32,12 @@ BASH_EXEC_IGNORE_LIBRARIES="false" GITHUB_WORKSPACE="$(pwd)" # shellcheck disable=SC2034 IGNORE_GITIGNORED_FILES="false" -LINTER_RULES_PATH="TEMPLATES" # shellcheck disable=SC2034 TYPESCRIPT_STANDARD_TSCONFIG_FILE=".github/linters/tsconfig.json" # shellcheck disable=SC2034 YAML_ERROR_ON_WARNING="false" for LANGUAGE in "${LANGUAGE_ARRAY_FOR_LINTER_RULES[@]}"; do - GetLinterRules "${LANGUAGE}" "${LINTER_RULES_PATH}" + GetLinterRules "${LANGUAGE}" "TEMPLATES" done ValidateValidationVariables diff --git a/test/lib/linterRulesTest.sh b/test/lib/linterRulesTest.sh index 75978311..0732138e 100755 --- a/test/lib/linterRulesTest.sh +++ b/test/lib/linterRulesTest.sh @@ -43,7 +43,12 @@ function GetLinterRulesTest() { GetLinterRules "${LANGUAGE}" "${DEFAULT_RULES_LOCATION}" done - local EXPECTED_TEST_LANGUAGE_LINTER_RULES="${DEFAULT_RULES_LOCATION}/${TEST_LANGUAGE_FILE_NAME}" + local EXPECTED_TEST_LANGUAGE_LINTER_RULES="${GITHUB_WORKSPACE}" + if [[ -n "${LINTER_RULES_PATH:-}" ]]; then + EXPECTED_TEST_LANGUAGE_LINTER_RULES="${EXPECTED_TEST_LANGUAGE_LINTER_RULES}/${LINTER_RULES_PATH}" + fi + EXPECTED_TEST_LANGUAGE_LINTER_RULES="${EXPECTED_TEST_LANGUAGE_LINTER_RULES}/${TEST_LANGUAGE_FILE_NAME}" + if [[ "${TEST_LANGUAGE_LINTER_RULES}" == "${EXPECTED_TEST_LANGUAGE_LINTER_RULES}" ]]; then debug "TEST_LANGUAGE_LINTER_RULES (${TEST_LANGUAGE_LINTER_RULES}) matches the expected value (${EXPECTED_TEST_LANGUAGE_LINTER_RULES})" else diff --git a/test/linters-config/fix-mode/.ansible-lint.yml b/test/linters-config/fix-mode/.ansible-lint.yml new file mode 100644 index 00000000..bcbfc8c4 --- /dev/null +++ b/test/linters-config/fix-mode/.ansible-lint.yml @@ -0,0 +1,9 @@ +--- +# Customize the ansible-lint configuration file because fix mode needs the +# yaml rule not to be disabled, but we disable the yaml rule in the default +# ansible-lint configuration +parseable: true +quiet: true +use_default_rules: true +verbosity: 1 +... diff --git a/test/linters-config/fix-mode/.eslintrc.yml b/test/linters-config/fix-mode/.eslintrc.yml new file mode 100644 index 00000000..31282eb8 --- /dev/null +++ b/test/linters-config/fix-mode/.eslintrc.yml @@ -0,0 +1,44 @@ +--- +env: + browser: true + es6: true + jest: true + node: true + +extends: + - "eslint:recommended" + +ignorePatterns: + - "!.*" + - "**/node_modules/.*" + +parser: '@typescript-eslint/parser' + +plugins: + - '@typescript-eslint' + +# Don't set the jsonSyntax parser option for JSON, JSON5, and JSONC +# so we can use eslint-plugin-jsonc to automatically fix issues +# in tests, otherwise ESLint reports parsing errors and stops +overrides: + - files: + - "*.json" + extends: + - plugin:jsonc/recommended-with-json + parser: jsonc-eslint-parser + - files: + - "*.jsonc" + extends: + - plugin:jsonc/recommended-with-jsonc + parser: jsonc-eslint-parser + - files: + - "*.json5" + extends: + - plugin:jsonc/recommended-with-json5 + parser: jsonc-eslint-parser + - files: + - "*.jsx" + - "*.tsx" + extends: + - plugin:react/recommended +... diff --git a/test/linters-config/fix-mode/.golangci.yml b/test/linters-config/fix-mode/.golangci.yml new file mode 100644 index 00000000..e6dd6cad --- /dev/null +++ b/test/linters-config/fix-mode/.golangci.yml @@ -0,0 +1,5 @@ +--- +linters: + enable: + - gofmt +... diff --git a/test/linters/ansible/bad/playbooks/ansible_bad_1.yml b/test/linters/ansible/bad/playbooks/ansible_bad_1.yml index f4570c18..ae010a2a 100644 --- a/test/linters/ansible/bad/playbooks/ansible_bad_1.yml +++ b/test/linters/ansible/bad/playbooks/ansible_bad_1.yml @@ -1,8 +1,11 @@ --- -- name: Remove temp files - become: true - file: - path: "{{ item }}" - state: absent - with_items: - - "/tmp/test-1" +- name: Test playbook + hosts: all + tasks: + - name: Remove temp files + become: true + file: + path: "{{ item }}" + state: absent + with_items: + - "/tmp/test-1" diff --git a/test/linters/ansible/good/playbooks/ansible_good_1.yml b/test/linters/ansible/good/playbooks/ansible_good_1.yml index d9c8a5c2..7d7da3cd 100644 --- a/test/linters/ansible/good/playbooks/ansible_good_1.yml +++ b/test/linters/ansible/good/playbooks/ansible_good_1.yml @@ -8,4 +8,4 @@ path: "{{ item }}" state: absent with_items: - - "/tmp/test-1" + - /tmp/test-1 diff --git a/test/linters/go/golang_bad_01.go b/test/linters/go/golang_bad_01.go index 370c8c5f..e69badb3 100644 --- a/test/linters/go/golang_bad_01.go +++ b/test/linters/go/golang_bad_01.go @@ -1,3 +1,7 @@ -if len(in) == 0 { - return "", fmt.Errorf("Input is empty") +package main + +import "fmt" + +func main() { +fmt.Println("hello world") } diff --git a/test/linters/go_modules/go_modules_bad/golang_bad_01.go b/test/linters/go_modules/go_modules_bad/golang_bad_01.go index e2487d09..e69badb3 100644 --- a/test/linters/go_modules/go_modules_bad/golang_bad_01.go +++ b/test/linters/go_modules/go_modules_bad/golang_bad_01.go @@ -1,18 +1,7 @@ package main -import ( - "github.com/go-playground/validator/v10" - "github.com/labstack/echo/v4" -) +import "fmt" -if len(in) == 0 { - return "", fmt.Errorf("Input is empty") +func main() { +fmt.Println("hello world") } - -x := 0 -{ - var x int - x++ -} - -fmt.Println(x) diff --git a/test/linters/javascript_es/javascript_bad_1.js b/test/linters/javascript_es/javascript_bad_1.js index 3379e608..66e8f898 100644 --- a/test/linters/javascript_es/javascript_bad_1.js +++ b/test/linters/javascript_es/javascript_bad_1.js @@ -1,199 +1,10 @@ -var http = require('http') -var createHandler = require('github-webhook-handler') +var teamId = "teamId" +var booleanTest = false; -var handler = createHandler({ - path: /webhook, secret : (process.env.SECRET) }) - -var userArray = ['user1'] -here is some garbage = that - -var teamDescription = Team of Robots -var teamPrivacy = 'closed' // closed (visible) / secret (hidden) are options here - -var teamName = process.env.GHES_TEAM_NAME -var teamAccess = 'pull' // pull,push,admin options here -var teamId = '' - -var orgRepos = [] - -// var creator = "" - -var foo = someFunction(); - var bar = a + 1; - - http.createServer(function (req, res) { - handler(req, res, function (err) { - console.log(err) - res.statusCode = 404 - res.end('no such location') - }) - }).listen(3000) - -handler.on('error', function (err) { - console.await.error('Error:', err.message) - }) - -handler.on('repository', function (event) { - if (event.payload.action === 'created') { - const repo = event.payload.repository.full_name - console.log(repo) - const org = event.payload.repository.owner.login - getTeamID(org) - setTimeout(checkTeamIDVariable, 1000) - } - }) - -handler.on('team', function (event) { - // TODO user events such as being removed from team or org - if (event.payload.action === 'deleted') { - // const name = event.payload.team.name - const org = event.payload.organization.login - getRepositories(org) - setTimeout(checkReposVariable, 5000) - } else if (event.payload.action === 'removed_from_repository') { - const org = event.payload.organization.login - getTeamID(org) - // const repo = event.payload.repository.full_name - setTimeout(checkTeamIDVariable, 1000) - } - }) - -function getTeamID(org) { - const https = require('https') - - const options = { - hostname: (process.env.GHE_HOST), - port: 443 - path: '/api/v3/orgs/' + org + '/teams', - method: 'GET', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json' - } - } - - req.on('error, (error) => { - console.error(error) - }) - -req.end() -} - -function checkTeamIDVariable(repo) { - if (typeof teamId != 'undefined') { - addTeamToRepo(repo, teamId) +function checkTeamIDVariable(teamId, booleanTest) { + if (typeof teamId != "undefined" || Boolean(!!booleanTest)) { + console.log(teamId) } } -function checkReposVariable(org) { - if (typeof orgRepos !== 'undefined') { - // for(var repo of orgRepos) { - // addTeamToRepo(repo, teamId) - // } - reCreateTeam(org) - } -} - -function addTeamToRepo(repo, teamId) { - const https = require('https') - const data = JSON.stringify({ - permission: teamAccess - }) - - const options = { - hostname: (process.env.GHE_HOST), - port: 443, - path: '/api/v3/teams/' + teamId + '/repos/' + repo, - method: 'PUT', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json', - 'Content-Length': data.length - } - } - let body = [] - - const req = https.request(options, (res) => { - res.on('data', (chunk) => { - - body.push(chunk) - - }).on('end', () => { - - body = Buffer.concat(body).toString() - console.log(res.statusCode) - console.log('added team to ' + repo) - }) - }) - - req.on('error', (error) => { - console.error(error) - }) - - req.write(data) - req.end() -} - -function reCreateTeam(org) { - const https = require('https') - const data = JSON.stringify({ - name: teamName, - description: teamDescription, - privacy: teamPrivacy - maintainers: userArray, - repo_names: orgRepos - }) - - const options = { - hostname: (process.env.GHE_HOST), - port: 443 - path: '/api/v3/orgs/' + org + '/teams', - method: 'POST', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json', - 'Content-Length': data.length - } - } - // const body = [] - const req = https.request(options, (res) => { - if (res.statusCode !== 201) { - console.log('Status code: ' + res.statusCode) - console.log('Added ' + teamName + ' to ' + org + ' Failed') - res.on('data', function (chunk) { - console.log('BODY: ' + chunk) - }) - } else { - console.log('Added ' + teamName ' to ' + org) - } - }) - - req.on('error', (error) => { - console.error(error) - }) - - req.write(data) - req.end() -} - -function getRepositories(org) { - orgRepos = [] - - const https = require('https') - - const options = { - hostname: (process.env.GHE_HOST), - port: '443', - path: '/api/v3/orgs/' + org + "/repos", - method: 'GET', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json' - } - } - - req.on('error', (error) => { - console.error(error) - }) - req.end() -} +checkTeamIDVariable(teamId, booleanTest) diff --git a/test/linters/javascript_es/javascript_good_1.js b/test/linters/javascript_es/javascript_good_1.js index 58dc8bfb..97198b86 100644 --- a/test/linters/javascript_es/javascript_good_1.js +++ b/test/linters/javascript_es/javascript_good_1.js @@ -1,137 +1,10 @@ -const http = require('http') -const createHandler = require('github-webhook-handler') -const handler = createHandler({ path: '/webhook', secret: (process.env.SECRET) }) +var teamId = "teamId" +var booleanTest = false; -const userArray = ['user1'] - -const teamDescription = 'Team of Robots' -const teamPrivacy = 'closed' // closed (visible) / secret (hidden) are options here - -const teamName = process.env.GHES_TEAM_NAME -const teamAccess = 'pull' // pull,push,admin options here -const teamId = '' - -// var creator = "" - -http.createServer(function (req, res) { - handler(req, res, function (err) { - console.log(err) - res.statusCode = 404 - res.end('no such location') - }) -}).listen(3000) - -handler.on('error', function (err) { - console.error('Error:', err.message) -}) - -handler.on('repository', function (event) { - if (event.payload.action === 'created') { - const repo = event.payload.repository.full_name - console.log(repo) - setTimeout(checkTeamIDVariable, 1000) - } -}) - -handler.on('team', function (event) { - // TODO user events such as being removed from team or org - if (event.payload.action === 'deleted') { - // const name = event.payload.team.name - setTimeout(checkReposVariable, 5000) - } else if (event.payload.action === 'removed_from_repository') { - // const repo = event.payload.repository.full_name - setTimeout(checkTeamIDVariable, 1000) - } -}) - -function checkTeamIDVariable (repo) { - if (typeof teamId !== 'undefined') { - addTeamToRepo(repo, teamId) +function checkTeamIDVariable(teamId, booleanTest) { + if (typeof teamId != "undefined" || booleanTest) { + console.log(teamId) } } -function checkReposVariable (org) { - if (typeof orgRepos !== 'undefined') { - // for(var repo of orgRepos) { - // addTeamToRepo(repo, teamId) - // } - reCreateTeam(org) - } -} - -function addTeamToRepo (repo, teamId) { - const https = require('https') - const data = JSON.stringify({ - permission: teamAccess - }) - - const options = { - hostname: (process.env.GHE_HOST), - port: 443, - path: '/api/v3/teams/' + teamId + '/repos/' + repo, - method: 'PUT', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json', - 'Content-Length': data.length - } - } - let body = [] - const req = https.request(options, (res) => { - res.on('data', (chunk) => { - body.push(chunk) - }).on('end', () => { - body = Buffer.concat(body).toString() - console.log(res.statusCode) - console.log('added team to ' + repo) - }) - }) - - req.on('error', (error) => { - console.error(error) - }) - - req.write(data) - req.end() -} - -function reCreateTeam (org) { - const https = require('https') - const data = JSON.stringify({ - name: teamName, - description: teamDescription, - privacy: teamPrivacy, - maintainers: userArray - }) - - const options = { - hostname: (process.env.GHE_HOST), - port: 443, - path: '/api/v3/orgs/' + org + '/teams', - method: 'POST', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json', - 'Content-Length': data.length - } - } - // const body = [] - const req = https.request(options, (res) => { - if (res.statusCode !== 201) { - console.log('Status code: ' + res.statusCode) - console.log('Added ' + teamName + ' to ' + org + ' Failed') - res.on('data', function (chunk) { - console.log('BODY: ' + chunk) - }) - } else { - console.log('Added ' + teamName + ' to ' + org) - } - }) - - req.on('error', (error) => { - console.error(error) - }) - - req.write(data) - req.end() -} +checkTeamIDVariable(teamId, booleanTest) diff --git a/test/linters/javascript_prettier/javascript_bad_1.js b/test/linters/javascript_prettier/javascript_bad_1.js index 98e5ee29..13345350 100644 --- a/test/linters/javascript_prettier/javascript_bad_1.js +++ b/test/linters/javascript_prettier/javascript_bad_1.js @@ -1,12 +1,8 @@ var http = require('http') var createHandler = require( 'github-webhook-handler') -var handler = createHandler( { path : /webhook, secret : (process.env.SECRET) }) +var userArray = ['user1'] -var userArray = [ 'user1' ] -here is some garbage = that - -var teamDescription = Team of Robots var teamPrivacy = 'closed' // closed (visible) / secret (hidden) are options here var teamName = process.env.GHES_TEAM_NAME @@ -62,8 +58,7 @@ function getTeamID (org) { const options = { hostname: (process.env.GHE_HOST), - port: 443 - path: '/api/v3/orgs/' + org + '/teams', + port: 443, method: 'GET', headers: { Authorization: 'token ' + (process.env.GHE_TOKEN), @@ -84,7 +79,7 @@ function getTeamID (org) { }) }) - req.on('error, (error) => { + req.on('error', (error) => { console.error(error) }) @@ -151,14 +146,14 @@ function reCreateTeam (org) { const data = JSON.stringify({ name: teamName, description: teamDescription, - privacy: teamPrivacy + privacy: teamPrivacy, maintainers: userArray, repo_names: orgRepos }) const options = { hostname: (process.env.GHE_HOST), - port: 443 + port: 443, path: '/api/v3/orgs/' + org + '/teams', method: 'POST', headers: { @@ -176,7 +171,7 @@ function reCreateTeam (org) { console.log('BODY: ' + chunk) }) } else { - console.log('Added ' + teamName ' to ' + org) + console.log('Added ' + teamName + ' to ' + org) } }) diff --git a/test/linters/javascript_standard/javascript_bad_1.js b/test/linters/javascript_standard/javascript_bad_1.js index 3379e608..89624628 100644 --- a/test/linters/javascript_standard/javascript_bad_1.js +++ b/test/linters/javascript_standard/javascript_bad_1.js @@ -1,199 +1,9 @@ -var http = require('http') -var createHandler = require('github-webhook-handler') +var teamId = "teamId" -var handler = createHandler({ - path: /webhook, secret : (process.env.SECRET) }) - -var userArray = ['user1'] -here is some garbage = that - -var teamDescription = Team of Robots -var teamPrivacy = 'closed' // closed (visible) / secret (hidden) are options here - -var teamName = process.env.GHES_TEAM_NAME -var teamAccess = 'pull' // pull,push,admin options here -var teamId = '' - -var orgRepos = [] - -// var creator = "" - -var foo = someFunction(); - var bar = a + 1; - - http.createServer(function (req, res) { - handler(req, res, function (err) { - console.log(err) - res.statusCode = 404 - res.end('no such location') - }) - }).listen(3000) - -handler.on('error', function (err) { - console.await.error('Error:', err.message) - }) - -handler.on('repository', function (event) { - if (event.payload.action === 'created') { - const repo = event.payload.repository.full_name - console.log(repo) - const org = event.payload.repository.owner.login - getTeamID(org) - setTimeout(checkTeamIDVariable, 1000) - } - }) - -handler.on('team', function (event) { - // TODO user events such as being removed from team or org - if (event.payload.action === 'deleted') { - // const name = event.payload.team.name - const org = event.payload.organization.login - getRepositories(org) - setTimeout(checkReposVariable, 5000) - } else if (event.payload.action === 'removed_from_repository') { - const org = event.payload.organization.login - getTeamID(org) - // const repo = event.payload.repository.full_name - setTimeout(checkTeamIDVariable, 1000) - } - }) - -function getTeamID(org) { - const https = require('https') - - const options = { - hostname: (process.env.GHE_HOST), - port: 443 - path: '/api/v3/orgs/' + org + '/teams', - method: 'GET', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json' - } - } - - req.on('error, (error) => { - console.error(error) - }) - -req.end() -} - -function checkTeamIDVariable(repo) { - if (typeof teamId != 'undefined') { - addTeamToRepo(repo, teamId) +function checkTeamIDVariable(teamId) { + if (typeof teamId != "undefined") { + console.log(teamId) } } -function checkReposVariable(org) { - if (typeof orgRepos !== 'undefined') { - // for(var repo of orgRepos) { - // addTeamToRepo(repo, teamId) - // } - reCreateTeam(org) - } -} - -function addTeamToRepo(repo, teamId) { - const https = require('https') - const data = JSON.stringify({ - permission: teamAccess - }) - - const options = { - hostname: (process.env.GHE_HOST), - port: 443, - path: '/api/v3/teams/' + teamId + '/repos/' + repo, - method: 'PUT', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json', - 'Content-Length': data.length - } - } - let body = [] - - const req = https.request(options, (res) => { - res.on('data', (chunk) => { - - body.push(chunk) - - }).on('end', () => { - - body = Buffer.concat(body).toString() - console.log(res.statusCode) - console.log('added team to ' + repo) - }) - }) - - req.on('error', (error) => { - console.error(error) - }) - - req.write(data) - req.end() -} - -function reCreateTeam(org) { - const https = require('https') - const data = JSON.stringify({ - name: teamName, - description: teamDescription, - privacy: teamPrivacy - maintainers: userArray, - repo_names: orgRepos - }) - - const options = { - hostname: (process.env.GHE_HOST), - port: 443 - path: '/api/v3/orgs/' + org + '/teams', - method: 'POST', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json', - 'Content-Length': data.length - } - } - // const body = [] - const req = https.request(options, (res) => { - if (res.statusCode !== 201) { - console.log('Status code: ' + res.statusCode) - console.log('Added ' + teamName + ' to ' + org + ' Failed') - res.on('data', function (chunk) { - console.log('BODY: ' + chunk) - }) - } else { - console.log('Added ' + teamName ' to ' + org) - } - }) - - req.on('error', (error) => { - console.error(error) - }) - - req.write(data) - req.end() -} - -function getRepositories(org) { - orgRepos = [] - - const https = require('https') - - const options = { - hostname: (process.env.GHE_HOST), - port: '443', - path: '/api/v3/orgs/' + org + "/repos", - method: 'GET', - headers: { - Authorization: 'token ' + (process.env.GHE_TOKEN), - 'Content-Type': 'application/json' - } - } - - req.on('error', (error) => { - console.error(error) - }) - req.end() -} +checkTeamIDVariable(teamId) diff --git a/test/linters/json/.dotfile_json_bad_2.json b/test/linters/json/.dotfile_json_bad_2.json index 096c082a..88dbd29a 100644 --- a/test/linters/json/.dotfile_json_bad_2.json +++ b/test/linters/json/.dotfile_json_bad_2.json @@ -1,12 +1,14 @@ { "arrow_spacing": { - "level": ["ignore"] + "level": [ + "ignore" + ] }, "foo": "bar", "foo": "baz", "braces_spacing": { "level": 'ignore', - "spaces": 0 + "spaces": 0, "empty_object_spaces": 0 } } diff --git a/test/linters/json/json_bad_1.json b/test/linters/json/json_bad_1.json index 096c082a..88dbd29a 100644 --- a/test/linters/json/json_bad_1.json +++ b/test/linters/json/json_bad_1.json @@ -1,12 +1,14 @@ { "arrow_spacing": { - "level": ["ignore"] + "level": [ + "ignore" + ] }, "foo": "bar", "foo": "baz", "braces_spacing": { "level": 'ignore', - "spaces": 0 + "spaces": 0, "empty_object_spaces": 0 } } diff --git a/test/linters/jsonc/json_bad_1.jsonc b/test/linters/jsonc/json_bad_1.jsonc index f90d345e..6029d92e 100644 --- a/test/linters/jsonc/json_bad_1.jsonc +++ b/test/linters/jsonc/json_bad_1.jsonc @@ -1,11 +1,13 @@ { "arrow_spacing": { - "level": ["ignore"] + "level": [ + "ignore" + ] }, // more test "braces_spacing": { "level": 'ignore', - "spaces": 0 + "spaces": 0, "empty_object_spaces": 0 } } diff --git a/test/linters/jsx/jsx_bad_1.jsx b/test/linters/jsx/jsx_bad_1.jsx index cb96f0fe..618c7ed3 100644 --- a/test/linters/jsx/jsx_bad_1.jsx +++ b/test/linters/jsx/jsx_bad_1.jsx @@ -1 +1,3 @@ -const element =

Hello, world! +import React from 'react'; + +var Hello = diff --git a/test/linters/jsx/jsx_good_1.jsx b/test/linters/jsx/jsx_good_1.jsx index f19f0a8f..6c773f98 100644 --- a/test/linters/jsx/jsx_good_1.jsx +++ b/test/linters/jsx/jsx_good_1.jsx @@ -1 +1,4 @@ +import React from 'react'; + const element =

Hello, world!

; +console.log(element) diff --git a/test/linters/markdown/markdown_bad_1.md b/test/linters/markdown/markdown_bad_1.md index 6599ac74..b9e5395c 100644 --- a/test/linters/markdown/markdown_bad_1.md +++ b/test/linters/markdown/markdown_bad_1.md @@ -1,20 +1,15 @@ -## Bad Markdown +# Bad Markdown This is just standard good markdown. -###### Second level header - -This header does **NOT** follow the step down from `level 1`. +## Second level header - Here it *is* - Some more indention - why so much? -``` -ls -la -``` - -# Walk away - We're all done **here**. -- [Link Action]https://github.com + +- [Link Action] + +[ Link Action ](https://github.com) diff --git a/test/linters/protobuf/protobuf_bad_1.proto b/test/linters/protobuf/protobuf_bad_1.proto index b42980ba..9dd4def3 100644 --- a/test/linters/protobuf/protobuf_bad_1.proto +++ b/test/linters/protobuf/protobuf_bad_1.proto @@ -1,6 +1,5 @@ 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"; @@ -34,14 +33,7 @@ message outer { 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/test/linters/python_ruff/python_bad_1.py b/test/linters/python_ruff/python_bad_1.py index 93bc7e92..8ab9699e 100644 --- a/test/linters/python_ruff/python_bad_1.py +++ b/test/linters/python_ruff/python_bad_1.py @@ -1,4 +1,5 @@ -a=1;b=2 -c=a+b -BROKEN_VAR=BROKEN_VAR +import pandas +import numpy as np + +c = 1 + 2 print(c) diff --git a/test/linters/rust_clippy/bad/src/main.rs b/test/linters/rust_clippy/bad/src/main.rs index ccc88353..7c138b9b 100644 --- a/test/linters/rust_clippy/bad/src/main.rs +++ b/test/linters/rust_clippy/bad/src/main.rs @@ -1,4 +1,7 @@ +use std::fs::OpenOptions; +use std::os::unix::fs::OpenOptionsExt; + fn main() { - let x = 3.14; - let _y = 1_f64 / x; + let mut options = OpenOptions::new(); + options.mode(644); } diff --git a/test/linters/sqlfluff/sqlfluff_bad_1.sql b/test/linters/sqlfluff/sqlfluff_bad_1.sql index 82e1f3eb..6a80ab34 100644 --- a/test/linters/sqlfluff/sqlfluff_bad_1.sql +++ b/test/linters/sqlfluff/sqlfluff_bad_1.sql @@ -1 +1,3 @@ -DELETE from person; +SELECT + a +FROM foo AS zoo; diff --git a/test/linters/tsx/tsx_bad_1.tsx b/test/linters/tsx/tsx_bad_1.tsx index a6f6738f..ee723bb8 100644 --- a/test/linters/tsx/tsx_bad_1.tsx +++ b/test/linters/tsx/tsx_bad_1.tsx @@ -1 +1 @@ -var foo = bar as +var Hello = diff --git a/test/linters/tsx/tsx_good_1.tsx b/test/linters/tsx/tsx_good_1.tsx index ac5b8f4f..6c773f98 100644 --- a/test/linters/tsx/tsx_good_1.tsx +++ b/test/linters/tsx/tsx_good_1.tsx @@ -1 +1,4 @@ -var foo = bar as foo; +import React from 'react'; + +const element =

Hello, world!

; +console.log(element) diff --git a/test/linters/typescript_es/typescript_bad_1.ts b/test/linters/typescript_es/typescript_bad_1.ts index 1b484c9f..66e8f898 100644 --- a/test/linters/typescript_es/typescript_bad_1.ts +++ b/test/linters/typescript_es/typescript_bad_1.ts @@ -1,8 +1,10 @@ -const spiderman = (person: String) => { - return 'Hello, ' + person; +var teamId = "teamId" +var booleanTest = false; + +function checkTeamIDVariable(teamId, booleanTest) { + if (typeof teamId != "undefined" || Boolean(!!booleanTest)) { + console.log(teamId) + } } -var handler = createHandler( { path : /webhook, secret : (process.env.SECRET) }) - -let user = 1; -console.log(spiderman(user)); +checkTeamIDVariable(teamId, booleanTest) diff --git a/test/linters/typescript_prettier/typescript_bad_1.ts b/test/linters/typescript_prettier/typescript_bad_1.ts index c9c15a5c..39327f1e 100644 --- a/test/linters/typescript_prettier/typescript_bad_1.ts +++ b/test/linters/typescript_prettier/typescript_bad_1.ts @@ -6,7 +6,5 @@ const spiderman = (person: String) => { return 'Hello, ' + person; } -var handler = createHandler( { path : /webhook, secret : (process.env.SECRET) }) - let user = 1; console.log(spiderman(user)); diff --git a/test/linters/typescript_standard/typescript_bad_1.ts b/test/linters/typescript_standard/typescript_bad_1.ts index c9c15a5c..f69cfcd4 100644 --- a/test/linters/typescript_standard/typescript_bad_1.ts +++ b/test/linters/typescript_standard/typescript_bad_1.ts @@ -1,12 +1,11 @@ enum Test { - Hoo = 'hoo' + Hoo = "hoo" } -const spiderman = (person: String) => { - return 'Hello, ' + person; +const spiderman = (person: string): string => { + return `Hello, ${person}` } -var handler = createHandler( { path : /webhook, secret : (process.env.SECRET) }) - -let user = 1; -console.log(spiderman(user)); +const user = "Peter Parker" +console.log(spiderman(user)) +console.log(Test.Hoo) diff --git a/test/run-super-linter-tests.sh b/test/run-super-linter-tests.sh index b01bc0fe..1f9a9da1 100755 --- a/test/run-super-linter-tests.sh +++ b/test/run-super-linter-tests.sh @@ -10,7 +10,7 @@ source "test/testUtils.sh" SUPER_LINTER_TEST_CONTAINER_URL="${1}" TEST_FUNCTION_NAME="${2}" SUPER_LINTER_CONTAINER_IMAGE_TYPE="${3}" -echo "Super-linter container image type: ${SUPER_LINTER_CONTAINER_IMAGE_TYPE}" +debug "Super-linter container image type: ${SUPER_LINTER_CONTAINER_IMAGE_TYPE}" DEFAULT_BRANCH="main" @@ -22,8 +22,13 @@ ignore_test_cases() { COMMAND_TO_RUN+=(-e FILTER_REGEX_EXCLUDE=".*(/test/linters/|CHANGELOG.md).*") } +configure_typescript_for_test_cases() { + COMMAND_TO_RUN+=(--env TYPESCRIPT_STANDARD_TSCONFIG_FILE=".github/linters/tsconfig.json") +} + configure_linters_for_test_cases() { - COMMAND_TO_RUN+=(-e TEST_CASE_RUN=true -e JSCPD_CONFIG_FILE=".jscpd-test-linters.json" -e RENOVATE_SHAREABLE_CONFIG_PRESET_FILE_NAMES="default.json,hoge.json" -e TYPESCRIPT_STANDARD_TSCONFIG_FILE=".github/linters/tsconfig.json") + COMMAND_TO_RUN+=(-e TEST_CASE_RUN=true -e JSCPD_CONFIG_FILE=".jscpd-test-linters.json" -e RENOVATE_SHAREABLE_CONFIG_PRESET_FILE_NAMES="default.json,hoge.json") + configure_typescript_for_test_cases } run_test_cases_expect_failure() { @@ -68,7 +73,9 @@ run_test_case_bash_exec_library_expect_success() { initialize_git_repository_and_test_args() { local GIT_REPOSITORY_PATH="${1}" # shellcheck disable=SC2064 # Once the path is set, we don't expect it to change - trap "rm -fr '${GIT_REPOSITORY_PATH}'" EXIT + trap "sudo rm -fr '${GIT_REPOSITORY_PATH}'" EXIT + + debug "GIT_REPOSITORY_PATH: ${GIT_REPOSITORY_PATH}" local GITHUB_EVENT_FILE_PATH="${2}" @@ -87,7 +94,13 @@ initialize_git_repository_and_test_args() { COMMAND_TO_RUN+=(-e GITHUB_EVENT_PATH="/tmp/lint/$(basename "${GITHUB_EVENT_FILE_PATH}")") COMMAND_TO_RUN+=(-e MULTI_STATUS=false) COMMAND_TO_RUN+=(-e VALIDATE_ALL_CODEBASE=false) - COMMAND_TO_RUN+=(-e VALIDATE_JSON=true) +} + +initialize_github_sha() { + local GIT_REPOSITORY_PATH="${1}" + local TEST_GITHUB_SHA + TEST_GITHUB_SHA="$(git -C "${GIT_REPOSITORY_PATH}" rev-parse HEAD)" + COMMAND_TO_RUN+=(-e GITHUB_SHA="${TEST_GITHUB_SHA}") } run_test_case_git_initial_commit() { @@ -95,10 +108,8 @@ run_test_case_git_initial_commit() { GIT_REPOSITORY_PATH="$(mktemp -d)" initialize_git_repository_and_test_args "${GIT_REPOSITORY_PATH}" "test/data/github-event/github-event-push.json" - - local TEST_GITHUB_SHA - TEST_GITHUB_SHA="$(git -C "${GIT_REPOSITORY_PATH}" rev-parse HEAD)" - COMMAND_TO_RUN+=(-e GITHUB_SHA="${TEST_GITHUB_SHA}") + initialize_github_sha "${GIT_REPOSITORY_PATH}" + COMMAND_TO_RUN+=(-e VALIDATE_JSON=true) } run_test_case_merge_commit_push() { @@ -128,9 +139,8 @@ run_test_case_merge_commit_push() { git -C "${GIT_REPOSITORY_PATH}" log --all --graph --abbrev-commit --decorate --format=oneline - local TEST_GITHUB_SHA - TEST_GITHUB_SHA="$(git -C "${GIT_REPOSITORY_PATH}" rev-parse HEAD)" - COMMAND_TO_RUN+=(-e GITHUB_SHA="${TEST_GITHUB_SHA}") + initialize_github_sha "${GIT_REPOSITORY_PATH}" + COMMAND_TO_RUN+=(-e VALIDATE_JSON=true) } run_test_case_use_find_and_ignore_gitignored_files() { @@ -155,46 +165,54 @@ run_test_case_custom_summary() { } run_test_case_fix_mode() { - run_test_cases_expect_success CREATE_LOG_FILE="true" SAVE_SUPER_LINTER_OUTPUT="true" - COMMAND_TO_RUN+=(--env FIX_ANSIBLE="true") - COMMAND_TO_RUN+=(--env FIX_CLANG_FORMAT="true") - COMMAND_TO_RUN+=(--env FIX_CSHARP="true") - COMMAND_TO_RUN+=(--env FIX_CSS="true") - COMMAND_TO_RUN+=(--env FIX_ENV="true") - COMMAND_TO_RUN+=(--env FIX_GO_MODULES="true") - COMMAND_TO_RUN+=(--env FIX_GO="true") - COMMAND_TO_RUN+=(--env FIX_GOOGLE_JAVA_FORMAT="true") - COMMAND_TO_RUN+=(--env FIX_GROOVY="true") - COMMAND_TO_RUN+=(--env FIX_JAVASCRIPT_ES="true") - COMMAND_TO_RUN+=(--env FIX_JAVASCRIPT_PRETTIER="true") - COMMAND_TO_RUN+=(--env FIX_JAVASCRIPT_STANDARD="true") - COMMAND_TO_RUN+=(--env FIX_JSON="true") - COMMAND_TO_RUN+=(--env FIX_JSONC="true") - COMMAND_TO_RUN+=(--env FIX_JSX="true") - COMMAND_TO_RUN+=(--env FIX_MARKDOWN="true") - COMMAND_TO_RUN+=(--env FIX_POWERSHELL="true") - COMMAND_TO_RUN+=(--env FIX_PROTOBUF="true") - COMMAND_TO_RUN+=(--env FIX_PYTHON_BLACK="true") - COMMAND_TO_RUN+=(--env FIX_PYTHON_ISORT="true") - COMMAND_TO_RUN+=(--env FIX_PYTHON_RUFF="true") - COMMAND_TO_RUN+=(--env FIX_RUBY="true") - COMMAND_TO_RUN+=(--env FIX_RUST_2015="true") - COMMAND_TO_RUN+=(--env FIX_RUST_2018="true") - COMMAND_TO_RUN+=(--env FIX_RUST_2021="true") - # Temporarily disable fix mode for rust clippy due to a dependency on another PR - # COMMAND_TO_RUN+=(--env FIX_RUST_CLIPPY="true") - COMMAND_TO_RUN+=(--env FIX_SCALAFMT="true") - COMMAND_TO_RUN+=(--env FIX_SHELL_SHFMT="true") - COMMAND_TO_RUN+=(--env FIX_SNAKEMAKE_SNAKEFMT="true") - COMMAND_TO_RUN+=(--env FIX_SQLFLUFF="true") - COMMAND_TO_RUN+=(--env FIX_TERRAFORM_FMT="true") - COMMAND_TO_RUN+=(--env FIX_TSX="true") - COMMAND_TO_RUN+=(--env FIX_TYPESCRIPT_ES="true") - COMMAND_TO_RUN+=(--env FIX_TYPESCRIPT_PRETTIER="true") - COMMAND_TO_RUN+=(--env FIX_TYPESCRIPT_STANDARD="true") + GIT_REPOSITORY_PATH="$(mktemp -d)" + + initialize_git_repository_and_test_args "${GIT_REPOSITORY_PATH}" "test/data/github-event/github-event-push.json" + + local LINTERS_TEST_CASES_FIX_MODE_DESTINATION_PATH="${GIT_REPOSITORY_PATH}/${LINTERS_TEST_CASE_DIRECTORY}" + mkdir -p "${LINTERS_TEST_CASES_FIX_MODE_DESTINATION_PATH}" + + for LANGUAGE in "${LANGUAGES_WITH_FIX_MODE[@]}"; do + if [[ "${SUPER_LINTER_CONTAINER_IMAGE_TYPE}" == "slim" ]] && + ! IsLanguageInSlimImage "${LANGUAGE}"; then + debug "Skip ${LANGUAGE} because it's not available in the Super-linter ${SUPER_LINTER_CONTAINER_IMAGE_TYPE} image" + continue + fi + local -l LOWERCASE_LANGUAGE="${LANGUAGE}" + cp -rv "${LINTERS_TEST_CASE_DIRECTORY}/${LOWERCASE_LANGUAGE}" "${LINTERS_TEST_CASES_FIX_MODE_DESTINATION_PATH}/" + eval "COMMAND_TO_RUN+=(--env FIX_${LANGUAGE}=\"true\")" + eval "COMMAND_TO_RUN+=(--env VALIDATE_${LANGUAGE}=\"true\")" + done + + # Copy gitignore so we don't commit eventual leftovers from previous runs + cp -v ".gitignore" "${GIT_REPOSITORY_PATH}/" + + # Copy fix mode linter configuration files because default ones are not always + # suitable for fix mode + local FIX_MODE_LINTERS_CONFIG_DIR="${GIT_REPOSITORY_PATH}/.github/linters" + mkdir -p "${FIX_MODE_LINTERS_CONFIG_DIR}" + cp -rv "test/linters-config/fix-mode/." "${FIX_MODE_LINTERS_CONFIG_DIR}/" + cp -rv ".github/linters/tsconfig.json" "${FIX_MODE_LINTERS_CONFIG_DIR}/" + git -C "${GIT_REPOSITORY_PATH}" add . + git -C "${GIT_REPOSITORY_PATH}" commit --no-verify -m "feat: add fix mode test cases" + initialize_github_sha "${GIT_REPOSITORY_PATH}" + + CREATE_LOG_FILE="true" + SAVE_SUPER_LINTER_OUTPUT="true" + VERIFY_FIX_MODE="true" + + # Enable test mode so we run linters and formatters only against their test + # cases + COMMAND_TO_RUN+=(--env FIX_MODE_TEST_CASE_RUN=true) + COMMAND_TO_RUN+=(--env TEST_CASE_RUN=true) + COMMAND_TO_RUN+=(--env ANSIBLE_DIRECTORY="/test/linters/ansible/bad") + configure_typescript_for_test_cases + + # Some linters report a non-zero exit code even if they fix all the issues + EXPECTED_EXIT_CODE=2 } # Run the test setup function @@ -203,28 +221,30 @@ ${TEST_FUNCTION_NAME} CREATE_LOG_FILE="${CREATE_LOG_FILE:-false}" SAVE_SUPER_LINTER_OUTPUT="${SAVE_SUPER_LINTER_OUTPUT:-false}" +SUPER_LINTER_WORKSPACE="${SUPER_LINTER_WORKSPACE:-$(pwd)}" +COMMAND_TO_RUN+=(-v "${SUPER_LINTER_WORKSPACE}":"/tmp/lint") + if [ -n "${SUPER_LINTER_OUTPUT_DIRECTORY_NAME:-}" ]; then COMMAND_TO_RUN+=(-e SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}") fi SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME:-"super-linter-output"}" -SUPER_LINTER_MAIN_OUTPUT_PATH="$(pwd)/${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}" -echo "Super-linter main output path: ${SUPER_LINTER_MAIN_OUTPUT_PATH}" +SUPER_LINTER_MAIN_OUTPUT_PATH="${SUPER_LINTER_WORKSPACE}/${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}" +debug "Super-linter main output path: ${SUPER_LINTER_MAIN_OUTPUT_PATH}" SUPER_LINTER_OUTPUT_PATH="${SUPER_LINTER_MAIN_OUTPUT_PATH}/super-linter" -echo "Super-linter output path: ${SUPER_LINTER_OUTPUT_PATH}" +debug "Super-linter output path: ${SUPER_LINTER_OUTPUT_PATH}" COMMAND_TO_RUN+=(-e CREATE_LOG_FILE="${CREATE_LOG_FILE}") COMMAND_TO_RUN+=(-e LOG_LEVEL="${LOG_LEVEL:-"DEBUG"}") COMMAND_TO_RUN+=(-e RUN_LOCAL="${RUN_LOCAL:-true}") COMMAND_TO_RUN+=(-e SAVE_SUPER_LINTER_OUTPUT="${SAVE_SUPER_LINTER_OUTPUT}") -COMMAND_TO_RUN+=(-v "${SUPER_LINTER_WORKSPACE:-$(pwd)}":"/tmp/lint") -SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH="$(pwd)/github-step-summary.md" +SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH="${SUPER_LINTER_WORKSPACE}/github-step-summary.md" # We can't put this inside SUPER_LINTER_MAIN_OUTPUT_PATH because it doesn't exist # before Super-linter creates it, and we want to verify that as well. -echo "SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH: ${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" +debug "SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH: ${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" if [ -n "${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH:-}" ]; then - echo "Expected Super-linter step summary file path: ${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH}" + debug "Expected Super-linter step summary file path: ${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH}" ENABLE_GITHUB_ACTIONS_STEP_SUMMARY="true" SAVE_SUPER_LINTER_SUMMARY="true" @@ -240,71 +260,105 @@ if [ -n "${SUPER_LINTER_SUMMARY_FILE_NAME:-}" ]; then COMMAND_TO_RUN+=(-e SUPER_LINTER_SUMMARY_FILE_NAME="${SUPER_LINTER_SUMMARY_FILE_NAME}") fi SUPER_LINTER_SUMMARY_FILE_NAME="${SUPER_LINTER_SUMMARY_FILE_NAME:-"super-linter-summary.md"}" -echo "SUPER_LINTER_SUMMARY_FILE_NAME: ${SUPER_LINTER_SUMMARY_FILE_NAME}" +debug "SUPER_LINTER_SUMMARY_FILE_NAME: ${SUPER_LINTER_SUMMARY_FILE_NAME}" SUPER_LINTER_SUMMARY_FILE_PATH="${SUPER_LINTER_MAIN_OUTPUT_PATH}/${SUPER_LINTER_SUMMARY_FILE_NAME}" -echo "Super-linter summary output path: ${SUPER_LINTER_SUMMARY_FILE_PATH}" +debug "Super-linter summary output path: ${SUPER_LINTER_SUMMARY_FILE_PATH}" -LOG_FILE_PATH="$(pwd)/super-linter.log" +LOG_FILE_PATH="${SUPER_LINTER_WORKSPACE}/super-linter.log" COMMAND_TO_RUN+=("${SUPER_LINTER_TEST_CONTAINER_URL}") declare -i EXPECTED_EXIT_CODE EXPECTED_EXIT_CODE=${EXPECTED_EXIT_CODE:-0} -echo "Cleaning eventual leftovers before running tests: ${LEFTOVERS_TO_CLEAN[*]}" +debug "Cleaning eventual leftovers before running tests: ${LEFTOVERS_TO_CLEAN[*]}" LEFTOVERS_TO_CLEAN+=("${LOG_FILE_PATH}") LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}") LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_MAIN_OUTPUT_PATH}") LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_SUMMARY_FILE_PATH}") +LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_SUMMARY_FILE_PATH}") +LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_WORKSPACE}/${LINTERS_TEST_CASE_DIRECTORY}/rust_clippy/bad/target") +LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_WORKSPACE}/${LINTERS_TEST_CASE_DIRECTORY}/rust_clippy/bad/Cargo.lock") +LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_WORKSPACE}/${LINTERS_TEST_CASE_DIRECTORY}/rust_clippy/good/target") +LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_WORKSPACE}/${LINTERS_TEST_CASE_DIRECTORY}/rust_clippy/good/Cargo.lock") +# Delete leftovers in pwd in case the workspace is not pwd +LEFTOVERS_TO_CLEAN+=("$(pwd)/$(basename "${LOG_FILE_PATH}")") +LEFTOVERS_TO_CLEAN+=("$(pwd)/$(basename "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}")") +LEFTOVERS_TO_CLEAN+=("$(pwd)/$(basename "${SUPER_LINTER_MAIN_OUTPUT_PATH}")") +LEFTOVERS_TO_CLEAN+=("$(pwd)/$(basename "${SUPER_LINTER_SUMMARY_FILE_PATH}")") +LEFTOVERS_TO_CLEAN+=("$(pwd)/${LINTERS_TEST_CASE_DIRECTORY}/rust_clippy/bad/target") +LEFTOVERS_TO_CLEAN+=("$(pwd)/${LINTERS_TEST_CASE_DIRECTORY}/rust_clippy/bad/Cargo.lock") +LEFTOVERS_TO_CLEAN+=("$(pwd)/${LINTERS_TEST_CASE_DIRECTORY}/rust_clippy/good/target") +LEFTOVERS_TO_CLEAN+=("$(pwd)/${LINTERS_TEST_CASE_DIRECTORY}/rust_clippy/good/Cargo.lock") sudo rm -rfv "${LEFTOVERS_TO_CLEAN[@]}" if [[ "${ENABLE_GITHUB_ACTIONS_STEP_SUMMARY}" == "true" ]]; then - echo "Creating GitHub Actions step summary file: ${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" + debug "Creating GitHub Actions step summary file: ${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" touch "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" fi -if [ ${EXPECTED_EXIT_CODE} -ne 0 ]; then - echo "Disable failures on error because the expected exit code is ${EXPECTED_EXIT_CODE}" - set +o errexit -fi +debug "Command to run: ${COMMAND_TO_RUN[*]}" -echo "Command to run: ${COMMAND_TO_RUN[*]}" +# Disable failures on error so we can continue with tests regardless +# of the Super-linter exit code +set +o errexit "${COMMAND_TO_RUN[@]}" - SUPER_LINTER_EXIT_CODE=$? -# Enable the errexit option in case we disabled it +# Enable the errexit option that we check later set -o errexit -echo "Super-linter exit code: ${SUPER_LINTER_EXIT_CODE}" +debug "Super-linter workspace: ${SUPER_LINTER_WORKSPACE}" +debug "Super-linter exit code: ${SUPER_LINTER_EXIT_CODE}" if [[ "${CREATE_LOG_FILE}" == true ]]; then if [ ! -e "${LOG_FILE_PATH}" ]; then - echo "Log file was requested but it's not available at ${LOG_FILE_PATH}" + debug "Log file was requested but it's not available at ${LOG_FILE_PATH}" exit 1 else sudo chown -R "$(id -u)":"$(id -g)" "${LOG_FILE_PATH}" - echo "Log file contents:" - cat "${LOG_FILE_PATH}" + debug "Log file path: ${LOG_FILE_PATH}" + if [[ "${CI:-}" == "true" ]]; then + debug "Log file contents:" + cat "${LOG_FILE_PATH}" + else + debug "Not in CI environment, skip emitting log file (${LOG_FILE_PATH}) contents" + fi + + if [[ "${SUPER_LINTER_WORKSPACE}" != "$(pwd)" ]]; then + debug "Copying Super-linter log from the workspace (${SUPER_LINTER_WORKSPACE}) to the current working directory for easier inspection" + cp -v "${LOG_FILE_PATH}" "$(pwd)/" + fi fi else - echo "Log file was not requested. CREATE_LOG_FILE: ${CREATE_LOG_FILE}" + debug "Log file was not requested. CREATE_LOG_FILE: ${CREATE_LOG_FILE}" fi if [[ "${SAVE_SUPER_LINTER_OUTPUT}" == true ]]; then if [ ! -d "${SUPER_LINTER_OUTPUT_PATH}" ]; then - echo "Super-linter output was requested but it's not available at ${SUPER_LINTER_OUTPUT_PATH}" + debug "Super-linter output was requested but it's not available at ${SUPER_LINTER_OUTPUT_PATH}" exit 1 else sudo chown -R "$(id -u)":"$(id -g)" "${SUPER_LINTER_OUTPUT_PATH}" - echo "Super-linter output path (${SUPER_LINTER_OUTPUT_PATH}) contents:" - ls -alhR "${SUPER_LINTER_OUTPUT_PATH}" + if [[ "${CI:-}" == "true" ]]; then + debug "Super-linter output path (${SUPER_LINTER_OUTPUT_PATH}) contents:" + ls -alhR "${SUPER_LINTER_OUTPUT_PATH}" + else + debug "Not in CI environment, skip emitting ${SUPER_LINTER_OUTPUT_PATH} contents" + fi + + if [[ "${SUPER_LINTER_WORKSPACE}" != "$(pwd)" ]]; then + debug "Copying Super-linter output from the workspace (${SUPER_LINTER_WORKSPACE}) to the current working directory for easier inspection" + SUPER_LINTER_OUTPUT_PATH_PWD="$(pwd)/super-linter-output/" + mkdir -p "${SUPER_LINTER_OUTPUT_PATH_PWD}" + cp -r "${SUPER_LINTER_OUTPUT_PATH}" "${SUPER_LINTER_OUTPUT_PATH_PWD}" + fi fi else - echo "Super-linter output was not requested. SAVE_SUPER_LINTER_OUTPUT: ${SAVE_SUPER_LINTER_OUTPUT}" + debug "Super-linter output was not requested. SAVE_SUPER_LINTER_OUTPUT: ${SAVE_SUPER_LINTER_OUTPUT}" if [ -e "${SUPER_LINTER_OUTPUT_PATH}" ]; then - echo "Super-linter output was not requested but it's available at ${SUPER_LINTER_OUTPUT_PATH}" + debug "Super-linter output was not requested but it's available at ${SUPER_LINTER_OUTPUT_PATH}" exit 1 fi fi @@ -312,37 +366,96 @@ fi if [ -n "${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH:-}" ]; then # Remove eventual HTML comments from the expected file because we use them to disable certain linter rules if ! diff "${SUPER_LINTER_SUMMARY_FILE_PATH}" <(grep -vE '^\s*