Skip to content
Closed
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
22 changes: 13 additions & 9 deletions pkg/workflow/agent_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (c *Compiler) validateAgentFile(workflowData *WorkflowData, markdownPath st
Column: 1,
},
Type: "error",
Message: fmt.Sprintf("agent file '%s' does not exist. Ensure the file exists in the repository and is properly imported.", agentPath),
Message: fmt.Sprintf("πŸ” Can't find the agent file '%s'.\n\nPlease check:\n β€’ The file path is correct\n β€’ The file exists in your repository\n β€’ The path is relative to repository root\n\nExample:\n agent:\n file: \".github/agents/developer.md\"", agentPath),
})
return errors.New(formattedErr)
}
Expand Down Expand Up @@ -126,7 +126,7 @@ func (c *Compiler) validateHTTPTransportSupport(tools map[string]any, engine Cod
for toolName, toolConfig := range tools {
if config, ok := toolConfig.(map[string]any); ok {
if hasMcp, mcpType := hasMCPConfig(config); hasMcp && mcpType == "http" {
return fmt.Errorf("tool '%s' uses HTTP transport which is not supported by engine '%s'. Only stdio transport is supported. Use a different engine (e.g., copilot) or change the tool to use stdio transport. Example:\ntools:\n %s:\n type: stdio\n command: \"node server.js\"", toolName, engine.GetID(), toolName)
return fmt.Errorf("⚠️ The '%s' tool uses HTTP transport, which isn't supported by the '%s' engine.\n\nWhy? The %s engine only supports stdio (local process) communication.\n\nYour options:\n 1. Switch to an engine that supports HTTP (recommended):\n engine: copilot\n\n 2. Change the tool to use stdio transport:\n tools:\n %s:\n type: stdio\n command: \"node server.js\"\n\nLearn more: https://githubnext.github.io/gh-aw/reference/engines/", toolName, engine.GetID(), engine.GetID(), toolName)
}
}
}
Expand All @@ -149,7 +149,7 @@ func (c *Compiler) validateMaxTurnsSupport(frontmatter map[string]any, engine Co

// max-turns is specified, check if the engine supports it
if !engine.SupportsMaxTurns() {
return fmt.Errorf("max-turns not supported: engine '%s' does not support the max-turns feature. Use engine: copilot or remove max-turns from your configuration. Example:\nengine:\n id: copilot\n max-turns: 5", engine.GetID())
return fmt.Errorf("⚠️ The max-turns feature isn't supported by the '%s' engine.\n\nWhy? This feature requires specific engine capabilities for conversation management.\n\nYour options:\n 1. Switch to Copilot (recommended):\n engine:\n id: copilot\n max-turns: 5\n\n 2. Remove max-turns from your configuration\n\nLearn more: https://githubnext.github.io/gh-aw/reference/engines/#max-turns", engine.GetID())
}

// Engine supports max-turns - additional validation could be added here if needed
Expand All @@ -170,7 +170,7 @@ func (c *Compiler) validateWebSearchSupport(tools map[string]any, engine CodingA

// web-search is specified, check if the engine supports it
if !engine.SupportsWebSearch() {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Engine '%s' does not support the web-search tool. See https://githubnext.github.io/gh-aw/guides/web-search/ for alternatives.", engine.GetID())))
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("πŸ’‘ The '%s' engine doesn't support the web-search tool yet.\n\nThe workflow will compile, but web-search won't be available at runtime.\n\nSee alternatives: https://githubnext.github.io/gh-aw/guides/web-search/", engine.GetID())))
c.IncrementWarningCount()
}
}
Expand Down Expand Up @@ -230,17 +230,21 @@ func (c *Compiler) validateWorkflowRunBranches(workflowData *WorkflowData, markd
}

// workflow_run without branches - this is a warning or error depending on mode
message := "workflow_run trigger should include branch restrictions for security and performance.\n\n" +
"Without branch restrictions, the workflow will run for workflow runs on ALL branches,\n" +
"which can cause unexpected behavior and security issues.\n\n" +
"Suggested fix: Add branch restrictions to your workflow_run trigger:\n" +
message := "⚠️ Your workflow_run trigger is missing branch restrictions.\n\n" +
"Why this matters: Without branch restrictions, the workflow will run for ALL branches,\n" +
"which can cause:\n" +
" β€’ Unexpected behavior on feature branches\n" +
" β€’ Wasted CI resources\n" +
" β€’ Potential security issues\n\n" +
"Recommended fix - Add branch restrictions:\n" +
"on:\n" +
" workflow_run:\n" +
" workflows: [\"your-workflow\"]\n" +
" types: [completed]\n" +
" branches:\n" +
" - main\n" +
" - develop"
" - develop\n\n" +
"Learn more: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run"

if c.strictMode {
// In strict mode, this is an error
Expand Down
14 changes: 7 additions & 7 deletions pkg/workflow/compiler_expression_size_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ safe-outputs:
// This should fail with an expression size validation error
if err == nil {
t.Error("Expected error for workflow with oversized expressions, got nil")
} else if !strings.Contains(err.Error(), "exceeds maximum allowed") {
t.Errorf("Expected 'exceeds maximum allowed' error, got: %v", err)
} else if !strings.Contains(err.Error(), "is too large") {
t.Errorf("Expected 'is too large' error, got: %v", err)
} else if !strings.Contains(err.Error(), "expression size validation failed") {
t.Errorf("Expected 'expression size validation failed' error, got: %v", err)
}
Expand Down Expand Up @@ -146,8 +146,8 @@ safe-outputs:
// This should fail with an expression size validation error even without explicit validation enablement
if err == nil {
t.Error("Expected error for workflow with oversized expressions with default validation settings, got nil")
} else if !strings.Contains(err.Error(), "exceeds maximum allowed") {
t.Errorf("Expected 'exceeds maximum allowed' error with default validation, got: %v", err)
} else if !strings.Contains(err.Error(), "is too large") {
t.Errorf("Expected 'is too large' error with default validation, got: %v", err)
} else if !strings.Contains(err.Error(), "expression size validation failed") {
t.Errorf("Expected 'expression size validation failed' error with default validation, got: %v", err)
}
Expand All @@ -166,12 +166,12 @@ safe-outputs:
actualSize := console.FormatFileSize(testLineSize)
maxSizeFormatted := console.FormatFileSize(int64(MaxExpressionSize))

expectedMessage := fmt.Sprintf("expression value for 'WORKFLOW_MARKDOWN' (%s) exceeds maximum allowed size (%s)",
expectedMessage := fmt.Sprintf("expression value for 'WORKFLOW_MARKDOWN' (%s) is too large size (%s)",
actualSize, maxSizeFormatted)

// Verify the message contains expected elements
if !strings.Contains(expectedMessage, "exceeds maximum allowed size") {
t.Error("Error message should contain 'exceeds maximum allowed size'")
if !strings.Contains(expectedMessage, "is too large size") {
t.Error("Error message should contain 'is too large size'")
}
if !strings.Contains(expectedMessage, "KB") {
t.Error("Error message should contain size in KB")
Expand Down
24 changes: 18 additions & 6 deletions pkg/workflow/dangerous_permissions_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,33 @@ func findWritePermissions(permissions *Permissions) []PermissionScope {
return writePerms
}

// formatDangerousPermissionsError formats an error message for write permissions violations
// formatDangerousPermissionsError formats an empathetic error message for write permissions violations
func formatDangerousPermissionsError(writePermissions []PermissionScope) error {
var lines []string
lines = append(lines, "Write permissions are not allowed.")
lines = append(lines, "πŸ”’ Write permissions detected in your workflow.")
lines = append(lines, "")
lines = append(lines, "Why this matters: For security, workflows use read-only permissions by default.")
lines = append(lines, "Write permissions can modify repository contents and settings, which requires")
lines = append(lines, "explicit opt-in through a feature flag.")
lines = append(lines, "")
lines = append(lines, "Found write permissions:")
for _, scope := range writePermissions {
lines = append(lines, fmt.Sprintf(" - %s: write", scope))
lines = append(lines, fmt.Sprintf(" β€’ %s: write", scope))
}
lines = append(lines, "")
lines = append(lines, "To fix this issue, change write permissions to read:")
lines = append(lines, "permissions:")
lines = append(lines, "You have two options:")
lines = append(lines, "")
lines = append(lines, "1. Change to read-only (recommended):")
lines = append(lines, " permissions:")
for _, scope := range writePermissions {
lines = append(lines, fmt.Sprintf(" %s: read", scope))
lines = append(lines, fmt.Sprintf(" %s: read", scope))
}
lines = append(lines, "")
lines = append(lines, "2. Enable write permissions feature flag:")
lines = append(lines, " features:")
lines = append(lines, " dangerous-permissions-write: true")
lines = append(lines, "")
lines = append(lines, "Learn more: https://githubnext.github.io/gh-aw/reference/permissions/")

return fmt.Errorf("%s", strings.Join(lines, "\n"))
}
20 changes: 10 additions & 10 deletions pkg/workflow/dangerous_permissions_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ func TestValidateDangerousPermissions(t *testing.T) {
name: "write permission without feature flag - should error",
permissions: "permissions:\n contents: write",
shouldError: true,
errorContains: "Write permissions are not allowed",
errorContains: "Write permissions detected",
},
{
name: "multiple write permissions without feature flag - should error",
permissions: "permissions:\n contents: write\n issues: write",
shouldError: true,
errorContains: "Write permissions are not allowed",
errorContains: "Write permissions detected",
},
{
name: "write permission with feature flag enabled - should pass",
Expand All @@ -50,7 +50,7 @@ func TestValidateDangerousPermissions(t *testing.T) {
"dangerous-permissions-write": false,
},
shouldError: true,
errorContains: "Write permissions are not allowed",
errorContains: "Write permissions detected",
},
{
name: "shorthand read-all - should pass",
Expand All @@ -61,7 +61,7 @@ func TestValidateDangerousPermissions(t *testing.T) {
name: "shorthand write-all without feature flag - should error",
permissions: "permissions: write-all",
shouldError: true,
errorContains: "Write permissions are not allowed",
errorContains: "Write permissions detected",
},
{
name: "shorthand write-all with feature flag - should pass",
Expand Down Expand Up @@ -198,13 +198,13 @@ func TestFormatDangerousPermissionsError(t *testing.T) {
PermissionContents,
},
expectedContains: []string{
"Write permissions are not allowed",
"Write permissions detected",
"contents: write",
"contents: read",
"dangerous-permissions-write: true",
},
expectedNotContain: []string{
"dangerous-permissions-write: true",
"Option 2",
// Empty - we now include the feature flag option
},
},
{
Expand All @@ -214,15 +214,15 @@ func TestFormatDangerousPermissionsError(t *testing.T) {
PermissionIssues,
},
expectedContains: []string{
"Write permissions are not allowed",
"Write permissions detected",
"contents: write",
"issues: write",
"contents: read",
"issues: read",
"dangerous-permissions-write: true",
},
expectedNotContain: []string{
"dangerous-permissions-write: true",
"Option 2",
// Empty - we now include the feature flag option
},
},
}
Expand Down
Loading