superlint/lib/functions/validation.sh
Marco Ferrari 91dc6d7234
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.
2024-08-12 12:31:38 +02:00

403 lines
17 KiB
Bash
Executable file

#!/usr/bin/env bash
function ValidateBooleanConfigurationVariables() {
ValidateBooleanVariable "ACTIONS_RUNNER_DEBUG" "${ACTIONS_RUNNER_DEBUG}"
ValidateBooleanVariable "BASH_EXEC_IGNORE_LIBRARIES" "${BASH_EXEC_IGNORE_LIBRARIES}"
ValidateBooleanVariable "CREATE_LOG_FILE" "${CREATE_LOG_FILE}"
ValidateBooleanVariable "DISABLE_ERRORS" "${DISABLE_ERRORS}"
ValidateBooleanVariable "ENABLE_GITHUB_ACTIONS_GROUP_TITLE" "${ENABLE_GITHUB_ACTIONS_GROUP_TITLE}"
ValidateBooleanVariable "ENABLE_GITHUB_ACTIONS_STEP_SUMMARY" "${ENABLE_GITHUB_ACTIONS_STEP_SUMMARY}"
ValidateBooleanVariable "FIX_MODE_TEST_CASE_RUN" "${FIX_MODE_TEST_CASE_RUN}"
ValidateBooleanVariable "IGNORE_GENERATED_FILES" "${IGNORE_GENERATED_FILES}"
ValidateBooleanVariable "IGNORE_GITIGNORED_FILES" "${IGNORE_GITIGNORED_FILES}"
ValidateBooleanVariable "LOG_DEBUG" "${LOG_DEBUG}"
ValidateBooleanVariable "LOG_ERROR" "${LOG_ERROR}"
ValidateBooleanVariable "LOG_NOTICE" "${LOG_NOTICE}"
ValidateBooleanVariable "LOG_VERBOSE" "${LOG_VERBOSE}"
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 "SAVE_SUPER_LINTER_SUMMARY" "${SAVE_SUPER_LINTER_SUMMARY}"
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}"
ValidateBooleanVariable "SUPPRESS_POSSUM" "${SUPPRESS_POSSUM}"
ValidateBooleanVariable "TEST_CASE_RUN" "${TEST_CASE_RUN}"
ValidateBooleanVariable "USE_FIND_ALGORITHM" "${USE_FIND_ALGORITHM}"
ValidateBooleanVariable "VALIDATE_ALL_CODEBASE" "${VALIDATE_ALL_CODEBASE}"
ValidateBooleanVariable "YAML_ERROR_ON_WARNING" "${YAML_ERROR_ON_WARNING}"
}
function ValidateGitHubWorkspace() {
local GITHUB_WORKSPACE
GITHUB_WORKSPACE="${1}"
if [ -z "${GITHUB_WORKSPACE}" ]; then
fatal "Failed to get GITHUB_WORKSPACE: ${GITHUB_WORKSPACE}"
fi
if [ ! -d "${GITHUB_WORKSPACE}" ]; then
fatal "The workspace (${GITHUB_WORKSPACE}) is not a directory!"
fi
info "Successfully validated GITHUB_WORKSPACE: ${GITHUB_WORKSPACE}"
}
function ValidateFindMode() {
debug "Validating find mode. USE_FIND_ALGORITHM: ${USE_FIND_ALGORITHM}, VALIDATE_ALL_CODEBASE: ${VALIDATE_ALL_CODEBASE}"
if [[ "${USE_FIND_ALGORITHM}" == "true" ]] && [[ "${VALIDATE_ALL_CODEBASE}" == "false" ]]; then
error "Setting USE_FIND_ALGORITHM to true and VALIDATE_ALL_CODEBASE to false is not supported because super-linter relies on Git to validate changed files."
return 1
fi
}
function ValidateAnsibleDirectory() {
if [ -z "${ANSIBLE_DIRECTORY:-}" ]; then
ANSIBLE_DIRECTORY="${GITHUB_WORKSPACE}/ansible"
debug "Set ANSIBLE_DIRECTORY to the default: ${ANSIBLE_DIRECTORY}"
else
debug "ANSIBLE_DIRECTORY before considering corner cases: ${ANSIBLE_DIRECTORY}"
# Check if first char is '/'
if [[ ${ANSIBLE_DIRECTORY:0:1} == "/" ]]; then
# Remove first char
ANSIBLE_DIRECTORY="${ANSIBLE_DIRECTORY:1}"
fi
if [ -z "${ANSIBLE_DIRECTORY}" ] || [[ ${ANSIBLE_DIRECTORY} == "." ]]; then
# Catches the case where ANSIBLE_DIRECTORY="/" or ANSIBLE_DIRECTORY="."
TEMP_ANSIBLE_DIRECTORY="${GITHUB_WORKSPACE}"
else
# Need to give it full path
TEMP_ANSIBLE_DIRECTORY="${GITHUB_WORKSPACE}/${ANSIBLE_DIRECTORY}"
fi
# Set the value
ANSIBLE_DIRECTORY="${TEMP_ANSIBLE_DIRECTORY}"
debug "Setting Ansible directory to: ${ANSIBLE_DIRECTORY}"
fi
export ANSIBLE_DIRECTORY
}
function ValidateValidationVariables() {
################################################
# Determine if any linters were explicitly set #
################################################
local ANY_SET="false"
local ANY_TRUE="false"
local ANY_FALSE="false"
for LANGUAGE in "${LANGUAGE_ARRAY[@]}"; do
debug "Check if configuration provided a custom value to enable or disable ${LANGUAGE}"
local VALIDATE_LANGUAGE
VALIDATE_LANGUAGE="VALIDATE_${LANGUAGE}"
if [ -n "${!VALIDATE_LANGUAGE:-}" ]; then
debug "Configuration provided a custom value for ${VALIDATE_LANGUAGE}: ${!VALIDATE_LANGUAGE}"
# Validate if user provided a string representing a valid boolean
ValidateBooleanVariable "${VALIDATE_LANGUAGE}" "${!VALIDATE_LANGUAGE}"
ANY_SET="true"
if [ "${!VALIDATE_LANGUAGE}" == "true" ]; then
ANY_TRUE="true"
# We already checked that VALIDATE_LANGUAGE is either true or false
else
ANY_FALSE="true"
fi
else
debug "Configuration didn't provide a custom value for ${VALIDATE_LANGUAGE}"
fi
done
debug "ANY_SET: ${ANY_SET}, ANY_TRUE: ${ANY_TRUE}, ANY_FALSE: ${ANY_FALSE}"
if [ $ANY_TRUE == "true" ] && [ $ANY_FALSE == "true" ]; then
error "Behavior not supported, please either only include (VALIDATE=true) or exclude (VALIDATE=false) linters, but not both"
return 1
fi
#########################################################
# Validate if we should check/omit individual languages #
#########################################################
for LANGUAGE in "${LANGUAGE_ARRAY[@]}"; do
local VALIDATE_LANGUAGE
VALIDATE_LANGUAGE="VALIDATE_${LANGUAGE}"
if [[ ${ANY_SET} == "true" ]]; then
debug "Configuration contains at least one custom value to enable or disable linters."
if [ -z "${!VALIDATE_LANGUAGE:-}" ]; then
# Flag was not set, default to:
# - true if the configuration provided any false value -> enable linters that the user didn't explicitly disable
# - false if the configuration didn't provid any false value -> disable linters that the user didn't explicitly enable
eval "${VALIDATE_LANGUAGE}='$ANY_FALSE'"
fi
else
# The user didn't provide and configuration value -> enable all linters by default
eval "${VALIDATE_LANGUAGE}='true'"
debug "Configuration doesn't include any custom values to enable or disable linters. Setting VALIDATE variable for ${LANGUAGE} to: ${!VALIDATE_LANGUAGE}"
fi
if [[ "${!VALIDATE_LANGUAGE}" == "true" ]]; then
debug "- Validating [${LANGUAGE}] files in code base..."
else
debug "- Excluding [$LANGUAGE] files in code base..."
fi
eval "export ${VALIDATE_LANGUAGE}"
done
}
function ValidateCheckModeAndFixModeVariables() {
for LANGUAGE in "${LANGUAGE_ARRAY[@]}"; do
local FIX_MODE_OPTIONS_VARIABLE_NAME="${LANGUAGE}_FIX_MODE_OPTIONS"
local CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME="${LANGUAGE}_CHECK_ONLY_MODE_OPTIONS"
local FIX_MODE_VARIABLE_NAME="FIX_${LANGUAGE}"
debug "Check if ${LANGUAGE} supports fix mode by checking if ${FIX_MODE_OPTIONS_VARIABLE_NAME}, ${CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME}, or both variables are set."
if [[ -v "${FIX_MODE_OPTIONS_VARIABLE_NAME}" ]] ||
[[ -v "${CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME}" ]]; then
debug "Assuming that ${LANGUAGE} supports fix mode because ${FIX_MODE_OPTIONS_VARIABLE_NAME}, ${CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME}, or both variables are set."
local -n FIX_MODE_REF="${FIX_MODE_VARIABLE_NAME}"
if [[ -n "${FIX_MODE_REF:-}" ]]; then
debug "The configuration provided a value for ${FIX_MODE_VARIABLE_NAME}: ${FIX_MODE_REF}"
else
FIX_MODE_REF="false"
debug "The configuration didn't provide a value for ${FIX_MODE_VARIABLE_NAME} for ${LANGUAGE}. Setting it to: ${FIX_MODE_REF}"
fi
# TODO: After refactoring ValidateBooleanVariable to return an error instead
# of exiting the whole program, add a test case for when ValidateBooleanVariable fails
ValidateBooleanVariable "${!FIX_MODE_REF}" "${FIX_MODE_REF}"
local -n VALIDATE_MODE_REF="VALIDATE_${LANGUAGE}"
if [[ "${FIX_MODE_REF}" == "true" ]] && [[ "${VALIDATE_MODE_REF}" == "false" ]]; then
error "Cannot set ${!FIX_MODE_REF} to ${FIX_MODE_REF} when ${!VALIDATE_MODE_REF} is ${VALIDATE_MODE_REF}"
return 1
fi
export FIX_MODE_REF
else
debug "Assuming that ${LANGUAGE} doesn't support fix mode because it doesn't have ${FIX_MODE_OPTIONS_VARIABLE_NAME}, nor ${CHECK_ONLY_MODE_OPTIONS_VARIABLE_NAME} variables defined."
if [[ -v "${FIX_MODE_VARIABLE_NAME}" ]]; then
error "The configuration provided a value for ${FIX_MODE_VARIABLE_NAME} but it's not supported for ${LANGUAGE}"
return 1
else
debug "The configuration didn't provide a value for ${FIX_MODE_VARIABLE_NAME} for ${LANGUAGE}"
fi
fi
unset -n FIX_MODE_REF
unset -n VALIDATE_MODE_REF
done
}
function CheckIfGitBranchExists() {
local BRANCH_NAME="${1}"
debug "Check if the ${BRANCH_NAME} branch exists in ${GITHUB_WORKSPACE}"
if ! git -C "${GITHUB_WORKSPACE}" rev-parse --quiet --verify "${BRANCH_NAME}"; then
info "The ${BRANCH_NAME} branch doesn't exist in ${GITHUB_WORKSPACE}"
return 1
else
debug "The ${BRANCH_NAME} branch exists in ${GITHUB_WORKSPACE}"
return 0
fi
}
function ValidateBooleanVariable() {
local VAR_NAME
VAR_NAME="${1}"
local VAR_VALUE
VAR_VALUE="${2}"
if [[ "${VAR_VALUE}" != "true" ]] && [[ "${VAR_VALUE}" != "false" ]]; then
fatal "Set ${VAR_NAME} to either true or false. It was set to: ${VAR_VALUE}"
else
debug "${VAR_NAME} has a valid boolean string value: ${VAR_VALUE}"
fi
}
export -f ValidateBooleanVariable
function ValidateLocalGitRepository() {
debug "Check if ${GITHUB_WORKSPACE} is a Git repository"
if ! git -C "${GITHUB_WORKSPACE}" rev-parse --git-dir; then
fatal "${GITHUB_WORKSPACE} is not a Git repository."
else
debug "${GITHUB_WORKSPACE} is a Git repository"
fi
debug "Git branches: $(git -C "${GITHUB_WORKSPACE}" branch -a)"
}
function CheckIfGitRefExists() {
local GIT_REF=${1}
if git -C "${GITHUB_WORKSPACE}" cat-file -e "${GIT_REF}"; then
return 0
else
return 1
fi
}
function IsUnsignedInteger() {
case ${1} in
'' | *[!0-9]*)
return 1
;;
*)
return 0
;;
esac
}
function ValidateGitShaReference() {
debug "Git HEAD: $(git -C "${GITHUB_WORKSPACE}" show HEAD --stat)"
debug "Validate that the GITHUB_SHA reference (${GITHUB_SHA}) exists in this Git repository."
if ! CheckIfGitRefExists "${GITHUB_SHA}"; then
IssueHintForFullGitHistory
fatal "The GITHUB_SHA reference (${GITHUB_SHA}) doesn't exist in this Git repository"
else
debug "The GITHUB_SHA reference (${GITHUB_SHA}) exists in this repository"
fi
}
function ValidateGitBeforeShaReference() {
debug "Validating GITHUB_BEFORE_SHA: ${GITHUB_BEFORE_SHA}"
if [ -z "${GITHUB_BEFORE_SHA}" ] ||
[ "${GITHUB_BEFORE_SHA}" == "null" ] ||
[ "${GITHUB_BEFORE_SHA}" == "0000000000000000000000000000000000000000" ]; then
fatal "Failed to get GITHUB_BEFORE_SHA: [${GITHUB_BEFORE_SHA}]"
fi
debug "Validate that the GITHUB_BEFORE_SHA reference (${GITHUB_BEFORE_SHA}) exists in this Git repository."
if ! CheckIfGitRefExists "${GITHUB_BEFORE_SHA}"; then
fatal "The GITHUB_BEFORE_SHA reference (${GITHUB_BEFORE_SHA}) doesn't exist in this Git repository"
else
debug "The GITHUB_BEFORE_SHA reference (${GITHUB_BEFORE_SHA}) exists in this repository"
fi
}
function ValidateDefaultGitBranch() {
debug "Check if the default branch (${DEFAULT_BRANCH}) exists"
if ! CheckIfGitBranchExists "${DEFAULT_BRANCH}"; then
REMOTE_DEFAULT_BRANCH="origin/${DEFAULT_BRANCH}"
debug "The default branch (${DEFAULT_BRANCH}) doesn't exist in this Git repository. Trying with ${REMOTE_DEFAULT_BRANCH}"
if ! CheckIfGitBranchExists "${REMOTE_DEFAULT_BRANCH}"; then
fatal "Neither ${DEFAULT_BRANCH}, nor ${REMOTE_DEFAULT_BRANCH} exist in ${GITHUB_WORKSPACE}"
else
info "${DEFAULT_BRANCH} doesn't exist, however ${REMOTE_DEFAULT_BRANCH} exists. Setting DEFAULT_BRANCH to: ${REMOTE_DEFAULT_BRANCH}"
DEFAULT_BRANCH="${REMOTE_DEFAULT_BRANCH}"
debug "Updated DEFAULT_BRANCH: ${DEFAULT_BRANCH}"
fi
else
debug "The default branch (${DEFAULT_BRANCH}) exists in this repository"
fi
}
function CheckovConfigurationFileContainsDirectoryOption() {
local CHECKOV_LINTER_RULES_PATH="${1}"
local CONFIGURATION_OPTION_KEY="directory:"
debug "Checking if ${CHECKOV_LINTER_RULES_PATH} contains a '${CONFIGURATION_OPTION_KEY}' configuration option"
if [ ! -e "${CHECKOV_LINTER_RULES_PATH}" ]; then
fatal "${CHECKOV_LINTER_RULES_PATH} doesn't exist. Cannot check if it contains a '${CONFIGURATION_OPTION_KEY}' configuration option"
fi
if grep -q "${CONFIGURATION_OPTION_KEY}" "${CHECKOV_LINTER_RULES_PATH}"; then
debug "${CHECKOV_LINTER_RULES_PATH} contains a '${CONFIGURATION_OPTION_KEY}' statement"
return 0
else
debug "${CHECKOV_LINTER_RULES_PATH} doesn't contain a '${CONFIGURATION_OPTION_KEY}' statement"
return 1
fi
}
export -f CheckovConfigurationFileContainsDirectoryOption
function ValidateGitHubUrls() {
if [[ -z "${DEFAULT_GITHUB_DOMAIN:-}" ]]; then
error "DEFAULT_GITHUB_DOMAIN is empty."
return 1
fi
debug "Default GitHub domain: ${DEFAULT_GITHUB_DOMAIN}"
if [[ -z "${GITHUB_DOMAIN:-}" ]]; then
error "GITHUB_DOMAIN is empty."
return 1
fi
debug "GitHub domain: ${GITHUB_DOMAIN}"
if [[ "${GITHUB_DOMAIN}" != "${DEFAULT_GITHUB_DOMAIN}" ]]; then
debug "GITHUB_DOMAIN (${GITHUB_DOMAIN}) is not set to the default GitHub domain (${DEFAULT_GITHUB_DOMAIN})"
if [[ -n "${GITHUB_CUSTOM_API_URL:-}" || -n "${GITHUB_CUSTOM_SERVER_URL:-}" ]]; then
error "Cannot set GITHUB_DOMAIN (${GITHUB_DOMAIN}) along with GITHUB_CUSTOM_API_URL (${GITHUB_CUSTOM_API_URL:-}) or with GITHUB_CUSTOM_SERVER_URL (${GITHUB_CUSTOM_SERVER_URL:-})."
return 1
fi
else
debug "GITHUB_DOMAIN (${GITHUB_DOMAIN}) is set to the default GitHub domain (${DEFAULT_GITHUB_DOMAIN})"
if [[ -n "${GITHUB_CUSTOM_API_URL:-}" && -z "${GITHUB_CUSTOM_SERVER_URL:-}" ]] ||
[[ -z "${GITHUB_CUSTOM_API_URL:-}" && -n "${GITHUB_CUSTOM_SERVER_URL:-}" ]]; then
error "Configure both GITHUB_CUSTOM_API_URL and GITHUB_CUSTOM_SERVER_URL. Current values: GITHUB_CUSTOM_API_URL: ${GITHUB_CUSTOM_API_URL:-}, GITHUB_CUSTOM_SERVER_URL: ${GITHUB_CUSTOM_SERVER_URL:-}"
return 1
fi
fi
}
function ValidateSuperLinterSummaryOutputPath() {
debug "Validating SUPER_LINTER_SUMMARY_OUTPUT_PATH"
if [[ -z "${SUPER_LINTER_SUMMARY_OUTPUT_PATH:-}" ]]; then
error "SUPER_LINTER_SUMMARY_OUTPUT_PATH is not set."
return 1
fi
debug "SUPER_LINTER_SUMMARY_OUTPUT_PATH is set to: ${SUPER_LINTER_SUMMARY_OUTPUT_PATH}"
if [[ ! -e "${SUPER_LINTER_SUMMARY_OUTPUT_PATH}" ]]; then
error "SUPER_LINTER_SUMMARY_OUTPUT_PATH (${SUPER_LINTER_SUMMARY_OUTPUT_PATH}) doesn't exist."
return 1
fi
if [[ ! -f "${SUPER_LINTER_SUMMARY_OUTPUT_PATH}" ]]; then
error "SUPER_LINTER_SUMMARY_OUTPUT_PATH (${SUPER_LINTER_SUMMARY_OUTPUT_PATH}) is not a file."
return 1
fi
debug "Super-linter summary ouput path passed validation"
}
function WarnIfVariableIsSet() {
local INPUT_VARIABLE="${1}"
shift
local INPUT_VARIABLE_NAME="${1}"
if [ -n "${INPUT_VARIABLE:-}" ]; then
warn "${INPUT_VARIABLE_NAME} environment variable is set, it's deprecated, and super-linter will ignore it. Remove it from your configuration. This warning may turn in a fatal error in the future. For more information, see the upgrade guide: https://github.com/super-linter/super-linter/blob/main/docs/upgrade-guide.md"
fi
}
function WarnIfDeprecatedValueForConfigurationVariableIsSet() {
local INPUT_VARIABLE_VALUE
INPUT_VARIABLE_VALUE="${1}"
shift
local DEPRECATED_VARIABLE_VALUE
DEPRECATED_VARIABLE_VALUE="${1}"
shift
local INPUT_VARIABLE_NAME
INPUT_VARIABLE_NAME="${1}"
shift
local VALUE_TO_UPDATE_TO
VALUE_TO_UPDATE_TO="${1}"
if [[ "${INPUT_VARIABLE_VALUE}" == "${DEPRECATED_VARIABLE_VALUE}" ]]; then
warn "${INPUT_VARIABLE_NAME} is set to a deprecated value: ${DEPRECATED_VARIABLE_VALUE}. Set it to ${VALUE_TO_UPDATE_TO} instead. Falling back to ${VALUE_TO_UPDATE_TO}. This warning may turn in a fatal error in the future."
fi
}
function ValidateDeprecatedVariables() {
# The following variables have been deprecated in v6.0.0
WarnIfVariableIsSet "${ERROR_ON_MISSING_EXEC_BIT:-}" "ERROR_ON_MISSING_EXEC_BIT"
WarnIfVariableIsSet "${EXPERIMENTAL_BATCH_WORKER:-}" "EXPERIMENTAL_BATCH_WORKER"
WarnIfVariableIsSet "${VALIDATE_JSCPD_ALL_CODEBASE:-}" "VALIDATE_JSCPD_ALL_CODEBASE"
WarnIfVariableIsSet "${VALIDATE_KOTLIN_ANDROID:-}" "VALIDATE_KOTLIN_ANDROID"
# The following values have been deprecated in v6.1.0
WarnIfDeprecatedValueForConfigurationVariableIsSet "${LOG_LEVEL}" "TRACE" "LOG_LEVEL" "DEBUG"
WarnIfDeprecatedValueForConfigurationVariableIsSet "${LOG_LEVEL}" "VERBOSE" "LOG_LEVEL" "INFO"
# The following variables have been deprecated in v6.8.0
WarnIfVariableIsSet "${JAVASCRIPT_DEFAULT_STYLE:-}" "JAVASCRIPT_DEFAULT_STYLE"
WarnIfVariableIsSet "${TYPESCRIPT_DEFAULT_STYLE:-}" "TYPESCRIPT_DEFAULT_STYLE"
}