From 09743dd4f3b86487dcff71fe77d7991323e43f02 Mon Sep 17 00:00:00 2001 From: Astrid Gealer Date: Tue, 20 Oct 2026 16:09:24 +0100 Subject: [PATCH 1/2] chore(gitignore): ignore root artifacts written by Make targets make build-lambda, test-coverage, complexity-report and the security-scan-* targets write bootstrap, coverage.html, complexity-report.txt and gosec/trivy/tfsec-report.json at the repo root, and none of them was ignored, so a `git add -A` after a build or scan could commit the multi-MB Lambda binary or a scanner report. Add root-anchored patterns for all six so same-named files elsewhere in the tree stay trackable, and add scripts/test-gitignore-make-artifacts.sh (run by a new CI job) asserting each root artifact is ignored by .gitignore itself, the same name one level down is not, and the Makefile still writes each file. Closes #279 Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 21 ++++++ .gitignore | 10 +++ scripts/test-gitignore-make-artifacts.sh | 93 ++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100755 scripts/test-gitignore-make-artifacts.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b1e8106e..022c6d533 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1209,6 +1209,26 @@ jobs: - name: Run smoke-gate self-test run: bash scripts/test-wait-for-healthy.sh + # Assert that the files the documented Make targets write at the repo root + # (the `bootstrap` Lambda binary, coverage.html, the gosec/trivy/tfsec JSON + # reports, complexity-report.txt) are gitignored, and that the patterns stay + # root-anchored so same-named files elsewhere remain trackable (#279). + # Fast (shell only), so it always runs. + gitignore-make-artifacts: + name: Gitignore covers Make artifacts + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + with: + persist-credentials: false + + - name: Assert Make artifacts are gitignored + run: bash scripts/test-gitignore-make-artifacts.sh + # Summary job - all checks must pass ci-success: name: CI Success @@ -1231,6 +1251,7 @@ jobs: - cloud-sql-delete-scope - aws-tfstate-platform-key - azure-kv-access-policy + - gitignore-make-artifacts if: always() permissions: contents: read diff --git a/.gitignore b/.gitignore index 8465ec5af..5c990c274 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,16 @@ azure_sanity_report.json ri-exchange_*.json sanity_report*.json +# Root artifacts written by documented Make targets (#279). Anchored with a +# leading slash so same-named files elsewhere in the tree stay trackable. +# scripts/test-gitignore-make-artifacts.sh asserts both directions. +/bootstrap +/coverage.html +/gosec-report.json +/trivy-report.json +/tfsec-report.json +/complexity-report.txt + # Compiled binaries cudly-final cudly-test diff --git a/scripts/test-gitignore-make-artifacts.sh b/scripts/test-gitignore-make-artifacts.sh new file mode 100755 index 000000000..b169fd769 --- /dev/null +++ b/scripts/test-gitignore-make-artifacts.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# test-gitignore-make-artifacts.sh +# +# Asserts that the files the documented Make targets write at the repo root are +# ignored by the committed .gitignore (#279), so `git add -A` after a build, +# coverage run or scan cannot pick them up. The worst case is the multi-MB +# `bootstrap` Lambda binary from `make build-lambda`; binaries of that size have +# reached branch history before. +# +# Both directions are asserted: +# - each root artifact IS ignored, and the match comes from .gitignore itself +# (not from a developer's global excludes file or .git/info/exclude, which +# would let this pass locally on a checkout CI does not have); +# - the same file name one directory down is NOT ignored, so the patterns +# stay anchored to the root and cannot hide a tracked file or fixture of +# the same name elsewhere in the tree. +# +# Each artifact name must also still appear in the Makefile. If a target is +# renamed to write a different file, this fails rather than passing on a +# pattern that no longer guards anything. +# +# Exits 0 when all cases pass; exits 1 on any failure. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +# Root artifact -> the Make target that writes it. +ARTIFACTS=( + "bootstrap:build-lambda" + "coverage.html:test-coverage" + "gosec-report.json:security-scan-go" + "trivy-report.json:security-scan-docker" + "tfsec-report.json:security-scan-terraform" + "complexity-report.txt:complexity-report" +) + +pass=0 +fail=0 + +ok() { + echo "PASS: $1" + (( pass++ )) || true +} + +not_ok() { + echo "FAIL: $1" + (( fail++ )) || true +} + +# Prints the .gitignore rule that ignores $1, or nothing. --no-index makes the +# answer independent of whether the path happens to be tracked or staged. +gitignore_rule_for() { + local verdict + verdict="$(git -C "$REPO_ROOT" check-ignore -v --no-index -- "$1" 2>/dev/null || true)" + # Output shape: :: + if [[ "$verdict" == .gitignore:* ]]; then + printf '%s\n' "${verdict%%$'\t'*}" + fi +} + +for entry in "${ARTIFACTS[@]}"; do + name="${entry%%:*}" + target="${entry#*:}" + + if grep -qF -- "$name" "${REPO_ROOT}/Makefile"; then + ok "Makefile still writes ${name} (make ${target})" + else + not_ok "Makefile no longer mentions ${name}; update this test and .gitignore together" + fi + + rule="$(gitignore_rule_for "$name")" + if [[ -n "$rule" ]]; then + ok "root ${name} is ignored by ${rule}" + else + not_ok "root ${name} (make ${target}) is not ignored by .gitignore" + fi + + nested="some/nested/dir/${name}" + rule="$(gitignore_rule_for "$nested")" + if [[ -z "$rule" ]]; then + ok "nested ${nested} is not ignored (pattern is root-anchored)" + else + not_ok "nested ${nested} is ignored by ${rule}; anchor the pattern with a leading /" + fi +done + +echo "" +echo "Results: ${pass} passed, ${fail} failed" +if [[ "$fail" -gt 0 ]]; then + exit 1 +fi From 6124b1a34d61157aa1a6328a2dbd025879592c79 Mon Sep 17 00:00:00 2001 From: Astrid Gealer Date: Fri, 23 Oct 2026 02:37:53 +0000 Subject: [PATCH 2/2] chore(gitignore): drop gosec-report.json now that no Make target writes it Since #1145 (#340), `make security-scan-go` runs scripts/run-gosec.sh, which prints text findings or writes SARIF into a given directory; nothing writes gosec-report.json at the repo root any more. Drop the /gosec-report.json rule and its test row. The test's "still in the Makefile" check kept passing for that name only because the `make clean` rm line still lists it, which is the stale guard the check was meant to catch. Skip `rm` lines when looking for the name, so a target that stops writing a file fails the test. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 4 ++-- .gitignore | 1 - scripts/test-gitignore-make-artifacts.sh | 17 +++++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4aaa4113..d80b3f958 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1300,8 +1300,8 @@ jobs: run: bash scripts/test-migration-down-gate.sh # Assert that the files the documented Make targets write at the repo root - # (the `bootstrap` Lambda binary, coverage.html, the gosec/trivy/tfsec JSON - # reports, complexity-report.txt) are gitignored, and that the patterns stay + # (the `bootstrap` Lambda binary, coverage.html, the trivy/tfsec JSON reports, + # complexity-report.txt) are gitignored, and that the patterns stay # root-anchored so same-named files elsewhere remain trackable (#279). # Fast (shell only), so it always runs. gitignore-make-artifacts: diff --git a/.gitignore b/.gitignore index 5c990c274..57a35f11e 100644 --- a/.gitignore +++ b/.gitignore @@ -89,7 +89,6 @@ sanity_report*.json # scripts/test-gitignore-make-artifacts.sh asserts both directions. /bootstrap /coverage.html -/gosec-report.json /trivy-report.json /tfsec-report.json /complexity-report.txt diff --git a/scripts/test-gitignore-make-artifacts.sh b/scripts/test-gitignore-make-artifacts.sh index b169fd769..4c16aeace 100755 --- a/scripts/test-gitignore-make-artifacts.sh +++ b/scripts/test-gitignore-make-artifacts.sh @@ -15,9 +15,12 @@ # stay anchored to the root and cannot hide a tracked file or fixture of # the same name elsewhere in the tree. # -# Each artifact name must also still appear in the Makefile. If a target is -# renamed to write a different file, this fails rather than passing on a -# pattern that no longer guards anything. +# Each artifact name must also still appear in the Makefile outside an `rm` +# line. If a target is renamed or stops writing the file, this fails rather +# than passing on a pattern that no longer guards anything; the `make clean` +# rm lines are skipped because they can outlive the target that wrote the file +# (as gosec-report.json did once `make security-scan-go` moved to +# scripts/run-gosec.sh, #340). # # Exits 0 when all cases pass; exits 1 on any failure. @@ -30,7 +33,6 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" ARTIFACTS=( "bootstrap:build-lambda" "coverage.html:test-coverage" - "gosec-report.json:security-scan-go" "trivy-report.json:security-scan-docker" "tfsec-report.json:security-scan-terraform" "complexity-report.txt:complexity-report" @@ -60,14 +62,17 @@ gitignore_rule_for() { fi } +# Makefile lines that can write an artifact: everything except `rm` lines. +makefile_writers="$(grep -vE '^[[:space:]]*@?rm[[:space:]]' "${REPO_ROOT}/Makefile")" + for entry in "${ARTIFACTS[@]}"; do name="${entry%%:*}" target="${entry#*:}" - if grep -qF -- "$name" "${REPO_ROOT}/Makefile"; then + if grep -qF -- "$name" <<<"$makefile_writers"; then ok "Makefile still writes ${name} (make ${target})" else - not_ok "Makefile no longer mentions ${name}; update this test and .gitignore together" + not_ok "Makefile no longer writes ${name} (only rm lines, if any, mention it); update this test and .gitignore together" fi rule="$(gitignore_rule_for "$name")"