#!/usr/bin/env bash set -o errexit set -o nounset set -o pipefail # shellcheck source=/dev/null source "test/testUtils.sh" SUPER_LINTER_TEST_CONTAINER_URL="${1}" TEST_FUNCTION_NAME="${2}" SUPER_LINTER_CONTAINER_IMAGE_TYPE="${3}" debug "Super-linter container image type: ${SUPER_LINTER_CONTAINER_IMAGE_TYPE}" DEFAULT_BRANCH="main" COMMAND_TO_RUN=(docker run --rm -t -e DEFAULT_BRANCH="${DEFAULT_BRANCH}" -e ENABLE_GITHUB_ACTIONS_GROUP_TITLE="true") 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") configure_typescript_for_test_cases } run_test_cases_expect_failure() { configure_linters_for_test_cases COMMAND_TO_RUN+=(-e ANSIBLE_DIRECTORY="/test/linters/ansible/bad" -e CHECKOV_FILE_NAME=".checkov-test-linters-failure.yaml" -e FILTER_REGEX_INCLUDE=".*bad.*") EXPECTED_EXIT_CODE=1 EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH="test/data/super-linter-summary/markdown/table/expected-summary-test-linters-expect-failure-${SUPER_LINTER_CONTAINER_IMAGE_TYPE}.md" } run_test_cases_expect_success() { configure_linters_for_test_cases COMMAND_TO_RUN+=(-e ANSIBLE_DIRECTORY="/test/linters/ansible/good" -e CHECKOV_FILE_NAME=".checkov-test-linters-success.yaml" -e FILTER_REGEX_INCLUDE=".*good.*") EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH="test/data/super-linter-summary/markdown/table/expected-summary-test-linters-expect-success-${SUPER_LINTER_CONTAINER_IMAGE_TYPE}.md" } run_test_cases_log_level() { run_test_cases_expect_success LOG_LEVEL="NOTICE" } run_test_cases_expect_failure_notice_log() { run_test_cases_expect_failure LOG_LEVEL="NOTICE" } run_test_cases_non_default_home() { run_test_cases_expect_success COMMAND_TO_RUN+=(-e HOME=/tmp) } run_test_case_bash_exec_library_expect_failure() { run_test_cases_expect_failure COMMAND_TO_RUN+=(-e BASH_EXEC_IGNORE_LIBRARIES="true") } run_test_case_bash_exec_library_expect_success() { run_test_cases_expect_success COMMAND_TO_RUN+=(-e BASH_EXEC_IGNORE_LIBRARIES="true") } run_test_case_dont_save_super_linter_log_file() { run_test_cases_expect_success CREATE_LOG_FILE="false" } run_test_case_dont_save_super_linter_output() { run_test_cases_expect_success SAVE_SUPER_LINTER_OUTPUT="false" } 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 "sudo rm -fr '${GIT_REPOSITORY_PATH}'" EXIT debug "GIT_REPOSITORY_PATH: ${GIT_REPOSITORY_PATH}" local GITHUB_EVENT_FILE_PATH="${2}" git -C "${GIT_REPOSITORY_PATH}" init --initial-branch="${DEFAULT_BRANCH}" git -C "${GIT_REPOSITORY_PATH}" config user.name "Super-linter Test" git -C "${GIT_REPOSITORY_PATH}" config user.email "super-linter-test@example.com" # Put an arbitrary JSON file in the repository to trigger some validation cp -v "${GITHUB_EVENT_FILE_PATH}" "${GIT_REPOSITORY_PATH}/" git -C "${GIT_REPOSITORY_PATH}" add . git -C "${GIT_REPOSITORY_PATH}" commit -m "feat: initial commit" RUN_LOCAL=false SUPER_LINTER_WORKSPACE="${GIT_REPOSITORY_PATH}" COMMAND_TO_RUN+=(-e GITHUB_WORKSPACE="/tmp/lint") COMMAND_TO_RUN+=(-e GITHUB_EVENT_NAME="push") 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) } 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() { local GIT_REPOSITORY_PATH GIT_REPOSITORY_PATH="$(mktemp -d)" initialize_git_repository_and_test_args "${GIT_REPOSITORY_PATH}" "test/data/github-event/github-event-push.json" initialize_github_sha "${GIT_REPOSITORY_PATH}" COMMAND_TO_RUN+=(-e VALIDATE_JSON="true") } run_test_case_merge_commit_push() { local GIT_REPOSITORY_PATH GIT_REPOSITORY_PATH="$(mktemp -d)" initialize_git_repository_and_test_args "${GIT_REPOSITORY_PATH}" "test/data/github-event/github-event-push-merge-commit.json" local NEW_BRANCH_NAME="branch-1" git -C "${GIT_REPOSITORY_PATH}" switch --create "${NEW_BRANCH_NAME}" cp -v "test/data/github-event/github-event-push-merge-commit.json" "${GIT_REPOSITORY_PATH}/new-file-1.json" git -C "${GIT_REPOSITORY_PATH}" add . git -C "${GIT_REPOSITORY_PATH}" commit -m "feat: add new file 1" cp -v "test/data/github-event/github-event-push-merge-commit.json" "${GIT_REPOSITORY_PATH}/new-file-2.json" git -C "${GIT_REPOSITORY_PATH}" add . git -C "${GIT_REPOSITORY_PATH}" commit -m "feat: add new file 2" cp -v "test/data/github-event/github-event-push-merge-commit.json" "${GIT_REPOSITORY_PATH}/new-file-3.json" git -C "${GIT_REPOSITORY_PATH}" add . git -C "${GIT_REPOSITORY_PATH}" commit -m "feat: add new file 3" git -C "${GIT_REPOSITORY_PATH}" switch "${DEFAULT_BRANCH}" # Force the creation of a merge commit git -C "${GIT_REPOSITORY_PATH}" merge \ -m "Merge commit" \ --no-ff \ "${NEW_BRANCH_NAME}" git -C "${GIT_REPOSITORY_PATH}" branch -d "${NEW_BRANCH_NAME}" git -C "${GIT_REPOSITORY_PATH}" log --all --graph --abbrev-commit --decorate --format=oneline initialize_github_sha "${GIT_REPOSITORY_PATH}" COMMAND_TO_RUN+=(-e VALIDATE_JSON="true") } run_test_case_use_find_and_ignore_gitignored_files() { ignore_test_cases COMMAND_TO_RUN+=(-e IGNORE_GITIGNORED_FILES="true") COMMAND_TO_RUN+=(-e USE_FIND_ALGORITHM="true") } run_test_cases_save_super_linter_output() { run_test_cases_expect_success } 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_test_case_custom_summary() { run_test_cases_expect_success SUPER_LINTER_SUMMARY_FILE_NAME="custom-github-step-summary.md" } run_test_case_gitleaks_custom_log_level() { run_test_cases_expect_success COMMAND_TO_RUN+=(--env GITLEAKS_LOG_LEVEL="warn") } run_test_case_fix_mode() { VERIFY_FIX_MODE="true" GIT_REPOSITORY_PATH="$(mktemp -d)" initialize_git_repository_and_test_args "${GIT_REPOSITORY_PATH}" "test/data/github-event/github-event-push.json" # Remove leftovers before copying test files because other tests might have # created temporary files and caches as the root user, so commands that # need access to those files might fail if they run as a non-root user. RemoveTestLeftovers 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}" # 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 EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH="test/data/super-linter-summary/markdown/table/expected-summary-test-linters-fix-mode-${SUPER_LINTER_CONTAINER_IMAGE_TYPE}.md" } # Run the test setup function ${TEST_FUNCTION_NAME} CREATE_LOG_FILE="${CREATE_LOG_FILE:-"true"}" debug "CREATE_LOG_FILE: ${CREATE_LOG_FILE}" SAVE_SUPER_LINTER_OUTPUT="${SAVE_SUPER_LINTER_OUTPUT:-true}" 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="${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" 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}") 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. debug "SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH: ${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" if [ -n "${EXPECTED_SUPER_LINTER_SUMMARY_FILE_PATH:-}" ]; then 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" COMMAND_TO_RUN+=(-e GITHUB_STEP_SUMMARY="${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}") COMMAND_TO_RUN+=(-v "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}":"${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}") fi ENABLE_GITHUB_ACTIONS_STEP_SUMMARY="${ENABLE_GITHUB_ACTIONS_STEP_SUMMARY:-"false"}" COMMAND_TO_RUN+=(-e ENABLE_GITHUB_ACTIONS_STEP_SUMMARY="${ENABLE_GITHUB_ACTIONS_STEP_SUMMARY}") COMMAND_TO_RUN+=(-e SAVE_SUPER_LINTER_SUMMARY="${SAVE_SUPER_LINTER_SUMMARY:-"false"}") 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"}" 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}" debug "Super-linter summary output path: ${SUPER_LINTER_SUMMARY_FILE_PATH}" LOG_FILE_PATH="${SUPER_LINTER_WORKSPACE}/super-linter.log" debug "Super-linter log file path: ${LOG_FILE_PATH}" COMMAND_TO_RUN+=("${SUPER_LINTER_TEST_CONTAINER_URL}") declare -i EXPECTED_EXIT_CODE EXPECTED_EXIT_CODE=${EXPECTED_EXIT_CODE:-0} RemoveTestLeftovers if [[ "${ENABLE_GITHUB_ACTIONS_STEP_SUMMARY}" == "true" ]]; then debug "Creating GitHub Actions step summary file: ${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" touch "${SUPER_LINTER_GITHUB_STEP_SUMMARY_FILE_PATH}" fi debug "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 that we check later set -o errexit 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 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}" 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 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 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}" 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 debug "Super-linter output was not requested. SAVE_SUPER_LINTER_OUTPUT: ${SAVE_SUPER_LINTER_OUTPUT}" if [ -e "${SUPER_LINTER_OUTPUT_PATH}" ]; then debug "Super-linter output was not requested but it's available at ${SUPER_LINTER_OUTPUT_PATH}" exit 1 fi 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*