diff --git a/.github/run-linter-locally.md b/.github/run-linter-locally.md index 073b8e88..13f2c7d6 100644 --- a/.github/run-linter-locally.md +++ b/.github/run-linter-locally.md @@ -66,6 +66,10 @@ Once the container has been downloaded to your local environment, you can then b - `-e VALIDATE_JAVASCRIPT=` - Default: `true` - Flag to enable or disable the linting process of the language + - **VALIDATE_DOCKER** + - `-e VALIDATE_DOCKER=` + - Default: `true` + - Flag to enable or disable the linting process of the language - **ANSIBLE_DIRECTORY** - `-e ANSIBLE_DIRECTORY=` - Default: `/ansible` diff --git a/Dockerfile b/Dockerfile index f76b941f..0768e571 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,6 +59,12 @@ RUN npm -g --no-cache install \ @typescript-eslint/eslint-plugin \ @typescript-eslint/parser +#################################### +# Install dockerfilelint from repo # +#################################### + +RUN git clone https://github.com/replicatedhq/dockerfilelint.git && cd /dockerfilelint && npm install + # I think we could fix this with path but not sure the language... # https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md #################### @@ -89,6 +95,7 @@ ENV GITHUB_SHA=${GITHUB_SHA} \ VALIDATE_RUBY=${VALIDATE_RUBY} \ VALIDATE_COFFEE=${VALIDATE_COFFEE} \ VALIDATE_ANSIBLE=${VALIDATE_ANSIBLE} \ + VALIDATE_DOCKER=${VALIDATE_DOCKER} \ VALIDATE_JAVASCRIPT=${VALIDATE_JAVASCRIPT} \ ANSIBLE_DIRECTORY=${ANSIBLE_DIRECTORY} \ RUN_LOCAL=${RUN_LOCAL} @@ -106,7 +113,4 @@ COPY TEMPLATES /action/lib/.automation ###################### # Set the entrypoint # ###################### -#RUN find / -name node_modules ENTRYPOINT ["/action/lib/linter.sh"] - -#CMD tail -f /dev/null diff --git a/README.md b/README.md index 8b2c93a5..122ff5a6 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,13 @@ Developers on **GitHub** can call this Action to lint their code base with the f - **XML** (LibXML) - **Coffeescript** (coffeelint) - **Javascript** (eslint)(standard) +- **Dockerfile** (dockerfilelint) ## How to use To use this **GitHub** Action you will need to complete the following: - Copy **any** or **all** template rules files from `TEMPLATES/` into your repository in the location: `.github/linters/` - If your repository does not have rules files, they will fall back to defaults in this repositories `TEMPLATE` folder -- Add the **Github** Action: **Super-Linter** to your current **Github** Actions workflow +- Add the **GitHub** Action: **Super-Linter** to your current **GitHub** Actions workflow - Enjoy your more *stable*, and *cleaner* code base ### Example connecting GitHub Action Workflow @@ -120,6 +121,9 @@ The super-linter allows you to pass the following `ENV` variables to be able to - **ANSIBLE_DIRECTORY** - Default: `/ansible` - Flag to set the root directory for Ansible file location(s) +- **VALIDATE_DOCKER** + - Default: `true` + - Flag to enable or disable the linting process of the language ## Docker Hub The **Docker** container that is built from this repository is located at `https://cloud.docker.com/u/admiralawkbar/repository/docker/admiralawkbar/super-linter` @@ -128,7 +132,7 @@ The **Docker** container that is built from this repository is located at `https If you find that you need to run super-linter locally, you can follow the documentation at [Running super-linter locally](https://github.com/github/super-linter/blob/master/.github/run-linter-locally.md) ## How to contribute -If you would like to help contribute to this **Github** Action, please see [CONTRIBUTING](https://github.com/github/super-linter/blob/master/.github/CONTRIBUTING.md) +If you would like to help contribute to this **GitHub** Action, please see [CONTRIBUTING](https://github.com/github/super-linter/blob/master/.github/CONTRIBUTING.md) -------------------------------------------------------------------------------- diff --git a/TEMPLATES/.dockerfilelintrc b/TEMPLATES/.dockerfilelintrc new file mode 100644 index 00000000..3f8d0271 --- /dev/null +++ b/TEMPLATES/.dockerfilelintrc @@ -0,0 +1,104 @@ +--- +########################### +########################### +## Dockerfile Lint rules ## +########################### +########################### + +################################# +# Default is 'on' for all rules # +# You can disable as needed. # +################################# +# Additional Info can be found at: +# https://github.com/replicatedhq/dockerfilelint + +# Set the rules +rules: + # All commands in a Dockerfile require at least 1 argument + required_params: on + + # For clarity and readability, all instructions in + # a Dockerfile should be uppercase + uppercase_commands: on + + # The first instruction in a Dockerfile must specify + # the base image using a FROM + from_first: on + + # This line is not a valid Dockerfile line + invalid_line: on + + # Use of sudo is not allowed in a Dockerfile + sudo_usage: on + + # Consider using a `--no-install-recommends` when `apt-get` + # installing packages + apt-get_missing_param: on + + # Consider using a `--no-install-recommends` when `apt-get` + # installing packages + apt-get_recommends: on + + # Use of `apt-get upgrade` is not allowed in a Dockerfile + apt-get-upgrade: on + + # Use of `apt-get dist-upgrade` is not allowed in a Dockerfile + apt-get-dist-upgrade: on + + # All instances of `apt-get update` should have the `apt-get install` + # commands on the same line to reduce image size + apt-get-update_require_install: on + + # Consider using a `--no-cache` (supported in alpine linux >= 3.3) or + # `--update` followed by the command `rm -rf /var/cache/apk/*` + # when `apk` adding packages. This will result in a smaller image size + apkadd-missing_nocache_or_updaterm: on + + # Consider using a `--virtual` or `-t` switch to group multiple packages + # for easy cleanup. This will help ensure future authors will continue + # to clean up build dependencies and other temporary packages + apkadd-missing-virtual: on + + # Exposing ports should only be valid port numbers + invalid_port: on + + # Only valid commands are allowed in a Dockerfile + invalid_command: on + + # Expose Only Container Port + expose_host_port: on + + # Using LABEL should be in key=value format + label_invalid: on + + # Base images should specify a tag to use + missing_tag: on + + # Base images should not use the latest tag + latest_tag: on + + # This command has extra arguments and will be ignored + extra_args: on + + # This command requires additional arguments + missing_args: on + + # All files referenced in an ADD command should + # be part of the Docker build context + add_src_invalid: on + + # When adding multiple files, the destination should be a directory + add_dest_invalid: on + + # Using a WORKDIR parameter that has spaces should be escaped + invalid_workdir: on + + # The arguments to this command are invalid + invalid_format: on + + # Use of apt-get update should be paired with + # rm -rf /var/lib/apt/lists/* in the same layer + apt-get_missing_rm: on + + # This INSTRUCTION is deprecated as of Docker 1.13 + deprecated_in_1.13: on diff --git a/lib/linter.sh b/lib/linter.sh index 632ab8d3..880fc2c5 100755 --- a/lib/linter.sh +++ b/lib/linter.sh @@ -29,13 +29,17 @@ JAVASCRIPT_FILE_NAME='.eslintrc.yml' # Name o JAVASCRIPT_LINTER_RULES="$DEFAULT_RULES_LOCATION/$JAVASCRIPT_FILE_NAME" # Path to the Javascript lint rules # Ansible Vars ANSIBLE_FILE_NAME='.ansible-lint.yml' # Name of the file -ANSIBLE_LINTER_RULES="$DEFAULT_RULES_LOCATION/$ANSIBLE_FILE_NAME" # Path to the coffescript lint rules +ANSIBLE_LINTER_RULES="$DEFAULT_RULES_LOCATION/$ANSIBLE_FILE_NAME" # Path to the Ansible lint rules + +# Docker Vars +DOCKER_FILE_NAME='.dockerfilelintrc' # Name of the file +DOCKER_LINTER_RULES="$DEFAULT_RULES_LOCATION/$DOCKER_FILE_NAME" # Path to the Docker lint rules ####################################### # Linter array for information prints # ####################################### LINTER_ARRAY=("jsonlint" "yamllint" "xmllint" "markdownlint" "shellcheck" - "pylint" "perl" "rubocop" "coffeelint" "eslint" "standard" "ansible-lint") + "pylint" "perl" "rubocop" "coffeelint" "eslint" "standard" "ansible-lint" "/dockerfilelint/bin/dockerfilelint") ################### # GitHub ENV Vars # @@ -56,6 +60,7 @@ VALIDATE_RUBY="${VALIDATE_RUBY}" # Boolean to validate language VALIDATE_COFFEE="${VALIDATE_COFFEE}" # Boolean to validate language VALIDATE_ANSIBLE="${VALIDATE_ANSIBLE}" # Boolean to validate language VALIDATE_JAVASCRIPT="${VALIDATE_JAVASCRIPT}" # Boolean to validate language +VALIDATE_DOCKER="${VALIDATE_DOCKER}" # Boolean to validate language RUN_LOCAL="${RUN_LOCAL}" # Boolean to see if we are running locally ################ @@ -81,6 +86,7 @@ FILE_ARRAY_RUBY=() # Array of files to check FILE_ARRAY_PYTHON=() # Array of files to check FILE_ARRAY_COFFEE=() # Array of files to check FILE_ARRAY_JAVASCRIPT=() # Array of files to check +FILE_ARRAY_DOCKER=() # Array of files to check ############ # Counters # @@ -96,6 +102,7 @@ ERRORS_FOUND_PYTHON=0 # Count of errors found ERRORS_FOUND_COFFEE=0 # Count of errors found ERRORS_FOUND_ANSIBLE=0 # Count of errors found ERRORS_FOUND_JAVASCRIPT=0 # Count of errors found +ERRORS_FOUND_DOCKER=0 # Count of errors found READ_ONLY_CHANGE_FLAG=0 # Flag set to 1 if files changed are not txt or md ################################################################################ @@ -182,9 +189,9 @@ GetLinterRules() echo "Gathering Linter rules from repository, or defaulting..." echo "" - ##################################### - # Validate we have the linter rules # - ##################################### + ######################################### + # YML Validate we have the linter rules # + ######################################### if [ -f "$GITHUB_WORKSPACE/.github/linters/$YAML_FILE_NAME" ]; then echo "User provided file:[$YAML_FILE_NAME], setting rules file..." @@ -210,9 +217,9 @@ GetLinterRules() echo "Codebase does not have file:[.github/linters/$YAML_FILE_NAME], using Default rules at:[$YAML_LINTER_RULES]" fi - ##################################### - # Validate we have the linter rules # - ##################################### + ############################################## + # MarkDown Validate we have the linter rules # + ############################################## if [ -f "$GITHUB_WORKSPACE/.github/linters/$MD_FILE_NAME" ]; then echo "User provided file:[$MD_FILE_NAME], setting rules file..." @@ -238,9 +245,9 @@ GetLinterRules() echo "Codebase does not have file:[.github/linters/$MD_FILE_NAME], using Default rules at:[$MD_LINTER_RULES]" fi - ##################################### - # Validate we have the linter rules # - ##################################### + ############################################ + # Python Validate we have the linter rules # + ############################################ if [ -f "$GITHUB_WORKSPACE/.github/linters/$PYTHON_FILE_NAME" ]; then echo "User provided file:[$PYTHON_FILE_NAME], setting rules file..." @@ -266,9 +273,9 @@ GetLinterRules() echo "Codebase does not have file:[.github/linters/$PYTHON_FILE_NAME], using Default rules at:[$PYTHON_LINTER_RULES]" fi - ##################################### - # Validate we have the linter rules # - ##################################### + ########################################## + # Ruby Validate we have the linter rules # + ########################################## if [ -f "$GITHUB_WORKSPACE/.github/linters/$RUBY_FILE_NAME" ]; then echo "User provided file:[$RUBY_FILE_NAME], setting rules file..." @@ -294,9 +301,9 @@ GetLinterRules() echo "Codebase does not have file:[.github/linters/$RUBY_FILE_NAME], using Default rules at:[$RUBY_LINTER_RULES]" fi - ##################################### - # Validate we have the linter rules # - ##################################### + ################################################## + # Coffeescript Validate we have the linter rules # + ################################################## if [ -f "$GITHUB_WORKSPACE/.github/linters/$COFFEE_FILE_NAME" ]; then echo "User provided file:[$COFFEE_FILE_NAME], setting rules file..." @@ -322,9 +329,9 @@ GetLinterRules() echo "Codebase does not have file:[.github/linters/$COFFEE_FILE_NAME], using Default rules at:[$COFFEE_LINTER_RULES]" fi - ##################################### - # Validate we have the linter rules # - ##################################### + ############################################# + # Ansible Validate we have the linter rules # + ############################################# if [ -f "$GITHUB_WORKSPACE/.github/linters/$ANSIBLE_FILE_NAME" ]; then echo "User provided file:[$ANSIBLE_FILE_NAME], setting rules file..." @@ -350,9 +357,9 @@ GetLinterRules() echo "Codebase does not have file:[.github/linters/$ANSIBLE_FILE_NAME], using Default rules at:[$ANSIBLE_LINTER_RULES]" fi - ##################################### - # Validate we have the linter rules # - ##################################### + ################################################ + # Javascript Validate we have the linter rules # + ################################################ if [ -f "$GITHUB_WORKSPACE/.github/linters/$JAVASCRIPT_FILE_NAME" ]; then echo "User provided file:[$JAVASCRIPT_FILE_NAME], setting rules file..." @@ -377,6 +384,34 @@ GetLinterRules() else echo "Codebase does not have file:[.github/linters/$JAVASCRIPT_FILE_NAME], using Default rules at:[$JAVASCRIPT_LINTER_RULES]" fi + + ############################################ + # Docker Validate we have the linter rules # + ############################################ + if [ -f "$GITHUB_WORKSPACE/.github/linters/$DOCKER_FILE_NAME" ]; then + echo "User provided file:[$DOCKER_FILE_NAME], setting rules file..." + + #################################### + # Move users into default location # + #################################### + MV_CMD=$(mv "$GITHUB_WORKSPACE/.github/linters/$DOCKER_FILE_NAME" "$DOCKER_LINTER_RULES" 2>&1) + + ################### + # Load Error code # + ################### + ERROR_CODE=$? + + ############################## + # Check the shell for errors # + ############################## + if [ $ERROR_CODE -ne 0 ]; then + echo "ERROR! Failed to set file:[$DOCKER_FILE_NAME] as default!" + echo "ERROR:[$MV_CMD]" + exit 1 + fi + else + echo "Codebase does not have file:[.github/linters/$DOCKER_FILE_NAME], using Default rules at:[$DOCKER_LINTER_RULES]" + fi } ################################################################################ #### Function LintJsonFiles #################################################### @@ -1894,6 +1929,120 @@ LintAnsibleFiles() fi } ################################################################################ +#### Function LintDockerFiles ################################################## +LintDockerFiles() +{ + ################ + # print header # + ################ + echo "" + echo "----------------------------------------------" + echo "----------------------------------------------" + echo "Linting Dockerfiles..." + echo "----------------------------------------------" + echo "----------------------------------------------" + echo "" + + ###################### + # Name of the linter # + ###################### + LINTER_NAME="dockerfilelint" + LINTER_PATH="/dockerfilelint/bin/dockerfilelint" + + ######################################### + # Validate we have shellcheck installed # + ######################################### + # shellcheck disable=SC2230 + VALIDATE_INSTALL_CMD=$(command -v "$LINTER_PATH" 2>&1) + + ####################### + # Load the error code # + ####################### + ERROR_CODE=$? + + ############################## + # Check the shell for errors # + ############################## + if [ $ERROR_CODE -ne 0 ]; then + # Failed + echo "ERROR! Failed to find $LINTER_NAME in system!" + echo "ERROR:[$VALIDATE_INSTALL_CMD]" + exit 1 + else + # Success + echo "Successfully found binary in system" + echo "Location:[$VALIDATE_INSTALL_CMD]" + fi + + ########################## + # Initialize empty Array # + ########################## + LIST_FILES=() + + ############################################################ + # Check to see if we need to go through array or all files # + ############################################################ + if [ ${#FILE_ARRAY_DOCKER[@]} -eq 0 ] && [ "$VALIDATE_ALL_CODEBASE" == "false" ]; then + # No files found in commit and user has asked to not validate code base + echo " - No files found in chageset to lint for language:[DOCKERFILE]" + elif [ ${#FILE_ARRAY_DOCKER[@]} -ne 0 ]; then + # We have files added to array of files to check + LIST_FILES=("${FILE_ARRAY_DOCKER[@]}") # Copy the array into list + else + ################################# + # Get list of all files to lint # + ################################# + # shellcheck disable=SC2207 + LIST_FILES=($(cd "$GITHUB_WORKSPACE" || exit; find . -type f -name "Dockerfile" 2>&1)) + fi + + ################## + # Lint the files # + ################## + for FILE in "${LIST_FILES[@]}" + do + + #################### + # Get the filename # + #################### + FILE_NAME=$(basename "$FILE" 2>&1) + + ############## + # File print # + ############## + echo "---------------------------" + echo "File:[$FILE]" + + ################################ + # Lint the file with the rules # + ################################ + LINT_CMD=$(cd "$GITHUB_WORKSPACE" || exit; "$LINTER_PATH" "$FILE" 2>&1) + + ####################### + # Load the error code # + ####################### + ERROR_CODE=$? + + ############################## + # Check the shell for errors # + ############################## + if [ $ERROR_CODE -ne 0 ]; then + ######### + # Error # + ######### + echo "ERROR! Found errors in [$LINTER_NAME] linter!" + echo "ERROR:[$LINT_CMD]" + # Increment error count + ((ERRORS_FOUND_DOCKER++)) + else + ########### + # Success # + ########### + echo " - File:[$FILE_NAME] was linted with [$LINTER_NAME] successfully" + fi + done +} +################################################################################ #### Function GetGitHubVars #################################################### GetGitHubVars() { @@ -2219,6 +2368,23 @@ GetGitHubVars() echo "- Excluding [JAVASCRIPT] files in code base..." fi + ############################### + # Convert string to lowercase # + ############################### + VALIDATE_DOCKER=$(echo "$VALIDATE_DOCKER" | awk '{print tolower($0)}') + ###################################### + # Validate we should check all files # + ###################################### + if [[ "$VALIDATE_DOCKER" != "false" ]]; then + # Set to true + VALIDATE_DOCKER="$DEFAULT_VALIDATE_LANGUAGE" + echo "- Validating [DOCKER] files in code base..." + else + # Its false + echo "- Excluding [DOCKER] files in code base..." + fi + + ############################## # Validate Ansible Directory # ############################## @@ -2440,6 +2606,15 @@ BuildFileList() # Set the READ_ONLY_CHANGE_FLAG since this could be exec # ########################################################## READ_ONLY_CHANGE_FLAG=1 + elif [ "$FILE" == "Dockerfile" ]; then + ################################ + # Append the file to the array # + ################################ + FILE_ARRAY_DOCKER+=("$FILE") + ########################################################## + # Set the READ_ONLY_CHANGE_FLAG since this could be exec # + ########################################################## + READ_ONLY_CHANGE_FLAG=1 else ############################ # Extension was not found! # @@ -2500,6 +2675,7 @@ Footer() echo "ERRORS FOUND in RUBY:[$ERRORS_FOUND_RUBY]" echo "ERRORS FOUND in ANSIBLE:[$ERRORS_FOUND_ANSIBLE]" echo "ERRORS FOUND in JAVASCRIPT:[$ERRORS_FOUND_JAVASCRIPT]" + echo "ERRORS FOUND in DOCKER:[$ERRORS_FOUND_DOCKER]" echo "----------------------------------------------" echo "" @@ -2516,6 +2692,7 @@ Footer() [ $ERRORS_FOUND_COFFEE -ne 0 ] || \ [ $ERRORS_FOUND_ANSIBLE -ne 0 ] || \ [ $ERRORS_FOUND_JAVASCRIPT -ne 0 ] || \ + [ $ERRORS_FOUND_DOCKER -ne 0 ] || \ [ $ERRORS_FOUND_RUBY -ne 0 ]; then # Failed exit echo "Exiting with errors found!" @@ -2635,6 +2812,13 @@ if [ "$VALIDATE_JAVASCRIPT" == "true" ]; then LintJavascriptFiles fi +############################# +# Lint the Dockerfiles # +############################# +if [ "$VALIDATE_DOCKER" == "true" ]; then + LintDockerFiles +fi + ########## # Footer #