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
40 changes: 14 additions & 26 deletions pkg/cli/compile_batch_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,48 +37,36 @@ import (

var compileBatchOperationsLog = logger.New("cli:compile_batch_operations")

// runBatchActionlint runs actionlint on all lock files in batch
func runBatchActionlint(lockFiles []string, verbose bool, strict bool) error {
// runBatchLockFileTool runs a batch tool on lock files with uniform error handling
func runBatchLockFileTool(toolName string, lockFiles []string, verbose bool, strict bool, runner func([]string, bool, bool) error) error {
if len(lockFiles) == 0 {
compileBatchOperationsLog.Print("No lock files to lint with actionlint")
compileBatchOperationsLog.Printf("No lock files to process with %s", toolName)
return nil
}

compileBatchOperationsLog.Printf("Running batch actionlint on %d lock files", len(lockFiles))
compileBatchOperationsLog.Printf("Running batch %s on %d lock files", toolName, len(lockFiles))

if err := RunActionlintOnFiles(lockFiles, verbose, strict); err != nil {
if err := runner(lockFiles, verbose, strict); err != nil {
if strict {
return fmt.Errorf("actionlint linter failed: %w", err)
return fmt.Errorf("%s failed: %w", toolName, err)
}
// In non-strict mode, actionlint errors are warnings
// In non-strict mode, errors are warnings
if verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("actionlint warnings: %v", err)))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("%s warnings: %v", toolName, err)))
Comment on lines +51 to +55
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The batch strict/warning messages became less descriptive and inconsistent with the per-file validation errors (e.g., compile_validation.go uses "actionlint linter failed" / "zizmor security scan failed"). Consider letting runBatchLockFileTool accept tool-specific message templates (or an operation description) so strict-mode errors and verbose warnings remain clear and consistent across batch vs per-file paths.

Copilot uses AI. Check for mistakes.
}
}

return nil
}

// runBatchActionlint runs actionlint on all lock files in batch
func runBatchActionlint(lockFiles []string, verbose bool, strict bool) error {
return runBatchLockFileTool("actionlint", lockFiles, verbose, strict, RunActionlintOnFiles)
}

// runBatchZizmor runs zizmor security scanner on all lock files in batch
func runBatchZizmor(lockFiles []string, verbose bool, strict bool) error {
if len(lockFiles) == 0 {
compileBatchOperationsLog.Print("No lock files to scan with zizmor")
return nil
}

compileBatchOperationsLog.Printf("Running batch zizmor on %d lock files", len(lockFiles))

if err := RunZizmorOnFiles(lockFiles, verbose, strict); err != nil {
if strict {
return fmt.Errorf("zizmor security scan failed: %w", err)
}
// In non-strict mode, zizmor errors are warnings
if verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("zizmor warnings: %v", err)))
}
}

return nil
return runBatchLockFileTool("zizmor", lockFiles, verbose, strict, RunZizmorOnFiles)
}

// runBatchPoutine runs poutine security scanner once for the entire directory
Expand Down
6 changes: 3 additions & 3 deletions pkg/cli/compile_orchestration.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ func outputResults(
if len(config.MarkdownFiles) > 0 {
statsList = collectWorkflowStatisticsWrapper(config.MarkdownFiles)
}
formatStatsTable(statsList)
displayStatsTable(statsList)
}

// Output JSON if requested
Expand All @@ -501,12 +501,12 @@ func outputResults(
fmt.Println(jsonStr)
} else if !config.Stats {
// Print summary for text output (skip if stats mode)
formatCompilationSummary(stats)
printCompilationSummary(stats)
}

// Display actionlint summary if enabled
if config.Actionlint && !config.NoEmit && !config.JSONOutput {
formatActionlintOutput()
displayActionlintSummary()
}

return nil
Expand Down
22 changes: 0 additions & 22 deletions pkg/cli/compile_output_formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@
// # Key Functions
//
// Summary Output:
// - formatCompilationSummary() - Format compilation statistics
// - formatValidationOutput() - Format validation results as JSON
Comment on lines 14 to 17
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After removing the summary/stats wrapper functions, this file now only formats validation JSON. Please update the surrounding file documentation/"Key Functions" text to match the remaining scope (or consider relocating/renaming the file) so readers aren’t misled about what’s implemented here.

Copilot uses AI. Check for mistakes.
//
// These functions abstract output formatting, allowing the main compile
// orchestrator to focus on coordination while these handle presentation.

package cli

Expand All @@ -31,12 +27,6 @@ import (

var compileOutputFormatterLog = logger.New("cli:compile_output_formatter")

// formatCompilationSummary formats compilation statistics for display
// This is a wrapper around printCompilationSummary for consistency
func formatCompilationSummary(stats *CompilationStats) {
printCompilationSummary(stats)
}

// formatValidationOutput formats validation results as JSON
func formatValidationOutput(results []ValidationResult) (string, error) {
compileOutputFormatterLog.Printf("Formatting validation output for %d workflow(s)", len(results))
Expand All @@ -52,15 +42,3 @@ func formatValidationOutput(results []ValidationResult) (string, error) {

return string(jsonBytes), nil
}

// formatActionlintOutput displays the actionlint summary
// This is a wrapper around displayActionlintSummary for consistency
func formatActionlintOutput() {
displayActionlintSummary()
}

// formatStatsTable displays the workflow statistics table
// This is a wrapper around displayStatsTable for consistency
func formatStatsTable(statsList []*WorkflowStats) {
displayStatsTable(statsList)
}
6 changes: 0 additions & 6 deletions pkg/cli/mcp_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ func GenerateSchema[T any]() (*jsonschema.Schema, error) {
return jsonschema.For[T](nil)
}

// GenerateOutputSchema is deprecated. Use GenerateSchema instead.
// This function is kept for backward compatibility but will be removed in a future version.
func GenerateOutputSchema[T any]() (*jsonschema.Schema, error) {
return GenerateSchema[T]()
}

// AddSchemaDefault adds a default value to a property in a JSON schema.
// This is useful for elicitation defaults (SEP-1024) that improve UX by
// suggesting sensible starting values to MCP clients.
Expand Down
56 changes: 28 additions & 28 deletions pkg/cli/mcp_schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import (
"github.com/google/jsonschema-go/jsonschema"
)

func TestGenerateOutputSchema(t *testing.T) {
func TestGenerateSchema(t *testing.T) {
t.Run("generates schema for simple struct", func(t *testing.T) {
type SimpleOutput struct {
Name string `json:"name" jsonschema:"Name of the item"`
Count int `json:"count" jsonschema:"Number of items"`
}

schema, err := GenerateOutputSchema[SimpleOutput]()
schema, err := GenerateSchema[SimpleOutput]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

if schema == nil {
Expand Down Expand Up @@ -54,9 +54,9 @@ func TestGenerateOutputSchema(t *testing.T) {
Optional *string `json:"optional,omitempty" jsonschema:"Optional field"`
}

schema, err := GenerateOutputSchema[OutputWithOptional]()
schema, err := GenerateSchema[OutputWithOptional]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

if schema == nil {
Expand Down Expand Up @@ -87,9 +87,9 @@ func TestGenerateOutputSchema(t *testing.T) {
Nested NestedData `json:"nested" jsonschema:"Nested data"`
}

schema, err := GenerateOutputSchema[OutputWithNested]()
schema, err := GenerateSchema[OutputWithNested]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

if schema == nil {
Expand Down Expand Up @@ -122,9 +122,9 @@ func TestGenerateOutputSchema(t *testing.T) {
Items []string `json:"items" jsonschema:"List of items"`
}

schema, err := GenerateOutputSchema[OutputWithSlice]()
schema, err := GenerateSchema[OutputWithSlice]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

if schema == nil {
Expand Down Expand Up @@ -157,9 +157,9 @@ func TestGenerateOutputSchema(t *testing.T) {
})

t.Run("generates schema for WorkflowStatus", func(t *testing.T) {
schema, err := GenerateOutputSchema[WorkflowStatus]()
schema, err := GenerateSchema[WorkflowStatus]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed for WorkflowStatus: %v", err)
t.Fatalf("GenerateSchema failed for WorkflowStatus: %v", err)
}

if schema == nil {
Expand All @@ -176,9 +176,9 @@ func TestGenerateOutputSchema(t *testing.T) {
})

t.Run("generates schema for LogsData", func(t *testing.T) {
schema, err := GenerateOutputSchema[LogsData]()
schema, err := GenerateSchema[LogsData]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed for LogsData: %v", err)
t.Fatalf("GenerateSchema failed for LogsData: %v", err)
}

if schema == nil {
Expand All @@ -195,9 +195,9 @@ func TestGenerateOutputSchema(t *testing.T) {
})

t.Run("generates schema for AuditData", func(t *testing.T) {
schema, err := GenerateOutputSchema[AuditData]()
schema, err := GenerateSchema[AuditData]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed for AuditData: %v", err)
t.Fatalf("GenerateSchema failed for AuditData: %v", err)
}

if schema == nil {
Expand All @@ -221,9 +221,9 @@ func TestAddSchemaDefault(t *testing.T) {
Count int `json:"count" jsonschema:"Count field"`
}

schema, err := GenerateOutputSchema[TestStruct]()
schema, err := GenerateSchema[TestStruct]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

// Add defaults
Expand Down Expand Up @@ -263,9 +263,9 @@ func TestAddSchemaDefault(t *testing.T) {
Name string `json:"name" jsonschema:"Name field"`
}

schema, err := GenerateOutputSchema[TestStruct]()
schema, err := GenerateSchema[TestStruct]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

// Try to add default to non-existent property - should not error
Expand All @@ -282,17 +282,17 @@ func TestAddSchemaDefault(t *testing.T) {
})
}

func TestGenerateOutputSchemaWithDefaults(t *testing.T) {
func TestGenerateSchemaWithDefaults(t *testing.T) {
t.Run("manually adds default values to schema", func(t *testing.T) {
type OutputWithDefaults struct {
Name string `json:"name" jsonschema:"Name of the item"`
Count int `json:"count" jsonschema:"Number of items"`
Enabled bool `json:"enabled" jsonschema:"Whether enabled"`
}

schema, err := GenerateOutputSchema[OutputWithDefaults]()
schema, err := GenerateSchema[OutputWithDefaults]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

if schema == nil {
Expand Down Expand Up @@ -363,9 +363,9 @@ func TestGenerateOutputSchemaWithDefaults(t *testing.T) {
func TestGeneratedSchemasValidateRealOutput(t *testing.T) {
t.Run("validates LogsData schema against real data", func(t *testing.T) {
// Generate schema for LogsData
schema, err := GenerateOutputSchema[LogsData]()
schema, err := GenerateSchema[LogsData]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

// Resolve the schema to prepare it for validation
Expand Down Expand Up @@ -420,9 +420,9 @@ func TestGeneratedSchemasValidateRealOutput(t *testing.T) {

t.Run("validates AuditData schema against real data", func(t *testing.T) {
// Generate schema for AuditData
schema, err := GenerateOutputSchema[AuditData]()
schema, err := GenerateSchema[AuditData]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

// Resolve the schema to prepare it for validation
Expand Down Expand Up @@ -476,9 +476,9 @@ func TestGeneratedSchemasValidateRealOutput(t *testing.T) {

t.Run("validates WorkflowStatus schema against real data", func(t *testing.T) {
// Generate schema for WorkflowStatus
schema, err := GenerateOutputSchema[WorkflowStatus]()
schema, err := GenerateSchema[WorkflowStatus]()
if err != nil {
t.Fatalf("GenerateOutputSchema failed: %v", err)
t.Fatalf("GenerateSchema failed: %v", err)
}

// Resolve the schema to prepare it for validation
Expand Down
6 changes: 3 additions & 3 deletions pkg/cli/mcp_server_defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestMCPToolElicitationDefaults(t *testing.T) {
Fix bool `json:"fix,omitempty" jsonschema:"Apply automatic codemod fixes to workflows before compiling"`
}

schema, err := GenerateOutputSchema[compileArgs]()
schema, err := GenerateSchema[compileArgs]()
if err != nil {
t.Fatalf("Failed to generate schema: %v", err)
}
Expand Down Expand Up @@ -60,7 +60,7 @@ func TestMCPToolElicitationDefaults(t *testing.T) {
MaxTokens int `json:"max_tokens,omitempty" jsonschema:"Maximum number of tokens in output before triggering guardrail"`
}

schema, err := GenerateOutputSchema[logsArgs]()
schema, err := GenerateSchema[logsArgs]()
if err != nil {
t.Fatalf("Failed to generate schema: %v", err)
}
Expand Down Expand Up @@ -131,7 +131,7 @@ func TestMCPToolElicitationDefaults(t *testing.T) {
Count int `json:"count" jsonschema:"Count field"`
}

schema, err := GenerateOutputSchema[testArgs]()
schema, err := GenerateSchema[testArgs]()
if err != nil {
t.Fatalf("Failed to generate schema: %v", err)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/cli/mcp_tool_schemas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func TestMCPToolOutputSchemas(t *testing.T) {
t.Run("logs schema can be generated (for future use)", func(t *testing.T) {
// The logs tool currently doesn't use output schemas, but we verify
// the helper can generate them for when they're needed in the future
schema, err := GenerateOutputSchema[LogsData]()
schema, err := GenerateSchema[LogsData]()
if err != nil {
t.Fatalf("Failed to generate schema for LogsData: %v", err)
}
Expand Down Expand Up @@ -52,7 +52,7 @@ func TestMCPToolOutputSchemas(t *testing.T) {
t.Run("audit schema can be generated (for future use)", func(t *testing.T) {
// The audit tool currently doesn't use output schemas (output can be filtered with jq),
// but we verify the helper can generate them for when they're needed in the future
schema, err := GenerateOutputSchema[AuditData]()
schema, err := GenerateSchema[AuditData]()
if err != nil {
t.Fatalf("Failed to generate schema for AuditData: %v", err)
}
Expand Down Expand Up @@ -90,7 +90,7 @@ func TestMCPToolOutputSchemas(t *testing.T) {
t.Run("status tool array schema can be generated", func(t *testing.T) {
// Even though status tool doesn't use the schema (MCP requires objects),
// verify the helper can generate a schema for the array type
schema, err := GenerateOutputSchema[[]WorkflowStatus]()
schema, err := GenerateSchema[[]WorkflowStatus]()
if err != nil {
t.Fatalf("Failed to generate schema for []WorkflowStatus: %v", err)
}
Expand Down
4 changes: 0 additions & 4 deletions pkg/cli/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ var (
includePattern = regexp.MustCompile(`^@include(\?)?\s+(.+)$`)
)

// WorkflowSourceInfo is an alias for FetchedWorkflow for backward compatibility.
// Deprecated: Use FetchedWorkflow directly instead.
type WorkflowSourceInfo = FetchedWorkflow

// isValidWorkflowFile checks if a markdown file is a valid workflow by attempting to parse its frontmatter.
// It validates that the file has proper YAML frontmatter delimited by "---" and contains the required "on" field.
//
Expand Down
Loading
Loading