From b5eb3e67916f697437af4cb8ac37aac811bf69d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 15 Nov 2025 12:17:15 +0000 Subject: [PATCH] Add debug logging to 5 additional Go files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhanced the following files with debug logging statements: 1. **pkg/cli/update_command.go** - Added logging for workflow update lifecycle - Update process initiation with parameters - Workflow scanning and discovery - Ref resolution and version detection - Merge operations and conflict detection - Compilation status 2. **pkg/parser/mcp.go** - Added logging for MCP configuration parsing - MCP configuration extraction with filters - Safe-outputs and safe-jobs detection - Built-in and custom MCP tool processing - MCP type inference (http, stdio, container) 3. **pkg/workflow/threat_detection.go** - Added logging for threat detection - Configuration parsing (enabled/disabled states) - AI engine configuration - Job building with step counts 4. **pkg/workflow/tools_types.go** - Added logging for tools configuration - Tools configuration creation with entry counts - GitHub tool configuration parsing - Custom tools detection 5. **pkg/parser/github_urls.go** - Added logging for GitHub URL parsing - URL parsing and validation - Host and type detection - File, PR, issue, and run URL handling All logging follows project guidelines: - No side effects in logger arguments - Meaningful messages for debugging - Proper logger naming conventions - Function entry logging with parameters 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- pkg/cli/update_command.go | 30 ++++++++++++++++++++++++++++++ pkg/parser/github_urls.go | 15 +++++++++++++++ pkg/parser/mcp.go | 13 +++++++++++++ pkg/workflow/threat_detection.go | 14 ++++++++++++++ pkg/workflow/tools_types.go | 15 ++++++++++++++- 5 files changed, 86 insertions(+), 1 deletion(-) diff --git a/pkg/cli/update_command.go b/pkg/cli/update_command.go index c82c8f531d..d8a5988c18 100644 --- a/pkg/cli/update_command.go +++ b/pkg/cli/update_command.go @@ -10,11 +10,14 @@ import ( "github.com/githubnext/gh-aw/pkg/console" "github.com/githubnext/gh-aw/pkg/constants" + "github.com/githubnext/gh-aw/pkg/logger" "github.com/githubnext/gh-aw/pkg/parser" "github.com/githubnext/gh-aw/pkg/workflow" "github.com/spf13/cobra" ) +var updateLog = logger.New("cli:update_command") + // NewUpdateCommand creates the update command func NewUpdateCommand(validateEngine func(string) error) *cobra.Command { cmd := &cobra.Command{ @@ -118,6 +121,8 @@ func checkExtensionUpdate(verbose bool) error { // 2. Update workflows from source repositories (compiles each workflow after update) // 3. Optionally create a PR func UpdateWorkflowsWithExtensionCheck(workflowNames []string, allowMajor, force, verbose bool, engineOverride string, createPR bool, workflowsDir string, noStopAfter bool, stopAfter string) error { + updateLog.Printf("Starting update process: workflows=%v, allowMajor=%v, force=%v, createPR=%v", workflowNames, allowMajor, force, createPR) + // Step 1: Check for gh-aw extension updates if err := checkExtensionUpdate(verbose); err != nil { return fmt.Errorf("extension update check failed: %w", err) @@ -224,6 +229,8 @@ func createUpdatePR(verbose bool) error { // UpdateWorkflows updates workflows from their source repositories func UpdateWorkflows(workflowNames []string, allowMajor, force, verbose bool, engineOverride string, workflowsDir string, noStopAfter bool, stopAfter string) error { + updateLog.Printf("Scanning for workflows with source field: dir=%s, filter=%v", workflowsDir, workflowNames) + // Use provided workflows directory or default if workflowsDir == "" { workflowsDir = getWorkflowsDir() @@ -235,6 +242,8 @@ func UpdateWorkflows(workflowNames []string, allowMajor, force, verbose bool, en return err } + updateLog.Printf("Found %d workflows with source field", len(workflows)) + if len(workflows) == 0 { if len(workflowNames) > 0 { return fmt.Errorf("no workflows found matching the specified names with source field") @@ -394,12 +403,15 @@ func findWorkflowsWithSource(workflowsDir string, filterNames []string, verbose // resolveLatestRef resolves the latest ref for a workflow source func resolveLatestRef(repo, currentRef string, allowMajor, verbose bool) (string, error) { + updateLog.Printf("Resolving latest ref: repo=%s, currentRef=%s, allowMajor=%v", repo, currentRef, allowMajor) + if verbose { fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Resolving latest ref for %s (current: %s)", repo, currentRef))) } // Check if current ref is a tag (looks like a semantic version) if isSemanticVersionTag(currentRef) { + updateLog.Print("Current ref is semantic version tag, resolving latest release") return resolveLatestRelease(repo, currentRef, allowMajor, verbose) } @@ -414,10 +426,12 @@ func resolveLatestRef(repo, currentRef string, allowMajor, verbose bool) (string } if isBranch { + updateLog.Printf("Current ref is branch: %s", currentRef) return resolveBranchHead(repo, currentRef, verbose) } // Otherwise, use default branch + updateLog.Print("Using default branch for ref resolution") return resolveDefaultBranchHead(repo, verbose) } @@ -546,6 +560,8 @@ func resolveDefaultBranchHead(repo string, verbose bool) (string, error) { // updateWorkflow updates a single workflow from its source func updateWorkflow(wf *workflowWithSource, allowMajor, force, verbose bool, engineOverride string, noStopAfter bool, stopAfter string) error { + updateLog.Printf("Updating workflow: name=%s, source=%s, force=%v", wf.Name, wf.SourceSpec, force) + if verbose { fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("\nUpdating workflow: %s", wf.Name))) fmt.Fprintln(os.Stderr, console.FormatVerboseMessage(fmt.Sprintf("Source: %s", wf.SourceSpec))) @@ -554,6 +570,7 @@ func updateWorkflow(wf *workflowWithSource, allowMajor, force, verbose bool, eng // Parse source spec sourceSpec, err := parseSourceSpec(wf.SourceSpec) if err != nil { + updateLog.Printf("Failed to parse source spec: %v", err) return fmt.Errorf("failed to parse source spec: %w", err) } @@ -576,6 +593,8 @@ func updateWorkflow(wf *workflowWithSource, allowMajor, force, verbose bool, eng // Check if update is needed if !force && currentRef == latestRef { + updateLog.Printf("Workflow already at latest ref: %s, checking for local modifications", currentRef) + // Download the source content to check if local file has been modified sourceContent, err := downloadWorkflowContent(sourceSpec.Repo, sourceSpec.Path, currentRef, verbose) if err != nil { @@ -595,11 +614,13 @@ func updateWorkflow(wf *workflowWithSource, allowMajor, force, verbose bool, eng // Check if local file differs from source if hasLocalModifications(string(sourceContent), string(currentContent), wf.SourceSpec, verbose) { + updateLog.Printf("Local modifications detected in workflow: %s", wf.Name) fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Workflow %s is already up to date (%s)", wf.Name, currentRef))) fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("⚠️ Local copy of %s has been modified from source", wf.Name))) return nil } + updateLog.Printf("Workflow %s is up to date with no local modifications", wf.Name) fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Workflow %s is already up to date (%s)", wf.Name, currentRef))) return nil } @@ -631,11 +652,17 @@ func updateWorkflow(wf *workflowWithSource, allowMajor, force, verbose bool, eng } // Perform 3-way merge using git merge-file + updateLog.Printf("Performing 3-way merge for workflow: %s", wf.Name) mergedContent, hasConflicts, err := MergeWorkflowContent(string(baseContent), string(currentContent), string(newContent), wf.SourceSpec, latestRef, verbose) if err != nil { + updateLog.Printf("Merge failed for workflow %s: %v", wf.Name, err) return fmt.Errorf("failed to merge workflow content: %w", err) } + if hasConflicts { + updateLog.Printf("Merge conflicts detected in workflow: %s", wf.Name) + } + // Handle stop-after field modifications if noStopAfter { // Remove stop-after field if requested @@ -675,10 +702,13 @@ func updateWorkflow(wf *workflowWithSource, allowMajor, force, verbose bool, eng return nil // Not an error, but user needs to resolve conflicts } + updateLog.Printf("Successfully updated workflow %s from %s to %s", wf.Name, currentRef, latestRef) fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Updated %s from %s to %s", wf.Name, currentRef, latestRef))) // Compile the updated workflow with refreshStopTime enabled + updateLog.Printf("Compiling updated workflow: %s", wf.Name) if err := compileWorkflowWithRefresh(wf.Path, verbose, engineOverride, true); err != nil { + updateLog.Printf("Compilation failed for workflow %s: %v", wf.Name, err) return fmt.Errorf("failed to compile updated workflow: %w", err) } diff --git a/pkg/parser/github_urls.go b/pkg/parser/github_urls.go index 50cdd1291c..fb60fdd588 100644 --- a/pkg/parser/github_urls.go +++ b/pkg/parser/github_urls.go @@ -6,8 +6,12 @@ import ( "path/filepath" "strconv" "strings" + + "github.com/githubnext/gh-aw/pkg/logger" ) +var urlLog = logger.New("parser:github_urls") + // GitHubURLType represents the type of GitHub URL type GitHubURLType string @@ -45,9 +49,11 @@ type GitHubURLComponents struct { // - Raw content: https://raw.githubusercontent.com/owner/repo/main/path/to/file.md // - Enterprise URLs: https://github.example.com/owner/repo/... func ParseGitHubURL(urlStr string) (*GitHubURLComponents, error) { + urlLog.Printf("Parsing GitHub URL: %s", urlStr) // Parse the URL parsedURL, err := url.Parse(urlStr) if err != nil { + urlLog.Printf("Failed to parse URL: %v", err) return nil, fmt.Errorf("invalid URL: %w", err) } @@ -57,8 +63,11 @@ func ParseGitHubURL(urlStr string) (*GitHubURLComponents, error) { return nil, fmt.Errorf("URL must include a host") } + urlLog.Printf("Detected host: %s", host) + // Handle raw.githubusercontent.com specially if host == "raw.githubusercontent.com" { + urlLog.Print("Detected raw.githubusercontent.com URL") return parseRawGitHubContentURL(parsedURL) } @@ -81,23 +90,27 @@ func ParseGitHubURL(urlStr string) (*GitHubURLComponents, error) { // Determine the type based on path structure if len(pathParts) >= 4 { urlType := pathParts[2] + urlLog.Printf("Detected URL type segment: %s for %s/%s", urlType, owner, repo) switch urlType { case "actions": // Pattern: /owner/repo/actions/runs/12345678 if len(pathParts) >= 5 && pathParts[3] == "runs" { + urlLog.Print("Parsing GitHub Actions run URL") return parseRunURL(host, owner, repo, pathParts[4:]) } case "runs": // Pattern: /owner/repo/runs/12345678 (short form) if len(pathParts) >= 4 { + urlLog.Print("Parsing GitHub Actions run URL (short form)") return parseRunURL(host, owner, repo, pathParts[3:]) } case "pull": // Pattern: /owner/repo/pull/123 if len(pathParts) >= 4 { + urlLog.Print("Parsing pull request URL") prNumber, err := strconv.ParseInt(pathParts[3], 10, 64) if err != nil { return nil, fmt.Errorf("invalid PR number: %s", pathParts[3]) @@ -130,6 +143,7 @@ func ParseGitHubURL(urlStr string) (*GitHubURLComponents, error) { case "blob", "tree", "raw": // Pattern: /owner/repo/{blob|tree|raw}/ref/path/to/file if len(pathParts) >= 5 { + urlLog.Printf("Parsing file URL (type=%s)", urlType) ref := pathParts[3] filePath := strings.Join(pathParts[4:], "/") @@ -143,6 +157,7 @@ func ParseGitHubURL(urlStr string) (*GitHubURLComponents, error) { urlTypeEnum = URLTypeRaw } + urlLog.Printf("Parsed file URL: ref=%s, path=%s", ref, filePath) return &GitHubURLComponents{ Host: host, Owner: owner, diff --git a/pkg/parser/mcp.go b/pkg/parser/mcp.go index 5f63474f13..dc40e05de7 100644 --- a/pkg/parser/mcp.go +++ b/pkg/parser/mcp.go @@ -96,6 +96,7 @@ func ExtractMCPConfigurations(frontmatter map[string]any, serverFilter string) ( // Check for safe-outputs configuration first (built-in MCP) if safeOutputsSection, hasSafeOutputs := frontmatter["safe-outputs"]; hasSafeOutputs { + mcpLog.Print("Found safe-outputs configuration") // Apply server filter if specified if serverFilter == "" || strings.Contains(constants.SafeOutputsMCPServerID, strings.ToLower(serverFilter)) { config := MCPServerConfig{ @@ -142,6 +143,7 @@ func ExtractMCPConfigurations(frontmatter map[string]any, serverFilter string) ( // Check for top-level safe-jobs configuration if safeJobsSection, hasSafeJobs := frontmatter["safe-jobs"]; hasSafeJobs { + mcpLog.Print("Found safe-jobs configuration") // Apply server filter if specified if serverFilter == "" || strings.Contains(constants.SafeOutputsMCPServerID, strings.ToLower(serverFilter)) { // Find existing safe-outputs config or create new one @@ -176,6 +178,7 @@ func ExtractMCPConfigurations(frontmatter map[string]any, serverFilter string) ( // Get mcp-servers section from frontmatter mcpServersSection, hasMCPServers := frontmatter["mcp-servers"] if !hasMCPServers { + mcpLog.Print("No mcp-servers section found, checking for built-in tools") // Also check tools section for built-in MCP tools (github, playwright) toolsSection, hasTools := frontmatter["tools"] if hasTools { @@ -188,12 +191,14 @@ func ExtractMCPConfigurations(frontmatter map[string]any, serverFilter string) ( return nil, err } if config != nil { + mcpLog.Printf("Added built-in MCP tool: %s", toolName) configs = append(configs, *config) } } } } } + mcpLog.Printf("Extracted %d MCP configurations total", len(configs)) return configs, nil // No mcp-servers configured, but we might have safe-outputs and built-in tools } @@ -222,6 +227,7 @@ func ExtractMCPConfigurations(frontmatter map[string]any, serverFilter string) ( } // Process custom MCP servers from mcp-servers section + mcpLog.Printf("Processing %d custom MCP servers", len(mcpServers)) for serverName, serverValue := range mcpServers { // Apply server filter if specified if serverFilter != "" && !strings.Contains(strings.ToLower(serverName), strings.ToLower(serverFilter)) { @@ -239,9 +245,11 @@ func ExtractMCPConfigurations(frontmatter map[string]any, serverFilter string) ( return nil, fmt.Errorf("failed to parse MCP config for %s: %w", serverName, err) } + mcpLog.Printf("Parsed custom MCP server: %s (type=%s)", serverName, config.Type) configs = append(configs, config) } + mcpLog.Printf("Extracted %d MCP configurations total", len(configs)) return configs, nil } @@ -512,10 +520,13 @@ func ParseMCPConfig(toolName string, mcpSection any, toolConfig map[string]any) // Infer type from presence of fields if _, hasURL := mcpConfig["url"]; hasURL { config.Type = "http" + mcpLog.Printf("Inferred MCP type 'http' for tool %s based on url field", toolName) } else if _, hasCommand := mcpConfig["command"]; hasCommand { config.Type = "stdio" + mcpLog.Printf("Inferred MCP type 'stdio' for tool %s based on command field", toolName) } else if _, hasContainer := mcpConfig["container"]; hasContainer { config.Type = "stdio" + mcpLog.Printf("Inferred MCP type 'stdio' for tool %s based on container field", toolName) } else { return config, fmt.Errorf("unable to determine MCP type for tool '%s': missing type, url, command, or container", toolName) } @@ -531,11 +542,13 @@ func ParseMCPConfig(toolName string, mcpSection any, toolConfig map[string]any) } // Extract configuration based on type + mcpLog.Printf("Extracting %s configuration for tool: %s", config.Type, toolName) switch config.Type { case "stdio": // Handle container field (simplified Docker run) if container, hasContainer := mcpConfig["container"]; hasContainer { if containerStr, ok := container.(string); ok { + mcpLog.Printf("Tool %s uses container: %s", toolName, containerStr) config.Container = containerStr config.Command = "docker" config.Args = []string{"run", "--rm", "-i"} diff --git a/pkg/workflow/threat_detection.go b/pkg/workflow/threat_detection.go index a0f0192972..1823e34d8c 100644 --- a/pkg/workflow/threat_detection.go +++ b/pkg/workflow/threat_detection.go @@ -6,8 +6,11 @@ import ( "strings" "github.com/githubnext/gh-aw/pkg/constants" + "github.com/githubnext/gh-aw/pkg/logger" ) +var threatLog = logger.New("workflow:threat_detection") + //go:embed templates/threat_detection.md var defaultThreatDetectionPrompt string @@ -22,12 +25,15 @@ type ThreatDetectionConfig struct { // parseThreatDetectionConfig handles threat-detection configuration func (c *Compiler) parseThreatDetectionConfig(outputMap map[string]any) *ThreatDetectionConfig { if configData, exists := outputMap["threat-detection"]; exists { + threatLog.Print("Found threat-detection configuration") // Handle boolean values if boolVal, ok := configData.(bool); ok { if !boolVal { + threatLog.Print("Threat detection explicitly disabled") // When explicitly disabled, return nil return nil } + threatLog.Print("Threat detection enabled with default settings") // When enabled as boolean, return empty config return &ThreatDetectionConfig{} } @@ -38,6 +44,7 @@ func (c *Compiler) parseThreatDetectionConfig(outputMap map[string]any) *ThreatD if enabled, exists := configMap["enabled"]; exists { if enabledBool, ok := enabled.(bool); ok { if !enabledBool { + threatLog.Print("Threat detection disabled via enabled field") // When explicitly disabled, return nil return nil } @@ -66,36 +73,43 @@ func (c *Compiler) parseThreatDetectionConfig(outputMap map[string]any) *ThreatD // Handle boolean false to disable AI engine if engineBool, ok := engine.(bool); ok { if !engineBool { + threatLog.Print("Threat detection AI engine disabled") // engine: false means no AI engine steps threatConfig.EngineConfig = nil threatConfig.EngineDisabled = true } } else if engineStr, ok := engine.(string); ok { + threatLog.Printf("Threat detection engine set to: %s", engineStr) // Handle string format threatConfig.EngineConfig = &EngineConfig{ID: engineStr} } else if engineObj, ok := engine.(map[string]any); ok { + threatLog.Print("Parsing threat detection engine configuration") // Handle object format - use extractEngineConfig logic _, engineConfig := c.ExtractEngineConfig(map[string]any{"engine": engineObj}) threatConfig.EngineConfig = engineConfig } } + threatLog.Printf("Threat detection configured with custom prompt: %v, custom steps: %v", threatConfig.Prompt != "", len(threatConfig.Steps) > 0) return threatConfig } } // Default behavior: enabled if any safe-outputs are configured + threatLog.Print("Using default threat detection configuration") return &ThreatDetectionConfig{} } // buildThreatDetectionJob creates the detection job func (c *Compiler) buildThreatDetectionJob(data *WorkflowData, mainJobName string) (*Job, error) { + threatLog.Printf("Building threat detection job for main job: %s", mainJobName) if data.SafeOutputs == nil || data.SafeOutputs.ThreatDetection == nil { return nil, fmt.Errorf("threat detection is not enabled") } // Build steps using a more structured approach steps := c.buildThreatDetectionSteps(data, mainJobName) + threatLog.Printf("Generated %d steps for threat detection job", len(steps)) // Generate agent concurrency configuration (same as main agent job) agentConcurrency := GenerateJobConcurrencyConfig(data) diff --git a/pkg/workflow/tools_types.go b/pkg/workflow/tools_types.go index 07edcae1be..3b05635b77 100644 --- a/pkg/workflow/tools_types.go +++ b/pkg/workflow/tools_types.go @@ -1,7 +1,13 @@ package workflow +import ( + "github.com/githubnext/gh-aw/pkg/logger" +) + +var toolsTypesLog = logger.New("workflow:tools_types") + // Tools represents the parsed tools configuration from workflow frontmatter -type Tools struct { +type Tools struct{ // Built-in tools - using pointers to distinguish between "not set" and "set to nil/empty" GitHub *GitHubToolConfig `yaml:"github,omitempty"` Bash *BashToolConfig `yaml:"bash,omitempty"` @@ -77,6 +83,7 @@ type CacheMemoryToolConfig struct { // NewTools creates a new Tools instance from a map func NewTools(toolsMap map[string]any) *Tools { + toolsTypesLog.Printf("Creating tools configuration from map with %d entries", len(toolsMap)) if toolsMap == nil { return &Tools{ Custom: make(map[string]any), @@ -144,28 +151,34 @@ func NewTools(toolsMap map[string]any) *Tools { "startup-timeout": true, } + customCount := 0 for name, config := range toolsMap { if !knownTools[name] { tools.Custom[name] = config + customCount++ } } + toolsTypesLog.Printf("Parsed tools: github=%v, bash=%v, playwright=%v, custom=%d", tools.GitHub != nil, tools.Bash != nil, tools.Playwright != nil, customCount) return tools } // parseGitHubTool converts raw github tool configuration to GitHubToolConfig func parseGitHubTool(val any) *GitHubToolConfig { if val == nil { + toolsTypesLog.Print("GitHub tool enabled with default configuration") return &GitHubToolConfig{} } // Handle string type (simple enable) if _, ok := val.(string); ok { + toolsTypesLog.Print("GitHub tool enabled with string configuration") return &GitHubToolConfig{} } // Handle map type (detailed configuration) if configMap, ok := val.(map[string]any); ok { + toolsTypesLog.Print("Parsing GitHub tool detailed configuration") config := &GitHubToolConfig{} if allowed, ok := configMap["allowed"].([]any); ok {