From 5f9757770f8e45d44dcb740c9c885f67808bc9b8 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 16:29:36 +0200 Subject: [PATCH 1/9] Add cpd sonar input and update external github actions --- .github/workflows/reusable-container-publication.yml | 4 ++-- .github/workflows/reusable-container-scan.yml | 4 ++-- .github/workflows/reusable-dotnet-quality.yml | 6 +++--- .github/workflows/reusable-markup-lint.yml | 2 +- .github/workflows/reusable-terraform-deployment.yml | 6 +++--- .github/workflows/reusable-terraform-quality.yml | 8 ++++---- actions/dotnet/build-test-sonar/action.yml | 10 +++++++--- actions/dotnet/install-lint-restore/action.yml | 2 +- actions/dotnet/pack-push/action.yml | 2 +- 9 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.github/workflows/reusable-container-publication.yml b/.github/workflows/reusable-container-publication.yml index c82fc17..3d7af06 100644 --- a/.github/workflows/reusable-container-publication.yml +++ b/.github/workflows/reusable-container-publication.yml @@ -100,9 +100,9 @@ jobs: echo "$SECRET_VARS" >> "$GITHUB_ENV" fi - name: Clone repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Checkout workflow parts - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts ref: ${{ inputs.workflow-parts-version }} diff --git a/.github/workflows/reusable-container-scan.yml b/.github/workflows/reusable-container-scan.yml index 4ad6c29..40e4340 100644 --- a/.github/workflows/reusable-container-scan.yml +++ b/.github/workflows/reusable-container-scan.yml @@ -64,7 +64,7 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - name: Clone repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Build container image run: docker build . --file ${{ inputs.image-definition }} --tag ${{ env.IMAGE_REF }} shell: bash @@ -80,7 +80,7 @@ jobs: # nv-scanner-image: neuvector/scanner:5 - name: Scan container image with Trivy if: ${{ inputs.trivy-enabled }} - uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 (https://github.com/aquasecurity/trivy-action/releases) with: image-ref: ${{ env.IMAGE_REF }} format: 'table' diff --git a/.github/workflows/reusable-dotnet-quality.yml b/.github/workflows/reusable-dotnet-quality.yml index 58c705b..7c0f7f8 100644 --- a/.github/workflows/reusable-dotnet-quality.yml +++ b/.github/workflows/reusable-dotnet-quality.yml @@ -128,11 +128,11 @@ jobs: run: git config --global core.autocrlf false shell: bash - name: Clone repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 - name: Checkout workflow parts - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts ref: ${{ inputs.workflow-parts-version }} @@ -187,7 +187,7 @@ jobs: # # upload-artifact: true # Auto-upload to workflow artifacts - name: Archive test results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: dotnet-test-results path: | diff --git a/.github/workflows/reusable-markup-lint.yml b/.github/workflows/reusable-markup-lint.yml index d0f0b60..54e8dd1 100644 --- a/.github/workflows/reusable-markup-lint.yml +++ b/.github/workflows/reusable-markup-lint.yml @@ -28,7 +28,7 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - name: Check out repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Lint Markdown files run: npx markdownlint-cli2 "**/*.md" - name: Lint YAML files diff --git a/.github/workflows/reusable-terraform-deployment.yml b/.github/workflows/reusable-terraform-deployment.yml index 0088b82..63cfe7c 100644 --- a/.github/workflows/reusable-terraform-deployment.yml +++ b/.github/workflows/reusable-terraform-deployment.yml @@ -90,9 +90,9 @@ jobs: - 8080:8080 steps: - name: Clone repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Checkout workflow parts - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts ref: ${{ inputs.workflow-parts-version }} @@ -120,7 +120,7 @@ jobs: run: | ${{ inputs.custom-commands }} - name: Cache Terraform plugins - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: | ~/.terraform.d/plugin-cache diff --git a/.github/workflows/reusable-terraform-quality.yml b/.github/workflows/reusable-terraform-quality.yml index c6b2d08..67ae297 100644 --- a/.github/workflows/reusable-terraform-quality.yml +++ b/.github/workflows/reusable-terraform-quality.yml @@ -28,9 +28,9 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - name: Clone repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Cache Terraform plugins - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: | ~/.terraform.d/plugin-cache @@ -59,7 +59,7 @@ jobs: # sarif_file: results.sarif # TFLint is a pluggable terraform linter (ref. https://github.com/terraform-linters/tflint) - name: Cache TFLint plugins - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: ~/.tflint.d/plugins key: tflint-${{ hashFiles('**/.tflint.hcl') }} @@ -85,7 +85,7 @@ jobs: ignore-unfixed: true severity: "HIGH,CRITICAL" - name: Upload SARIF as artifact - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 if: always() with: name: security-results diff --git a/actions/dotnet/build-test-sonar/action.yml b/actions/dotnet/build-test-sonar/action.yml index 78ac516..5cadcc9 100644 --- a/actions/dotnet/build-test-sonar/action.yml +++ b/actions/dotnet/build-test-sonar/action.yml @@ -10,6 +10,10 @@ inputs: description: Java version that will be installed (for Sonar CLI) required: false default: "21" + sonar-cpd-exclusions: + description: Files or directories that should be excluded from Sonar code duplication analysis + required: false + default: "**/*Generated*.cs" sonar-exclusions: description: Files or directories that should be excluded from Sonar analysis required: false @@ -48,14 +52,14 @@ runs: java-version: ${{ inputs.java-version }} distribution: "zulu" - name: Cache Sonar packages - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: ~/sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Sonar scanner id: cache-sonar-scanner - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: ./.sonar/scanner key: ${{ runner.os }}-sonar-scanner @@ -74,7 +78,7 @@ runs: /n:"${{ inputs.sonar-project-name }}" \ /d:sonar.token="${{ inputs.sonar-token}}" \ /d:sonar.host.url="${{ inputs.sonar-host-url }}" \ - /d:sonar.cpd.exclusions="**/*Generated*.cs,${{ inputs.report-folder }}/**" \ + /d:sonar.cpd.exclusions="${{ inputs.report-folder }}/**,${{ inputs.sonar-cpd-exclusions }}" \ /d:sonar.exclusions="${{ inputs.report-folder }}/**,${{ inputs.sonar-exclusions }}" \ /d:sonar.coverageReportPaths="${{ inputs.report-folder }}/SonarQube.xml" \ /d:sonar.cs.vstest.reportsPaths="${{ inputs.report-folder }}/TestResults/*.trx" diff --git a/actions/dotnet/install-lint-restore/action.yml b/actions/dotnet/install-lint-restore/action.yml index 41ab806..a595889 100644 --- a/actions/dotnet/install-lint-restore/action.yml +++ b/actions/dotnet/install-lint-restore/action.yml @@ -11,7 +11,7 @@ runs: using: "composite" steps: - name: Install .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: dotnet-version: ${{ inputs.dotnet-version }} - name: Install .NET linters diff --git a/actions/dotnet/pack-push/action.yml b/actions/dotnet/pack-push/action.yml index a1de296..324e291 100644 --- a/actions/dotnet/pack-push/action.yml +++ b/actions/dotnet/pack-push/action.yml @@ -22,7 +22,7 @@ runs: using: "composite" steps: - name: Install .NET - uses: actions/setup-dotnet@v5 + uses: actions/setup-dotnet@v6 with: dotnet-version: ${{ inputs.dotnet-version }} - name: Package From 0f7bdaf78d3d801a302a6fc6f7b33a11ea188fe1 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 16:36:34 +0200 Subject: [PATCH 2/9] Pre-change commit --- .../workflows/reusable-container-publication.yml | 10 +++++----- .github/workflows/reusable-dotnet-quality.yml | 14 +++++++------- .github/workflows/reusable-terraform-quality.yml | 3 +-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/reusable-container-publication.yml b/.github/workflows/reusable-container-publication.yml index 3d7af06..b71c780 100644 --- a/.github/workflows/reusable-container-publication.yml +++ b/.github/workflows/reusable-container-publication.yml @@ -130,11 +130,11 @@ jobs: cache-to: type=gha,mode=max build-args: ${{ inputs.extra-build-arguments }} # TODO: replace with a more secure action - # - name: Generate SBOM with Syft - # uses: anchore/sbom-action@v0 - # continue-on-error: true - # with: - # image: ${{ env.IMAGE_REF }} + - name: Generate SBOM with Syft + uses: anchore/sbom-action@v0 + continue-on-error: true + with: + image: ${{ env.IMAGE_REF }} - name: Push latest tag if: ${{ inputs.create-latest }} run: | diff --git a/.github/workflows/reusable-dotnet-quality.yml b/.github/workflows/reusable-dotnet-quality.yml index 7c0f7f8..35d967c 100644 --- a/.github/workflows/reusable-dotnet-quality.yml +++ b/.github/workflows/reusable-dotnet-quality.yml @@ -178,13 +178,13 @@ jobs: run: echo '${{ steps.fossa.outputs.report }}' > report/fossa.html continue-on-error: true # TODO: replace with more secure action - # - name: Generate SBOM with Syft - # uses: anchore/sbom-action@v0 - # # with: - # # path: . # Or Dockerfile path - # # format: spdx-json # Or cyclonedx-json - # # output-file: sbom.json - # # upload-artifact: true # Auto-upload to workflow artifacts + - name: Generate SBOM with Syft + uses: anchore/sbom-action@v0 + # with: + # path: . # Or Dockerfile path + # format: spdx-json # Or cyclonedx-json + # output-file: sbom.json + # upload-artifact: true # Auto-upload to workflow artifacts - name: Archive test results if: always() uses: actions/upload-artifact@v7 diff --git a/.github/workflows/reusable-terraform-quality.yml b/.github/workflows/reusable-terraform-quality.yml index 67ae297..3c38476 100644 --- a/.github/workflows/reusable-terraform-quality.yml +++ b/.github/workflows/reusable-terraform-quality.yml @@ -76,8 +76,7 @@ jobs: - name: Run TFLint run: tflint --recursive --format compact - name: Run Trivy IaC scan - # v0.35.0 - uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 with: scan-type: "config" format: "sarif" From db8cd04b0ec9a17d2e5e38bb93949aac491e0de4 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 16:45:52 +0200 Subject: [PATCH 3/9] Add missing input --- .github/workflows/reusable-dotnet-quality.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/reusable-dotnet-quality.yml b/.github/workflows/reusable-dotnet-quality.yml index 35d967c..d60eb60 100644 --- a/.github/workflows/reusable-dotnet-quality.yml +++ b/.github/workflows/reusable-dotnet-quality.yml @@ -48,6 +48,11 @@ on: type: boolean required: false default: false + sonar-cpd-exclusions: + description: Files or directories that should be excluded from Sonar code duplication analysis + type: string + required: false + default: "**/*Generated*.cs" sonar-exclusions: description: Files or directories that should be excluded from Sonar analysis type: string @@ -155,6 +160,7 @@ jobs: uses: ./workflow-parts/actions/dotnet/build-test-sonar with: dotnet-test-args: ${{ inputs.dotnet-test-args }} + sonar-cpd-exclusions: ${{ inputs.sonar-cpd-exclusions }} sonar-exclusions: ${{ inputs.sonar-exclusions }} sonar-host-url: ${{ inputs.sonar-host-url }} sonar-organization: ${{ inputs.sonar-organization }} From b08667dde447e4564e0a9ea9d38313a2597a5361 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 17:29:31 +0200 Subject: [PATCH 4/9] Replace external actions setup-terraform, setup-tflint, sbom-action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced the three external actions with self-hosted composite actions that download official release binaries directly and verify SHA256 checksums (same pattern already used by actions/cosign/sign in this repo), removing the third-party marketplace dependency: ┌───────────────────────────────────────┬───────────────────────────────────────────────────────────────────────────────┐ │ New action │ Replaces │ ├───────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ │ actions/terraform/setup/action.yml │ hashicorp/setup-terraform │ ├───────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ │ actions/tflint/setup/action.yml │ terraform-linters/setup-tflint │ ├───────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤ │ actions/syft/generate-sbom/action.yml │ anchore/sbom-action (the two # TODO: replace with a more secure action spots) │ └───────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────┘ I ran each install script for real (curl'd the actual releases, verified checksums, ran the binaries) to confirm the URLs/filenames are right before wiring them in — all three work. Updated the four reusable workflows that used them (reusable-terraform-quality.yml, reusable-terraform-deployment.yml, reusable-container-publication.yml, reusable-dotnet-quality.yml), adding a workflow-parts-version input + "checkout workflow parts" step to reusable-terraform-quality.yml (it didn't have one yet, needed to reach the local composite actions). Persisted tests in ci.yml (not thrown away after manual verification, per your note): - test-terraform-setup, test-tflint-setup, test-syft-generate-sbom — call each new composite action directly and assert the tool actually works. - test-terraform-quality — calls reusable-terraform-quality.yml end-to-end against a new minimal fixture at test/fixtures/terraform/, exercising terraform+tflint together plus Checkov/Trivy. Verified locally: terraform fmt/init/validate and a Trivy config scan both pass clean against the fixture. --- .editorconfig | 4 +- .github/workflows/ci.yml | 52 +++++++++++ .../reusable-container-publication.yml | 5 +- .github/workflows/reusable-dotnet-quality.yml | 9 +- .../reusable-terraform-deployment.yml | 2 +- .../workflows/reusable-terraform-quality.yml | 19 +++- .gitignore | 2 + README.md | 28 +++++- actions/syft/generate-sbom/action.yml | 90 +++++++++++++++++++ actions/terraform/setup/action.yml | 43 +++++++++ actions/tflint/setup/action.yml | 42 +++++++++ test/fixtures/terraform/.terraform.lock.hcl | 23 +++++ test/fixtures/terraform/.tflint.hcl | 4 + test/fixtures/terraform/main.tf | 20 +++++ 14 files changed, 323 insertions(+), 20 deletions(-) create mode 100644 .gitignore create mode 100644 actions/syft/generate-sbom/action.yml create mode 100644 actions/terraform/setup/action.yml create mode 100644 actions/tflint/setup/action.yml create mode 100644 test/fixtures/terraform/.terraform.lock.hcl create mode 100644 test/fixtures/terraform/.tflint.hcl create mode 100644 test/fixtures/terraform/main.tf diff --git a/.editorconfig b/.editorconfig index 9ae301b..42b5ed3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,7 +5,9 @@ end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true + +[*.{js,json,tf*,yml,yaml}] trim_trailing_whitespace = true -[md] +[*.md] trim_trailing_whitespace = false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee4192d..53a6349 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,3 +14,55 @@ jobs: markup-lint: name: Markup uses: ./.github/workflows/reusable-markup-lint.yml + + test-terraform-setup: + name: "Test: Terraform setup action" + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v7 + - name: Install terraform + uses: ./actions/terraform/setup + - name: Verify terraform is installed + run: | + terraform version + command -v terraform + + test-tflint-setup: + name: "Test: TFLint setup action" + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v7 + - name: Setup TFLint + uses: ./actions/tflint/setup + - name: Verify tflint is installed + run: | + tflint --version + command -v tflint + + test-syft-generate-sbom: + name: "Test: Syft generate-sbom action" + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v7 + - name: Generate SBOM for this repository + uses: ./actions/syft/generate-sbom + with: + target-path: actions + output-file: sbom-test.spdx.json + upload-artifact: "false" + - name: Verify SBOM was generated + run: | + test -s sbom-test.spdx.json + grep -q '"spdxVersion"' sbom-test.spdx.json + + test-terraform-quality: + name: "Test: Terraform quality reusable workflow" + uses: ./.github/workflows/reusable-terraform-quality.yml + with: + working-directory: test/fixtures/terraform + # github.ref (not github.sha) so the "checkout workflow parts" step inside the reusable + # workflow can fetch by ref name: refs/heads/ on push, refs/pull//merge on PRs. + workflow-parts-version: ${{ github.ref }} diff --git a/.github/workflows/reusable-container-publication.yml b/.github/workflows/reusable-container-publication.yml index b71c780..2573f05 100644 --- a/.github/workflows/reusable-container-publication.yml +++ b/.github/workflows/reusable-container-publication.yml @@ -129,12 +129,11 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max build-args: ${{ inputs.extra-build-arguments }} - # TODO: replace with a more secure action - name: Generate SBOM with Syft - uses: anchore/sbom-action@v0 + uses: ./workflow-parts/actions/syft/generate-sbom continue-on-error: true with: - image: ${{ env.IMAGE_REF }} + target-image: ${{ env.IMAGE_REF }} - name: Push latest tag if: ${{ inputs.create-latest }} run: | diff --git a/.github/workflows/reusable-dotnet-quality.yml b/.github/workflows/reusable-dotnet-quality.yml index d60eb60..cc0700e 100644 --- a/.github/workflows/reusable-dotnet-quality.yml +++ b/.github/workflows/reusable-dotnet-quality.yml @@ -183,14 +183,9 @@ jobs: if: ${{ inputs.fossa-enabled && steps.fossa.outcome == 'success' }} run: echo '${{ steps.fossa.outputs.report }}' > report/fossa.html continue-on-error: true - # TODO: replace with more secure action - name: Generate SBOM with Syft - uses: anchore/sbom-action@v0 - # with: - # path: . # Or Dockerfile path - # format: spdx-json # Or cyclonedx-json - # output-file: sbom.json - # upload-artifact: true # Auto-upload to workflow artifacts + uses: ./workflow-parts/actions/syft/generate-sbom + continue-on-error: true - name: Archive test results if: always() uses: actions/upload-artifact@v7 diff --git a/.github/workflows/reusable-terraform-deployment.yml b/.github/workflows/reusable-terraform-deployment.yml index 63cfe7c..b586e7c 100644 --- a/.github/workflows/reusable-terraform-deployment.yml +++ b/.github/workflows/reusable-terraform-deployment.yml @@ -126,7 +126,7 @@ jobs: ~/.terraform.d/plugin-cache key: terraform-${{ hashFiles('**/.terraform.lock.hcl') }} - name: Install terraform - uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e + uses: ./workflow-parts/actions/terraform/setup - name: Terraform init run: terraform init - name: Terraform validate diff --git a/.github/workflows/reusable-terraform-quality.yml b/.github/workflows/reusable-terraform-quality.yml index 3c38476..255722e 100644 --- a/.github/workflows/reusable-terraform-quality.yml +++ b/.github/workflows/reusable-terraform-quality.yml @@ -18,6 +18,11 @@ on: type: string required: false default: "ubuntu-latest" + workflow-parts-version: + description: "GitHub workflow parts version (branch/tag/SHA)" + type: string + required: false + default: "main" jobs: terraform-validate: @@ -29,6 +34,12 @@ jobs: steps: - name: Clone repository uses: actions/checkout@v7 + - name: Checkout workflow parts + uses: actions/checkout@v7 + with: + repository: devpro/github-workflow-parts + ref: ${{ inputs.workflow-parts-version }} + path: workflow-parts - name: Cache Terraform plugins uses: actions/cache@v6 with: @@ -36,7 +47,7 @@ jobs: ~/.terraform.d/plugin-cache key: terraform-${{ hashFiles('**/.terraform.lock.hcl') }} - name: Install terraform - uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e + uses: ./workflow-parts/actions/terraform/setup - name: Check Terraform format run: terraform fmt -recursive -check - name: Terraform Init @@ -64,10 +75,10 @@ jobs: path: ~/.tflint.d/plugins key: tflint-${{ hashFiles('**/.tflint.hcl') }} - name: Setup TFLint - uses: terraform-linters/setup-tflint@b480b8fcdaa6f2c577f8e4fa799e89e756bb7c93 + uses: ./workflow-parts/actions/tflint/setup with: - # ref. https://github.com/terraform-linters/tflint/pkgs/container/tflint - tflint_version: v0.60.0 + # ref. https://github.com/terraform-linters/tflint/releases + tflint-version: v0.60.0 - name: Initialize TFLint run: tflint --init --recursive env: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..372dd25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.terraform/ +*.tfplan diff --git a/README.md b/README.md index d4efd66..453e73b 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,16 @@ [![CI](https://github.com/devpro/github-workflow-parts/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/devpro/github-workflow-parts/actions/workflows/ci.yml) -GitHub workflow components you can trust for your repositories. -Keep your pipelines DRY! (Don't Repeat Yourself) +GitHub workflow components for code repositories. + +> [!IMPORTANT] +> Keep your pipelines **safe** and **DRY**! ## Reusable workflows -> Rather than copying and pasting from one workflow to another, you can make workflows reusable ([Reusing workflow configurations](https://docs.github.com/en/actions/concepts/workflows-and-actions/reusing-workflow-configurations)) +> [!TIP] +> Rather than copying and pasting from one workflow to another, you can make workflows reusable. +> Source: [Reusing workflow configurations](https://docs.github.com/en/actions/concepts/workflows-and-actions/reusing-workflow-configurations) Containers: @@ -29,7 +33,9 @@ Terraform: ## Composite actions -> Composite actions allow you to collect a series of workflow job steps into a single action which you can then run as a single job step in multiple workflows ([Creating a composite action](https://docs.github.com/en/actions/tutorials/create-actions/create-a-composite-action)) +> [!TIP] +> Composite actions allow you to collect a series of workflow job steps into a single action which you can then run as a single job step in multiple workflows. +> Source: [Creating a composite action](https://docs.github.com/en/actions/tutorials/create-actions/create-a-composite-action) Containers: @@ -45,3 +51,17 @@ MongoDB: - [Add your runner IP address to Atlas access list](actions/mongodb-atlas/add-runner-ip/action.yml) - [Start a server in your pipeline](actions/mongodb/start/action.yml) + +SBOM: + +- [Generate a SBOM with Syft](actions/syft/generate-sbom/action.yml) + +Terraform: + +- [Setup Terraform](actions/terraform/setup/action.yml) +- [Setup TFLint](actions/tflint/setup/action.yml) + +> [!NOTE] +> These composite actions download official release binaries directly (with SHA256 checksum verification) +> instead of relying on third-party marketplace actions (`hashicorp/setup-terraform`, `terraform-linters/setup-tflint`, `anchore/sbom-action`), reducing supply-chain exposure. +> Linux runners only (for example `ubuntu-latest`). diff --git a/actions/syft/generate-sbom/action.yml b/actions/syft/generate-sbom/action.yml new file mode 100644 index 0000000..13baa0e --- /dev/null +++ b/actions/syft/generate-sbom/action.yml @@ -0,0 +1,90 @@ +name: Generate SBOM with Syft +description: | + Installs Syft by downloading the official binary from its GitHub release, verifying its + SHA256 checksum, and uses it to generate a Software Bill of Materials for a container image + or a source directory (replacement for anchore/sbom-action, to reduce third-party GitHub + Action supply-chain risk). + Linux runners only (for example ubuntu-latest). + +inputs: + syft-version: + description: Version of Syft to install (check latest from https://github.com/anchore/syft/releases) + required: false + default: "1.49.0" + target-image: + description: Container image reference to scan (mutually exclusive with target-path) + required: false + default: "" + target-path: + description: Source directory to scan, used only when target-image is not set + required: false + default: "." + output-format: + description: SBOM format (see https://github.com/anchore/syft#supported-output-formats) + required: false + default: "spdx-json" + output-file: + description: Path of the generated SBOM file + required: false + default: "sbom.spdx.json" + upload-artifact: + description: Upload the generated SBOM as a workflow artifact + required: false + default: "true" + artifact-name: + description: Name of the uploaded workflow artifact + required: false + default: "sbom" + +runs: + using: "composite" + steps: + - name: Install Syft + shell: bash + env: + SYFT_VERSION: ${{ inputs.syft-version }} + SYFT_TARBALL: syft_${{ inputs.syft-version }}_linux_amd64.tar.gz + SYFT_CHECKSUMS: syft_${{ inputs.syft-version }}_checksums.txt + run: | + BASE_URL="https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}" + + echo "Downloading Syft binary and checksums..." + curl -sSL -O "${BASE_URL}/${SYFT_TARBALL}" + curl -sSL -O "${BASE_URL}/${SYFT_CHECKSUMS}" + + echo "Verifying checksum..." + grep " ${SYFT_TARBALL}$" "${SYFT_CHECKSUMS}" | sha256sum --check --status + + if [ $? -eq 0 ]; then + echo "Verification successful!" + tar -xzf "${SYFT_TARBALL}" syft + chmod +x syft + sudo mv syft /usr/local/bin/syft + rm -f "${SYFT_TARBALL}" "${SYFT_CHECKSUMS}" + syft version + else + echo "ERROR: Checksum verification failed!" >&2 + exit 1 + fi + - name: Generate SBOM + shell: bash + env: + TARGET_IMAGE: ${{ inputs.target-image }} + TARGET_PATH: ${{ inputs.target-path }} + OUTPUT_FORMAT: ${{ inputs.output-format }} + OUTPUT_FILE: ${{ inputs.output-file }} + run: | + if [ -n "$TARGET_IMAGE" ]; then + SOURCE="$TARGET_IMAGE" + else + SOURCE="dir:${TARGET_PATH}" + fi + + echo "Scanning ${SOURCE} with Syft..." + syft "$SOURCE" -o "${OUTPUT_FORMAT}=${OUTPUT_FILE}" + - name: Upload SBOM as artifact + if: ${{ inputs.upload-artifact == 'true' }} + uses: actions/upload-artifact@v7 + with: + name: ${{ inputs.artifact-name }} + path: ${{ inputs.output-file }} diff --git a/actions/terraform/setup/action.yml b/actions/terraform/setup/action.yml new file mode 100644 index 0000000..834f2bd --- /dev/null +++ b/actions/terraform/setup/action.yml @@ -0,0 +1,43 @@ +name: Setup Terraform +description: | + Installs the Terraform CLI by downloading the official binary from HashiCorp's release + server and verifying its SHA256 checksum (replacement for hashicorp/setup-terraform, to + reduce third-party GitHub Action supply-chain risk). + Linux runners only (for example ubuntu-latest). + +inputs: + terraform-version: + description: Version of Terraform to install (check latest from https://releases.hashicorp.com/terraform/) + required: false + default: "1.15.8" + +runs: + using: "composite" + steps: + - name: Install Terraform + shell: bash + env: + TERRAFORM_VERSION: ${{ inputs.terraform-version }} + TERRAFORM_ZIP: terraform_${{ inputs.terraform-version }}_linux_amd64.zip + TERRAFORM_SHA256SUMS: terraform_${{ inputs.terraform-version }}_SHA256SUMS + run: | + BASE_URL="https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}" + + echo "Downloading Terraform binary and checksums..." + curl -sSL -O "${BASE_URL}/${TERRAFORM_ZIP}" + curl -sSL -O "${BASE_URL}/${TERRAFORM_SHA256SUMS}" + + echo "Verifying checksum..." + grep " ${TERRAFORM_ZIP}$" "${TERRAFORM_SHA256SUMS}" | sha256sum --check --status + + if [ $? -eq 0 ]; then + echo "Verification successful!" + unzip -o "${TERRAFORM_ZIP}" + chmod +x terraform + sudo mv terraform /usr/local/bin/terraform + rm -f "${TERRAFORM_ZIP}" "${TERRAFORM_SHA256SUMS}" + terraform version + else + echo "ERROR: Checksum verification failed!" >&2 + exit 1 + fi diff --git a/actions/tflint/setup/action.yml b/actions/tflint/setup/action.yml new file mode 100644 index 0000000..29a04bb --- /dev/null +++ b/actions/tflint/setup/action.yml @@ -0,0 +1,42 @@ +name: Setup TFLint +description: | + Installs TFLint by downloading the official binary from its GitHub release and verifying + its SHA256 checksum (replacement for terraform-linters/setup-tflint, to reduce third-party + GitHub Action supply-chain risk). + Linux runners only (for example ubuntu-latest). + +inputs: + tflint-version: + description: Version of TFLint to install (check latest from https://github.com/terraform-linters/tflint/releases) + required: false + default: "v0.60.0" + +runs: + using: "composite" + steps: + - name: Install TFLint + shell: bash + env: + TFLINT_VERSION: ${{ inputs.tflint-version }} + TFLINT_ZIP: tflint_linux_amd64.zip + run: | + BASE_URL="https://github.com/terraform-linters/tflint/releases/download/${TFLINT_VERSION}" + + echo "Downloading TFLint binary and checksums..." + curl -sSL -O "${BASE_URL}/${TFLINT_ZIP}" + curl -sSL -O "${BASE_URL}/checksums.txt" + + echo "Verifying checksum..." + grep " ${TFLINT_ZIP}$" checksums.txt | sha256sum --check --status + + if [ $? -eq 0 ]; then + echo "Verification successful!" + unzip -o "${TFLINT_ZIP}" + chmod +x tflint + sudo mv tflint /usr/local/bin/tflint + rm -f "${TFLINT_ZIP}" checksums.txt + tflint --version + else + echo "ERROR: Checksum verification failed!" >&2 + exit 1 + fi diff --git a/test/fixtures/terraform/.terraform.lock.hcl b/test/fixtures/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..be7a6ca --- /dev/null +++ b/test/fixtures/terraform/.terraform.lock.hcl @@ -0,0 +1,23 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/local" { + version = "2.9.0" + constraints = "~> 2.5" + hashes = [ + "h1:9rBZCMNpxKwMlRbWH2QpwD3kqUCAejdOZQ/aiiDObXQ=", + "zh:0baa4566cf77f1ff52f4293d1c8536202dd23edc197c3196413a28343c3ac3a0", + "zh:16b5559c3c07088ddad11a9bb9e9c0799999363c2958e9a5be2bcbbf2cd9ca64", + "zh:197c79015a10d1cce904a8ea722cbc750c42aeae2da53f44a6a0751d9fd1aa90", + "zh:29d0b03e5343a80677ebfeb2e2c31cbe4b1f65e736e53417454a4277fec2544c", + "zh:4896bfa6cf1d2fd562b47ef2e87f47862ae92a04f8ad5d764380f0c6653473b8", + "zh:531f8529cbca49f681883e57761a05a8398afaef6d1ab0d205d26bf12f4428e8", + "zh:6aaf5011d83161c86d2bfb80c0923ec934e578288758da2f37acb7aec129004b", + "zh:7430275253d3d3c40aa6179e0ec0d63212874dbbc06c5a51b9d07ec590f9756c", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:be17dc611e95e26cdf6cad79dfccf1064f0e32032a2efeb939a9bbe7fb1cbfe9", + "zh:f0e3b0aa644202e1d79d2000dca91f6019425da71e9800fa23f27e51c034f195", + "zh:f62bae4519e4ead49182ddc8afe8cf61e2a4c3ba3973b0fbba967736a2696aa3", + "zh:fcafa360a5b0b96244f26f4e3a6d642b716a376557142c2442ff2fb12d11da18", + ] +} diff --git a/test/fixtures/terraform/.tflint.hcl b/test/fixtures/terraform/.tflint.hcl new file mode 100644 index 0000000..427121c --- /dev/null +++ b/test/fixtures/terraform/.tflint.hcl @@ -0,0 +1,4 @@ +plugin "terraform" { + enabled = true + preset = "recommended" +} diff --git a/test/fixtures/terraform/main.tf b/test/fixtures/terraform/main.tf new file mode 100644 index 0000000..e7a3b55 --- /dev/null +++ b/test/fixtures/terraform/main.tf @@ -0,0 +1,20 @@ +# Minimal, valid Terraform configuration used by this repository's own CI (see .github/workflows/ci.yml) +# to exercise the reusable-terraform-quality.yml workflow end-to-end: terraform fmt/init/validate, +# Checkov, TFLint and Trivy all run against it. +# It intentionally has no cloud provider dependency so it can run without any credentials. + +terraform { + required_version = ">= 1.9.0" + + required_providers { + local = { + source = "hashicorp/local" + version = "~> 2.5" + } + } +} + +resource "local_file" "example" { + filename = "${path.module}/example.txt" + content = "Hello from the github-workflow-parts CI test fixture.\n" +} From 401e3db7d40e069183be902b0a9f32af6a424343 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 17:47:09 +0200 Subject: [PATCH 5/9] Update mdlint file --- .github/workflows/ci.yml | 3 +-- .markdownlint-cli2.yaml | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53a6349..5b6bb71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,5 @@ jobs: uses: ./.github/workflows/reusable-terraform-quality.yml with: working-directory: test/fixtures/terraform - # github.ref (not github.sha) so the "checkout workflow parts" step inside the reusable - # workflow can fetch by ref name: refs/heads/ on push, refs/pull//merge on PRs. + # github.ref (not github.sha) so the "checkout workflow parts" step inside the reusable workflow can fetch by ref name: refs/heads/ on push, refs/pull//merge on PRs. workflow-parts-version: ${{ github.ref }} diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml index 5176f75..dae97fc 100644 --- a/.markdownlint-cli2.yaml +++ b/.markdownlint-cli2.yaml @@ -4,5 +4,6 @@ ignores: config: # ref. https://github.com/DavidAnson/markdownlint default: true + MD009: false MD013: line_length: 240 From 408593dab66f3060cfb91325fd8413ba1875b4cb Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 18:09:55 +0200 Subject: [PATCH 6/9] Trivy replacement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Trivy replacement — added actions/trivy/scan/action.yml (same download-and-verify pattern as the others) and wired it into both call sites: - reusable-container-scan.yml — blocking image scan (exit-code: 1) - reusable-terraform-quality.yml — non-blocking config/IaC scan → SARIF I tested both scan types for real, including against your devprofr/terraform-backend-mongodb:latest image (pulled it, ran the exact composite-action command — found real CVEs in the .NET runtime layer, as expected). One correction along the way: the original config-scan call passed ignore-unfixed: true, but current Trivy CLI actually errors on that flag for config scans (it's vuln-scan-only) — I dropped it there rather than silently replicate a flag that doesn't apply. I also added test-trivy-scan to ci.yml, using your real image for the image-scan test rather than a throwaway public one. 2. workflow-parts-version — better fix, applied to all 5 reusable workflows: default changed from the floating "main" to "", with the checkout step now doing ref: ${{ inputs.workflow-parts-version || github.workflow_sha }}. github.workflow_sha is populated by GitHub Actions to the exact commit of the reusable workflow file currently executing — so a caller pinning @v2.3.1 automatically gets composite actions checked out at that same v2.3.1, with zero extra config. Previously, even a caller pinning a release tag would silently get whatever's on main internally — a real reproducibility gap. I left ci.yml's own self-test (test-terraform-quality) with an explicit github.ref override since that job calls the reusable workflow via a local ./ path, and I couldn't verify workflow_sha's behavior for same-repo local calls without an actual GitHub Actions run. --- .github/workflows/ci.yml | 29 ++++- .../reusable-container-publication.yml | 6 +- .github/workflows/reusable-container-scan.yml | 26 +++-- .github/workflows/reusable-dotnet-quality.yml | 6 +- .../reusable-terraform-deployment.yml | 6 +- .../workflows/reusable-terraform-quality.yml | 18 +-- README.md | 3 +- actions/syft/generate-sbom/action.yml | 7 +- actions/terraform/setup/action.yml | 5 +- actions/tflint/setup/action.yml | 5 +- actions/trivy/scan/action.yml | 106 ++++++++++++++++++ 11 files changed, 179 insertions(+), 38 deletions(-) create mode 100644 actions/trivy/scan/action.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b6bb71..3850a84 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,10 +58,37 @@ jobs: test -s sbom-test.spdx.json grep -q '"spdxVersion"' sbom-test.spdx.json + test-trivy-scan: + name: "Test: Trivy scan action" + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v7 + - name: Scan a real image with Trivy + uses: ./actions/trivy/scan + with: + scan-type: image + # a real image used elsewhere in this repo (reusable-terraform-deployment.yml), so this + # doubles as a smoke test against realistic content instead of a throwaway public image + target: devprofr/terraform-backend-mongodb:latest + severity: CRITICAL,HIGH + exit-code: "0" + - name: Scan a directory with Trivy + uses: ./actions/trivy/scan + with: + scan-type: config + target: test/fixtures/terraform + format: sarif + output-file: trivy-test.sarif + severity: HIGH,CRITICAL + - name: Verify SARIF report was generated + run: test -s trivy-test.sarif + test-terraform-quality: name: "Test: Terraform quality reusable workflow" uses: ./.github/workflows/reusable-terraform-quality.yml with: working-directory: test/fixtures/terraform - # github.ref (not github.sha) so the "checkout workflow parts" step inside the reusable workflow can fetch by ref name: refs/heads/ on push, refs/pull//merge on PRs. + # explicit override: for this same-repo local "./" call, github.workflow_sha's behavior + # is unverified, so pin to a ref we know is fetchable (branch on push, PR merge ref on PRs) workflow-parts-version: ${{ github.ref }} diff --git a/.github/workflows/reusable-container-publication.yml b/.github/workflows/reusable-container-publication.yml index 2573f05..2bb69bc 100644 --- a/.github/workflows/reusable-container-publication.yml +++ b/.github/workflows/reusable-container-publication.yml @@ -58,10 +58,10 @@ on: required: false default: ubuntu-latest workflow-parts-version: - description: GitHub workflow parts version (branch/tag/SHA) + description: GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA. type: string required: false - default: main + default: "" working-directory: description: Working directory type: string @@ -105,7 +105,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version }} + ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} path: workflow-parts - name: Login to container registry uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee diff --git a/.github/workflows/reusable-container-scan.yml b/.github/workflows/reusable-container-scan.yml index 40e4340..8deead1 100644 --- a/.github/workflows/reusable-container-scan.yml +++ b/.github/workflows/reusable-container-scan.yml @@ -49,6 +49,11 @@ on: type: boolean required: false default: true + workflow-parts-version: + description: "GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA." + type: string + required: false + default: "" working-directory: description: Working directory type: string @@ -65,6 +70,12 @@ jobs: steps: - name: Clone repository uses: actions/checkout@v7 + - name: Checkout workflow parts + uses: actions/checkout@v7 + with: + repository: devpro/github-workflow-parts + ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} + path: workflow-parts - name: Build container image run: docker build . --file ${{ inputs.image-definition }} --tag ${{ env.IMAGE_REF }} shell: bash @@ -80,14 +91,15 @@ jobs: # nv-scanner-image: neuvector/scanner:5 - name: Scan container image with Trivy if: ${{ inputs.trivy-enabled }} - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 (https://github.com/aquasecurity/trivy-action/releases) + uses: ./workflow-parts/actions/trivy/scan with: - image-ref: ${{ env.IMAGE_REF }} - format: 'table' - exit-code: '1' - ignore-unfixed: true - vuln-type: 'os,library' - severity: 'CRITICAL,HIGH' + scan-type: image + target: ${{ env.IMAGE_REF }} + format: table + exit-code: "1" + ignore-unfixed: "true" + pkg-types: os,library + severity: CRITICAL,HIGH env: GITHUB_TOKEN: ${{ github.token }} IMAGE_REF: ${{ inputs.image-path }}/${{ inputs.image-name }}:${{ inputs.image-tag }} diff --git a/.github/workflows/reusable-dotnet-quality.yml b/.github/workflows/reusable-dotnet-quality.yml index cc0700e..b4adad3 100644 --- a/.github/workflows/reusable-dotnet-quality.yml +++ b/.github/workflows/reusable-dotnet-quality.yml @@ -79,10 +79,10 @@ on: required: false default: "" workflow-parts-version: - description: GitHub workflow parts version (branch/tag/SHA) + description: GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA. type: string required: false - default: main + default: "" working-directory: description: Working directory type: string @@ -140,7 +140,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version }} + ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} path: workflow-parts - name: Install .NET & lint uses: ./workflow-parts/actions/dotnet/install-lint-restore diff --git a/.github/workflows/reusable-terraform-deployment.yml b/.github/workflows/reusable-terraform-deployment.yml index b586e7c..06edc6d 100644 --- a/.github/workflows/reusable-terraform-deployment.yml +++ b/.github/workflows/reusable-terraform-deployment.yml @@ -37,10 +37,10 @@ on: required: false default: "" workflow-parts-version: - description: "GitHub workflow parts version (branch/tag/SHA)" + description: "GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA." type: string required: false - default: "main" + default: "" secrets: atlas-publickey: description: "Atlas public key" @@ -95,7 +95,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version }} + ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} path: workflow-parts - name: Add runner ID to MongoDB Atlas uses: ./workflow-parts/actions/mongodb-atlas/add-runner-ip diff --git a/.github/workflows/reusable-terraform-quality.yml b/.github/workflows/reusable-terraform-quality.yml index 255722e..47a73b0 100644 --- a/.github/workflows/reusable-terraform-quality.yml +++ b/.github/workflows/reusable-terraform-quality.yml @@ -19,10 +19,10 @@ on: required: false default: "ubuntu-latest" workflow-parts-version: - description: "GitHub workflow parts version (branch/tag/SHA)" + description: "GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA." type: string required: false - default: "main" + default: "" jobs: terraform-validate: @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version }} + ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} path: workflow-parts - name: Cache Terraform plugins uses: actions/cache@v6 @@ -87,13 +87,13 @@ jobs: - name: Run TFLint run: tflint --recursive --format compact - name: Run Trivy IaC scan - uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 + uses: ./workflow-parts/actions/trivy/scan with: - scan-type: "config" - format: "sarif" - output: "trivy-results.sarif" - ignore-unfixed: true - severity: "HIGH,CRITICAL" + scan-type: config + target: . + format: sarif + output-file: trivy-results.sarif + severity: HIGH,CRITICAL - name: Upload SARIF as artifact uses: actions/upload-artifact@v7 if: always() diff --git a/README.md b/README.md index 453e73b..cf9a308 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,5 @@ Terraform: - [Setup TFLint](actions/tflint/setup/action.yml) > [!NOTE] -> These composite actions download official release binaries directly (with SHA256 checksum verification) -> instead of relying on third-party marketplace actions (`hashicorp/setup-terraform`, `terraform-linters/setup-tflint`, `anchore/sbom-action`), reducing supply-chain exposure. +> These composite actions download official release binaries directly (with SHA256 checksum verification) instead of relying on third-party marketplace actions, reducing supply-chain exposure. > Linux runners only (for example `ubuntu-latest`). diff --git a/actions/syft/generate-sbom/action.yml b/actions/syft/generate-sbom/action.yml index 13baa0e..c4872ba 100644 --- a/actions/syft/generate-sbom/action.yml +++ b/actions/syft/generate-sbom/action.yml @@ -1,9 +1,8 @@ name: Generate SBOM with Syft description: | - Installs Syft by downloading the official binary from its GitHub release, verifying its - SHA256 checksum, and uses it to generate a Software Bill of Materials for a container image - or a source directory (replacement for anchore/sbom-action, to reduce third-party GitHub - Action supply-chain risk). + Installs Syft by downloading the official binary from its GitHub release, verifying its SHA256 checksum, + and uses it to generate a Software Bill of Materials for a container image or a source directory + (replacement for anchore/sbom-action, to reduce third-party GitHub Action supply-chain risk). Linux runners only (for example ubuntu-latest). inputs: diff --git a/actions/terraform/setup/action.yml b/actions/terraform/setup/action.yml index 834f2bd..21d6590 100644 --- a/actions/terraform/setup/action.yml +++ b/actions/terraform/setup/action.yml @@ -1,8 +1,7 @@ name: Setup Terraform description: | - Installs the Terraform CLI by downloading the official binary from HashiCorp's release - server and verifying its SHA256 checksum (replacement for hashicorp/setup-terraform, to - reduce third-party GitHub Action supply-chain risk). + Installs the Terraform CLI by downloading the official binary from HashiCorp's release server and verifying its SHA256 checksum + (replacement for hashicorp/setup-terraform, to reduce third-party GitHub Action supply-chain risk). Linux runners only (for example ubuntu-latest). inputs: diff --git a/actions/tflint/setup/action.yml b/actions/tflint/setup/action.yml index 29a04bb..770a67b 100644 --- a/actions/tflint/setup/action.yml +++ b/actions/tflint/setup/action.yml @@ -1,8 +1,7 @@ name: Setup TFLint description: | - Installs TFLint by downloading the official binary from its GitHub release and verifying - its SHA256 checksum (replacement for terraform-linters/setup-tflint, to reduce third-party - GitHub Action supply-chain risk). + Installs TFLint by downloading the official binary from its GitHub release and verifying its SHA256 checksum (replacement for terraform-linters/setup-tflint, + to reduce third-party GitHub Action supply-chain risk). Linux runners only (for example ubuntu-latest). inputs: diff --git a/actions/trivy/scan/action.yml b/actions/trivy/scan/action.yml new file mode 100644 index 0000000..98d7286 --- /dev/null +++ b/actions/trivy/scan/action.yml @@ -0,0 +1,106 @@ +name: Scan with Trivy +description: | + Installs Trivy by downloading the official binary from its GitHub release and verifying its SHA256 checksum, + then scans a container image or a source directory for vulnerabilities or misconfigurations (replacement for aquasecurity/trivy-action, + to reduce third-party GitHub Action supply-chain risk). + Linux runners only (for example ubuntu-latest). + +inputs: + trivy-version: + description: Version of Trivy to install (check latest from https://github.com/aquasecurity/trivy/releases) + required: false + default: "0.72.0" + scan-type: + description: What to scan, "image" (container image reference) or "config" (misconfiguration/IaC scan of a directory) + required: false + default: "image" + target: + description: Container image reference (scan-type "image") or directory path (scan-type "config") to scan + required: true + format: + description: Trivy report format (table, json, sarif, cyclonedx, spdx-json...) + required: false + default: "table" + output-file: + description: Path of the generated report file (empty prints to stdout) + required: false + default: "" + severity: + description: Comma-separated list of severities to report + required: false + default: "CRITICAL,HIGH" + exit-code: + description: Exit code to use when findings matching the severity filter are detected (0 = never fail the step) + required: false + default: "0" + ignore-unfixed: + description: Only report vulnerabilities with a fix available (scan-type "image" only) + required: false + default: "false" + pkg-types: + description: Comma-separated package types to scan, os and/or library (scan-type "image" only) + required: false + default: "os,library" + +runs: + using: "composite" + steps: + - name: Install Trivy + shell: bash + env: + TRIVY_VERSION: ${{ inputs.trivy-version }} + TRIVY_TARBALL: trivy_${{ inputs.trivy-version }}_Linux-64bit.tar.gz + TRIVY_CHECKSUMS: trivy_${{ inputs.trivy-version }}_checksums.txt + run: | + BASE_URL="https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}" + + echo "Downloading Trivy binary and checksums..." + curl -sSL -O "${BASE_URL}/${TRIVY_TARBALL}" + curl -sSL -O "${BASE_URL}/${TRIVY_CHECKSUMS}" + + echo "Verifying checksum..." + grep " ${TRIVY_TARBALL}$" "${TRIVY_CHECKSUMS}" | sha256sum --check --status + + if [ $? -eq 0 ]; then + echo "Verification successful!" + tar -xzf "${TRIVY_TARBALL}" trivy + chmod +x trivy + sudo mv trivy /usr/local/bin/trivy + rm -f "${TRIVY_TARBALL}" "${TRIVY_CHECKSUMS}" + trivy version + else + echo "ERROR: Checksum verification failed!" >&2 + exit 1 + fi + - name: Run Trivy scan + shell: bash + env: + SCAN_TYPE: ${{ inputs.scan-type }} + TARGET: ${{ inputs.target }} + FORMAT: ${{ inputs.format }} + OUTPUT_FILE: ${{ inputs.output-file }} + SEVERITY: ${{ inputs.severity }} + EXIT_CODE: ${{ inputs.exit-code }} + IGNORE_UNFIXED: ${{ inputs.ignore-unfixed }} + PKG_TYPES: ${{ inputs.pkg-types }} + run: | + args=("$SCAN_TYPE" --format "$FORMAT" --severity "$SEVERITY" --exit-code "$EXIT_CODE") + + if [ -n "$OUTPUT_FILE" ]; then + args+=(--output "$OUTPUT_FILE") + fi + + # ignore-unfixed and pkg-types only make sense for vulnerability (image) scans + if [ "$SCAN_TYPE" = "image" ]; then + if [ "$IGNORE_UNFIXED" = "true" ]; then + args+=(--ignore-unfixed) + fi + if [ -n "$PKG_TYPES" ]; then + args+=(--pkg-types "$PKG_TYPES") + fi + fi + + args+=("$TARGET") + + echo "Running: trivy ${args[*]}" + trivy "${args[@]}" From 8b2539f03ceee8ef2e812211274037716f173693 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 18:14:49 +0200 Subject: [PATCH 7/9] Update messages --- .github/workflows/ci.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3850a84..11a5e9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: uses: ./.github/workflows/reusable-markup-lint.yml test-terraform-setup: - name: "Test: Terraform setup action" + name: "Test > Terraform setup action" runs-on: ubuntu-latest steps: - name: Clone repository @@ -29,7 +29,7 @@ jobs: command -v terraform test-tflint-setup: - name: "Test: TFLint setup action" + name: "Test > TFLint setup action" runs-on: ubuntu-latest steps: - name: Clone repository @@ -42,7 +42,7 @@ jobs: command -v tflint test-syft-generate-sbom: - name: "Test: Syft generate-sbom action" + name: "Test > Syft generate-sbom action" runs-on: ubuntu-latest steps: - name: Clone repository @@ -59,7 +59,7 @@ jobs: grep -q '"spdxVersion"' sbom-test.spdx.json test-trivy-scan: - name: "Test: Trivy scan action" + name: "Test > Trivy scan action" runs-on: ubuntu-latest steps: - name: Clone repository @@ -68,8 +68,6 @@ jobs: uses: ./actions/trivy/scan with: scan-type: image - # a real image used elsewhere in this repo (reusable-terraform-deployment.yml), so this - # doubles as a smoke test against realistic content instead of a throwaway public image target: devprofr/terraform-backend-mongodb:latest severity: CRITICAL,HIGH exit-code: "0" @@ -85,10 +83,10 @@ jobs: run: test -s trivy-test.sarif test-terraform-quality: - name: "Test: Terraform quality reusable workflow" + name: "Test > Terraform quality reusable workflow" uses: ./.github/workflows/reusable-terraform-quality.yml with: working-directory: test/fixtures/terraform - # explicit override: for this same-repo local "./" call, github.workflow_sha's behavior - # is unverified, so pin to a ref we know is fetchable (branch on push, PR merge ref on PRs) + # explicit override: for this same-repo local "./" call, github.workflow_sha's behavior is unverified, + # so pin to a ref we know is fetchable (branch on push, PR merge ref on PRs) workflow-parts-version: ${{ github.ref }} From 59b9bf41346f5b8c32edb49046dd59512492ddd2 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 18:25:38 +0200 Subject: [PATCH 8/9] Revert part fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What went wrong: github.workflow_sha isn't scoped per reusable-workflow-call the way I claimed — it's tied to the top-level triggering run, shared across the whole run including every nested reusable workflow. So inside github-workflow-parts' own reusable workflow, it resolved to keeptrack's commit SHA (the caller), not a commit in github-workflow-parts — hence not our ref. I trusted a single WebFetch summary of GitHub's docs on a point that was easy to get backwards, and didn't cross-check before shipping it. That's on me, and I should have verified against an actual test run before recommending it as a fix rather than reasoning from documentation alone. Fix applied: reverted all 5 workflows to the original, known-working behavior — workflow-parts-version defaults back to "main", and the checkout step uses ref: ${{ inputs.workflow-parts-version }} directly, no fallback expression. I also caught and fixed a second bug from my own revert script: a blind string-replace had corrupted custom-commands's default ("" → "main") in reusable-terraform-deployment.yml since it matched the same pattern earlier in that file — fixed and re-verified with yamllint + a YAML parse of all 6 files. The reproducibility problem is real (a caller pinning @v2.3.1 still gets internal composite actions from main), but there's no reliable expression-based fix for it — it would need callers to explicitly pass workflow-parts-version matching their pin (documented via the input description now), or a heavier OIDC-claim-based approach, which isn't worth the complexity here. Recommend merging this revert to unblock keeptrack PR #467 before anything else. --- .github/workflows/reusable-container-publication.yml | 6 +++--- .github/workflows/reusable-container-scan.yml | 6 +++--- .github/workflows/reusable-dotnet-quality.yml | 6 +++--- .github/workflows/reusable-terraform-deployment.yml | 6 +++--- .github/workflows/reusable-terraform-quality.yml | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/reusable-container-publication.yml b/.github/workflows/reusable-container-publication.yml index 2bb69bc..cdad238 100644 --- a/.github/workflows/reusable-container-publication.yml +++ b/.github/workflows/reusable-container-publication.yml @@ -58,10 +58,10 @@ on: required: false default: ubuntu-latest workflow-parts-version: - description: GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA. + description: GitHub workflow parts version (branch/tag/SHA). Should match the ref/tag/SHA the caller pinned for this reusable workflow itself. type: string required: false - default: "" + default: main working-directory: description: Working directory type: string @@ -105,7 +105,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} + ref: ${{ inputs.workflow-parts-version }} path: workflow-parts - name: Login to container registry uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee diff --git a/.github/workflows/reusable-container-scan.yml b/.github/workflows/reusable-container-scan.yml index 8deead1..33405b3 100644 --- a/.github/workflows/reusable-container-scan.yml +++ b/.github/workflows/reusable-container-scan.yml @@ -50,10 +50,10 @@ on: required: false default: true workflow-parts-version: - description: "GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA." + description: "GitHub workflow parts version (branch/tag/SHA). Should match the ref/tag/SHA the caller pinned for this reusable workflow itself." type: string required: false - default: "" + default: "main" working-directory: description: Working directory type: string @@ -74,7 +74,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} + ref: ${{ inputs.workflow-parts-version }} path: workflow-parts - name: Build container image run: docker build . --file ${{ inputs.image-definition }} --tag ${{ env.IMAGE_REF }} diff --git a/.github/workflows/reusable-dotnet-quality.yml b/.github/workflows/reusable-dotnet-quality.yml index b4adad3..2f31708 100644 --- a/.github/workflows/reusable-dotnet-quality.yml +++ b/.github/workflows/reusable-dotnet-quality.yml @@ -79,10 +79,10 @@ on: required: false default: "" workflow-parts-version: - description: GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA. + description: GitHub workflow parts version (branch/tag/SHA). Should match the ref/tag/SHA the caller pinned for this reusable workflow itself. type: string required: false - default: "" + default: main working-directory: description: Working directory type: string @@ -140,7 +140,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} + ref: ${{ inputs.workflow-parts-version }} path: workflow-parts - name: Install .NET & lint uses: ./workflow-parts/actions/dotnet/install-lint-restore diff --git a/.github/workflows/reusable-terraform-deployment.yml b/.github/workflows/reusable-terraform-deployment.yml index 06edc6d..c2bc2ed 100644 --- a/.github/workflows/reusable-terraform-deployment.yml +++ b/.github/workflows/reusable-terraform-deployment.yml @@ -37,10 +37,10 @@ on: required: false default: "" workflow-parts-version: - description: "GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA." + description: "GitHub workflow parts version (branch/tag/SHA). Should match the ref/tag/SHA the caller pinned for this reusable workflow itself." type: string required: false - default: "" + default: "main" secrets: atlas-publickey: description: "Atlas public key" @@ -95,7 +95,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} + ref: ${{ inputs.workflow-parts-version }} path: workflow-parts - name: Add runner ID to MongoDB Atlas uses: ./workflow-parts/actions/mongodb-atlas/add-runner-ip diff --git a/.github/workflows/reusable-terraform-quality.yml b/.github/workflows/reusable-terraform-quality.yml index 47a73b0..4e41fbc 100644 --- a/.github/workflows/reusable-terraform-quality.yml +++ b/.github/workflows/reusable-terraform-quality.yml @@ -19,10 +19,10 @@ on: required: false default: "ubuntu-latest" workflow-parts-version: - description: "GitHub workflow parts version (branch/tag/SHA). Defaults to this reusable workflow's own pinned ref/SHA." + description: "GitHub workflow parts version (branch/tag/SHA). Should match the ref/tag/SHA the caller pinned for this reusable workflow itself." type: string required: false - default: "" + default: "main" jobs: terraform-validate: @@ -38,7 +38,7 @@ jobs: uses: actions/checkout@v7 with: repository: devpro/github-workflow-parts - ref: ${{ inputs.workflow-parts-version || github.workflow_sha }} + ref: ${{ inputs.workflow-parts-version }} path: workflow-parts - name: Cache Terraform plugins uses: actions/cache@v6 From 188783567fd98cf7e0b211e078486db7ac365ff8 Mon Sep 17 00:00:00 2001 From: Bertrand THOMAS Date: Sun, 26 Jul 2026 18:26:13 +0200 Subject: [PATCH 9/9] Update message --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11a5e9a..c814d7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: uses: ./.github/workflows/reusable-markup-lint.yml test-terraform-setup: - name: "Test > Terraform setup action" + name: "Test / Terraform setup action" runs-on: ubuntu-latest steps: - name: Clone repository @@ -29,7 +29,7 @@ jobs: command -v terraform test-tflint-setup: - name: "Test > TFLint setup action" + name: "Test / TFLint setup action" runs-on: ubuntu-latest steps: - name: Clone repository @@ -42,7 +42,7 @@ jobs: command -v tflint test-syft-generate-sbom: - name: "Test > Syft generate-sbom action" + name: "Test / Syft generate-sbom action" runs-on: ubuntu-latest steps: - name: Clone repository @@ -59,7 +59,7 @@ jobs: grep -q '"spdxVersion"' sbom-test.spdx.json test-trivy-scan: - name: "Test > Trivy scan action" + name: "Test / Trivy scan action" runs-on: ubuntu-latest steps: - name: Clone repository @@ -83,7 +83,7 @@ jobs: run: test -s trivy-test.sarif test-terraform-quality: - name: "Test > Terraform quality reusable workflow" + name: "Test / Terraform quality reusable workflow" uses: ./.github/workflows/reusable-terraform-quality.yml with: working-directory: test/fixtures/terraform