From f4dcb2dea6c5d510ae3c8ec912bd4b6f291a9dc2 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Mon, 28 Sep 2026 12:24:59 +0400 Subject: [PATCH 1/7] Run code scan only when dependency manifests change --- .github/workflows/test.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index aa8f65f9c95..2bd6d5f646c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,15 +41,38 @@ jobs: permissions: contents: read packages: read + pull-requests: read if: github.event_name == 'pull_request' steps: + - name: Check if scan should run + id: should_run + uses: dorny/paths-filter@v4.0.2 + with: + filters: | + deps: + - '**/pom.xml' + - '.mvn/**' + - '**/package.json' + - '**/package-lock.json' + - '**/yarn.lock' + - '**/pnpm-lock.yaml' + - '**/go.mod' + - '**/go.sum' + - '**/Cargo.toml' + - '**/Cargo.lock' + - '**/Dockerfile*' + - '.trivyignore' + - '.github/steps/trivy/**' + - name: Checkout + if: steps.should_run.outputs.deps == 'true' uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Scan code + if: steps.should_run.outputs.deps == 'true' uses: ./.github/steps/trivy with: scan: code From cbeb2a29ebee66a24e7a2bb305e304a6f93e6cc3 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Mon, 28 Sep 2026 12:59:05 +0400 Subject: [PATCH 2/7] Move scan gating into the trivy action --- .github/steps/trivy/action.yml | 23 ++++++++++++++++++++--- .github/workflows/test.yml | 22 ---------------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/.github/steps/trivy/action.yml b/.github/steps/trivy/action.yml index c37e9dda93b..a36514cdb28 100644 --- a/.github/steps/trivy/action.yml +++ b/.github/steps/trivy/action.yml @@ -16,14 +16,29 @@ inputs: runs: using: "composite" steps: + - name: Check if scan should run + id: should_run + if: inputs.scan == 'code' + uses: dorny/paths-filter@v4.0.3 + with: + predicate-quantifier: some-with-excludes + filters: | + changed: + - 'pom.xml' + - '.trivyignore' + - '.github/steps/trivy/**' + - '${{ inputs.path }}/**' + ${{ inputs.skip-dirs && format('- ''!{0}/{1}/**''', inputs.path, inputs.skip-dirs) || '' }} + - name: Install Trivy + if: steps.should_run.outputs.changed != 'false' uses: aquasecurity/setup-trivy@v0.3.1 with: version: v0.74.0 cache: true - name: Resolve Maven dependencies - if: inputs.scan == 'code' + if: inputs.scan == 'code' && steps.should_run.outputs.changed != 'false' shell: bash env: GITHUB_TOKEN: ${{ inputs.maven-token }} @@ -40,7 +55,7 @@ runs: echo "::warning::Some Maven dependencies could not be resolved; scan depth may be reduced" - name: Scan dependencies - if: inputs.scan == 'code' + if: inputs.scan == 'code' && steps.should_run.outputs.changed != 'false' shell: bash env: SCAN_DIR: ${{ inputs.path }} @@ -63,7 +78,7 @@ runs: done < "$report" > "$report.tmp" && mv "$report.tmp" "$report" - name: Scan Dockerfile base images - if: inputs.scan == 'code' + if: inputs.scan == 'code' && steps.should_run.outputs.changed != 'false' shell: bash env: SCAN_DIR: ${{ inputs.path }} @@ -105,6 +120,7 @@ runs: find . -maxdepth 1 -name 'trivy-*.tsv' -empty -delete - name: Upload report + if: steps.should_run.outputs.changed != 'false' uses: actions/upload-artifact@v4 with: name: ${{ inputs.scan == 'code' && (inputs.image-name && format('scan_code-{0}', inputs.image-name) || 'scan_code') || format('scan_image-{0}', inputs.image-name) }} @@ -112,6 +128,7 @@ runs: if-no-files-found: ignore - name: Evaluate + if: steps.should_run.outputs.changed != 'false' shell: bash env: ARTIFACT: ${{ inputs.scan == 'code' && 'scan_code' || 'scan_image' }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2bd6d5f646c..eef24b03ce7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,35 +44,13 @@ jobs: pull-requests: read if: github.event_name == 'pull_request' steps: - - name: Check if scan should run - id: should_run - uses: dorny/paths-filter@v4.0.2 - with: - filters: | - deps: - - '**/pom.xml' - - '.mvn/**' - - '**/package.json' - - '**/package-lock.json' - - '**/yarn.lock' - - '**/pnpm-lock.yaml' - - '**/go.mod' - - '**/go.sum' - - '**/Cargo.toml' - - '**/Cargo.lock' - - '**/Dockerfile*' - - '.trivyignore' - - '.github/steps/trivy/**' - - name: Checkout - if: steps.should_run.outputs.deps == 'true' uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Scan code - if: steps.should_run.outputs.deps == 'true' uses: ./.github/steps/trivy with: scan: code From 0f0cc92c54ceea5002f10c5915a3030a91dbfbb6 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Mon, 28 Sep 2026 13:31:18 +0400 Subject: [PATCH 3/7] Resolve Maven dependencies for nested modules under the scan path --- .github/steps/trivy/action.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/steps/trivy/action.yml b/.github/steps/trivy/action.yml index a36514cdb28..30c95dd355a 100644 --- a/.github/steps/trivy/action.yml +++ b/.github/steps/trivy/action.yml @@ -47,10 +47,11 @@ runs: ACTION_PATH: ${{ github.action_path }} run: | set -euo pipefail - [ -f "$SCAN_DIR/pom.xml" ] || exit 0 - mvn_args=(-B -q -fn -DskipTests -gs "$ACTION_PATH/central-mirror.xml" -f "$SCAN_DIR/pom.xml") + poms=$([ -f "$SCAN_DIR/pom.xml" ] && echo "$SCAN_DIR/pom.xml" || find "$SCAN_DIR" -maxdepth 2 -name pom.xml) + [ -n "$poms" ] || exit 0 + mvn_args=(-B -q -fn -DskipTests -gs "$ACTION_PATH/central-mirror.xml") [ -f .mvn/settings.xml ] && mvn_args+=(-s .mvn/settings.xml) - mvn "${mvn_args[@]}" dependency:go-offline | tee /tmp/mvn.log || true + for pom in $poms; do mvn "${mvn_args[@]}" -f "$pom" dependency:go-offline | tee -a /tmp/mvn.log || true; done ! grep -q '\[ERROR\]' /tmp/mvn.log || echo "::warning::Some Maven dependencies could not be resolved; scan depth may be reduced" From 523cf97a248b8a1b989dff2c032a05857972d141 Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Mon, 28 Sep 2026 22:05:52 +0400 Subject: [PATCH 4/7] Run code scans as a matrix and gate root scans on listed paths --- .github/steps/trivy/action.yml | 5 +++-- .github/workflows/test.yml | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/steps/trivy/action.yml b/.github/steps/trivy/action.yml index 30c95dd355a..937db802e04 100644 --- a/.github/steps/trivy/action.yml +++ b/.github/steps/trivy/action.yml @@ -6,6 +6,7 @@ inputs: description: "'code' or 'image'" required: true path: { default: "." } + paths: { default: "" } skip-dirs: { default: "" } skip-files: { default: "" } image-name: { default: "" } @@ -27,8 +28,8 @@ runs: - 'pom.xml' - '.trivyignore' - '.github/steps/trivy/**' - - '${{ inputs.path }}/**' - ${{ inputs.skip-dirs && format('- ''!{0}/{1}/**''', inputs.path, inputs.skip-dirs) || '' }} + - '${{ inputs.paths && format('{{{0},}}', inputs.paths) || format('{0}/**', inputs.path) }}' + ${{ !inputs.paths && inputs.skip-dirs && format('- ''!{0}/{1}/**''', inputs.path, inputs.skip-dirs) || '' }} - name: Install Trivy if: steps.should_run.outputs.changed != 'false' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eef24b03ce7..6c42b9ea928 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,7 +35,7 @@ jobs: !github.event.pull_request.draft scan: - name: "Scan Code" + name: "Scan Code: ${{ matrix.target.name }}" runs-on: ubuntu-latest needs: [changes] permissions: @@ -43,6 +43,11 @@ jobs: packages: read pull-requests: read if: github.event_name == 'pull_request' + strategy: + fail-fast: false + matrix: + target: + - { name: root, path: "." } steps: - name: Checkout uses: actions/checkout@v4 @@ -54,6 +59,8 @@ jobs: uses: ./.github/steps/trivy with: scan: code + path: ${{ matrix.target.path }} + image-name: ${{ matrix.target.name }} test_client: name: "Test Client (${{ matrix.name }})" From e861f2ec28e4b88c0ca314c26ca3e43fce64b14f Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Mon, 28 Sep 2026 22:23:48 +0400 Subject: [PATCH 5/7] Split root scans into backend and frontend targets --- .github/steps/trivy/action.yml | 5 ++++- .github/workflows/test.yml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/steps/trivy/action.yml b/.github/steps/trivy/action.yml index 937db802e04..36633a113ce 100644 --- a/.github/steps/trivy/action.yml +++ b/.github/steps/trivy/action.yml @@ -28,7 +28,7 @@ runs: - 'pom.xml' - '.trivyignore' - '.github/steps/trivy/**' - - '${{ inputs.paths && format('{{{0},}}', inputs.paths) || format('{0}/**', inputs.path) }}' + - '${{ inputs.paths || format('{0}/**', inputs.path) }}' ${{ !inputs.paths && inputs.skip-dirs && format('- ''!{0}/{1}/**''', inputs.path, inputs.skip-dirs) || '' }} - name: Install Trivy @@ -85,11 +85,14 @@ runs: env: SCAN_DIR: ${{ inputs.path }} SKIP_DIRS: ${{ inputs.skip-dirs }} + SKIP_FILES: ${{ inputs.skip-files }} ACTION_PATH: ${{ github.action_path }} run: | set -euo pipefail find_args=(-name 'Dockerfile*' -not -path '*/.git/*' -not -path '*/node_modules/*') for dir in ${SKIP_DIRS//,/ }; do find_args+=(-not -path "*/$dir/*" -not -path "$dir/*"); done + IFS=, read -ra skip_files <<< "$SKIP_FILES" + for file in "${skip_files[@]}"; do find_args+=(-not -name "${file##*/}"); done find "$SCAN_DIR" "${find_args[@]}" -print0 | xargs -0 -r awk 'toupper($1)=="FROM" { img=$2; if (img ~ /^--/) img=$3; print img; if (toupper($3)=="AS") print "~"$4; if (toupper($4)=="AS") print "~"$5 }' | sort -u > /tmp/base-images.txt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6c42b9ea928..5b0d3a77e7e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,8 @@ jobs: fail-fast: false matrix: target: - - { name: root, path: "." } + - { name: backend, path: ".", paths: "**/{go.mod,go.sum,Dockerfile*}", skip_files: "**/package.json,**/package-lock.json,**/yarn.lock" } + - { name: frontend, path: ".", paths: "**/{package.json,package-lock.json,yarn.lock}", skip_files: "**/go.mod,**/go.sum,**/Dockerfile*" } steps: - name: Checkout uses: actions/checkout@v4 @@ -60,6 +61,8 @@ jobs: with: scan: code path: ${{ matrix.target.path }} + paths: ${{ matrix.target.paths || '' }} + skip-files: ${{ matrix.target.skip_files || '' }} image-name: ${{ matrix.target.name }} test_client: From 173b5f15dca1a890c183dabf393cdce06a0af04d Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Mon, 28 Sep 2026 23:16:19 +0400 Subject: [PATCH 6/7] Run code scan only when dependency files change --- .github/steps/trivy/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/steps/trivy/action.yml b/.github/steps/trivy/action.yml index 36633a113ce..d32dfeaad96 100644 --- a/.github/steps/trivy/action.yml +++ b/.github/steps/trivy/action.yml @@ -28,7 +28,7 @@ runs: - 'pom.xml' - '.trivyignore' - '.github/steps/trivy/**' - - '${{ inputs.paths || format('{0}/**', inputs.path) }}' + - '${{ inputs.paths || format('{0}/**/{{pom.xml,package.json,package-lock.json,yarn.lock,pnpm-lock.yaml,go.mod,go.sum,Cargo.toml,Cargo.lock,Dockerfile*}}', inputs.path) }}' ${{ !inputs.paths && inputs.skip-dirs && format('- ''!{0}/{1}/**''', inputs.path, inputs.skip-dirs) || '' }} - name: Install Trivy From 7ae7afac8a2c12f981097f22e27ddfe6fdd7cb4c Mon Sep 17 00:00:00 2001 From: yaroslavmokflmg Date: Wed, 30 Sep 2026 13:09:28 +0400 Subject: [PATCH 7/7] Take all scan targets from the changes matrix --- .github/workflows/changes.yaml | 9 +++++++++ .github/workflows/test.yml | 4 +--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/changes.yaml b/.github/workflows/changes.yaml index d70a9b5eaba..0afc1109c5c 100644 --- a/.github/workflows/changes.yaml +++ b/.github/workflows/changes.yaml @@ -9,6 +9,9 @@ on: clients_matrix: description: "Clients matrix configuration for all components" value: ${{ jobs.collect.outputs.clients_matrix }} + scan_matrix: + description: "Code scan targets" + value: ${{ jobs.collect.outputs.scan_matrix }} changes: description: "JSON object mapping each component key to true/false" value: ${{ jobs.collect.outputs.changes }} @@ -22,6 +25,7 @@ jobs: outputs: images_matrix: ${{ steps.define.outputs.images_matrix }} clients_matrix: ${{ steps.define.outputs.clients_matrix }} + scan_matrix: ${{ steps.define.outputs.scan_matrix }} changes: ${{ steps.changes.outputs.changes }} steps: - uses: actions/checkout@v4 @@ -71,6 +75,10 @@ jobs: images_matrix=$(jq -c '[ .[] | { name } ]' <<< "$IMAGES") clients_matrix=$(jq -c '[ .[] | { name, os, go_os, go_arch, artifact_name } ]' <<< "$CLIENTS") + scan_matrix=$(jq -nc '[ + { name: "backend", path: ".", paths: "**/{go.mod,go.sum,Dockerfile*}", skip_files: "**/package.json,**/package-lock.json,**/yarn.lock" }, + { name: "frontend", path: ".", paths: "**/{package.json,package-lock.json,yarn.lock}", skip_files: "**/go.mod,**/go.sum,**/Dockerfile*" } + ]') filters=$(jq -nc \ --argjson images "$IMAGES" \ @@ -93,6 +101,7 @@ jobs: { echo "images_matrix=$images_matrix" echo "clients_matrix=$clients_matrix" + echo "scan_matrix=$scan_matrix" echo "filters=$filters" echo "keys=$keys" } >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5b0d3a77e7e..59fc24f093b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,9 +46,7 @@ jobs: strategy: fail-fast: false matrix: - target: - - { name: backend, path: ".", paths: "**/{go.mod,go.sum,Dockerfile*}", skip_files: "**/package.json,**/package-lock.json,**/yarn.lock" } - - { name: frontend, path: ".", paths: "**/{package.json,package-lock.json,yarn.lock}", skip_files: "**/go.mod,**/go.sum,**/Dockerfile*" } + target: ${{ fromJson(needs.changes.outputs.scan_matrix) }} steps: - name: Checkout uses: actions/checkout@v4