fix: add missing fix mode options and test cases (#5987)

- Add missing fix mode options for: CLANG_FORMAT, ENV,
  GOOGLE_JAVA_FORMAT, NATURAL_LANGUAGE, PYTHON_ISORT, RUST_CLIPPY.
- Refactor linter tests to make them shorter because there's no need to
  have big test files.
- Refactor 'bad' linter tests for linters that support fix mode so they
  contain only automatically fixable issues. This is needed to avoid
  adding another set of 'bad' linter tests for fix mode.
- Provide configuration files for linters that support fix mode and for
  which the default configuration is not suitable to enable fix mode:
  ansible-lint, ESLint, golangci-lint.
- Add a test case for linter commands options for linters that support
  fix mode, to ensure that fix mode and check-only mode options have
  been defined.
- Refactor the fix mode test to check if linters actually applied
  modifications to files.
- Update documentation about adding test cases for linters that support
  fix mode.
- Don't exit with a fatal error if VALIDATE_xxx is false when testing
  fix mode because not all linters support fix mode. To enable this, set
  the new FIX_MODE_TEST_CASE_RUN variable to true.
This commit is contained in:
Marco Ferrari 2024-08-12 12:31:38 +02:00 committed by GitHub
parent ea16cd9a1b
commit 91dc6d7234
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 595 additions and 712 deletions

44
.github/linters/.eslintrc.yml vendored Normal file
View file

@ -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
...

7
.github/linters/.golangci.yml vendored Normal file
View file

@ -0,0 +1,7 @@
---
# This file is only used in tests
# TODO: move in a dedicated directory in test/linters-config
linters:
enable:
- gofmt
...

View file

@ -38,6 +38,7 @@
"**/test/linters/typescript_es/**", "**/test/linters/typescript_es/**",
"**/test/linters/typescript_prettier/**", "**/test/linters/typescript_prettier/**",
"**/test/linters/typescript_standard/**", "**/test/linters/typescript_standard/**",
"**/test/linters-config/**",
"**/github_conf/**", "**/github_conf/**",
"**/workflows/cd.yml", "**/workflows/cd.yml",
"**/workflows/ci.yml" "**/workflows/ci.yml"

View file

@ -4,7 +4,7 @@
all: info docker test ## Run all targets. all: info docker test ## Run all targets.
.PHONY: test .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 # 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 # 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 \ --entrypoint /tmp/lint/test/lib/globalsLanguagesTest.sh \
$(SUPER_LINTER_TEST_CONTAINER_URL) $(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 .PHONY: test-linter-rules
test-linter-rules: ## Test linterRules.sh test-linter-rules: ## Test linterRules.sh
docker run \ docker run \
@ -416,14 +424,13 @@ test-non-default-home-directory: ## Test a non-default HOME directory
"run_test_cases_non_default_home" \ "run_test_cases_non_default_home" \
"$(IMAGE)" "$(IMAGE)"
.PHONY: test-linters-fix-mode-expect-success .PHONY: test-linters-fix-mode
test-linters-fix-mode-expect-success: ## Run the linters test suite (fix mode) expecting successes test-linters-fix-mode: ## Run the linters test suite (fix mode)
$(CURDIR)/test/run-super-linter-tests.sh \ $(CURDIR)/test/run-super-linter-tests.sh \
$(SUPER_LINTER_TEST_CONTAINER_URL) \ $(SUPER_LINTER_TEST_CONTAINER_URL) \
"run_test_case_fix_mode" \ "run_test_case_fix_mode" \
"$(IMAGE)" "$(IMAGE)"
.PHONY: test-linters .PHONY: test-linters
test-linters: test-linters-expect-success test-linters-expect-failure ## Run the linters test suite test-linters: test-linters-expect-success test-linters-expect-failure ## Run the linters test suite

View file

@ -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_2015** | `false` | Option to enable fix mode for `RUST_2015`. |
| **FIX_RUST_2018** | `false` | Option to enable fix mode for `RUST_2018`. | | **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_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_SCALAFMT** | `false` | Option to enable fix mode for `SCALAFMT`. |
| **FIX_SHELL_SHFMT** | `false` | Option to enable fix mode for `SHELL_SHFMT`. | | **FIX_SHELL_SHFMT** | `false` | Option to enable fix mode for `SHELL_SHFMT`. |
| **FIX_SNAKEMAKE_SNAKEFMT** | `false` | Option to enable fix mode for `SNAKEMAKE_SNAKEFMT`. | | **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 - If you're running Super-linter locally, you can commit the changes as you
would with any other change in your working directory. 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 ## Configure linters
Super-linter provides default configurations for some linters in the [`TEMPLATES/`](TEMPLATES/) Super-linter provides default configurations for some linters in the [`TEMPLATES/`](TEMPLATES/)

View file

@ -7,11 +7,16 @@ new tool, it should include:
- `README.md` - `README.md`
- Provide test cases: - Provide test cases:
1. Create the `test/linters/<LANGUGAGE>` directory. 1. Create the `test/linters/<LANGUAGE_NAME>` directory.
2. Provide at least one test case with a file that is supposed to pass validation, 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/<LANGUAGE>/<name-of-tool>-good` with the right file extension if needed: `test/linters/<LANGUAGE_NAME>/<name-of-tool>-good`
3. Provide at least one test case with a file that is supposed to fail validation, 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/<LANGUAGE>/<name-of-tool>-bad` with the right file extension if needed: `test/linters/<LANGUAGE_NAME>/<name-of-tool>-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 `<LANGUGAGE>`
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: - Update the test suite to check for installed packages, the commands that your new tool needs in the `PATH`, and the expected version command:

View file

@ -7,6 +7,7 @@ function ValidateBooleanConfigurationVariables() {
ValidateBooleanVariable "DISABLE_ERRORS" "${DISABLE_ERRORS}" ValidateBooleanVariable "DISABLE_ERRORS" "${DISABLE_ERRORS}"
ValidateBooleanVariable "ENABLE_GITHUB_ACTIONS_GROUP_TITLE" "${ENABLE_GITHUB_ACTIONS_GROUP_TITLE}" ValidateBooleanVariable "ENABLE_GITHUB_ACTIONS_GROUP_TITLE" "${ENABLE_GITHUB_ACTIONS_GROUP_TITLE}"
ValidateBooleanVariable "ENABLE_GITHUB_ACTIONS_STEP_SUMMARY" "${ENABLE_GITHUB_ACTIONS_STEP_SUMMARY}" 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_GENERATED_FILES" "${IGNORE_GENERATED_FILES}"
ValidateBooleanVariable "IGNORE_GITIGNORED_FILES" "${IGNORE_GITIGNORED_FILES}" ValidateBooleanVariable "IGNORE_GITIGNORED_FILES" "${IGNORE_GITIGNORED_FILES}"
ValidateBooleanVariable "LOG_DEBUG" "${LOG_DEBUG}" ValidateBooleanVariable "LOG_DEBUG" "${LOG_DEBUG}"

View file

@ -14,10 +14,15 @@ function LintCodebase() {
debug "Skip validation of ${FILE_TYPE} because VALIDATE_LANGUAGE is ${VALIDATE_LANGUAGE}" debug "Skip validation of ${FILE_TYPE} because VALIDATE_LANGUAGE is ${VALIDATE_LANGUAGE}"
unset -n VALIDATE_LANGUAGE unset -n VALIDATE_LANGUAGE
return 0 return 0
else
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 else
fatal "Don't disable any validation when running in test mode. VALIDATE_${FILE_TYPE} is set to: ${VALIDATE_LANGUAGE}. Set it to: true" 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 fi
fi
debug "Running LintCodebase. FILE_TYPE: ${FILE_TYPE}. TEST_CASE_RUN: ${TEST_CASE_RUN}" debug "Running LintCodebase. FILE_TYPE: ${FILE_TYPE}. TEST_CASE_RUN: ${TEST_CASE_RUN}"

View file

@ -35,10 +35,12 @@ STANDARD_FIX_MODE_OPTIONS=(--fix)
# Define configuration options to enable "fix mode". # Define configuration options to enable "fix mode".
# Not all linters and formatters support this. # Not all linters and formatters support this.
ANSIBLE_FIX_MODE_OPTIONS=(--fix) ANSIBLE_FIX_MODE_OPTIONS=(--fix)
CLANG_FORMAT_FIX_MODE_OPTIONS=(-i)
CSS_FIX_MODE_OPTIONS=(--fix) 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_FIX_MODE_OPTIONS=("${GOLANGCI_LINT_FIX_MODE_OPTIONS[@]}")
GO_MODULES_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) GROOVY_FIX_MODE_OPTIONS=(--fix)
JAVASCRIPT_ES_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}") JAVASCRIPT_ES_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}")
JAVASCRIPT_PRETTIER_FIX_MODE_OPTIONS=("${PRETTIER_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[@]}") JSONC_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}")
JSX_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}") JSX_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}")
MARKDOWN_FIX_MODE_OPTIONS=(--fix) MARKDOWN_FIX_MODE_OPTIONS=(--fix)
NATURAL_LANGUAGE_FIX_MODE_OPTIONS=(--fix)
POWERSHELL_FIX_MODE_OPTIONS=(-Fix) POWERSHELL_FIX_MODE_OPTIONS=(-Fix)
PROTOBUF_FIX_MODE_OPTIONS=(-fix) PROTOBUF_FIX_MODE_OPTIONS=(-fix)
PYTHON_ISORT_FIX_MODE_OPTIONS=(--overwrite-in-place)
PYTHON_RUFF_FIX_MODE_OPTIONS=(--fix) PYTHON_RUFF_FIX_MODE_OPTIONS=(--fix)
RUBY_FIX_MODE_OPTIONS=(--autocorrect) 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) SHELL_SHFMT_FIX_MODE_OPTIONS=(--write)
SQLFLUFF_FIX_MODE_OPTIONS=(fix) SQLFLUFF_FIX_MODE_OPTIONS=(fix)
TSX_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}") TSX_FIX_MODE_OPTIONS=("${ESLINT_FIX_MODE_OPTIONS[@]}")

View file

@ -113,6 +113,10 @@ declare -l TEST_CASE_RUN
TEST_CASE_RUN="${TEST_CASE_RUN:-"false"}" TEST_CASE_RUN="${TEST_CASE_RUN:-"false"}"
export TEST_CASE_RUN 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 # We want a lowercase value
declare -l USE_FIND_ALGORITHM declare -l USE_FIND_ALGORITHM
USE_FIND_ALGORITHM="${USE_FIND_ALGORITHM:-false}" USE_FIND_ALGORITHM="${USE_FIND_ALGORITHM:-false}"

View file

@ -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

View file

@ -32,13 +32,12 @@ BASH_EXEC_IGNORE_LIBRARIES="false"
GITHUB_WORKSPACE="$(pwd)" GITHUB_WORKSPACE="$(pwd)"
# shellcheck disable=SC2034 # shellcheck disable=SC2034
IGNORE_GITIGNORED_FILES="false" IGNORE_GITIGNORED_FILES="false"
LINTER_RULES_PATH="TEMPLATES"
# shellcheck disable=SC2034 # shellcheck disable=SC2034
TYPESCRIPT_STANDARD_TSCONFIG_FILE=".github/linters/tsconfig.json" TYPESCRIPT_STANDARD_TSCONFIG_FILE=".github/linters/tsconfig.json"
# shellcheck disable=SC2034 # shellcheck disable=SC2034
YAML_ERROR_ON_WARNING="false" YAML_ERROR_ON_WARNING="false"
for LANGUAGE in "${LANGUAGE_ARRAY_FOR_LINTER_RULES[@]}"; do for LANGUAGE in "${LANGUAGE_ARRAY_FOR_LINTER_RULES[@]}"; do
GetLinterRules "${LANGUAGE}" "${LINTER_RULES_PATH}" GetLinterRules "${LANGUAGE}" "TEMPLATES"
done done
ValidateValidationVariables ValidateValidationVariables

View file

@ -43,7 +43,12 @@ function GetLinterRulesTest() {
GetLinterRules "${LANGUAGE}" "${DEFAULT_RULES_LOCATION}" GetLinterRules "${LANGUAGE}" "${DEFAULT_RULES_LOCATION}"
done 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 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})" debug "TEST_LANGUAGE_LINTER_RULES (${TEST_LANGUAGE_LINTER_RULES}) matches the expected value (${EXPECTED_TEST_LANGUAGE_LINTER_RULES})"
else else

View file

@ -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
...

View file

@ -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
...

View file

@ -0,0 +1,5 @@
---
linters:
enable:
- gofmt
...

View file

@ -1,5 +1,8 @@
--- ---
- name: Remove temp files - name: Test playbook
hosts: all
tasks:
- name: Remove temp files
become: true become: true
file: file:
path: "{{ item }}" path: "{{ item }}"

View file

@ -8,4 +8,4 @@
path: "{{ item }}" path: "{{ item }}"
state: absent state: absent
with_items: with_items:
- "/tmp/test-1" - /tmp/test-1

View file

@ -1,3 +1,7 @@
if len(in) == 0 { package main
return "", fmt.Errorf("Input is empty")
import "fmt"
func main() {
fmt.Println("hello world")
} }

View file

@ -1,18 +1,7 @@
package main package main
import ( import "fmt"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
)
if len(in) == 0 { func main() {
return "", fmt.Errorf("Input is empty") fmt.Println("hello world")
} }
x := 0
{
var x int
x++
}
fmt.Println(x)

View file

@ -1,199 +1,10 @@
var http = require('http') var teamId = "teamId"
var createHandler = require('github-webhook-handler') var booleanTest = false;
var handler = createHandler({ function checkTeamIDVariable(teamId, booleanTest) {
path: /webhook, secret : (process.env.SECRET) }) if (typeof teamId != "undefined" || Boolean(!!booleanTest)) {
console.log(teamId)
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 checkReposVariable(org) { checkTeamIDVariable(teamId, booleanTest)
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()
}

View file

@ -1,137 +1,10 @@
const http = require('http') var teamId = "teamId"
const createHandler = require('github-webhook-handler') var booleanTest = false;
const handler = createHandler({ path: '/webhook', secret: (process.env.SECRET) })
const userArray = ['user1'] function checkTeamIDVariable(teamId, booleanTest) {
if (typeof teamId != "undefined" || booleanTest) {
const teamDescription = 'Team of Robots' console.log(teamId)
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 checkReposVariable (org) { checkTeamIDVariable(teamId, booleanTest)
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()
}

View file

@ -1,12 +1,8 @@
var http = require('http') var http = require('http')
var createHandler = require( 'github-webhook-handler') 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 teamPrivacy = 'closed' // closed (visible) / secret (hidden) are options here
var teamName = process.env.GHES_TEAM_NAME var teamName = process.env.GHES_TEAM_NAME
@ -62,8 +58,7 @@ function getTeamID (org) {
const options = { const options = {
hostname: (process.env.GHE_HOST), hostname: (process.env.GHE_HOST),
port: 443 port: 443,
path: '/api/v3/orgs/' + org + '/teams',
method: 'GET', method: 'GET',
headers: { headers: {
Authorization: 'token ' + (process.env.GHE_TOKEN), Authorization: 'token ' + (process.env.GHE_TOKEN),
@ -84,7 +79,7 @@ function getTeamID (org) {
}) })
}) })
req.on('error, (error) => { req.on('error', (error) => {
console.error(error) console.error(error)
}) })
@ -151,14 +146,14 @@ function reCreateTeam (org) {
const data = JSON.stringify({ const data = JSON.stringify({
name: teamName, name: teamName,
description: teamDescription, description: teamDescription,
privacy: teamPrivacy privacy: teamPrivacy,
maintainers: userArray, maintainers: userArray,
repo_names: orgRepos repo_names: orgRepos
}) })
const options = { const options = {
hostname: (process.env.GHE_HOST), hostname: (process.env.GHE_HOST),
port: 443 port: 443,
path: '/api/v3/orgs/' + org + '/teams', path: '/api/v3/orgs/' + org + '/teams',
method: 'POST', method: 'POST',
headers: { headers: {
@ -176,7 +171,7 @@ function reCreateTeam (org) {
console.log('BODY: ' + chunk) console.log('BODY: ' + chunk)
}) })
} else { } else {
console.log('Added ' + teamName ' to ' + org) console.log('Added ' + teamName + ' to ' + org)
} }
}) })

View file

@ -1,199 +1,9 @@
var http = require('http') var teamId = "teamId"
var createHandler = require('github-webhook-handler')
var handler = createHandler({ function checkTeamIDVariable(teamId) {
path: /webhook, secret : (process.env.SECRET) }) if (typeof teamId != "undefined") {
console.log(teamId)
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 checkReposVariable(org) { checkTeamIDVariable(teamId)
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()
}

View file

@ -1,12 +1,14 @@
{ {
"arrow_spacing": { "arrow_spacing": {
"level": ["ignore"] "level": [
"ignore"
]
}, },
"foo": "bar", "foo": "bar",
"foo": "baz", "foo": "baz",
"braces_spacing": { "braces_spacing": {
"level": 'ignore', "level": 'ignore',
"spaces": 0 "spaces": 0,
"empty_object_spaces": 0 "empty_object_spaces": 0
} }
} }

View file

@ -1,12 +1,14 @@
{ {
"arrow_spacing": { "arrow_spacing": {
"level": ["ignore"] "level": [
"ignore"
]
}, },
"foo": "bar", "foo": "bar",
"foo": "baz", "foo": "baz",
"braces_spacing": { "braces_spacing": {
"level": 'ignore', "level": 'ignore',
"spaces": 0 "spaces": 0,
"empty_object_spaces": 0 "empty_object_spaces": 0
} }
} }

View file

@ -1,11 +1,13 @@
{ {
"arrow_spacing": { "arrow_spacing": {
"level": ["ignore"] "level": [
"ignore"
]
}, },
// more test // more test
"braces_spacing": { "braces_spacing": {
"level": 'ignore', "level": 'ignore',
"spaces": 0 "spaces": 0,
"empty_object_spaces": 0 "empty_object_spaces": 0
} }
} }

View file

@ -1 +1,3 @@
const element = <h1>Hello, world! import React from 'react';
var Hello = <a target='_blank' href="https://example.com/"></a>

View file

@ -1 +1,4 @@
import React from 'react';
const element = <h1>Hello, world!</h1>; const element = <h1>Hello, world!</h1>;
console.log(element)

View file

@ -1,20 +1,15 @@
## Bad Markdown # Bad Markdown
This is just standard good markdown. This is just standard good markdown.
###### Second level header ## Second level header
This header does **NOT** follow the step down from `level 1`.
- Here it *is* - Here it *is*
- Some more indention - Some more indention
- why so much? - why so much?
```
ls -la
```
# Walk away
We're all done **here**. We're all done **here**.
- [Link Action]https://github.com
- [Link Action]<https://github.com>
[ Link Action ](https://github.com)

View file

@ -1,6 +1,5 @@
syntax = "proto3"; syntax = "proto3";
// A broken example of the official reference
// See https://developers.google.com/protocol-buffers/docs/reference/proto3-spec#proto_file
package examplePb; package examplePb;
option java_package = "com.example.foo"; option java_package = "com.example.foo";
@ -34,14 +33,7 @@ message outer {
message AccountForAdmin {} message AccountForAdmin {}
message SpecialEndOfSupport {} message SpecialEndOfSupport {}
required inner inner_message = 7; required inner inner_message = 7;
group Result = 8 {
string url = 9;
}
repeated group Result = 10 {
}
repeated inner paper = 11; repeated inner paper = 11;
repeated group Regular = 12 {
}
} }
service SearchApi { service SearchApi {
rpc search (SearchRequest) returns (SearchResponse) {}; rpc search (SearchRequest) returns (SearchResponse) {};

View file

@ -1,4 +1,5 @@
a=1;b=2 import pandas
c=a+b import numpy as np
BROKEN_VAR=BROKEN_VAR
c = 1 + 2
print(c) print(c)

View file

@ -1,4 +1,7 @@
use std::fs::OpenOptions;
use std::os::unix::fs::OpenOptionsExt;
fn main() { fn main() {
let x = 3.14; let mut options = OpenOptions::new();
let _y = 1_f64 / x; options.mode(644);
} }

View file

@ -1 +1,3 @@
DELETE from person; SELECT
a
FROM foo AS zoo;

View file

@ -1 +1 @@
var foo = bar as var Hello = <a target='_blank' href="https://example.com/"></a>

View file

@ -1 +1,4 @@
var foo = bar as foo; import React from 'react';
const element = <h1>Hello, world!</h1>;
console.log(element)

View file

@ -1,8 +1,10 @@
const spiderman = (person: String) => { var teamId = "teamId"
return 'Hello, ' + person; 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) }) checkTeamIDVariable(teamId, booleanTest)
let user = 1;
console.log(spiderman(user));

View file

@ -6,7 +6,5 @@ const spiderman = (person: String) => {
return 'Hello, ' + person; return 'Hello, ' + person;
} }
var handler = createHandler( { path : /webhook, secret : (process.env.SECRET) })
let user = 1; let user = 1;
console.log(spiderman(user)); console.log(spiderman(user));

View file

@ -1,12 +1,11 @@
enum Test { enum Test {
Hoo = 'hoo' Hoo = "hoo"
} }
const spiderman = (person: String) => { const spiderman = (person: string): string => {
return 'Hello, ' + person; return `Hello, ${person}`
} }
var handler = createHandler( { path : /webhook, secret : (process.env.SECRET) }) const user = "Peter Parker"
console.log(spiderman(user))
let user = 1; console.log(Test.Hoo)
console.log(spiderman(user));

View file

@ -10,7 +10,7 @@ source "test/testUtils.sh"
SUPER_LINTER_TEST_CONTAINER_URL="${1}" SUPER_LINTER_TEST_CONTAINER_URL="${1}"
TEST_FUNCTION_NAME="${2}" TEST_FUNCTION_NAME="${2}"
SUPER_LINTER_CONTAINER_IMAGE_TYPE="${3}" 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" DEFAULT_BRANCH="main"
@ -22,8 +22,13 @@ ignore_test_cases() {
COMMAND_TO_RUN+=(-e FILTER_REGEX_EXCLUDE=".*(/test/linters/|CHANGELOG.md).*") 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() { 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() { run_test_cases_expect_failure() {
@ -68,7 +73,9 @@ run_test_case_bash_exec_library_expect_success() {
initialize_git_repository_and_test_args() { initialize_git_repository_and_test_args() {
local GIT_REPOSITORY_PATH="${1}" local GIT_REPOSITORY_PATH="${1}"
# shellcheck disable=SC2064 # Once the path is set, we don't expect it to change # 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}" 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 GITHUB_EVENT_PATH="/tmp/lint/$(basename "${GITHUB_EVENT_FILE_PATH}")")
COMMAND_TO_RUN+=(-e MULTI_STATUS=false) COMMAND_TO_RUN+=(-e MULTI_STATUS=false)
COMMAND_TO_RUN+=(-e VALIDATE_ALL_CODEBASE=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() { run_test_case_git_initial_commit() {
@ -95,10 +108,8 @@ run_test_case_git_initial_commit() {
GIT_REPOSITORY_PATH="$(mktemp -d)" GIT_REPOSITORY_PATH="$(mktemp -d)"
initialize_git_repository_and_test_args "${GIT_REPOSITORY_PATH}" "test/data/github-event/github-event-push.json" initialize_git_repository_and_test_args "${GIT_REPOSITORY_PATH}" "test/data/github-event/github-event-push.json"
initialize_github_sha "${GIT_REPOSITORY_PATH}"
local TEST_GITHUB_SHA COMMAND_TO_RUN+=(-e VALIDATE_JSON=true)
TEST_GITHUB_SHA="$(git -C "${GIT_REPOSITORY_PATH}" rev-parse HEAD)"
COMMAND_TO_RUN+=(-e GITHUB_SHA="${TEST_GITHUB_SHA}")
} }
run_test_case_merge_commit_push() { 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 git -C "${GIT_REPOSITORY_PATH}" log --all --graph --abbrev-commit --decorate --format=oneline
local TEST_GITHUB_SHA initialize_github_sha "${GIT_REPOSITORY_PATH}"
TEST_GITHUB_SHA="$(git -C "${GIT_REPOSITORY_PATH}" rev-parse HEAD)" COMMAND_TO_RUN+=(-e VALIDATE_JSON=true)
COMMAND_TO_RUN+=(-e GITHUB_SHA="${TEST_GITHUB_SHA}")
} }
run_test_case_use_find_and_ignore_gitignored_files() { 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_case_fix_mode() {
run_test_cases_expect_success
CREATE_LOG_FILE="true" CREATE_LOG_FILE="true"
SAVE_SUPER_LINTER_OUTPUT="true" SAVE_SUPER_LINTER_OUTPUT="true"
COMMAND_TO_RUN+=(--env FIX_ANSIBLE="true") GIT_REPOSITORY_PATH="$(mktemp -d)"
COMMAND_TO_RUN+=(--env FIX_CLANG_FORMAT="true")
COMMAND_TO_RUN+=(--env FIX_CSHARP="true") initialize_git_repository_and_test_args "${GIT_REPOSITORY_PATH}" "test/data/github-event/github-event-push.json"
COMMAND_TO_RUN+=(--env FIX_CSS="true")
COMMAND_TO_RUN+=(--env FIX_ENV="true") local LINTERS_TEST_CASES_FIX_MODE_DESTINATION_PATH="${GIT_REPOSITORY_PATH}/${LINTERS_TEST_CASE_DIRECTORY}"
COMMAND_TO_RUN+=(--env FIX_GO_MODULES="true") mkdir -p "${LINTERS_TEST_CASES_FIX_MODE_DESTINATION_PATH}"
COMMAND_TO_RUN+=(--env FIX_GO="true")
COMMAND_TO_RUN+=(--env FIX_GOOGLE_JAVA_FORMAT="true") for LANGUAGE in "${LANGUAGES_WITH_FIX_MODE[@]}"; do
COMMAND_TO_RUN+=(--env FIX_GROOVY="true") if [[ "${SUPER_LINTER_CONTAINER_IMAGE_TYPE}" == "slim" ]] &&
COMMAND_TO_RUN+=(--env FIX_JAVASCRIPT_ES="true") ! IsLanguageInSlimImage "${LANGUAGE}"; then
COMMAND_TO_RUN+=(--env FIX_JAVASCRIPT_PRETTIER="true") debug "Skip ${LANGUAGE} because it's not available in the Super-linter ${SUPER_LINTER_CONTAINER_IMAGE_TYPE} image"
COMMAND_TO_RUN+=(--env FIX_JAVASCRIPT_STANDARD="true") continue
COMMAND_TO_RUN+=(--env FIX_JSON="true") fi
COMMAND_TO_RUN+=(--env FIX_JSONC="true") local -l LOWERCASE_LANGUAGE="${LANGUAGE}"
COMMAND_TO_RUN+=(--env FIX_JSX="true") cp -rv "${LINTERS_TEST_CASE_DIRECTORY}/${LOWERCASE_LANGUAGE}" "${LINTERS_TEST_CASES_FIX_MODE_DESTINATION_PATH}/"
COMMAND_TO_RUN+=(--env FIX_MARKDOWN="true") eval "COMMAND_TO_RUN+=(--env FIX_${LANGUAGE}=\"true\")"
COMMAND_TO_RUN+=(--env FIX_POWERSHELL="true") eval "COMMAND_TO_RUN+=(--env VALIDATE_${LANGUAGE}=\"true\")"
COMMAND_TO_RUN+=(--env FIX_PROTOBUF="true") done
COMMAND_TO_RUN+=(--env FIX_PYTHON_BLACK="true")
COMMAND_TO_RUN+=(--env FIX_PYTHON_ISORT="true") # Copy gitignore so we don't commit eventual leftovers from previous runs
COMMAND_TO_RUN+=(--env FIX_PYTHON_RUFF="true") cp -v ".gitignore" "${GIT_REPOSITORY_PATH}/"
COMMAND_TO_RUN+=(--env FIX_RUBY="true")
COMMAND_TO_RUN+=(--env FIX_RUST_2015="true") # Copy fix mode linter configuration files because default ones are not always
COMMAND_TO_RUN+=(--env FIX_RUST_2018="true") # suitable for fix mode
COMMAND_TO_RUN+=(--env FIX_RUST_2021="true") local FIX_MODE_LINTERS_CONFIG_DIR="${GIT_REPOSITORY_PATH}/.github/linters"
# Temporarily disable fix mode for rust clippy due to a dependency on another PR mkdir -p "${FIX_MODE_LINTERS_CONFIG_DIR}"
# COMMAND_TO_RUN+=(--env FIX_RUST_CLIPPY="true") cp -rv "test/linters-config/fix-mode/." "${FIX_MODE_LINTERS_CONFIG_DIR}/"
COMMAND_TO_RUN+=(--env FIX_SCALAFMT="true") cp -rv ".github/linters/tsconfig.json" "${FIX_MODE_LINTERS_CONFIG_DIR}/"
COMMAND_TO_RUN+=(--env FIX_SHELL_SHFMT="true") git -C "${GIT_REPOSITORY_PATH}" add .
COMMAND_TO_RUN+=(--env FIX_SNAKEMAKE_SNAKEFMT="true") git -C "${GIT_REPOSITORY_PATH}" commit --no-verify -m "feat: add fix mode test cases"
COMMAND_TO_RUN+=(--env FIX_SQLFLUFF="true") initialize_github_sha "${GIT_REPOSITORY_PATH}"
COMMAND_TO_RUN+=(--env FIX_TERRAFORM_FMT="true")
COMMAND_TO_RUN+=(--env FIX_TSX="true") CREATE_LOG_FILE="true"
COMMAND_TO_RUN+=(--env FIX_TYPESCRIPT_ES="true") SAVE_SUPER_LINTER_OUTPUT="true"
COMMAND_TO_RUN+=(--env FIX_TYPESCRIPT_PRETTIER="true") VERIFY_FIX_MODE="true"
COMMAND_TO_RUN+=(--env FIX_TYPESCRIPT_STANDARD="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 # Run the test setup function
@ -203,28 +221,30 @@ ${TEST_FUNCTION_NAME}
CREATE_LOG_FILE="${CREATE_LOG_FILE:-false}" CREATE_LOG_FILE="${CREATE_LOG_FILE:-false}"
SAVE_SUPER_LINTER_OUTPUT="${SAVE_SUPER_LINTER_OUTPUT:-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 if [ -n "${SUPER_LINTER_OUTPUT_DIRECTORY_NAME:-}" ]; then
COMMAND_TO_RUN+=(-e SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}") COMMAND_TO_RUN+=(-e SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}")
fi fi
SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME:-"super-linter-output"}" SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME:-"super-linter-output"}"
SUPER_LINTER_MAIN_OUTPUT_PATH="$(pwd)/${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}" SUPER_LINTER_MAIN_OUTPUT_PATH="${SUPER_LINTER_WORKSPACE}/${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}"
echo "Super-linter main output path: ${SUPER_LINTER_MAIN_OUTPUT_PATH}" debug "Super-linter main output path: ${SUPER_LINTER_MAIN_OUTPUT_PATH}"
SUPER_LINTER_OUTPUT_PATH="${SUPER_LINTER_MAIN_OUTPUT_PATH}/super-linter" 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 CREATE_LOG_FILE="${CREATE_LOG_FILE}")
COMMAND_TO_RUN+=(-e LOG_LEVEL="${LOG_LEVEL:-"DEBUG"}") COMMAND_TO_RUN+=(-e LOG_LEVEL="${LOG_LEVEL:-"DEBUG"}")
COMMAND_TO_RUN+=(-e RUN_LOCAL="${RUN_LOCAL:-true}") COMMAND_TO_RUN+=(-e RUN_LOCAL="${RUN_LOCAL:-true}")
COMMAND_TO_RUN+=(-e SAVE_SUPER_LINTER_OUTPUT="${SAVE_SUPER_LINTER_OUTPUT}") 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 # 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. # 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 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" ENABLE_GITHUB_ACTIONS_STEP_SUMMARY="true"
SAVE_SUPER_LINTER_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}") COMMAND_TO_RUN+=(-e SUPER_LINTER_SUMMARY_FILE_NAME="${SUPER_LINTER_SUMMARY_FILE_NAME}")
fi fi
SUPER_LINTER_SUMMARY_FILE_NAME="${SUPER_LINTER_SUMMARY_FILE_NAME:-"super-linter-summary.md"}" 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}" 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}") COMMAND_TO_RUN+=("${SUPER_LINTER_TEST_CONTAINER_URL}")
declare -i EXPECTED_EXIT_CODE declare -i EXPECTED_EXIT_CODE
EXPECTED_EXIT_CODE=${EXPECTED_EXIT_CODE:-0} 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+=("${LOG_FILE_PATH}")
LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_GITHUB_STEP_SUMMARY_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_MAIN_OUTPUT_PATH}")
LEFTOVERS_TO_CLEAN+=("${SUPER_LINTER_SUMMARY_FILE_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[@]}" sudo rm -rfv "${LEFTOVERS_TO_CLEAN[@]}"
if [[ "${ENABLE_GITHUB_ACTIONS_STEP_SUMMARY}" == "true" ]]; then 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}" touch "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}"
fi fi
if [ ${EXPECTED_EXIT_CODE} -ne 0 ]; then debug "Command to run: ${COMMAND_TO_RUN[*]}"
echo "Disable failures on error because the expected exit code is ${EXPECTED_EXIT_CODE}"
set +o errexit
fi
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[@]}" "${COMMAND_TO_RUN[@]}"
SUPER_LINTER_EXIT_CODE=$? SUPER_LINTER_EXIT_CODE=$?
# Enable the errexit option in case we disabled it # Enable the errexit option that we check later
set -o errexit 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 [[ "${CREATE_LOG_FILE}" == true ]]; then
if [ ! -e "${LOG_FILE_PATH}" ]; 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 exit 1
else else
sudo chown -R "$(id -u)":"$(id -g)" "${LOG_FILE_PATH}" sudo chown -R "$(id -u)":"$(id -g)" "${LOG_FILE_PATH}"
echo "Log file contents:" debug "Log file path: ${LOG_FILE_PATH}"
if [[ "${CI:-}" == "true" ]]; then
debug "Log file contents:"
cat "${LOG_FILE_PATH}" 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 fi
else 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 fi
if [[ "${SAVE_SUPER_LINTER_OUTPUT}" == true ]]; then if [[ "${SAVE_SUPER_LINTER_OUTPUT}" == true ]]; then
if [ ! -d "${SUPER_LINTER_OUTPUT_PATH}" ]; 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 exit 1
else else
sudo chown -R "$(id -u)":"$(id -g)" "${SUPER_LINTER_OUTPUT_PATH}" sudo chown -R "$(id -u)":"$(id -g)" "${SUPER_LINTER_OUTPUT_PATH}"
echo "Super-linter output path (${SUPER_LINTER_OUTPUT_PATH}) contents:" if [[ "${CI:-}" == "true" ]]; then
debug "Super-linter output path (${SUPER_LINTER_OUTPUT_PATH}) contents:"
ls -alhR "${SUPER_LINTER_OUTPUT_PATH}" 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 fi
else 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 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 exit 1
fi fi
fi fi
@ -312,37 +366,96 @@ fi
if [ -n "${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH:-}" ]; then 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 # 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*<!--' "${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH}"); then if ! diff "${SUPER_LINTER_SUMMARY_FILE_PATH}" <(grep -vE '^\s*<!--' "${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH}"); then
echo "Super-linter summary (${SUPER_LINTER_SUMMARY_FILE_PATH}) contents don't match with the expected contents (${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH})" debug "Super-linter summary (${SUPER_LINTER_SUMMARY_FILE_PATH}) contents don't match with the expected contents (${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH})"
exit 1 exit 1
else else
echo "Super-linter summary (${SUPER_LINTER_SUMMARY_FILE_PATH}) contents match with the expected contents (${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH})" debug "Super-linter summary (${SUPER_LINTER_SUMMARY_FILE_PATH}) contents match with the expected contents (${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH})"
fi fi
if ! diff "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" <(grep -vE '^\s*<!--' "${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH}"); then if ! diff "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" <(grep -vE '^\s*<!--' "${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH}"); then
echo "Super-linter GitHub step summary (${SUPER_LINTER_SUMMARY_FILE_PATH}) contents don't match with the expected contents (${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH})" debug "Super-linter GitHub step summary (${SUPER_LINTER_SUMMARY_FILE_PATH}) contents don't match with the expected contents (${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH})"
exit 1 exit 1
else else
echo "Super-linter GitHub step summary (${SUPER_LINTER_SUMMARY_FILE_PATH}) contents match with the expected contents (${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH})" debug "Super-linter GitHub step summary (${SUPER_LINTER_SUMMARY_FILE_PATH}) contents match with the expected contents (${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH})"
fi
if [[ "${SUPER_LINTER_WORKSPACE}" != "$(pwd)" ]]; then
debug "Copying Super-linter summary from the workspace (${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}) to the current working directory for easier inspection"
cp "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" "$(pwd)/"
fi
if [[ "${SUPER_LINTER_WORKSPACE}" != "$(pwd)" ]]; then
debug "Copying Super-linter GitHub step summary from the workspace (${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}) to the current working directory for easier inspection"
cp "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" "$(pwd)/"
fi fi
else else
echo "Super-linter summary output was not requested." debug "Super-linter summary output was not requested."
if [ -e "${SUPER_LINTER_SUMMARY_FILE_PATH}" ]; then if [ -e "${SUPER_LINTER_SUMMARY_FILE_PATH}" ]; then
echo "Super-linter summary was not requested but it's available at ${SUPER_LINTER_SUMMARY_FILE_PATH}" debug "Super-linter summary was not requested but it's available at ${SUPER_LINTER_SUMMARY_FILE_PATH}"
exit 1 exit 1
fi fi
if [ -e "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" ]; then if [ -e "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" ]; then
echo "Super-linter GitHub step summary was not requested but it's available at ${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" debug "Super-linter GitHub step summary was not requested but it's available at ${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}"
exit 1 exit 1
fi fi
fi fi
if [ ${SUPER_LINTER_EXIT_CODE} -ne ${EXPECTED_EXIT_CODE} ]; then if [ ${SUPER_LINTER_EXIT_CODE} -ne ${EXPECTED_EXIT_CODE} ]; then
echo "Super-linter exited with an unexpected code: ${SUPER_LINTER_EXIT_CODE}" debug "Super-linter exited with an unexpected code: ${SUPER_LINTER_EXIT_CODE}"
exit 1 exit 1
else else
echo "Super-linter exited with the expected code: ${SUPER_LINTER_EXIT_CODE}" debug "Super-linter exited with the expected code: ${SUPER_LINTER_EXIT_CODE}"
fi
VERIFY_FIX_MODE="${VERIFY_FIX_MODE:-"false"}"
if [[ "${VERIFY_FIX_MODE:-}" == "true" ]]; then
debug "Verifying fix mode"
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
declare -l LOWERCASE_LANGUAGE="${LANGUAGE}"
BAD_TEST_CASE_SOURCE_PATH="${LINTERS_TEST_CASE_DIRECTORY}/${LOWERCASE_LANGUAGE}"
debug "Source path to the ${LANGUAGE} test case expected to fail: ${BAD_TEST_CASE_SOURCE_PATH}"
BAD_TEST_CASE_DESTINATION_PATH="${SUPER_LINTER_WORKSPACE}/${LINTERS_TEST_CASE_DIRECTORY}/${LOWERCASE_LANGUAGE}"
debug "Destination path to ${LANGUAGE} test case expected to fail: ${BAD_TEST_CASE_DESTINATION_PATH}"
if [[ ! -e "${BAD_TEST_CASE_SOURCE_PATH}" ]]; then
fatal "${BAD_TEST_CASE_SOURCE_PATH} doesn't exist"
fi
if [[ ! -e "${BAD_TEST_CASE_DESTINATION_PATH}" ]]; then
fatal "${BAD_TEST_CASE_DESTINATION_PATH} doesn't exist"
fi
if find "${BAD_TEST_CASE_DESTINATION_PATH}" \( -type f ! -readable -or -type d \( ! -readable -or ! -executable -or ! -writable \) \) -print | grep -q .; then
if [[ "${LANGUAGE}" == "RUST_CLIPPY" ]] ||
[[ "${LANGUAGE}" == "SHELL_SHFMT" ]] ||
[[ "${LANGUAGE}" == "SQLFLUFF" ]]; then
debug "${LANGUAGE} is a known case of a tool that doesn't preserve the ownership of files or directories in fix mode. Need to recursively change ownership of ${BAD_TEST_CASE_DESTINATION_PATH}"
sudo chown -R "$(id -u)":"$(id -g)" "${BAD_TEST_CASE_DESTINATION_PATH}"
else
ls -alR "${BAD_TEST_CASE_DESTINATION_PATH}"
fatal "Cannot verify fix mode for ${LANGUAGE}: ${BAD_TEST_CASE_DESTINATION_PATH} is not readable, or contains unreadable files."
fi
else
debug "${BAD_TEST_CASE_DESTINATION_PATH} and its contents are readable"
fi
if [[ "${LANGUAGE}" == "RUST_CLIPPY" ]]; then
rm -rf \
"${BAD_TEST_CASE_DESTINATION_PATH}"/*/Cargo.lock \
"${BAD_TEST_CASE_DESTINATION_PATH}"/*/target
fi
if AssertFileContentsMatch "${BAD_TEST_CASE_DESTINATION_PATH}" "${BAD_TEST_CASE_SOURCE_PATH}"; then
fatal "${BAD_TEST_CASE_DESTINATION_PATH} contents match ${BAD_TEST_CASE_SOURCE_PATH} contents and they should differ because fix mode for ${LANGUAGE} should have fixed linting and formatting issues."
fi
done
fi fi
# Check if super-linter leaves leftovers behind # Check if super-linter leaves leftovers behind
@ -354,16 +467,16 @@ TEMP_ITEMS_TO_CLEAN+=("$(pwd)/.ruff_cache")
TEMP_ITEMS_TO_CLEAN+=("$(pwd)/logback.log") TEMP_ITEMS_TO_CLEAN+=("$(pwd)/logback.log")
for item in "${TEMP_ITEMS_TO_CLEAN[@]}"; do for item in "${TEMP_ITEMS_TO_CLEAN[@]}"; do
echo "Check if ${item} exists" debug "Check if ${item} exists"
if [[ -e "${item}" ]]; then if [[ -e "${item}" ]]; then
echo "Error: ${item} exists and it should have been deleted" debug "Error: ${item} exists and it should have been deleted"
exit 1 exit 1
else else
echo "${item} does not exist as expected" debug "${item} does not exist as expected"
fi fi
done done
if ! CheckUnexpectedGitChanges "$(pwd)"; then if ! CheckUnexpectedGitChanges "$(pwd)"; then
echo "There are unexpected modifications to the working directory after running tests." debug "There are unexpected modifications to the working directory after running tests."
exit 1 exit 1
fi fi

View file

@ -11,6 +11,64 @@ LOG_LEVEL="DEBUG"
# shellcheck source=/dev/null # shellcheck source=/dev/null
source "lib/functions/log.sh" source "lib/functions/log.sh"
# TODO: use TEST_CASE_FOLDER instead of redefining this after we extract the
# initialization of TEST_CASE_FOLDER from linter.sh
# shellcheck disable=SC2034
LINTERS_TEST_CASE_DIRECTORY="test/linters"
# shellcheck disable=SC2034
LANGUAGES_WITH_FIX_MODE=(
"ANSIBLE"
"CLANG_FORMAT"
"CSHARP"
"CSS"
"ENV"
"GO_MODULES"
"GO"
"GOOGLE_JAVA_FORMAT"
"GROOVY"
"JAVASCRIPT_ES"
"JAVASCRIPT_PRETTIER"
"JAVASCRIPT_STANDARD"
"JSON"
"JSONC"
"JSX"
"MARKDOWN"
"NATURAL_LANGUAGE"
"POWERSHELL"
"PROTOBUF"
"PYTHON_BLACK"
"PYTHON_ISORT"
"PYTHON_RUFF"
"RUBY"
"RUST_2015"
"RUST_2018"
"RUST_2021"
"RUST_CLIPPY"
"SCALAFMT"
"SHELL_SHFMT"
"SNAKEMAKE_SNAKEFMT"
"SQLFLUFF"
"TERRAFORM_FMT"
"TSX"
"TYPESCRIPT_ES"
"TYPESCRIPT_PRETTIER"
"TYPESCRIPT_STANDARD"
)
# TODO: extract this list from linter.sh (see REMOVE_ARRAY) instead of
# redefining it here
# shellcheck disable=SC2034
LANGUAGES_NOT_IN_SLIM_IMAGE=(
"ARM"
"CSHARP"
"POWERSHELL"
"RUST_2015"
"RUST_2018"
"RUST_2021"
"RUST_CLIPPY"
)
function AssertArraysElementsContentMatch() { function AssertArraysElementsContentMatch() {
local ARRAY_1_VARIABLE_NAME="${1}" local ARRAY_1_VARIABLE_NAME="${1}"
local ARRAY_2_VARIABLE_NAME="${2}" local ARRAY_2_VARIABLE_NAME="${2}"
@ -42,3 +100,26 @@ function CheckUnexpectedGitChanges() {
return 1 return 1
fi fi
} }
AssertFileContentsMatch() {
local FILE_1_PATH="${1}"
local FILE_2_PATH="${2}"
if diff -r "${FILE_1_PATH}" "${FILE_2_PATH}"; then
echo "${FILE_1_PATH} contents match with ${FILE_2_PATH} contents"
return 0
else
echo "${FILE_1_PATH} contents don't match with ${FILE_2_PATH} contents"
return 1
fi
}
IsLanguageInSlimImage() {
local LANGUAGE="${1}"
if [[ " ${LANGUAGES_NOT_IN_SLIM_IMAGE[*]} " =~ [[:space:]]${LANGUAGE}[[:space:]] ]]; then
debug "${LANGUAGE} is not available in the Super-linter slim image"
return 1
else
debug "${LANGUAGE} is available in the Super-linter slim image"
return 0
fi
}