Skip to content

fix(filter): apply defaults.tags.exclude to mappings with own tags#90

Merged
bryantbiggs merged 1 commit into
mainfrom
feat/defaults-exclude-merge
May 7, 2026
Merged

fix(filter): apply defaults.tags.exclude to mappings with own tags#90
bryantbiggs merged 1 commit into
mainfrom
feat/defaults-exclude-merge

Conversation

@bryantbiggs

Copy link
Copy Markdown
Member

Summary

  • defaults.tags.exclude: was silently dropped for any mapping with its own tags: block. Resolution was a whole-block override (mapping.tags.or(defaults.tags)); now it's a field-level merge.
  • Two exclude tiers, distinguished by where the pattern was written:
    • soft tier (defaults.tags.exclude: + built-in prerelease list) is bypassable by include: on any mapping. Project-wide opinions.
    • hard tier (mapping.tags.exclude:) blocks include: on the same mapping. Absolute per-mapping denies.
  • --dry-run drop report attributes each tag to its tier: exclude (mapping) / exclude (defaults) / exclude (built-in).
  • Behavior change for users relying on defaults.exclude blocking an include: pattern -- migration note added in configuration.md.

Test plan

  • 12 new unit tests across filter + config-merge layers
    • filter tiers: drop attribution, include rescue, mapping-exclude blocks include with defaults set, concat semantics
    • config merge: defaults reach mapping with own tags block, mapping wins on conflict, full inheritance fall-through
    • YAML round-trip through production deserializer
    • End-to-end Chainguard scenario with realistic cgr.dev tags + tier-by-tier dry-run report assertions
    • resolve_immutable_pattern helper covers partial-set edge cases
  • All 1345 tests pass (cargo test --workspace --locked)
  • cargo fmt --all -- --check clean
  • cargo clippy --workspace --all-targets --locked -- -D warnings clean
  • cargo deny check clean
  • npm run --prefix docs build clean (docs match new behavior)
  • Reviewer: spot-check the migration note in docs/src/content/configuration.md to confirm wording is clear for operators with existing defaults.exclude configs.

Resolution was `mapping.tags.or(defaults.tags)` -- a whole-block override.
Any mapping that defined its own `tags:` block silently dropped every field
under `defaults.tags`, including `exclude:` patterns the operator expected
to apply project-wide.

Now mapping fields override defaults field by field. The two `exclude` lists
are kept on separate tiers so `include:` can act as an escape hatch:

- `defaults.tags.exclude:` (soft tier) joins the built-in prerelease list
  and is bypassable by `include:` on any mapping. Use for project-wide
  opinions like "drop *-dev unless I say otherwise."
- `mapping.tags.exclude:` (hard tier) blocks `include:` on the same mapping.
  Use for absolute per-mapping denies.

The dry-run drop report attributes each dropped tag to its tier
(`exclude (mapping)` / `exclude (defaults)` / `exclude (built-in)`) so
operators can tell which knob to turn.

Behavior change: pre-fix, `defaults.tags.exclude:` was hard tier whenever
the mapping had no `tags:` block (because the whole defaults block was
inherited as the mapping's). Operators relying on `defaults.exclude`
blocking an `include:` pattern should move that exclude to
`mapping.tags.exclude:` (still hard tier).
@bryantbiggs bryantbiggs merged commit 57f65f4 into main May 7, 2026
16 checks passed
@bryantbiggs bryantbiggs deleted the feat/defaults-exclude-merge branch May 7, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant