Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions pkg/cli/compile_campaign.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ import (
var compileCampaignLog = logger.New("cli:compile_campaign")

// validateCampaigns validates campaign spec files and their referenced workflows.
// If campaignFiles is provided (non-nil), only those specific campaign files are validated.
// If campaignFiles is nil, all campaign specs are validated.
// Returns an error if any campaign specs are invalid or reference missing workflows.
func validateCampaigns(workflowDir string, verbose bool) error {
func validateCampaigns(workflowDir string, verbose bool, campaignFiles []string) error {
compileCampaignLog.Printf("Validating campaigns with workflow directory: %s", workflowDir)

// Get absolute path to workflows directory
Expand Down Expand Up @@ -81,16 +83,50 @@ func validateCampaigns(workflowDir string, verbose bool) error {
return nil
}

compileCampaignLog.Printf("Loaded %d campaign specs for validation", len(specs))
// Filter specs if specific campaign files were provided
var specsToValidate []campaign.CampaignSpec
if campaignFiles != nil && len(campaignFiles) > 0 {
compileCampaignLog.Printf("Filtering to validate only %d specific campaign file(s)", len(campaignFiles))
// Create a map of absolute paths for quick lookup
campaignFileMap := make(map[string]bool)
for _, cf := range campaignFiles {
absPath, err := filepath.Abs(cf)
if err == nil {
campaignFileMap[absPath] = true
}
}

for _, spec := range specs {
// Get absolute path of the spec's config file
specPath := spec.ConfigPath
if !filepath.IsAbs(specPath) {
specPath = filepath.Join(gitRoot, specPath)
}
absSpecPath, err := filepath.Abs(specPath)
if err == nil && campaignFileMap[absSpecPath] {
specsToValidate = append(specsToValidate, spec)
}
}
compileCampaignLog.Printf("Filtered to %d campaign spec(s) for validation", len(specsToValidate))
} else {
// Validate all specs
specsToValidate = specs
compileCampaignLog.Printf("Loaded %d campaign specs for validation", len(specs))
}

if len(specsToValidate) == 0 {
compileCampaignLog.Print("No matching campaign specs found to validate")
return nil
}

if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Validating %d campaign spec(s)...", len(specs))))
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Validating %d campaign spec(s)...", len(specsToValidate))))
}

var allProblems []string
hasErrors := false

for _, spec := range specs {
for _, spec := range specsToValidate {
// Validate the spec itself
problems := campaign.ValidateSpec(&spec)

Expand All @@ -115,9 +151,9 @@ func validateCampaigns(workflowDir string, verbose bool) error {
return fmt.Errorf("found %d problem(s) in campaign specs", len(allProblems))
}

compileCampaignLog.Printf("All %d campaign specs validated successfully", len(specs))
compileCampaignLog.Printf("All %d campaign specs validated successfully", len(specsToValidate))
if verbose {
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("All %d campaign spec(s) validated successfully", len(specs))))
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("All %d campaign spec(s) validated successfully", len(specsToValidate))))
}

return nil
Expand Down
19 changes: 13 additions & 6 deletions pkg/cli/compile_orchestration.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func compileSpecificFiles(
var errorMessages []string
var lockFilesForActionlint []string
var lockFilesForZizmor []string
var campaignFiles []string

// Compile each specified file
for _, markdownFile := range config.MarkdownFiles {
Expand Down Expand Up @@ -88,6 +89,7 @@ func compileSpecificFiles(

// Handle campaign spec files separately
if strings.HasSuffix(resolvedFile, ".campaign.md") {
campaignFiles = append(campaignFiles, resolvedFile)
campaignResult, success := processCampaignSpec(
compiler, resolvedFile, config.Verbose, config.JSONOutput,
config.NoEmit, false, false, false, // Disable per-file security tools
Expand Down Expand Up @@ -171,7 +173,7 @@ func compileSpecificFiles(
displayScheduleWarnings(compiler, config.JSONOutput)

// Post-processing
if err := runPostProcessing(compiler, workflowDataList, config, compiledCount); err != nil {
if err := runPostProcessing(compiler, workflowDataList, config, compiledCount, campaignFiles); err != nil {
return workflowDataList, err
}

Expand Down Expand Up @@ -422,6 +424,7 @@ func runPostProcessing(
workflowDataList []*workflow.WorkflowData,
config CompileConfig,
successCount int,
campaignFiles []string,
) error {
// Get action cache
actionCache := compiler.GetSharedActionCache()
Expand All @@ -442,10 +445,14 @@ func runPostProcessing(
}
}

// Validate campaigns
if err := validateCampaignsWrapper(config.WorkflowDir, config.Verbose, config.Strict); err != nil {
if config.Strict {
return err
// Validate campaigns only if we're compiling campaign files
// When compiling specific non-campaign workflows, skip campaign validation
// When compiling specific campaign files, validate only those campaign files
if len(campaignFiles) > 0 {
if err := validateCampaignsWrapper(config.WorkflowDir, config.Verbose, config.Strict, campaignFiles); err != nil {
if config.Strict {
return err
}
}
}

Expand Down Expand Up @@ -491,7 +498,7 @@ func runPostProcessingForDirectory(
}

// Validate campaigns
if err := validateCampaignsWrapper(config.WorkflowDir, config.Verbose, config.Strict); err != nil {
if err := validateCampaignsWrapper(config.WorkflowDir, config.Verbose, config.Strict, nil); err != nil {
if config.Strict {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/compile_post_processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ func generateMaintenanceWorkflowWrapper(
}

// validateCampaignsWrapper validates campaign specs if they exist
func validateCampaignsWrapper(workflowDir string, verbose bool, strict bool) error {
func validateCampaignsWrapper(workflowDir string, verbose bool, strict bool, campaignFiles []string) error {
compilePostProcessingLog.Print("Validating campaign specs")

if err := validateCampaigns(workflowDir, verbose); err != nil {
if err := validateCampaigns(workflowDir, verbose, campaignFiles); err != nil {
if strict {
return fmt.Errorf("campaign validation failed: %w", err)
}
Expand Down
Loading