diff --git a/.dockerignore b/.dockerignore index a78e675d2..e57569568 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,3 +2,4 @@ !.dockerignore !Dockerfile !tools/entrypoint.sh +!tools/install/*.sh diff --git a/.github/.container-structure-test-config.yaml b/.github/.container-structure-test-config.yaml index 8cbf7e4de..c746d83fc 100644 --- a/.github/.container-structure-test-config.yaml +++ b/.github/.container-structure-test-config.yaml @@ -13,7 +13,7 @@ commandTests: - name: "terraform" command: "terraform" args: ["-version"] - expectedOutput: ["^Terraform v([0-9]+\\.){2}[0-9]+\\non linux_amd64\\n$"] + expectedOutput: ["^Terraform v([0-9]+\\.){2}[0-9]+\\n"] - name: "gcc" command: "gcc" @@ -28,12 +28,12 @@ commandTests: - name: "infracost" command: "infracost" args: ["--version"] - expectedOutput: ["^Infracost v([0-9]+\\.){2}[0-9]+\\n$"] + expectedOutput: ["^Infracost v([0-9]+\\.){2}[0-9]+"] - name: "terraform-docs" command: "terraform-docs" args: ["--version"] - expectedOutput: ["^terraform-docs version v([0-9]+\\.){2}[0-9]+ [a-z0-9]+ linux/amd64\\n$"] + expectedOutput: ["^terraform-docs version v([0-9]+\\.){2}[0-9]+ [a-z0-9]+"] - name: "terragrunt" command: "terragrunt" diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 388bf3751..1d1568e51 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -113,6 +113,9 @@ You can use [this PR](https://github.com/antonbabenko/pre-commit-terraform/pull/ ### Add code +> [!TIP] +> Here is a screencast of [how to add new dependency in `tools/install/`](https://github.com/antonbabenko/pre-commit-terraform/assets/11096782/8fc461e9-f163-4592-9497-4a18fa89c0e8) - used in Dockerfile + 1. Based on prev. block, add hook dependencies installation to [Dockerfile](../Dockerfile). Check that works: * `docker build -t pre-commit --build-arg INSTALL_ALL=true .` diff --git a/.github/workflows/build-image-test.yaml b/.github/workflows/build-image-test.yaml index 9dfbda3c6..05fbc37b0 100644 --- a/.github/workflows/build-image-test.yaml +++ b/.github/workflows/build-image-test.yaml @@ -27,6 +27,13 @@ jobs: .dockerignore tools/entrypoint.sh .github/workflows/build-image-test.yaml + tools/*.sh + + - name: Set up QEMU + if: matrix.os != 'ubuntu-latest' || matrix.arch != 'amd64' + uses: docker/setup-qemu-action@v2 + with: + platforms: 'arm64' - name: Set up Docker Buildx uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 @@ -38,7 +45,7 @@ jobs: context: . build-args: | INSTALL_ALL=true - platforms: linux/amd64 # Only one allowed here, see https://github.com/docker/buildx/issues/59#issuecomment-1433097926 + platforms: linux/${{ matrix.arch }} # Only one allowed here, see https://github.com/docker/buildx/issues/59#issuecomment-1433097926 push: false load: true tags: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 17fc5a6fc..dee8870ae 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,13 +42,14 @@ repos: hooks: - id: hadolint args: [ - '--ignore', 'DL3027', # Do not use apt '--ignore', 'DL3007', # Using latest + '--ignore', 'DL3013', # Pin versions in pip + '--ignore', 'DL3027', # Do not use apt + '--ignore', 'DL3059', # Docker `RUN`s shouldn't be consolidated here '--ignore', 'DL4006', # Not related to alpine '--ignore', 'SC1091', # Useless check '--ignore', 'SC2015', # Useless check '--ignore', 'SC3037', # Not related to alpine - '--ignore', 'DL3013', # Pin versions in pip ] # JSON5 Linter diff --git a/Dockerfile b/Dockerfile index 242a45405..3822194e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,41 +7,43 @@ WORKDIR /bin_dir RUN apk add --no-cache \ # Builder deps + bash=~5 \ curl=~8 && \ # Upgrade packages for be able get latest Checkov python3 -m pip install --no-cache-dir --upgrade \ pip \ setuptools +COPY tools/install/ /install/ + +# +# Install required tools +# ARG PRE_COMMIT_VERSION=${PRE_COMMIT_VERSION:-latest} ARG TERRAFORM_VERSION=${TERRAFORM_VERSION:-latest} -# Install pre-commit -RUN if [ ${PRE_COMMIT_VERSION} = "latest" ]; \ - then pip3 install --no-cache-dir pre-commit; \ - else pip3 install --no-cache-dir pre-commit==${PRE_COMMIT_VERSION}; \ +RUN touch /.env && \ + if [ "$PRE_COMMIT_VERSION" = "false" ] || [ "$TERRAFORM_VERSION" = "false" ]; then \ + echo "Vital software can't be skipped" && exit 1; \ fi -# Install terraform because pre-commit needs it -RUN if [ "${TERRAFORM_VERSION}" = "latest" ]; then \ - TERRAFORM_VERSION="$(curl -s https://api.github.com/repos/hashicorp/terraform/releases/latest | grep tag_name | grep -o -E -m 1 "[0-9.]+")" \ - ; fi && \ - curl -L "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_${TARGETOS}_${TARGETARCH}.zip" > terraform.zip && \ - unzip terraform.zip terraform && rm terraform.zip + +RUN /install/pre-commit.sh +RUN /install/terraform.sh # # Install tools # ARG CHECKOV_VERSION=${CHECKOV_VERSION:-false} +ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false} ARG INFRACOST_VERSION=${INFRACOST_VERSION:-false} ARG TERRAFORM_DOCS_VERSION=${TERRAFORM_DOCS_VERSION:-false} ARG TERRAGRUNT_VERSION=${TERRAGRUNT_VERSION:-false} ARG TERRASCAN_VERSION=${TERRASCAN_VERSION:-false} ARG TFLINT_VERSION=${TFLINT_VERSION:-false} ARG TFSEC_VERSION=${TFSEC_VERSION:-false} -ARG TRIVY_VERSION=${TRIVY_VERSION:-false} ARG TFUPDATE_VERSION=${TFUPDATE_VERSION:-false} -ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false} +ARG TRIVY_VERSION=${TRIVY_VERSION:-false} # Tricky thing to install all tools by set only one arg. @@ -49,167 +51,45 @@ ARG HCLEDIT_VERSION=${HCLEDIT_VERSION:-false} # specified in step below ARG INSTALL_ALL=${INSTALL_ALL:-false} RUN if [ "$INSTALL_ALL" != "false" ]; then \ - echo "export CHECKOV_VERSION=latest" >> /.env && \ - echo "export INFRACOST_VERSION=latest" >> /.env && \ - echo "export TERRAFORM_DOCS_VERSION=latest" >> /.env && \ - echo "export TERRAGRUNT_VERSION=latest" >> /.env && \ - echo "export TERRASCAN_VERSION=latest" >> /.env && \ - echo "export TFLINT_VERSION=latest" >> /.env && \ - echo "export TFSEC_VERSION=latest" >> /.env && \ - echo "export TRIVY_VERSION=latest" >> /.env && \ - echo "export TFUPDATE_VERSION=latest" >> /.env && \ - echo "export HCLEDIT_VERSION=latest" >> /.env \ - ; else \ - touch /.env \ + echo "CHECKOV_VERSION=latest" >> /.env && \ + echo "HCLEDIT_VERSION=latest" >> /.env && \ + echo "INFRACOST_VERSION=latest" >> /.env && \ + echo "TERRAFORM_DOCS_VERSION=latest" >> /.env && \ + echo "TERRAGRUNT_VERSION=latest" >> /.env && \ + echo "TERRASCAN_VERSION=latest" >> /.env && \ + echo "TFLINT_VERSION=latest" >> /.env && \ + echo "TFSEC_VERSION=latest" >> /.env && \ + echo "TFUPDATE_VERSION=latest" >> /.env && \ + echo "TRIVY_VERSION=latest" >> /.env \ ; fi - -# Checkov -RUN . /.env && \ - if [ "$CHECKOV_VERSION" != "false" ]; then \ - ( \ - # cargo, gcc, git, musl-dev, rust and CARGO envvar required for compilation of rustworkx@0.13.2, no longer required once checkov version depends on rustworkx >0.14.0 - # https://github.com/bridgecrewio/checkov/pull/6045 - # gcc libffi-dev musl-dev required for compilation of cffi, until it contains musl aarch64 - export CARGO_NET_GIT_FETCH_WITH_CLI=true && \ - apk add --no-cache cargo=~1 gcc=~12 git=~2 libffi-dev=~3 libgcc=~12 musl-dev=~1 rust=~1 ; \ - if [ "$CHECKOV_VERSION" = "latest" ]; \ - then pip3 install --no-cache-dir checkov || exit 1; \ - else pip3 install --no-cache-dir checkov==${CHECKOV_VERSION} || exit 1; \ - fi; \ - apk del cargo gcc git libffi-dev musl-dev rust \ - ) \ - ; fi - -# infracost -RUN . /.env && \ - if [ "$INFRACOST_VERSION" != "false" ]; then \ - ( \ - INFRACOST_RELEASES="https://api.github.com/repos/infracost/infracost/releases" && \ - if [ "$INFRACOST_VERSION" = "latest" ]; \ - then curl -L "$(curl -s ${INFRACOST_RELEASES}/latest | grep -o -E -m 1 "https://.+?-${TARGETOS}-${TARGETARCH}.tar.gz")" > infracost.tgz; \ - else curl -L "$(curl -s ${INFRACOST_RELEASES} | grep -o -E "https://.+?v${INFRACOST_VERSION}/infracost-${TARGETOS}-${TARGETARCH}.tar.gz")" > infracost.tgz; \ - fi; \ - ) && tar -xzf infracost.tgz && rm infracost.tgz && mv infracost-${TARGETOS}-${TARGETARCH} infracost \ - ; fi - -# Terraform docs -RUN . /.env && \ - if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then \ - ( \ - TERRAFORM_DOCS_RELEASES="https://api.github.com/repos/terraform-docs/terraform-docs/releases" && \ - if [ "$TERRAFORM_DOCS_VERSION" = "latest" ]; \ - then curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES}/latest | grep -o -E -m 1 "https://.+?-${TARGETOS}-${TARGETARCH}.tar.gz")" > terraform-docs.tgz; \ - else curl -L "$(curl -s ${TERRAFORM_DOCS_RELEASES} | grep -o -E "https://.+?v${TERRAFORM_DOCS_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz")" > terraform-docs.tgz; \ - fi; \ - ) && tar -xzf terraform-docs.tgz terraform-docs && rm terraform-docs.tgz && chmod +x terraform-docs \ - ; fi - -# Terragrunt -RUN . /.env \ - && if [ "$TERRAGRUNT_VERSION" != "false" ]; then \ - ( \ - TERRAGRUNT_RELEASES="https://api.github.com/repos/gruntwork-io/terragrunt/releases" && \ - if [ "$TERRAGRUNT_VERSION" = "latest" ]; \ - then curl -L "$(curl -s ${TERRAGRUNT_RELEASES}/latest | grep -o -E -m 1 "https://.+?/terragrunt_${TARGETOS}_${TARGETARCH}")" > terragrunt; \ - else curl -L "$(curl -s ${TERRAGRUNT_RELEASES} | grep -o -E -m 1 "https://.+?v${TERRAGRUNT_VERSION}/terragrunt_${TARGETOS}_${TARGETARCH}")" > terragrunt; \ - fi; \ - ) && chmod +x terragrunt \ - ; fi +RUN /install/checkov.sh +RUN /install/hcledit.sh +RUN /install/infracost.sh +RUN /install/terraform-docs.sh +RUN /install/terragrunt.sh +RUN /install/terrascan.sh +RUN /install/tflint.sh +RUN /install/tfsec.sh +RUN /install/tfupdate.sh +RUN /install/trivy.sh -# Terrascan -RUN . /.env && \ - if [ "$TERRASCAN_VERSION" != "false" ]; then \ - if [ "$TARGETARCH" != "amd64" ]; then ARCH="$TARGETARCH"; else ARCH="x86_64"; fi; \ - # Convert the first letter to Uppercase - OS="$(echo ${TARGETOS} | cut -c1 | tr '[:lower:]' '[:upper:]' | xargs echo -n; echo ${TARGETOS} | cut -c2-)"; \ - ( \ - TERRASCAN_RELEASES="https://api.github.com/repos/tenable/terrascan/releases" && \ - if [ "$TERRASCAN_VERSION" = "latest" ]; \ - then curl -L "$(curl -s ${TERRASCAN_RELEASES}/latest | grep -o -E -m 1 "https://.+?_${OS}_${ARCH}.tar.gz")" > terrascan.tar.gz; \ - else curl -L "$(curl -s ${TERRASCAN_RELEASES} | grep -o -E "https://.+?${TERRASCAN_VERSION}_${OS}_${ARCH}.tar.gz")" > terrascan.tar.gz; \ - fi; \ - ) && tar -xzf terrascan.tar.gz terrascan && rm terrascan.tar.gz && \ - ./terrascan init \ - ; fi - -# TFLint -RUN . /.env && \ - if [ "$TFLINT_VERSION" != "false" ]; then \ - ( \ - TFLINT_RELEASES="https://api.github.com/repos/terraform-linters/tflint/releases" && \ - if [ "$TFLINT_VERSION" = "latest" ]; \ - then curl -L "$(curl -s ${TFLINT_RELEASES}/latest | grep -o -E -m 1 "https://.+?_${TARGETOS}_${TARGETARCH}.zip")" > tflint.zip; \ - else curl -L "$(curl -s ${TFLINT_RELEASES} | grep -o -E "https://.+?/v${TFLINT_VERSION}/tflint_${TARGETOS}_${TARGETARCH}.zip")" > tflint.zip; \ - fi; \ - ) && unzip tflint.zip && rm tflint.zip \ - ; fi - -# TFSec -RUN . /.env && \ - if [ "$TFSEC_VERSION" != "false" ]; then \ - ( \ - TFSEC_RELEASES="https://api.github.com/repos/aquasecurity/tfsec/releases" && \ - if [ "$TFSEC_VERSION" = "latest" ]; then \ - curl -L "$(curl -s ${TFSEC_RELEASES}/latest | grep -o -E -m 1 "https://.+?/tfsec-${TARGETOS}-${TARGETARCH}")" > tfsec; \ - else curl -L "$(curl -s ${TFSEC_RELEASES} | grep -o -E -m 1 "https://.+?v${TFSEC_VERSION}/tfsec-${TARGETOS}-${TARGETARCH}")" > tfsec; \ - fi; \ - ) && chmod +x tfsec \ - ; fi - -# Trivy -RUN . /.env && \ - if [ "$TRIVY_VERSION" != "false" ]; then \ - if [ "$TARGETARCH" != "amd64" ]; then ARCH="$TARGETARCH"; else ARCH="64bit"; fi; \ - ( \ - TRIVY_RELEASES="https://api.github.com/repos/aquasecurity/trivy/releases" && \ - if [ "$TRIVY_VERSION" = "latest" ]; \ - then curl -L "$(curl -s ${TRIVY_RELEASES}/latest | grep -o -E -i -m 1 "https://.+?/trivy_.+?_${TARGETOS}-${ARCH}.tar.gz")" > trivy.tar.gz; \ - else curl -L "$(curl -s ${TRIVY_RELEASES} | grep -o -E -i -m 1 "https://.+?/v${TRIVY_VERSION}/trivy_.+?_${TARGETOS}-${ARCH}.tar.gz")" > trivy.tar.gz; \ - fi; \ - ) && tar -xzf trivy.tar.gz trivy && rm trivy.tar.gz \ - ; fi - -# TFUpdate -RUN . /.env && \ - if [ "$TFUPDATE_VERSION" != "false" ]; then \ - ( \ - TFUPDATE_RELEASES="https://api.github.com/repos/minamijoyo/tfupdate/releases" && \ - if [ "$TFUPDATE_VERSION" = "latest" ]; \ - then curl -L "$(curl -s ${TFUPDATE_RELEASES}/latest | grep -o -E -m 1 "https://.+?_${TARGETOS}_${TARGETARCH}.tar.gz")" > tfupdate.tgz; \ - else curl -L "$(curl -s ${TFUPDATE_RELEASES} | grep -o -E -m 1 "https://.+?${TFUPDATE_VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz")" > tfupdate.tgz; \ - fi; \ - ) && tar -xzf tfupdate.tgz tfupdate && rm tfupdate.tgz \ - ; fi - -# hcledit -RUN . /.env && \ - if [ "$HCLEDIT_VERSION" != "false" ]; then \ - ( \ - HCLEDIT_RELEASES="https://api.github.com/repos/minamijoyo/hcledit/releases" && \ - if [ "$HCLEDIT_VERSION" = "latest" ]; \ - then curl -L "$(curl -s ${HCLEDIT_RELEASES}/latest | grep -o -E -m 1 "https://.+?_${TARGETOS}_${TARGETARCH}.tar.gz")" > hcledit.tgz; \ - else curl -L "$(curl -s ${HCLEDIT_RELEASES} | grep -o -E -m 1 "https://.+?${HCLEDIT_VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz")" > hcledit.tgz; \ - fi; \ - ) && tar -xzf hcledit.tgz hcledit && rm hcledit.tgz \ - ; fi - # Checking binaries versions and write it to debug file RUN . /.env && \ F=tools_versions_info && \ pre-commit --version >> $F && \ ./terraform --version | head -n 1 >> $F && \ (if [ "$CHECKOV_VERSION" != "false" ]; then echo "checkov $(checkov --version)" >> $F; else echo "checkov SKIPPED" >> $F ; fi) && \ + (if [ "$HCLEDIT_VERSION" != "false" ]; then echo "hcledit $(./hcledit version)" >> $F; else echo "hcledit SKIPPED" >> $F ; fi) && \ (if [ "$INFRACOST_VERSION" != "false" ]; then echo "$(./infracost --version)" >> $F; else echo "infracost SKIPPED" >> $F ; fi) && \ (if [ "$TERRAFORM_DOCS_VERSION" != "false" ]; then ./terraform-docs --version >> $F; else echo "terraform-docs SKIPPED" >> $F ; fi) && \ (if [ "$TERRAGRUNT_VERSION" != "false" ]; then ./terragrunt --version >> $F; else echo "terragrunt SKIPPED" >> $F ; fi) && \ (if [ "$TERRASCAN_VERSION" != "false" ]; then echo "terrascan $(./terrascan version)" >> $F; else echo "terrascan SKIPPED" >> $F ; fi) && \ (if [ "$TFLINT_VERSION" != "false" ]; then ./tflint --version >> $F; else echo "tflint SKIPPED" >> $F ; fi) && \ (if [ "$TFSEC_VERSION" != "false" ]; then echo "tfsec $(./tfsec --version)" >> $F; else echo "tfsec SKIPPED" >> $F ; fi) && \ - (if [ "$TRIVY_VERSION" != "false" ]; then echo "trivy $(./trivy --version)" >> $F; else echo "trivy SKIPPED" >> $F ; fi) && \ (if [ "$TFUPDATE_VERSION" != "false" ]; then echo "tfupdate $(./tfupdate --version)" >> $F; else echo "tfupdate SKIPPED" >> $F ; fi) && \ - (if [ "$HCLEDIT_VERSION" != "false" ]; then echo "hcledit $(./hcledit version)" >> $F; else echo "hcledit SKIPPED" >> $F ; fi) && \ + (if [ "$TRIVY_VERSION" != "false" ]; then echo "trivy $(./trivy --version)" >> $F; else echo "trivy SKIPPED" >> $F ; fi) && \ echo -e "\n\n" && cat $F && echo -e "\n\n" diff --git a/hooks/infracost_breakdown.sh b/hooks/infracost_breakdown.sh index 551579112..d5351b9bf 100755 --- a/hooks/infracost_breakdown.sh +++ b/hooks/infracost_breakdown.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_checkov.sh b/hooks/terraform_checkov.sh index a9603afd9..dbbdc463b 100755 --- a/hooks/terraform_checkov.sh +++ b/hooks/terraform_checkov.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_docs.sh b/hooks/terraform_docs.sh index c597730b0..472d6487c 100755 --- a/hooks/terraform_docs.sh +++ b/hooks/terraform_docs.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_fmt.sh b/hooks/terraform_fmt.sh index 727635caa..949a066a8 100755 --- a/hooks/terraform_fmt.sh +++ b/hooks/terraform_fmt.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_providers_lock.sh b/hooks/terraform_providers_lock.sh index bf2aa2084..34bce58b2 100755 --- a/hooks/terraform_providers_lock.sh +++ b/hooks/terraform_providers_lock.sh @@ -3,8 +3,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_tflint.sh b/hooks/terraform_tflint.sh index 9c8373676..211e648bc 100755 --- a/hooks/terraform_tflint.sh +++ b/hooks/terraform_tflint.sh @@ -3,8 +3,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_tfsec.sh b/hooks/terraform_tfsec.sh index 75966bf8c..52cab2c71 100755 --- a/hooks/terraform_tfsec.sh +++ b/hooks/terraform_tfsec.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_trivy.sh b/hooks/terraform_trivy.sh index fd9a3203a..7de40188b 100755 --- a/hooks/terraform_trivy.sh +++ b/hooks/terraform_trivy.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_validate.sh b/hooks/terraform_validate.sh index 9a6c50fce..a56e8c6db 100755 --- a/hooks/terraform_validate.sh +++ b/hooks/terraform_validate.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terraform_wrapper_module_for_each.sh b/hooks/terraform_wrapper_module_for_each.sh index e9a98e2a3..945bf9a8a 100755 --- a/hooks/terraform_wrapper_module_for_each.sh +++ b/hooks/terraform_wrapper_module_for_each.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terragrunt_fmt.sh b/hooks/terragrunt_fmt.sh index 1effbf47b..a39c78a79 100755 --- a/hooks/terragrunt_fmt.sh +++ b/hooks/terragrunt_fmt.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terragrunt_providers_lock.sh b/hooks/terragrunt_providers_lock.sh index f05a571c4..059f8d1f5 100755 --- a/hooks/terragrunt_providers_lock.sh +++ b/hooks/terragrunt_providers_lock.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terragrunt_validate.sh b/hooks/terragrunt_validate.sh index 8fafada0a..e595329b6 100755 --- a/hooks/terragrunt_validate.sh +++ b/hooks/terragrunt_validate.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/terrascan.sh b/hooks/terrascan.sh index 0c1447bd4..d7dc5f4a5 100755 --- a/hooks/terrascan.sh +++ b/hooks/terrascan.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/hooks/tfupdate.sh b/hooks/tfupdate.sh index 9397cfe94..5c9979a47 100755 --- a/hooks/tfupdate.sh +++ b/hooks/tfupdate.sh @@ -2,8 +2,8 @@ set -eo pipefail # globals variables -# shellcheck disable=SC2155 # No way to assign to readonly variable in separate lines -readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR # shellcheck source=_common.sh . "$SCRIPT_DIR/_common.sh" diff --git a/tools/install/_common.sh b/tools/install/_common.sh new file mode 100755 index 000000000..70297f297 --- /dev/null +++ b/tools/install/_common.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +set -eo pipefail + +# Tool name, based on filename. +# Tool filename MUST BE same as in package manager/binary name +TOOL=${0##*/} +readonly TOOL=${TOOL%%.*} + +# Get "TOOL_VERSION" +# shellcheck disable=SC1091 # Created in Dockerfile before execution of this script +source /.env +env_var_name="${TOOL//-/_}" +env_var_name="${env_var_name^^}_VERSION" +# shellcheck disable=SC2034 # Used in other scripts +readonly VERSION="${!env_var_name}" + +# Skip tool installation if the version is set to "false" +if [[ $VERSION == false ]]; then + echo "'$TOOL' skipped" + exit 0 +fi + +####################################################################### +# Install the latest or specific version of the tool from GitHub release +# Globals: +# TOOL - Name of the tool +# VERSION - Version of the tool +# Arguments: +# GH_ORG - GitHub organization name where the tool is hosted +# DISTRIBUTED_AS - How the tool is distributed. +# Can be: 'tar.gz', 'zip' or 'binary' +# GH_RELEASE_REGEX_LATEST - Regular expression to match the latest +# release URL +# GH_RELEASE_REGEX_SPECIFIC_VERSION - Regular expression to match the +# specific version release URL +# UNUSUAL_TOOL_NAME_IN_PKG - If the tool in the tar.gz package is +# not in the root or named differently than the tool name itself, +# For example, includes the version number or is in a subdirectory +####################################################################### +function common::install_from_gh_release { + local -r GH_ORG=$1 + local -r DISTRIBUTED_AS=$2 + local -r GH_RELEASE_REGEX_LATEST=$3 + local -r GH_RELEASE_REGEX_SPECIFIC_VERSION=$4 + local -r UNUSUAL_TOOL_NAME_IN_PKG=$5 + + case $DISTRIBUTED_AS in + tar.gz | zip) + local -r PKG="${TOOL}.${DISTRIBUTED_AS}" + ;; + binary) + local -r PKG="$TOOL" + ;; + *) + echo "Unknown DISTRIBUTED_AS: '$DISTRIBUTED_AS'. Should be one of: 'tar.gz', 'zip' or 'binary'." >&2 + exit 1 + ;; + esac + + # Download tool + local -r RELEASES="https://api.github.com/repos/${GH_ORG}/${TOOL}/releases" + + if [[ $VERSION == latest ]]; then + curl -L "$(curl -s "${RELEASES}/latest" | grep -o -E -i -m 1 "$GH_RELEASE_REGEX_LATEST")" > "$PKG" + else + curl -L "$(curl -s "$RELEASES" | grep -o -E -i -m 1 "$GH_RELEASE_REGEX_SPECIFIC_VERSION")" > "$PKG" + fi + + # Make tool ready to use + if [[ $DISTRIBUTED_AS == tar.gz ]]; then + if [[ -z $UNUSUAL_TOOL_NAME_IN_PKG ]]; then + tar -xzf "$PKG" "$TOOL" + else + tar -xzf "$PKG" "$UNUSUAL_TOOL_NAME_IN_PKG" + mv "$UNUSUAL_TOOL_NAME_IN_PKG" "$TOOL" + fi + rm "$PKG" + + elif [[ $DISTRIBUTED_AS == zip ]]; then + unzip "$PKG" + rm "$PKG" + else + chmod +x "$PKG" + fi +} diff --git a/tools/install/checkov.sh b/tools/install/checkov.sh new file mode 100755 index 000000000..8be8c649e --- /dev/null +++ b/tools/install/checkov.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +apk add --no-cache \ + gcc=~12 \ + libffi-dev=~3 \ + musl-dev=~1 + +# cargo, gcc, git, musl-dev, rust and CARGO envvar required for compilation of rustworkx@0.13.2 +# no longer required once checkov version depends on rustworkx >0.14.0 +# https://github.com/bridgecrewio/checkov/pull/6045 +# gcc libffi-dev musl-dev required for compilation of cffi, until it contains musl aarch64 +export CARGO_NET_GIT_FETCH_WITH_CLI=true +apk add --no-cache \ + cargo=~1 \ + git=~2 \ + libgcc=~12 \ + rust=~1 + +if [[ $VERSION == latest ]]; then + pip3 install --no-cache-dir "${TOOL}" +else + pip3 install --no-cache-dir "${TOOL}==${VERSION}" +fi + +apk del gcc libffi-dev musl-dev +# no longer required once checkov version depends on rustworkx >0.14.0 +# https://github.com/bridgecrewio/checkov/pull/6045 +apk del cargo git rust diff --git a/tools/install/hcledit.sh b/tools/install/hcledit.sh new file mode 100755 index 000000000..498e4fb6f --- /dev/null +++ b/tools/install/hcledit.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +GH_ORG="minamijoyo" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?${VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz" +GH_RELEASE_REGEX_LATEST="https://.+?_${TARGETOS}_${TARGETARCH}.tar.gz" +DISTRIBUTED_AS="tar.gz" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" diff --git a/tools/install/infracost.sh b/tools/install/infracost.sh new file mode 100755 index 000000000..9974ca1d1 --- /dev/null +++ b/tools/install/infracost.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +GH_ORG="infracost" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?v${VERSION}/${TOOL}-${TARGETOS}-${TARGETARCH}.tar.gz" +GH_RELEASE_REGEX_LATEST="https://.+?-${TARGETOS}-${TARGETARCH}.tar.gz" +DISTRIBUTED_AS="tar.gz" +UNUSUAL_TOOL_NAME_IN_PKG="${TOOL}-${TARGETOS}-${TARGETARCH}" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" \ + "$UNUSUAL_TOOL_NAME_IN_PKG" diff --git a/tools/install/pre-commit.sh b/tools/install/pre-commit.sh new file mode 100755 index 000000000..9f3bdfb24 --- /dev/null +++ b/tools/install/pre-commit.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR + +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +if [[ $VERSION == latest ]]; then + pip3 install --no-cache-dir "$TOOL" +else + pip3 install --no-cache-dir "${TOOL}==${VERSION}" +fi diff --git a/tools/install/terraform-docs.sh b/tools/install/terraform-docs.sh new file mode 100755 index 000000000..9eec05394 --- /dev/null +++ b/tools/install/terraform-docs.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +GH_ORG="terraform-docs" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?v${VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz" +GH_RELEASE_REGEX_LATEST="https://.+?-${TARGETOS}-${TARGETARCH}.tar.gz" +DISTRIBUTED_AS="tar.gz" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" diff --git a/tools/install/terraform.sh b/tools/install/terraform.sh new file mode 100755 index 000000000..65ec21c2b --- /dev/null +++ b/tools/install/terraform.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# +# shellcheck disable=SC2153 # We are using the variable from _common.sh +if [[ $VERSION == latest ]]; then + version="$(curl -s https://api.github.com/repos/hashicorp/terraform/releases/latest | grep tag_name | grep -o -E -m 1 "[0-9.]+")" +else + version=$VERSION +fi +readonly version + +curl -L "https://releases.hashicorp.com/terraform/${version}/${TOOL}_${version}_${TARGETOS}_${TARGETARCH}.zip" > "${TOOL}.zip" +unzip "${TOOL}.zip" "$TOOL" +rm "${TOOL}.zip" diff --git a/tools/install/terragrunt.sh b/tools/install/terragrunt.sh new file mode 100755 index 000000000..20cc60ff7 --- /dev/null +++ b/tools/install/terragrunt.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +GH_ORG="gruntwork-io" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?v${VERSION}/${TOOL}_${TARGETOS}_${TARGETARCH}" +GH_RELEASE_REGEX_LATEST="https://.+?/${TOOL}_${TARGETOS}_${TARGETARCH}" +DISTRIBUTED_AS="binary" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" diff --git a/tools/install/terrascan.sh b/tools/install/terrascan.sh new file mode 100755 index 000000000..4393159d3 --- /dev/null +++ b/tools/install/terrascan.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +[[ $TARGETARCH == amd64 ]] && ARCH="x86_64" || ARCH="$TARGETARCH" +readonly ARCH +# Convert the first letter to Uppercase +OS="${TARGETOS^}" + +GH_ORG="tenable" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?${VERSION}_${OS}_${ARCH}.tar.gz" +GH_RELEASE_REGEX_LATEST="https://.+?_${OS}_${ARCH}.tar.gz" +DISTRIBUTED_AS="tar.gz" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" + +# Download (caching) terrascan rego policies to save time during terrascan run +# https://runterrascan.io/docs/usage/_print/#pg-2cba380a2ef14e4ae3c674e02c5f9f53 +./"$TOOL" init diff --git a/tools/install/tflint.sh b/tools/install/tflint.sh new file mode 100755 index 000000000..ac2556b81 --- /dev/null +++ b/tools/install/tflint.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +GH_ORG="terraform-linters" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?/v${VERSION}/${TOOL}_${TARGETOS}_${TARGETARCH}.zip" +GH_RELEASE_REGEX_LATEST="https://.+?_${TARGETOS}_${TARGETARCH}.zip" +DISTRIBUTED_AS="zip" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" diff --git a/tools/install/tfsec.sh b/tools/install/tfsec.sh new file mode 100755 index 000000000..3c9c2430d --- /dev/null +++ b/tools/install/tfsec.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +GH_ORG="aquasecurity" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?v${VERSION}/${TOOL}-${TARGETOS}-${TARGETARCH}" +GH_RELEASE_REGEX_LATEST="https://.+?/${TOOL}-${TARGETOS}-${TARGETARCH}" +DISTRIBUTED_AS="binary" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" diff --git a/tools/install/tfupdate.sh b/tools/install/tfupdate.sh new file mode 100755 index 000000000..498e4fb6f --- /dev/null +++ b/tools/install/tfupdate.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +GH_ORG="minamijoyo" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?${VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz" +GH_RELEASE_REGEX_LATEST="https://.+?_${TARGETOS}_${TARGETARCH}.tar.gz" +DISTRIBUTED_AS="tar.gz" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION" diff --git a/tools/install/trivy.sh b/tools/install/trivy.sh new file mode 100755 index 000000000..c07625b53 --- /dev/null +++ b/tools/install/trivy.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +readonly SCRIPT_DIR +# shellcheck source=_common.sh +. "$SCRIPT_DIR/_common.sh" + +# +# Unique part +# + +[[ $TARGETARCH == amd64 ]] && ARCH="64bit" || ARCH="$TARGETARCH" +readonly ARCH + +GH_ORG="aquasecurity" +GH_RELEASE_REGEX_SPECIFIC_VERSION="https://.+?/v${VERSION}/${TOOL}_.+?_${TARGETOS}-${ARCH}.tar.gz" +GH_RELEASE_REGEX_LATEST="https://.+?/${TOOL}_.+?_${TARGETOS}-${ARCH}.tar.gz" +DISTRIBUTED_AS="tar.gz" + +common::install_from_gh_release "$GH_ORG" "$DISTRIBUTED_AS" \ + "$GH_RELEASE_REGEX_LATEST" "$GH_RELEASE_REGEX_SPECIFIC_VERSION"