Skip to content

Commit 5483b67

Browse files
feat: add paths-ignore input; default-skip vendored/fixture content (#7)
Validators that scan content patterns must distinguish a target file from a vendored / fixture / training-corpus file that legitimately contains the very pattern being checked. Same architectural lesson as hyperpolymath/hypatia#243 — that scanner had 11/11 `secret_detected` findings turn out to be FPs in `.audittraining/`, `lib/rules/`, `scripts/fix-scripts/`, and `test/` paths until provenance was added. This action was firing "Missing required identity field" on every .a2ml file in `verified-container-spec/` (a vendored project tree) consumed by stapeln and other sibling repos. The vendored manifests have their own identity declarations in their upstream context — flagging them here is noise that no consumer can usefully act on. New input: paths-ignore: | vendor/ vendored/ verified-container-spec/ .audittraining/ integration/fixtures/ test/fixtures/ tests/fixtures/ Newline-separated, substring match against each candidate file's path, default-on so consumers benefit without editing every workflow. Pass an empty string to disable. README updated with rationale + usage. validate-a2ml.sh prints a notice for each skipped file count so the carve-out is auditable in the action log. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e7488b5 commit 5483b67

3 files changed

Lines changed: 81 additions & 1 deletion

File tree

README.adoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ jobs:
4040
with:
4141
path: '.' # Directory to scan (default: repo root)
4242
strict: 'false' # Promote warnings to errors (default: false)
43+
# paths-ignore: defaults to vendored / fixture patterns; override
44+
# via newline-separated string. Use '' to disable.
4345
----
4446

4547
=== Inputs
@@ -55,8 +57,27 @@ jobs:
5557
| `strict`
5658
| `false`
5759
| When `true`, warnings become errors and the action fails on any issue
60+
61+
| `paths-ignore`
62+
| _vendored & fixture defaults_
63+
| Newline-separated path fragments to skip. Substring match against each
64+
file path. Default set: `vendor/`, `vendored/`, `verified-container-spec/`,
65+
`.audittraining/`, `integration/fixtures/`, `test/fixtures/`,
66+
`tests/fixtures/`. Pass an empty string (`paths-ignore: ''`) to disable
67+
and scan everything. See https://github.com/hyperpolymath/hypatia/pull/243
68+
for the architectural rationale (content-pattern validators must
69+
distinguish targets from fixtures / vendored / training-corpus files
70+
that legitimately contain the very pattern being checked).
5871
|===
5972

73+
==== Why default-on path exemptions?
74+
75+
A2ML files inside vendored projects (e.g. `verified-container-spec/`) have
76+
their own identity declarations elsewhere or are themselves training corpora.
77+
Flagging every such file as "missing identity field" is provenance noise,
78+
not signal. The defaults match the canonical RSR vendored-content paths;
79+
override for project-specific carve-outs.
80+
6081
=== Outputs
6182

6283
[cols="1,3"]

action.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ inputs:
2929
will fail on any validation issue. Defaults to false.
3030
required: false
3131
default: 'false'
32+
paths-ignore:
33+
description: >-
34+
Newline-separated path fragments to skip. Each line is matched as a
35+
substring against the file's path. Defaults to common vendored /
36+
training-corpus / fixture patterns so consumers don't have to repeat
37+
this carve-out in every repo. Pass an empty string to disable.
38+
Pattern follows hyperpolymath/hypatia#243 — validators that scan
39+
content patterns must distinguish a target file from a fixture /
40+
vendored / training-corpus file that legitimately contains the
41+
pattern being checked.
42+
required: false
43+
default: |
44+
vendor/
45+
vendored/
46+
verified-container-spec/
47+
.audittraining/
48+
integration/fixtures/
49+
test/fixtures/
50+
tests/fixtures/
3251
3352
outputs:
3453
files-scanned:
@@ -50,5 +69,6 @@ runs:
5069
env:
5170
INPUT_PATH: ${{ inputs.path }}
5271
INPUT_STRICT: ${{ inputs.strict }}
72+
INPUT_PATHS_IGNORE: ${{ inputs.paths-ignore }}
5373
run: |
5474
"${GITHUB_ACTION_PATH}/validate-a2ml.sh"

validate-a2ml.sh

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,30 @@ set -euo pipefail
2626

2727
SCAN_PATH="${INPUT_PATH:-.}"
2828
STRICT="${INPUT_STRICT:-false}"
29+
PATHS_IGNORE_RAW="${INPUT_PATHS_IGNORE:-}"
30+
31+
# Parse paths-ignore: newline-separated fragments, blank lines and # comments
32+
# stripped. Each fragment is a substring match against the file path. Pattern
33+
# adopted from hyperpolymath/hypatia#243 — content-pattern validators must
34+
# distinguish a target from a vendored / fixture file that legitimately
35+
# contains the very pattern being checked.
36+
PATHS_IGNORE=()
37+
while IFS= read -r _frag; do
38+
# Strip leading and trailing whitespace (canonical bash idiom).
39+
_frag="${_frag#"${_frag%%[![:space:]]*}"}"
40+
_frag="${_frag%"${_frag##*[![:space:]]}"}"
41+
[[ -z "$_frag" || "$_frag" == \#* ]] && continue
42+
PATHS_IGNORE+=("$_frag")
43+
done <<< "$PATHS_IGNORE_RAW"
44+
45+
# Returns 0 if path should be skipped (matches any ignore fragment)
46+
path_ignored() {
47+
local p="$1" frag
48+
for frag in "${PATHS_IGNORE[@]}"; do
49+
[[ "$p" == *"$frag"* ]] && return 0
50+
done
51+
return 1
52+
}
2953

3054
# Counters
3155
FILES_SCANNED=0
@@ -193,7 +217,22 @@ echo "Scanning ${SCAN_PATH} for .a2ml files..."
193217
echo ""
194218

195219
# Find all .a2ml files, excluding .git directory
196-
mapfile -t a2ml_files < <(find "$SCAN_PATH" -name '*.a2ml' -not -path '*/.git/*' -type f | sort)
220+
mapfile -t a2ml_candidates < <(find "$SCAN_PATH" -name '*.a2ml' -not -path '*/.git/*' -type f | sort)
221+
222+
# Apply paths-ignore filter
223+
a2ml_files=()
224+
SKIPPED=0
225+
for _f in "${a2ml_candidates[@]}"; do
226+
if path_ignored "$_f"; then
227+
SKIPPED=$((SKIPPED + 1))
228+
continue
229+
fi
230+
a2ml_files+=("$_f")
231+
done
232+
233+
if [[ $SKIPPED -gt 0 ]]; then
234+
echo "::notice::Skipped ${SKIPPED} file(s) matching paths-ignore"
235+
fi
197236

198237
if [[ ${#a2ml_files[@]} -eq 0 ]]; then
199238
echo "::notice::No .a2ml files found in ${SCAN_PATH}"

0 commit comments

Comments
 (0)