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
2 changes: 2 additions & 0 deletions pkg/workflow/close_entity_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ func (c *Compiler) parseCloseEntityConfig(outputMap map[string]any, params Close
// Set default max if not specified
if config.Max == 0 {
config.Max = 1
logger.Printf("Set default max to 1 for %s", params.ConfigKey)
}

// Validate target-repo (wildcard "*" is not allowed)
if validateTargetRepoSlug(config.TargetRepoSlug, logger) {
logger.Printf("Invalid target-repo slug for %s", params.ConfigKey)
return nil
}

Expand Down
12 changes: 11 additions & 1 deletion pkg/workflow/expression_patterns.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,17 @@

package workflow

import "regexp"
import (
"regexp"

"github.com/github/gh-aw/pkg/logger"
)

var expressionPatternsLog = logger.New("workflow:expression_patterns")

func init() {
expressionPatternsLog.Print("Initializing expression pattern regex compilation")
}

// Core Expression Patterns
var (
Expand Down
14 changes: 14 additions & 0 deletions pkg/workflow/frontmatter_extraction_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import (
"fmt"
"math"
"strings"

"github.com/github/gh-aw/pkg/logger"
)

var frontmatterMetadataLog = logger.New("workflow:frontmatter_extraction_metadata")

// extractFeatures extracts the features field from frontmatter
// Returns a map of feature flags and configuration options (supports boolean flags and string values)
func (c *Compiler) extractFeatures(frontmatter map[string]any) map[string]any {
Expand Down Expand Up @@ -67,27 +71,33 @@ func (c *Compiler) extractTrackerID(frontmatter map[string]any) (string, error)
return "", nil
}

frontmatterMetadataLog.Print("Extracting and validating tracker-id")

// Convert the value to string
strValue, ok := value.(string)
if !ok {
frontmatterMetadataLog.Printf("Invalid tracker-id type: %T", value)
return "", fmt.Errorf("tracker-id must be a string, got %T. Example: tracker-id: \"my-tracker-123\"", value)
}

trackerID := strings.TrimSpace(strValue)

// Validate minimum length
if len(trackerID) < 8 {
frontmatterMetadataLog.Printf("tracker-id too short: %d characters", len(trackerID))
return "", fmt.Errorf("tracker-id must be at least 8 characters long (got %d)", len(trackerID))
}

// Validate that it's a valid identifier (alphanumeric, hyphens, underscores)
for i, char := range trackerID {
if (char < 'a' || char > 'z') && (char < 'A' || char > 'Z') &&
(char < '0' || char > '9') && char != '-' && char != '_' {
frontmatterMetadataLog.Printf("Invalid character in tracker-id at position %d", i+1)
return "", fmt.Errorf("tracker-id contains invalid character at position %d: '%c' (only alphanumeric, hyphens, and underscores allowed)", i+1, char)
}
}

frontmatterMetadataLog.Printf("Successfully validated tracker-id: %s", trackerID)
return trackerID, nil
}

Expand Down Expand Up @@ -152,6 +162,7 @@ func (c *Compiler) extractToolsTimeout(tools map[string]any) (int, error) {

// Check if timeout is explicitly set in tools
if timeoutValue, exists := tools["timeout"]; exists {
frontmatterMetadataLog.Printf("Extracting tools.timeout value: type=%T", timeoutValue)
var timeout int
// Handle different numeric types with safe conversions to prevent overflow
switch v := timeoutValue.(type) {
Expand All @@ -166,14 +177,17 @@ func (c *Compiler) extractToolsTimeout(tools map[string]any) (int, error) {
case float64:
timeout = int(v)
default:
frontmatterMetadataLog.Printf("Invalid tools.timeout type: %T", timeoutValue)
return 0, fmt.Errorf("tools.timeout must be an integer, got %T", timeoutValue)
}

// Validate minimum value per schema constraint
if timeout < 1 {
frontmatterMetadataLog.Printf("Invalid tools.timeout value: %d (must be >= 1)", timeout)
return 0, fmt.Errorf("tools.timeout must be at least 1 second, got %d. Example:\ntools:\n timeout: 60", timeout)
}

frontmatterMetadataLog.Printf("Extracted tools.timeout: %d seconds", timeout)
return timeout, nil
}

Expand Down
12 changes: 12 additions & 0 deletions pkg/workflow/safe_outputs_config_helpers_reflection.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ package workflow
import (
"reflect"
"sort"

"github.com/github/gh-aw/pkg/logger"
)

var safeOutputReflectionLog = logger.New("workflow:safe_outputs_config_helpers_reflection")

// safeOutputFieldMapping maps struct field names to their tool names
var safeOutputFieldMapping = map[string]string{
"CreateIssues": "create_issue",
Expand Down Expand Up @@ -46,8 +50,11 @@ func hasAnySafeOutputEnabled(safeOutputs *SafeOutputsConfig) bool {
return false
}

safeOutputReflectionLog.Print("Checking if any safe outputs are enabled using reflection")

// Check Jobs separately as it's a map
if len(safeOutputs.Jobs) > 0 {
safeOutputReflectionLog.Printf("Found %d custom jobs enabled", len(safeOutputs.Jobs))
return true
}

Expand All @@ -56,10 +63,12 @@ func hasAnySafeOutputEnabled(safeOutputs *SafeOutputsConfig) bool {
for fieldName := range safeOutputFieldMapping {
field := val.FieldByName(fieldName)
if field.IsValid() && !field.IsNil() {
safeOutputReflectionLog.Printf("Found enabled safe output field: %s", fieldName)
return true
}
}

safeOutputReflectionLog.Print("No safe outputs enabled")
return false
}

Expand All @@ -69,6 +78,7 @@ func getEnabledSafeOutputToolNamesReflection(safeOutputs *SafeOutputsConfig) []s
return nil
}

safeOutputReflectionLog.Print("Getting enabled safe output tool names using reflection")
var tools []string

// Use reflection to check all pointer fields
Expand All @@ -83,10 +93,12 @@ func getEnabledSafeOutputToolNamesReflection(safeOutputs *SafeOutputsConfig) []s
// Add custom job tools
for jobName := range safeOutputs.Jobs {
tools = append(tools, jobName)
safeOutputReflectionLog.Printf("Added custom job tool: %s", jobName)
}

// Sort tools to ensure deterministic compilation
sort.Strings(tools)

safeOutputReflectionLog.Printf("Found %d enabled safe output tools", len(tools))
return tools
}
11 changes: 11 additions & 0 deletions pkg/workflow/safe_outputs_config_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,25 @@ package workflow
import (
"encoding/json"
"fmt"

"github.com/github/gh-aw/pkg/logger"
)

var safeOutputMessagesLog = logger.New("workflow:safe_outputs_config_messages")

// ========================================
// Safe Output Messages Configuration
// ========================================

// parseMessagesConfig parses the messages configuration from safe-outputs frontmatter
func parseMessagesConfig(messagesMap map[string]any) *SafeOutputMessagesConfig {
safeOutputMessagesLog.Printf("Parsing messages configuration with %d fields", len(messagesMap))
config := &SafeOutputMessagesConfig{}

if appendOnly, exists := messagesMap["append-only-comments"]; exists {
if appendOnlyBool, ok := appendOnly.(bool); ok {
config.AppendOnlyComments = appendOnlyBool
safeOutputMessagesLog.Printf("Set append-only-comments: %t", appendOnlyBool)
}
}

Expand Down Expand Up @@ -82,11 +88,13 @@ func parseMessagesConfig(messagesMap map[string]any) *SafeOutputMessagesConfig {
// - true: always allows mentions (error in strict mode)
// - object: detailed configuration with allow-team-members, allow-context, allowed, max
func parseMentionsConfig(mentions any) *MentionsConfig {
safeOutputMessagesLog.Printf("Parsing mentions configuration: type=%T", mentions)
config := &MentionsConfig{}

// Handle boolean value
if boolVal, ok := mentions.(bool); ok {
config.Enabled = &boolVal
safeOutputMessagesLog.Printf("Mentions configured as boolean: %t", boolVal)
return config
}

Expand Down Expand Up @@ -157,9 +165,12 @@ func serializeMessagesConfig(messages *SafeOutputMessagesConfig) (string, error)
if messages == nil {
return "", nil
}
safeOutputMessagesLog.Print("Serializing messages configuration to JSON")
jsonBytes, err := json.Marshal(messages)
if err != nil {
safeOutputMessagesLog.Printf("Failed to serialize messages config: %v", err)
return "", fmt.Errorf("failed to serialize messages config: %w", err)
}
safeOutputMessagesLog.Printf("Serialized messages config: %d bytes", len(jsonBytes))
return string(jsonBytes), nil
}