diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index f519b68..3903c81 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,53 +1,14 @@ -FROM mcr.microsoft.com/devcontainers/base:ubuntu - -# provide DOCKER_GID via build args if you need to force group id to match host -ARG DOCKER_GID +ARG IMAGE_NAME=node_24_python_3_14 +ARG IMAGE_VERSION=latest +FROM ghcr.io/nhsdigital/eps-devcontainers/${IMAGE_NAME}:${IMAGE_VERSION} +USER root # specify DOCKER_GID to force container docker group id to match host RUN if [ -n "${DOCKER_GID}" ]; then \ - if ! getent group docker; then \ - groupadd -g ${DOCKER_GID} docker; \ - else \ - groupmod -g ${DOCKER_GID} docker; \ - fi && \ - usermod -aG docker vscode; \ + if ! getent group docker; then \ + groupadd -g ${DOCKER_GID} docker; \ + else \ + groupmod -g ${DOCKER_GID} docker; \ + fi && \ + usermod -aG docker vscode; \ fi - -# Anticipate and resolve potential permission issues with apt -RUN mkdir -p /tmp && chmod 1777 /tmp - -RUN apt-get update \ - && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y dist-upgrade \ - && apt-get -y install --no-install-recommends htop vim curl git build-essential \ - libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev libbz2-dev \ - zlib1g-dev unixodbc unixodbc-dev libsecret-1-0 libsecret-1-dev libsqlite3-dev \ - jq apt-transport-https ca-certificates gnupg-agent \ - software-properties-common bash-completion python3-pip make libbz2-dev \ - libreadline-dev libsqlite3-dev wget llvm libncurses5-dev libncursesw5-dev \ - xz-utils tk-dev liblzma-dev netcat-traditional libyaml-dev - -USER vscode - -# Install ASDF -RUN git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.11.3 && \ - echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc && \ - echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc - -ENV PATH="$PATH:/home/vscode/.asdf/bin/:/workspaces/eps-prescription-tracker-ui/node_modules/.bin:/workspaces/eps-common-workflows/.venv/bin" - -# Install ASDF plugins# -RUN asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git && \ - asdf plugin add actionlint && \ - asdf plugin add shellcheck https://github.com/luizm/asdf-shellcheck.git && \ - asdf plugin add poetry https://github.com/asdf-community/asdf-poetry.git && \ - asdf plugin add python - -WORKDIR /workspaces/eps-common-workflows - -ADD .tool-versions /workspaces/eps-common-workflows/.tool-versions -ADD .tool-versions /home/vscode/.tool-versions - -RUN asdf install python && \ - asdf install && \ - asdf reshim nodejs diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fe626eb..5257c4d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,15 +1,18 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu { - "name": "Ubuntu", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "name": "eps-common-workflows", "build": { "dockerfile": "Dockerfile", "context": "..", "args": { - "DOCKER_GID": "${env:DOCKER_GID:}" - } + "DOCKER_GID": "${env:DOCKER_GID:}", + "IMAGE_NAME": "node_24_python_3_14", + "IMAGE_VERSION": "pr-18-fce01b4", + "USER_UID": "${localEnv:USER_ID:}", + "USER_GID": "${localEnv:GROUP_ID:}" + }, + "updateRemoteUserUID": false }, + "postAttachCommand": "git-secrets --register-aws; git-secrets --add-provider -- cat /usr/share/secrets-scanner/nhsd-rules-deny.txt", "mounts": [ "source=${env:HOME}${env:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind", "source=${env:HOME}${env:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind", @@ -20,15 +23,7 @@ "remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }, - "postAttachCommand": "docker build -f /workspaces/eps-common-workflows/dockerfiles/nhsd-git-secrets.dockerfile -t git-secrets . && pre-commit install --install-hooks -f", - "features": { - "ghcr.io/devcontainers/features/github-cli:1": {}, - "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { - "version": "latest", - "moby": "true", - "installDockerBuildx": "true" - } - }, + "features": {}, "customizations": { "vscode": { "extensions": [ diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index f606e1f..8d046ae 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -16,11 +16,13 @@ jobs: AUTOMERGE_PEM: ${{ secrets.AUTOMERGE_PEM }} pr_title_format_check: uses: ./.github/workflows/pr_title_check.yml - get_asdf_version: + get_config_values: runs-on: ubuntu-22.04 outputs: asdf_version: ${{ steps.asdf-version.outputs.version }} tag_format: ${{ steps.load-config.outputs.TAG_FORMAT }} + devcontainer_version: ${{ steps.load-config.outputs.DEVCONTAINER_VERSION }} + devcontainer_image: ${{ steps.load-config.outputs.DEVCONTAINER_IMAGE }} steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd @@ -32,20 +34,26 @@ jobs: id: load-config run: | TAG_FORMAT=$(yq '.TAG_FORMAT' .github/config/settings.yml) - echo "TAG_FORMAT=$TAG_FORMAT" >> "$GITHUB_OUTPUT" + DEVCONTAINER_IMAGE=$(jq -r '.build.args.IMAGE_NAME' .devcontainer/devcontainer.json) + DEVCONTAINER_VERSION=$(jq -r '.build.args.IMAGE_VERSION' .devcontainer/devcontainer.json) + { + echo "TAG_FORMAT=$TAG_FORMAT" + echo "DEVCONTAINER_IMAGE=$DEVCONTAINER_IMAGE" + echo "DEVCONTAINER_VERSION=$DEVCONTAINER_VERSION" + } >> "$GITHUB_OUTPUT" quality_checks: uses: ./.github/workflows/quality-checks.yml - needs: [get_asdf_version] + needs: [get_config_values] with: - asdfVersion: ${{ needs.get_asdf_version.outputs.asdf_version }} + runtime_docker_image: "${{ needs.get_config_values.outputs.devcontainer_image }}:githubactions-${{ needs.get_config_values.outputs.devcontainer_version }}" secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} tag_release: - needs: [quality_checks, get_asdf_version] + needs: [quality_checks, get_config_values] uses: ./.github/workflows/tag-release.yml with: dry_run: true - asdfVersion: ${{ needs.get_asdf_version.outputs.asdf_version }} + asdfVersion: ${{ needs.get_config_values.outputs.asdf_version }} branch_name: ${{ github.event.pull_request.head.ref }} - tag_format: ${{ needs.get_asdf_version.outputs.tag_format }} + tag_format: ${{ needs.get_config_values.outputs.tag_format }} secrets: inherit diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml index faef2de..ee9dbda 100644 --- a/.github/workflows/quality-checks.yml +++ b/.github/workflows/quality-checks.yml @@ -6,23 +6,11 @@ on: SONAR_TOKEN: required: false inputs: - install_java: - type: boolean - description: "If true, the action will install java into the runner, separately from ASDF." - default: false - required: false run_sonar: type: boolean description: Toggle to run sonar code analyis on this repository. default: true required: false - asdfVersion: - type: string - required: true - reinstall_poetry: - type: boolean - description: Toggle to reinstall poetry on top of python version installed by asdf. - default: false run_docker_scan: type: boolean description: Toggle to run docker vulnerability scan on this repository. @@ -33,17 +21,14 @@ on: description: comma separated list of docker image references to scan when docker scanning is enabled. default: "" required: false + runtime_docker_image: + type: string + required: true jobs: quality_checks: runs-on: ubuntu-22.04 steps: - - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 - if: ${{ inputs.install_java }} - with: - java-version: "21" - distribution: "corretto" - - &checkout name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd @@ -51,69 +36,6 @@ jobs: ref: ${{ env.BRANCH_NAME }} fetch-depth: 0 - # Must be done before anything installs, or it will check dependencies for secrets too. - - name: Ensure .gitallowed exists, for secret scanning - run: | - if [ ! -f ".gitallowed" ]; then - echo "Creating empty .gitallowed file" - touch .gitallowed - fi - echo "./nhsd-rules-deny.txt:10" >> .gitallowed - echo "Allowing the following regex patterns:" - cat .gitallowed - - - name: Install git-secrets - run: | - sudo apt-get update - sudo apt-get install -y git curl - git clone https://github.com/awslabs/git-secrets.git /tmp/git-secrets - cd /tmp/git-secrets - sudo make install - - - name: Download regex patterns - run: | - curl -L https://raw.githubusercontent.com/NHSDigital/software-engineering-quality-framework/main/tools/nhsd-git-secrets/nhsd-rules-deny.txt -o nhsd-rules-deny.txt - - - name: Configure git-secrets - run: | - git-secrets --register-aws - git-secrets --add-provider -- cat nhsd-rules-deny.txt - - - name: Run secrets scan - run: | - git-secrets --scan-history . - - # using git commit sha for version of action to ensure we have stable version - - &install_asdf - name: Install asdf - uses: asdf-vm/actions/setup@b7bcd026f18772e44fe1026d729e1611cc435d47 - with: - asdf_version: ${{ inputs.asdfVersion }} - - - &cache_asdf - name: Cache asdf - uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb - with: - path: ~/.asdf - key: ${{ runner.os }}-asdf-${{ hashFiles('**/.tool-versions') }}-${{ inputs.asdfVersion }} - - - &install_asdf_deps - name: Install asdf dependencies in .tool-versions - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 - with: - asdf_version: ${{ inputs.asdfVersion }} - env: - PYTHON_CONFIGURE_OPTS: --enable-shared - - - &reinstall_poetry - name: Reinstall poetry - if: ${{ inputs.reinstall_poetry }} - run: | - poetry_tool_version=$(cat .tool-versions | grep poetry) - poetry_version=${poetry_tool_version//"poetry "} - asdf uninstall poetry "$poetry_version" - asdf install poetry - - &setup_npmrc name: Setting up .npmrc env: @@ -130,8 +52,32 @@ jobs: key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - name: make install - run: | - make install + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make install + echo "These are env vars" + echo "VAR1 is $VAR1" + echo "VAR2 is $VAR2" + env: + VAR1: value1 + VAR2: value2 + - name: Run secrets scan + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make secret-scan + - name: Run actionlint + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make actionlint - name: Check language tools used and setup trivy config id: check_languages @@ -180,153 +126,103 @@ jobs: echo "****************" echo "uses_go=false" >> "$GITHUB_OUTPUT" fi - touch trivy.yaml - - name: Update trivy config to include dev dependencies - uses: mikefarah/yq@2be0094729a1006f61e8339ce9934bfb3cbb549f - with: - cmd: yq -i '.pkg.include-dev-deps = true' 'trivy.yaml' - - name: convert python dependencies to requirements.txt - if: ${{ steps.check_languages.outputs.uses_poetry == 'true' }} - run: | - POETRY_VERSION=$(poetry --version | awk '{print $3}') - - if [[ "$(printf '%s\n' "2.0.0" "$POETRY_VERSION" "3.0.0" | sort -V | head -n1)" == "2.0.0" ]] \ - && [[ "$(printf '%s\n' "$POETRY_VERSION" "3.0.0" | sort -V | head -n1)" == "$POETRY_VERSION" ]]; then - echo "Poetry version $POETRY_VERSION is >=2.0.0 and <3.0.0 - installing plugin-export" - poetry self add poetry-plugin-export - else - echo "Poetry version $POETRY_VERSION is outside the required range so not installing plugin-export" - fi - poetry export -f requirements.txt --with dev --without-hashes --output=requirements.txt - - name: download go dependencies - if: ${{ steps.check_languages.outputs.uses_go == 'true' }} - run: | - cd src - go mod vendor - name: Check licenses - uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 with: - scan-type: "fs" - scan-ref: "." - severity: "CRITICAL,HIGH" - scanners: "license" - format: "table" - output: "license_scan.txt" - exit-code: "1" - list-all-pkgs: "false" - trivy-config: trivy.yaml - env: - VIRTUAL_ENV: "./.venv/" - - name: remove requirements.txt - if: ${{ steps.check_languages.outputs.uses_poetry == 'true' }} - run: | - rm -f requirements.txt - - name: clean go dependencies - if: ${{ steps.check_languages.outputs.uses_go == 'true' }} - run: | - cd src - rm -rf vendor + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make trivy-license-check + - name: Show license scan output if: always() run: | if [ -f license_scan.txt ]; then - cat license_scan.txt + cat .trivy_out/license_scan.txt fi - name: Run code lint - run: make lint + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make lint - name: Run ShellCheck - uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 with: - ignore_paths: >- - *test* - .venv - node_modules - .git + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make shellcheck - name: Run unit tests - run: make test - - name: Generate SBOM - uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make test + - name: make generate sbom + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 with: - scan-type: "fs" - scan-ref: "." - scanners: "vuln" - format: "cyclonedx" - output: "sbom.cdx.json" - exit-code: "0" - trivy-config: trivy.yaml + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make trivy-generate-sbom - name: Upload sbom uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f with: name: sbom.cdx.json - path: sbom.cdx.json + path: .trivy_out/sbom.cdx.json - name: Check python vulnerabilities if: ${{ steps.check_languages.outputs.uses_poetry == 'true' }} - uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 with: - scan-type: "fs" - skip-files: "**/package-lock.json,**/go.mod,**/pom.xml" - scan-ref: "." - severity: "CRITICAL,HIGH" - scanners: "vuln" - format: "table" - output: "dependency_results_python.txt" - exit-code: "1" - trivy-config: trivy.yaml + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make trivy-scan-python + - name: Check node vulnerabilities if: ${{ steps.check_languages.outputs.uses_node == 'true' }} - uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 with: - scan-type: "fs" - skip-files: "**/poetry.lock,**/go.mod,**/pom.xml" - scan-ref: "." - severity: "CRITICAL,HIGH" - scanners: "vuln" - format: "table" - output: "dependency_results_node.txt" - exit-code: "1" - trivy-config: trivy.yaml + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make trivy-scan-node - name: Check go vulnerabilities if: ${{ steps.check_languages.outputs.uses_go == 'true' }} - uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 with: - scan-type: "fs" - skip-files: "**/poetry.lock,**/package-lock.json,**/pom.xml" - scan-ref: "." - severity: "CRITICAL,HIGH" - scanners: "vuln" - format: "table" - output: "dependency_results_go.txt" - exit-code: "1" + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make trivy-scan-go - name: Check java vulnerabilities if: ${{ steps.check_languages.outputs.uses_java == 'true' }} - uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 with: - scan-type: "fs" - skip-files: "**/poetry.lock,**/package-lock.json,**/go.mod" - scan-ref: "." - severity: "CRITICAL,HIGH" - scanners: "vuln" - format: "table" - output: "dependency_results_java.txt" - exit-code: "1" - trivy-config: trivy.yaml + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make trivy-scan-java - name: Show vulnerability output if: always() run: | - if [ -f dependency_results_python.txt ]; then - cat dependency_results_python.txt + if [ -f .trivy_out/dependency_results_python.txt ]; then + cat .trivy_out/dependency_results_python.txt fi - if [ -f dependency_results_node.txt ]; then - cat dependency_results_node.txt + if [ -f .trivy_out/dependency_results_node.txt ]; then + cat .trivy_out/dependency_results_node.txt fi - if [ -f dependency_results_java.txt ]; then - cat dependency_results_java.txt + if [ -f .trivy_out/dependency_results_java.txt ]; then + cat .trivy_out/dependency_results_java.txt fi - if [ -f dependency_results_go.txt ]; then - cat dependency_results_go.txt + if [ -f .trivy_out/dependency_results_go.txt ]; then + cat .trivy_out/dependency_results_go.txt fi - name: "check is SONAR_TOKEN exists" env: @@ -431,36 +327,6 @@ jobs: with: ref: ${{ env.BRANCH_NAME }} fetch-depth: 0 - # using git commit sha for version of action to ensure we have stable version - - name: Install asdf - uses: asdf-vm/actions/setup@b7bcd026f18772e44fe1026d729e1611cc435d47 - with: - asdf_version: ${{ inputs.asdfVersion }} - - - name: Cache asdf - uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb - with: - path: | - ~/.asdf - key: ${{ runner.os }}-asdf-${{ hashFiles('**/.tool-versions') }}-${{ inputs.asdfVersion }} - restore-keys: | - ${{ runner.os }}-asdf-${{ hashFiles('**/.tool-versions') }}-${{ inputs.asdfVersion }} - - - name: Install asdf dependencies in .tool-versions - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 - with: - asdf_version: ${{ inputs.asdfVersion }} - env: - PYTHON_CONFIGURE_OPTS: --enable-shared - - - name: Reinstall poetry - if: ${{ inputs.reinstall_poetry }} - run: | - poetry_tool_version=$(cat .tool-versions | grep poetry) - poetry_version=${poetry_tool_version//"poetry "} - asdf uninstall poetry "$poetry_version" - asdf install poetry - - name: Setting up .npmrc env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -477,43 +343,44 @@ jobs: ${{ runner.os }}-node- - name: make install - run: | - make install + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make install - name: Build docker images if: ${{ inputs.run_docker_scan == true }} - run: | - make docker-build + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make docker-build - name: Check docker vulnerabilities - uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 with: - scan-type: "image" - image-ref: ${{ matrix.docker_image }} - severity: "CRITICAL,HIGH" - scanners: "vuln" - vuln-type: "os,library" - format: "table" - output: "dependency_results_docker.txt" - exit-code: "1" - trivy-config: trivy.yaml + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make trivy-scan-docker + env: + DOCKER_IMAGE: ${{ matrix.docker_image }} - name: Show docker vulnerability output if: always() run: | echo "Scan output for ${{ matrix.docker_image }}" - if [ -f dependency_results_docker.txt ]; then - cat dependency_results_docker.txt + if [ -f .trivy_out/dependency_results_docker.txt ]; then + cat .trivy_out/dependency_results_docker.txt fi IaC-validation: runs-on: ubuntu-22.04 steps: - *checkout - - *install_asdf - - *cache_asdf - - *install_asdf_deps - - *reinstall_poetry - name: Check for SAM templates id: check_sam_templates @@ -562,102 +429,58 @@ jobs: - name: Run cfn-lint if: steps.check_sam_templates.outputs.sam_exists == 'true' || steps.check_cf_templates.outputs.cf_exists == 'true' - run: | - pip install cfn-lint - cfn-lint -I "cloudformation/**/*.y*ml" 2>&1 | awk '/Run scan/ { print } /^[EW][0-9]/ { print; getline; print }' - cfn-lint -I "SAMtemplates/**/*.y*ml" 2>&1 | awk '/Run scan/ { print } /^[EW][0-9]/ { print; getline; print }' + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make cfn-lint - *cache_npm - *setup_npmrc - name: make install NodeJS if: steps.check_cdk.outputs.cdk_exists == 'true' - run: | - make install-node && make compile + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make install-node compile - name: Run cdk-synth if: steps.check_cdk.outputs.cdk_exists == 'true' - run: | - make cdk-synth - - - name: Install AWS SAM CLI - if: steps.check_sam_templates.outputs.sam_exists == 'true' - run: | - pip install aws-sam-cli - - - name: Init cfn-guard - run: | - #!/usr/bin/env bash - set -eou pipefail - - rm -rf /tmp/ruleset - rm -rf cfn_guard_output - - wget -O /tmp/ruleset.zip https://github.com/aws-cloudformation/aws-guard-rules-registry/releases/download/1.0.2/ruleset-build-v1.0.2.zip >/dev/null 2>&1 - unzip /tmp/ruleset.zip -d /tmp/ruleset/ >/dev/null 2>&1 - - curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/aws-cloudformation/cloudformation-guard/main/install-guard.sh | sh >/dev/null 2>&1 - - mkdir -p cfn_guard_output + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make cdk-synth - name: Run cfn-guard script for sam templates - if: steps.check_sam_templates.outputs.sam_exists == 'true' - run: | - #!/usr/bin/env bash - set -eou pipefail - - declare -a rulesets=("ncsc" "ncsc-cafv3" "wa-Reliability-Pillar" "wa-Security-Pillar") - for ruleset in "${rulesets[@]}" - do - while IFS= read -r -d '' file - do - echo "checking SAM template $file with ruleset $ruleset" - mkdir -p "$(dirname cfn_guard_output/"$file")" - - # Transform the SAM template to CloudFormation and then run through cfn-guard - SAM_OUTPUT=$(sam validate -t "$file" --region eu-west-2 --debug 2>&1 | \ - grep -Pazo '(?s)AWSTemplateFormatVersion.*\n\/' | tr -d '\0') - echo "${SAM_OUTPUT::-1}" | ~/.guard/bin/cfn-guard validate \ - --rules "/tmp/ruleset/output/$ruleset.guard" \ - --show-summary fail \ - > "cfn_guard_output/${file}_${ruleset}.txt" - - done < <(find ./SAMtemplates -name '*.y*ml' -print0) - done + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make cfn-guard-sam-templates - name: Run cfn-guard script for cloudformation templates if: steps.check_cf_templates.outputs.cf_exists == 'true' - run: | - #!/usr/bin/env bash - - declare -a rulesets=("ncsc" "ncsc-cafv3" "wa-Reliability-Pillar" "wa-Security-Pillar") - for ruleset in "${rulesets[@]}" - do - echo "Checking all templates in cloudformation folder with ruleset $ruleset" - - ~/.guard/bin/cfn-guard validate \ - --data cloudformation \ - --rules "/tmp/ruleset/output/$ruleset.guard" \ - --show-summary fail \ - > "cfn_guard_output/cloudformation_$ruleset.txt" - done - + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make cfn-guard-cloudformation - name: Run cfn-guard script for cdk templates if: steps.check_cdk.outputs.cdk_exists == 'true' - run: | - #!/usr/bin/env bash - - declare -a rulesets=("ncsc" "ncsc-cafv3" "wa-Reliability-Pillar" "wa-Security-Pillar") - for ruleset in "${rulesets[@]}" - do - echo "Checking all templates in cdk.out folder with ruleset $ruleset" - - ~/.guard/bin/cfn-guard validate \ - --data cdk.out \ - --rules "/tmp/ruleset/output/$ruleset.guard" \ - --show-summary fail \ - > "cfn_guard_output/cdk.out_$ruleset.txt" - done + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make cfn-guard-cdk - name: Download terraform plans uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 @@ -680,28 +503,20 @@ jobs: - name: Run cfn-guard script for terraform plans if: steps.check_terraform_plans.outputs.terraform_plans_exist == 'true' - run: | - #!/usr/bin/env bash - - declare -a rulesets=("ncsc" "ncsc-cafv3" "wa-Reliability-Pillar" "wa-Security-Pillar") - for ruleset in "${rulesets[@]}" - do - echo "Checking terraform plans with ruleset $ruleset" - - ~/.guard/bin/cfn-guard validate \ - --data terraform_plans \ - --rules "/tmp/ruleset/output/$ruleset.guard" \ - --show-summary fail \ - > "cfn_guard_output/terraform_plans_$ruleset.txt" - done + uses: anthony-nhs/docker-run-action-fork@80b8bf1eaca8275e1c2b848cf7ea5fabb2443d78 + with: + workspace_folder: ${{ github.workspace }} + image: ${{ inputs.runtime_docker_image }} + run: | + make cfn-guard-terraform - name: Show cfn-guard output if: failure() - run: find cfn_guard_output -type f -print0 | xargs -0 cat + run: find .cfn_guard_out -type f -print0 | xargs -0 cat - name: Upload cfn_guard_output if: failure() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f with: name: cfn_guard_output - path: cfn_guard_output + path: .cfn_guard_out diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4cc00a..dce12be 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,11 +8,12 @@ env: BRANCH_NAME: ${{ github.event.ref.BRANCH_NAME }} jobs: - get_asdf_version: + get_config_values: runs-on: ubuntu-22.04 outputs: asdf_version: ${{ steps.asdf-version.outputs.version }} tag_format: ${{ steps.load-config.outputs.TAG_FORMAT }} + devcontainer_version: ${{ steps.load-config.outputs.DEVCONTAINER_VERSION }} steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd @@ -24,20 +25,22 @@ jobs: id: load-config run: | TAG_FORMAT=$(yq '.TAG_FORMAT' .github/config/settings.yml) + DEVCONTAINER_VERSION=$(jq -r '.build.args.IMAGE_VERSION' .devcontainer/devcontainer.json) echo "TAG_FORMAT=$TAG_FORMAT" >> "$GITHUB_OUTPUT" + echo "DEVCONTAINER_VERSION=$DEVCONTAINER_VERSION" >> "$GITHUB_OUTPUT" quality_checks: - needs: [get_asdf_version] + needs: [get_config_values] uses: ./.github/workflows/quality-checks.yml with: - asdfVersion: ${{ needs.get_asdf_version.outputs.asdf_version }} + runtime_docker_image: "ghcr.io/nhsdigital/eps-devcontainers/node_24_python_3_14:${{ needs.get_config_values.outputs.devcontainer_version }}" secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} tag_release: - needs: [quality_checks, get_asdf_version] + needs: [quality_checks, get_config_values] uses: ./.github/workflows/tag-release.yml with: dry_run: false - asdfVersion: ${{ needs.get_asdf_version.outputs.asdf_version }} + asdfVersion: ${{ needs.get_config_values.outputs.asdf_version }} branch_name: main - tag_format: ${{ needs.get_asdf_version.outputs.tag_format }} + tag_format: ${{ needs.get_config_values.outputs.tag_format }} secrets: inherit diff --git a/.gitignore b/.gitignore index 5642dae..0ed3cb4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ .DS_Store release_notes .venv -.asdf \ No newline at end of file +.asdf +.trivy_out diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index db94a2e..f319077 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: entry: bash args: - -c - - 'docker run -v "$LOCAL_WORKSPACE_FOLDER:/src" git-secrets --pre_commit_hook' + - "git-secrets --pre_commit_hook" language: system - id: lint-githubactions name: Lint github actions diff --git a/.tool-versions b/.tool-versions index 8605e6e..d3c826a 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,5 +1,5 @@ -nodejs 24.12.0 +nodejs 24.13.0 actionlint 1.7.10 shellcheck 0.11.0 -python 3.14.2 -poetry 2.2.1 +python 3.14.3 +poetry 2.3.2 diff --git a/Makefile b/Makefile index aae27f8..864b5bb 100644 --- a/Makefile +++ b/Makefile @@ -32,3 +32,6 @@ test: build: echo "Not implemented" + +%: + @$(MAKE) -f /usr/local/share/eps/Mk/common.mk $@