Skip to content

[plan] Implement compiler validation for filter mutual exclusivity #5809

Description

@github-actions

Objective

Add compiler-level validation to detect and reject workflows that use both branches/branches-ignore or paths/paths-ignore together, matching GitHub Actions requirements.

Context

GitHub Actions rejects workflows that specify both branches and branches-ignore (or both paths and paths-ignore) in the same event filter. Currently gh-aw allows these invalid configurations to compile, leading to runtime failures in GitHub Actions.

This issue implements the runtime validation that complements the schema documentation added in #5804.

Approach

Add validation logic in the compiler to check for mutual exclusivity violations in push and pull_request event filters.

Files to Modify

  • Update: pkg/workflow/compiler.go (add validation function)
  • Update: pkg/parser/schema.go (or create new validator if preferred)
  • Create: pkg/workflow/compiler_filters_test.go (test coverage)

Implementation

1. Add Validation Function

// ValidateEventFilters checks for GitHub Actions filter mutual exclusivity rulesfuncValidateEventFilters(frontmattermap[string]any) error {
on, exists:=frontmatter["on"]
if!exists {
returnnil
}
onMap, ok:=on.(map[string]any)
if!ok {
returnnil
}
// Check push eventifpushVal, exists:=onMap["push"]; exists {
iferr:=validateFilterExclusivity(pushVal, "push"); err!=nil {
returnerr
}
}
// Check pull_request eventifprVal, exists:=onMap["pull_request"]; exists {
iferr:=validateFilterExclusivity(prVal, "pull_request"); err!=nil {
returnerr
}
}
returnnil
}
funcvalidateFilterExclusivity(eventValany, eventNamestring) error {
eventMap, ok:=eventVal.(map[string]any)
if!ok {
returnnil
}
// Check branches/branches-ignoreif_, hasBranches:=eventMap["branches"]; hasBranches {
if_, hasIgnore:=eventMap["branches-ignore"]; hasIgnore {
returnfmt.Errorf("%s event cannot specify both 'branches' and 'branches-ignore' - they are mutually exclusive per GitHub Actions requirements", eventName)
}
}
// Check paths/paths-ignoreif_, hasPaths:=eventMap["paths"]; hasPaths {
if_, hasIgnore:=eventMap["paths-ignore"]; hasIgnore {
returnfmt.Errorf("%s event cannot specify both 'paths' and 'paths-ignore' - they are mutually exclusive per GitHub Actions requirements", eventName)
}
}
returnnil
}

2. Call Validation

Add call in compiler's validation chain (around existing event validation):

// In CompileWorkflow or validateWorkflow functioniferr:=ValidateEventFilters(frontmatter); err!=nil {
returnerr
}

3. Add Tests

Create comprehensive test coverage for all cases:

funcTestValidateEventFilters(t*testing.T) {
tests:= []struct {
namestringfrontmattermap[string]anywantErrbool
}{
{
name: "valid branches only",
frontmatter: map[string]any{
"on": map[string]any{
"push": map[string]any{
"branches": []string{"main"},
},
},
},
wantErr: false,
},
{
name: "invalid both branches and branches-ignore",
frontmatter: map[string]any{
"on": map[string]any{
"push": map[string]any{
"branches": []string{"main"},
"branches-ignore": []string{"dev"},
},
},
},
wantErr: true,
},
// Add similar tests for paths/paths-ignore, pull_request event, etc.
}
for_, tt:=rangetests {
t.Run(tt.name, func(t*testing.T) {
err:=ValidateEventFilters(tt.frontmatter)
if (err!=nil) !=tt.wantErr {
t.Errorf("ValidateEventFilters() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

Acceptance Criteria

  • Validation function added to compiler
  • Both branches/branches-ignore and paths/paths-ignore conflicts detected
  • Both push and pull_request events validated
  • Clear error messages using console.FormatErrorMessage
  • Comprehensive test coverage in new test file
  • Tests pass: make test-unit
  • No regressions: make test

Testing

# Rebuild and test
make build
make test-unit
# Manual validationecho'---on: push: branches: [main] branches-ignore: [dev]---Test'> /tmp/test-invalid.md
./gh-aw compile /tmp/test-invalid.md # Should fail with clear errorecho'---on: pull_request: paths: ["src/**"] paths-ignore: ["docs/**"]---Test'> /tmp/test-invalid2.md
./gh-aw compile /tmp/test-invalid2.md # Should fail with clear errorecho'---on: push: branches: [main]---Test'> /tmp/test-valid.md
./gh-aw compile /tmp/test-valid.md # Should succeed

Notes

AI generated by Plan Command for discussion #5799

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions