diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b74d55..feca82f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.13.0] - 2026-02-14 + +### Added +- Per-target `ignores` field in target definitions. Per-target ignores are additive with the global `ignores` and only apply to the specific target's detection. + ## [0.12.0] - 2026-02-14 ### Changed @@ -156,6 +161,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Multi-stage Docker build - Automated vendor upgrade workflow +[0.13.0]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.12.0...v0.13.0 [0.12.0]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.11.2...v0.12.0 [0.11.2]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.11.1...v0.11.2 [0.11.1]: https://github.com/gooddata/gooddata-goodchanges/compare/v0.11.0...v0.11.1 diff --git a/README.md b/README.md index 901530d..5530f9a 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ Each `changeDirs` entry is an object with: | `app` | `string` | Target | Package name of the corresponding app this e2e package tests | | `targetName` | `string` | Virtual target | Output name emitted when the virtual target is triggered | | `changeDirs` | `ChangeDir[]` | Virtual target | Glob patterns to match files. Each entry: `{"glob": "...", "filter?": "...", "type?": "fine-grained"}` | +| `ignores` | `string[]` | Both | Per-target ignore globs. Additive with the global `ignores` -- only applies to this target's detection | The `.goodchangesrc.json` file itself is always ignored. diff --git a/VERSION b/VERSION index ac454c6..54d1a4f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.12.0 +0.13.0 diff --git a/internal/rush/rush.go b/internal/rush/rush.go index 59b5eec..56e7304 100644 --- a/internal/rush/rush.go +++ b/internal/rush/rush.go @@ -123,6 +123,7 @@ type TargetDef struct { App *string `json:"app,omitempty"` // rush project name of corresponding app TargetName *string `json:"targetName,omitempty"` // output name for virtual targets ChangeDirs []ChangeDir `json:"changeDirs,omitempty"` // globs to watch for virtual targets + Ignores []string `json:"ignores,omitempty"` // per-target ignore globs (additive with global) } // IsTarget returns true if this target definition is a regular target. @@ -181,7 +182,20 @@ func (pc *ProjectConfig) IsIgnored(relPath string) bool { return false } - +// WithTargetIgnores returns a new ProjectConfig with the target's ignores merged in. +// The returned config's IsIgnored checks both global and per-target patterns. +func (pc *ProjectConfig) WithTargetIgnores(td TargetDef) *ProjectConfig { + if len(td.Ignores) == 0 { + return pc + } + merged := &ProjectConfig{ + Targets: pc.Targets, + Ignores: make([]string, 0, len(pc.Ignores)+len(td.Ignores)), + } + merged.Ignores = append(merged.Ignores, pc.Ignores...) + merged.Ignores = append(merged.Ignores, td.Ignores...) + return merged +} // FindChangedProjects determines which projects have files in the changed file list. // Files matching ignore globs in .goodchangesrc.json are excluded. diff --git a/main.go b/main.go index 5335613..eb2407a 100644 --- a/main.go +++ b/main.go @@ -313,6 +313,9 @@ func main() { } for _, td := range cfg.Targets { + // Merge global + per-target ignores for this target's detection + targetCfg := cfg.WithTargetIgnores(td) + if td.IsTarget() { if len(targetPatterns) > 0 && !matchesTargetFilter(rp.PackageName, targetPatterns) { continue @@ -332,7 +335,7 @@ func main() { for _, f := range changedFiles { if strings.HasPrefix(f, rp.ProjectFolder+"/") { relPath := strings.TrimPrefix(f, rp.ProjectFolder+"/") - if !cfg.IsIgnored(relPath) { + if !targetCfg.IsIgnored(relPath) { triggered = true break } @@ -350,7 +353,7 @@ func main() { } // Condition 3: Tainted workspace imports - if analyzer.HasTaintedImports(rp.ProjectFolder, allUpstreamTaint, cfg) { + if analyzer.HasTaintedImports(rp.ProjectFolder, allUpstreamTaint, targetCfg) { changedE2E[rp.PackageName] = &TargetResult{Name: rp.PackageName} continue } @@ -388,7 +391,7 @@ func main() { if cd.Filter != nil { filterPattern = *cd.Filter } - detected := analyzer.FindAffectedFiles(cd.Glob, filterPattern, allUpstreamTaint, changedFiles, rp.ProjectFolder, cfg, depChangedDeps[rp.ProjectFolder], mergeBase, flagIncludeTypes) + detected := analyzer.FindAffectedFiles(cd.Glob, filterPattern, allUpstreamTaint, changedFiles, rp.ProjectFolder, targetCfg, depChangedDeps[rp.ProjectFolder], mergeBase, flagIncludeTypes) if len(detected) > 0 { fineGrainedDetections = append(fineGrainedDetections, detected...) } @@ -399,7 +402,7 @@ func main() { continue } relPath := strings.TrimPrefix(f, rp.ProjectFolder+"/") - if cfg.IsIgnored(relPath) { + if targetCfg.IsIgnored(relPath) { continue } if matched, _ := doublestar.Match(cd.Glob, relPath); matched { @@ -408,7 +411,7 @@ func main() { } } if !normalTriggered { - if analyzer.HasTaintedImportsForGlob(rp.ProjectFolder, cd.Glob, allUpstreamTaint, cfg) { + if analyzer.HasTaintedImportsForGlob(rp.ProjectFolder, cd.Glob, allUpstreamTaint, targetCfg) { normalTriggered = true } }