diff --git a/pkg/cli/compile_campaign.go b/pkg/cli/compile_campaign.go index 31befc0cca..6fc2bc63ce 100644 --- a/pkg/cli/compile_campaign.go +++ b/pkg/cli/compile_campaign.go @@ -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 @@ -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) @@ -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 diff --git a/pkg/cli/compile_orchestration.go b/pkg/cli/compile_orchestration.go index 8e259933c7..a62d9a52c2 100644 --- a/pkg/cli/compile_orchestration.go +++ b/pkg/cli/compile_orchestration.go @@ -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 { @@ -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 @@ -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 } @@ -422,6 +424,7 @@ func runPostProcessing( workflowDataList []*workflow.WorkflowData, config CompileConfig, successCount int, + campaignFiles []string, ) error { // Get action cache actionCache := compiler.GetSharedActionCache() @@ -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 + } } } @@ -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 } diff --git a/pkg/cli/compile_post_processing.go b/pkg/cli/compile_post_processing.go index 1ddda6b44a..bf0e4d0ebd 100644 --- a/pkg/cli/compile_post_processing.go +++ b/pkg/cli/compile_post_processing.go @@ -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) }