diff --git a/.gitignore b/.gitignore index c924b195..f510b7bb 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,7 @@ test/reports .lintr test/linters/rust_clippy/**/Cargo.lock test/linters/rust_clippy/**/target/** + +# Super-linter ouputs +super-linter-output/ +custom-super-linter-output-directory-name/ diff --git a/Makefile b/Makefile index 1d5ed8b2..496db420 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all: info docker test ## Run all targets. .PHONY: test -test: info validate-container-image-labels test-lib inspec lint-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-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-linters ## Run the test suite +test: info validate-container-image-labels test-lib inspec lint-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-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-linters ## 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 @@ -393,6 +393,18 @@ test-use-find-and-ignore-gitignored-files: ## Run super-linter with USE_FIND_ALG $(SUPER_LINTER_TEST_CONTAINER_URL) \ "run_test_case_use_find_and_ignore_gitignored_files" +.PHONY: test-save-super-linter-output +test-save-super-linter-output: ## Run super-linter with SAVE_SUPER_LINTER_OUTPUT=true + $(CURDIR)/test/run-super-linter-tests.sh \ + $(SUPER_LINTER_TEST_CONTAINER_URL) \ + "run_test_cases_save_super_linter_output" + +.PHONY: test-save-super-linter-output-custom-path +test-save-super-linter-output-custom-path: ## Run super-linter with SAVE_SUPER_LINTER_OUTPUT=true and save output in a custom directory + $(CURDIR)/test/run-super-linter-tests.sh \ + $(SUPER_LINTER_TEST_CONTAINER_URL) \ + "run_test_cases_save_super_linter_output_custom_path" + .PHONY: build-dev-container-image build-dev-container-image: ## Build commit linter container image DOCKER_BUILDKIT=1 docker buildx build --load \ diff --git a/README.md b/README.md index 0910dac7..604c6cd8 100644 --- a/README.md +++ b/README.md @@ -240,6 +240,7 @@ You can configure super-linter using the following environment variables: | **PYTHON_RUFF_CONFIG_FILE** | `.ruff.toml` | Filename for [ruff configuration](https://docs.astral.sh/ruff/configuration/) | | **RENOVATE_SHAREABLE_CONFIG_PRESET_FILE_NAMES** | not set | Comma-separated filenames for [renovate shareable config preset](https://docs.renovatebot.com/config-presets/) (ex: `default.json`) | | **RUBY_CONFIG_FILE** | `.ruby-lint.yml` | Filename for [rubocop configuration](https://docs.rubocop.org/rubocop/configuration.html) (ex: `.ruby-lint.yml`, `.rubocop.yml`) | +| **SAVE_SUPER_LINTER_OUTPUT** | `false` | If set to `true`, super-linter will save its output to `${DEFAULT_WORKSPACE}/${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}` | | **SCALAFMT_CONFIG_FILE** | `.scalafmt.conf` | Filename for [scalafmt configuration](https://scalameta.org/scalafmt/docs/configuration.html) (ex: `.scalafmt.conf`) | | **SNAKEMAKE_SNAKEFMT_CONFIG_FILE** | `.snakefmt.toml` | Filename for [Snakemake configuration](https://github.com/snakemake/snakefmt#configuration) (ex: `pyproject.toml`, `.snakefmt.toml`) | | **SSL_CERT_SECRET** | `none` | SSL cert to add to the **Super-Linter** trust store. This is needed for users on `self-hosted` runners or need to inject the cert for security standards (ex. ${{ secrets.SSL_CERT }}) | @@ -248,6 +249,7 @@ You can configure super-linter using the following environment variables: | **SSH_INSECURE_NO_VERIFY_GITHUB_KEY** | `false` | **INSECURE -** If set to `true`, does not verify the fingerprint of the github.com SSH key before adding this. This is not recommended! | | **SQL_CONFIG_FILE** | `.sql-config.json` | Filename for [SQL-Lint configuration](https://sql-lint.readthedocs.io/en/latest/files/configuration.html) (ex: `sql-config.json` , `.config.json`) | | **SQLFLUFF_CONFIG_FILE** | `/.sqlfluff` | Filename for [SQLFLUFF configuration](https://docs.sqlfluff.com/en/stable/configuration.html) (ex: `/.sqlfluff`, `pyproject.toml`) | +| **SUPER_LINTER_OUTPUT_DIRECTORY_NAME** | `super-linter-output` | Name of the directory where super-linter saves its output. | | **SUPPRESS_FILE_TYPE_WARN** | `false` | If set to `true`, will hide warning messages about files without their proper extensions. Default is `false` | | **SUPPRESS_POSSUM** | `false` | If set to `true`, will hide the ASCII possum at top of log output. Default is `false` | | **TERRAFORM_TERRASCAN_CONFIG_FILE** | `terrascan.toml` | Filename for [terrascan configuration](https://github.com/accurics/terrascan) (ex: `terrascan.toml`) | @@ -451,6 +453,28 @@ path to the files that contains a CA that can be used to valide the certificate: SSL_CERT_SECRET: ${{ secrets.ROOT_CA }} ``` +## Super-linter outputs + +If you set `SAVE_SUPER_LINTER_OUTPUT` to `true`, Super-linter saves its output +to `${DEFAULT_WORKSPACE}/${DEFAULT_SUPER_LINTER_OUTPUT_DIRECTORY_NAME}`, so you +can further process it, if needed. + +Most outputs are in JSON format. + +The output of previous Super-linter runs is not preserved when running locally. + +## Linter reports and outputs + +Some linters support configuring the format of their outputs for further +processing. To get access to that output, enable it using the respective linter +configuration file. If a linter requires a path for the output directory, you +can use the value of the `${DEFAULT_WORKSPACE}` variable. + +If a linter doesn't support setting an arbitrary output path as described in the +previous paragraph, but only supports emitting results to standard output or +standard error streams, you can +[enable Super-linter outputs](#super-linter-outputs) and parse them. + ## How to contribute If you would like to help contribute to super-linter, see diff --git a/lib/functions/buildFileList.sh b/lib/functions/buildFileList.sh index 5da25ffe..c8434917 100755 --- a/lib/functions/buildFileList.sh +++ b/lib/functions/buildFileList.sh @@ -131,7 +131,7 @@ function BuildFileList() { fi local PARALLEL_RESULTS_FILE_PATH - PARALLEL_RESULTS_FILE_PATH="/tmp/super-linter-parallel-results-build-file-list.json" + PARALLEL_RESULTS_FILE_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-parallel-results-build-file-list.json" debug "PARALLEL_RESULTS_FILE_PATH when building the file list: ${PARALLEL_RESULTS_FILE_PATH}" local -a PARALLEL_COMMAND @@ -148,7 +148,7 @@ function BuildFileList() { PARALLEL_COMMAND+=("BuildFileArrays") debug "PARALLEL_COMMAND to build the list of files and directories to lint: ${PARALLEL_COMMAND[*]}" - FILE_ARRAYS_DIRECTORY_PATH="/tmp/super-linter-file-arrays" + FILE_ARRAYS_DIRECTORY_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-file-arrays" mkdir -p "${FILE_ARRAYS_DIRECTORY_PATH}" export FILE_ARRAYS_DIRECTORY_PATH debug "Created FILE_ARRAYS_DIRECTORY_PATH: ${FILE_ARRAYS_DIRECTORY_PATH}" diff --git a/lib/functions/validation.sh b/lib/functions/validation.sh index 1f68c52c..268b61d3 100755 --- a/lib/functions/validation.sh +++ b/lib/functions/validation.sh @@ -15,6 +15,7 @@ function ValidateBooleanConfigurationVariables() { ValidateBooleanVariable "LOG_WARN" "${LOG_WARN}" ValidateBooleanVariable "MULTI_STATUS" "${MULTI_STATUS}" ValidateBooleanVariable "RUN_LOCAL" "${RUN_LOCAL}" + ValidateBooleanVariable "SAVE_SUPER_LINTER_OUTPUT" "${SAVE_SUPER_LINTER_OUTPUT}" ValidateBooleanVariable "SSH_INSECURE_NO_VERIFY_GITHUB_KEY" "${SSH_INSECURE_NO_VERIFY_GITHUB_KEY}" ValidateBooleanVariable "SSH_SETUP_GITHUB" "${SSH_SETUP_GITHUB}" ValidateBooleanVariable "SUPPRESS_FILE_TYPE_WARN" "${SUPPRESS_FILE_TYPE_WARN}" diff --git a/lib/functions/worker.sh b/lib/functions/worker.sh index 76156884..4c70b684 100755 --- a/lib/functions/worker.sh +++ b/lib/functions/worker.sh @@ -78,7 +78,7 @@ function LintCodebase() { info "Linting ${FILE_TYPE} items..." local PARALLEL_RESULTS_FILE_PATH - PARALLEL_RESULTS_FILE_PATH="/tmp/super-linter-worker-results-${FILE_TYPE}.json" + PARALLEL_RESULTS_FILE_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-worker-results-${FILE_TYPE}.json" debug "PARALLEL_RESULTS_FILE_PATH for ${FILE_TYPE}: ${PARALLEL_RESULTS_FILE_PATH}" local -a PARALLEL_COMMAND @@ -163,7 +163,7 @@ function LintCodebase() { debug "PARALLEL_COMMAND_OUTPUT for ${FILE_TYPE} (exit code: ${PARALLEL_COMMAND_RETURN_CODE}): ${PARALLEL_COMMAND_OUTPUT}" debug "Parallel output file (${PARALLEL_RESULTS_FILE_PATH}) contents for ${FILE_TYPE}:\n$(cat "${PARALLEL_RESULTS_FILE_PATH}")" - echo ${PARALLEL_COMMAND_RETURN_CODE} >"/tmp/super-linter-parallel-command-exit-code-${FILE_TYPE}" + echo ${PARALLEL_COMMAND_RETURN_CODE} >"${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-parallel-command-exit-code-${FILE_TYPE}" if [ ${PARALLEL_COMMAND_RETURN_CODE} -ne 0 ]; then error "Found errors when linting ${FILE_TYPE}. Exit code: ${PARALLEL_COMMAND_RETURN_CODE}." @@ -199,7 +199,7 @@ function LintCodebase() { if [ ${PARALLEL_COMMAND_RETURN_CODE} -ne 0 ]; then local STDOUT_LINTER_FILE_PATH - STDOUT_LINTER_FILE_PATH="/tmp/super-linter-parallel-stdout-${FILE_TYPE}" + STDOUT_LINTER_FILE_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-parallel-stdout-${FILE_TYPE}" debug "Saving stdout for ${FILE_TYPE} to ${STDOUT_LINTER_FILE_PATH} in case we need it later" printf '%s\n' "${STDOUT_LINTER_LOG_MESSAGE}" >"${STDOUT_LINTER_FILE_PATH}" fi @@ -218,7 +218,7 @@ function LintCodebase() { info "${STDERR_LINTER_LOG_MESSAGE}" if [ ${PARALLEL_COMMAND_RETURN_CODE} -ne 0 ]; then local STDERR_LINTER_FILE_PATH - STDERR_LINTER_FILE_PATH="/tmp/super-linter-parallel-stderr-${FILE_TYPE}" + STDERR_LINTER_FILE_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-parallel-stderr-${FILE_TYPE}" debug "Saving stderr for ${FILE_TYPE} to ${STDERR_LINTER_FILE_PATH} in case we need it later" printf '%s\n' "${STDERR_LINTER_LOG_MESSAGE}" >"${STDERR_LINTER_FILE_PATH}" fi diff --git a/lib/linter.sh b/lib/linter.sh index 56fdab46..900f5df5 100755 --- a/lib/linter.sh +++ b/lib/linter.sh @@ -79,6 +79,10 @@ export IGNORE_GITIGNORED_FILES declare -l MULTI_STATUS MULTI_STATUS="${MULTI_STATUS:-true}" +# We want a lowercase value +declare -l SAVE_SUPER_LINTER_OUTPUT +SAVE_SUPER_LINTER_OUTPUT="${SAVE_SUPER_LINTER_OUTPUT:-false}" + # We want a lowercase value declare -l SSH_INSECURE_NO_VERIFY_GITHUB_KEY SSH_INSECURE_NO_VERIFY_GITHUB_KEY="${SSH_INSECURE_NO_VERIFY_GITHUB_KEY:-false}" @@ -594,7 +598,7 @@ Footer() { # but a number that's less than that because of how GNU parallel returns # exit codes. # Ref: https://www.gnu.org/software/parallel/parallel.html#exit-status - ERROR_COUNTER_FILE_PATH="/tmp/super-linter-parallel-command-exit-code-${LANGUAGE}" + ERROR_COUNTER_FILE_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-parallel-command-exit-code-${LANGUAGE}" if [ ! -f "${ERROR_COUNTER_FILE_PATH}" ]; then debug "Error counter ${ERROR_COUNTER_FILE_PATH} doesn't exist" else @@ -607,7 +611,7 @@ Footer() { # get feedback if [[ "${LOG_VERBOSE}" != "true" ]]; then local STDOUT_LINTER_FILE_PATH - STDOUT_LINTER_FILE_PATH="/tmp/super-linter-parallel-stdout-${LANGUAGE}" + STDOUT_LINTER_FILE_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-parallel-stdout-${LANGUAGE}" if [[ -e "${STDOUT_LINTER_FILE_PATH}" ]]; then error "$(cat "${STDOUT_LINTER_FILE_PATH}")" else @@ -615,7 +619,7 @@ Footer() { fi local STDERR_LINTER_FILE_PATH - STDERR_LINTER_FILE_PATH="/tmp/super-linter-parallel-stderr-${LANGUAGE}" + STDERR_LINTER_FILE_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-parallel-stderr-${LANGUAGE}" if [[ -e "${STDERR_LINTER_FILE_PATH}" ]]; then error "$(cat "${STDERR_LINTER_FILE_PATH}")" else @@ -702,8 +706,20 @@ cleanup() { --force \ "${LOG_TEMP}" "${LOG_FILE_PATH}" else - debug "Skipping the moving of the log file from ${LOG_TEMP} to ${LOG_FILE_PATH}" + debug "Skip moving the log file from ${LOG_TEMP} to ${LOG_FILE_PATH}" fi + + if [ "${SAVE_SUPER_LINTER_OUTPUT}" = "true" ]; then + if [ -e "${SUPER_LINTER_OUTPUT_DIRECTORY_PATH}" ]; then + debug "${SUPER_LINTER_OUTPUT_DIRECTORY_PATH} already exists. Deleting it before moving the new output directory there." + rm -fr "${SUPER_LINTER_OUTPUT_DIRECTORY_PATH}" + fi + debug "Moving Super-linter output from ${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH} to ${SUPER_LINTER_OUTPUT_DIRECTORY_PATH}" + mv "${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}" "${SUPER_LINTER_OUTPUT_DIRECTORY_PATH}" + else + debug "Skip moving the output directory from ${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH} to ${SUPER_LINTER_OUTPUT_DIRECTORY_PATH}" + fi + else debug "GITHUB_WORKSPACE is not set. Skipping filesystem cleanup steps" fi @@ -752,6 +768,20 @@ debug "TYPESCRIPT_STANDARD_TSCONFIG_FILE: ${TYPESCRIPT_STANDARD_TSCONFIG_FILE}" R_RULES_FILE_PATH_IN_ROOT="${GITHUB_WORKSPACE}/${R_FILE_NAME}" debug "R_RULES_FILE_PATH_IN_ROOT: ${R_RULES_FILE_PATH_IN_ROOT}" +DEFAULT_SUPER_LINTER_OUTPUT_DIRECTORY_NAME="super-linter-output" +SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME:-${DEFAULT_SUPER_LINTER_OUTPUT_DIRECTORY_NAME}}" +export SUPER_LINTER_OUTPUT_DIRECTORY_NAME +debug "Super-linter output directory name: ${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}" + +SUPER_LINTER_OUTPUT_DIRECTORY_PATH="${GITHUB_WORKSPACE}/${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}" +export SUPER_LINTER_OUTPUT_DIRECTORY_PATH +debug "Super-linter output directory path: ${SUPER_LINTER_OUTPUT_DIRECTORY_PATH}" + +SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH="/tmp/${DEFAULT_SUPER_LINTER_OUTPUT_DIRECTORY_NAME}" +export SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH +debug "Super-linter private output directory path: ${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}" +mkdir -p "${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}" + ############################ # Validate the environment # ############################ @@ -818,7 +848,7 @@ endGitHubActionsLogGroup "${SUPER_LINTER_INITIALIZATION_LOG_GROUP_TITLE}" # Run linters # ############### declare PARALLEL_RESULTS_FILE_PATH -PARALLEL_RESULTS_FILE_PATH="/tmp/super-linter-results.json" +PARALLEL_RESULTS_FILE_PATH="${SUPER_LINTER_PRIVATE_OUTPUT_DIRECTORY_PATH}/super-linter-results.json" debug "PARALLEL_RESULTS_FILE_PATH: ${PARALLEL_RESULTS_FILE_PATH}" declare -a PARALLEL_COMMAND diff --git a/test/lib/buildFileListTest.sh b/test/lib/buildFileListTest.sh index 5104d1d3..4fe19f20 100755 --- a/test/lib/buildFileListTest.sh +++ b/test/lib/buildFileListTest.sh @@ -137,7 +137,7 @@ function BuildFileArraysAnsibleGitHubWorkspaceTest() { local TEST_CASE_RUN=false # shellcheck disable=SC2034 local IGNORE_GENERATED_FILES=false - local FILE_ARRAYS_DIRECTORY_PATH="/tmp/super-linter-file-arrays" + local FILE_ARRAYS_DIRECTORY_PATH="/tmp/super-linter-output/super-linter-file-arrays" mkdir -p "${FILE_ARRAYS_DIRECTORY_PATH}" # shellcheck disable=SC2034 diff --git a/test/run-super-linter-tests.sh b/test/run-super-linter-tests.sh index b8cba3f5..541d46d1 100755 --- a/test/run-super-linter-tests.sh +++ b/test/run-super-linter-tests.sh @@ -89,14 +89,30 @@ run_test_case_use_find_and_ignore_gitignored_files() { COMMAND_TO_RUN+=(-e USE_FIND_ALGORITHM=true) } +run_test_cases_save_super_linter_output() { + run_test_cases_expect_success + SAVE_SUPER_LINTER_OUTPUT="true" +} + +run_test_cases_save_super_linter_output_custom_path() { + run_test_cases_save_super_linter_output + SUPER_LINTER_OUTPUT_DIRECTORY_NAME="custom-super-linter-output-directory-name" +} + # Run the test setup function ${TEST_FUNCTION_NAME} CREATE_LOG_FILE="${CREATE_LOG_FILE:-false}" +SAVE_SUPER_LINTER_OUTPUT="${SAVE_SUPER_LINTER_OUTPUT:-false}" + +if [ -n "${SUPER_LINTER_OUTPUT_DIRECTORY_NAME:-}" ]; then + COMMAND_TO_RUN+=(-e SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}") +fi 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") COMMAND_TO_RUN+=("${SUPER_LINTER_TEST_CONTAINER_URL}") @@ -120,7 +136,7 @@ echo "Super-linter exit code: ${SUPER_LINTER_EXIT_CODE}" if [[ "${CREATE_LOG_FILE}" == true ]]; then LOG_FILE_PATH="$(pwd)/super-linter.log" if [ ! -e "${LOG_FILE_PATH}" ]; then - echo "Log file was requested but it's not available" + echo "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}" @@ -131,6 +147,23 @@ else echo "Log file was not requested. CREATE_LOG_FILE: ${CREATE_LOG_FILE}" fi +if [[ "${SAVE_SUPER_LINTER_OUTPUT}" == true ]]; then + + SUPER_LINTER_OUTPUT_DIRECTORY_NAME="${SUPER_LINTER_OUTPUT_DIRECTORY_NAME:-"super-linter-output"}" + + SUPER_LINTER_OUTPUT_PATH="$(pwd)/${SUPER_LINTER_OUTPUT_DIRECTORY_NAME}" + if [ ! -d "${SUPER_LINTER_OUTPUT_PATH}" ]; then + echo "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}" + fi +else + echo "Super-linter output was not requested. SAVE_SUPER_LINTER_OUTPUT: ${SAVE_SUPER_LINTER_OUTPUT}" +fi + if [ ${SUPER_LINTER_EXIT_CODE} -ne ${EXPECTED_EXIT_CODE} ]; then echo "Super-linter exited with an unexpected code: ${SUPER_LINTER_EXIT_CODE}" exit 1