From d251415cf5f1a3605f3e202a9641e19f776d1848 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:39:01 +0000 Subject: [PATCH] feat(logging): add debug logging to 5 CLI files for improved troubleshooting Add targeted debug log statements to under-logged functions in the cli package: - audit.go: log step extraction entry/outcome and failing step detection - firewall_policy.go: log rule matching decisions per host/entry - deps_security.go: log advisory API requests and matched dependencies - workflows.go: log workflow file discovery and name suggestion results - mcp_safe_update_cache.go: log manifest cache file write operations Co-Authored-By: Claude Sonnet 4.6 --- pkg/cli/audit.go | 6 ++++++ pkg/cli/deps_security.go | 2 ++ pkg/cli/firewall_policy.go | 4 ++++ pkg/cli/mcp_safe_update_cache.go | 2 ++ pkg/cli/workflows.go | 8 +++++++- 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/pkg/cli/audit.go b/pkg/cli/audit.go index abacfc0ca7e..d90aa99f9a2 100644 --- a/pkg/cli/audit.go +++ b/pkg/cli/audit.go @@ -636,6 +636,7 @@ func auditJobRun(runID int64, jobID int64, stepNumber int, owner, repo, hostname // extractStepOutput extracts the output of a specific step from job logs func extractStepOutput(jobLog string, stepNumber int) (string, error) { + auditLog.Printf("Extracting output for step %d from job logs (%d bytes)", stepNumber, len(jobLog)) lines := strings.Split(jobLog, "\n") var stepOutput []string inStep := false @@ -662,14 +663,17 @@ func extractStepOutput(jobLog string, stepNumber int) (string, error) { } if len(stepOutput) == 0 { + auditLog.Printf("Step %d not found in job logs (scanned %d lines)", stepNumber, len(lines)) return "", fmt.Errorf("step %d not found in job logs", stepNumber) } + auditLog.Printf("Extracted %d lines for step %d", len(stepOutput), stepNumber) return strings.Join(stepOutput, "\n"), nil } // findFirstFailingStep finds the first step that failed in the job logs func findFirstFailingStep(jobLog string) (int, string) { + auditLog.Printf("Searching for first failing step in job logs (%d bytes)", len(jobLog)) lines := strings.Split(jobLog, "\n") var stepOutput []string inStep := false @@ -700,9 +704,11 @@ func findFirstFailingStep(jobLog string) (int, string) { } if foundFailure && len(stepOutput) > 0 { + auditLog.Printf("Found failing step %d with %d lines of output", currentStep, len(stepOutput)) return currentStep, strings.Join(stepOutput, "\n") } + auditLog.Print("No failing step found in job logs") return 0, "" } diff --git a/pkg/cli/deps_security.go b/pkg/cli/deps_security.go index a71cb491b6e..71304dd2c17 100644 --- a/pkg/cli/deps_security.go +++ b/pkg/cli/deps_security.go @@ -134,6 +134,7 @@ func querySecurityAdvisories(depVersions map[string]string, verbose bool) ([]Sec // GitHub Security Advisory API endpoint url := "https://api.github.com/advisories?ecosystem=go&per_page=100" + depsSecurityLog.Printf("Querying GitHub Security Advisory API: url=%s, dep_count=%d", url, len(depVersions)) client := &http.Client{Timeout: 30 * time.Second} req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { @@ -190,6 +191,7 @@ func querySecurityAdvisories(depVersions map[string]string, verbose bool) ([]Sec adv.PatchedVers = []string{vuln.FirstPatchedVersion} } + depsSecurityLog.Printf("Advisory matched dependency: package=%s, version=%s, severity=%s, id=%s", vuln.Package.Name, currentVersion, apiAdv.Severity, apiAdv.GHSAID) matchingAdvisories = append(matchingAdvisories, adv) if verbose { diff --git a/pkg/cli/firewall_policy.go b/pkg/cli/firewall_policy.go index ad10cb0c9c1..f2210807c2c 100644 --- a/pkg/cli/firewall_policy.go +++ b/pkg/cli/firewall_policy.go @@ -271,6 +271,7 @@ func findMatchingRule(entry AuditLogEntry, rules []PolicyRule) *PolicyRule { if isEntryAllowed(entry) { expectedAction = "allow" } + firewallPolicyLog.Printf("Finding matching rule for host=%s, expected_action=%s, rules=%d", entry.Host, expectedAction, len(rules)) for i := range rules { rule := &rules[i] @@ -283,6 +284,7 @@ func findMatchingRule(entry AuditLogEntry, rules []PolicyRule) *PolicyRule { // aclName "all" is a catch-all rule (typically the default deny) if rule.ACLName == "all" { if rule.Action == expectedAction { + firewallPolicyLog.Printf("Matched catch-all rule (action=%s) for host=%s", rule.Action, entry.Host) return rule } continue @@ -291,10 +293,12 @@ func findMatchingRule(entry AuditLogEntry, rules []PolicyRule) *PolicyRule { // Domain match if domainMatchesRule(entry.Host, *rule) { if rule.Action == expectedAction { + firewallPolicyLog.Printf("Matched rule %s (action=%s) for host=%s", rule.ACLName, rule.Action, entry.Host) return rule } } } + firewallPolicyLog.Printf("No matching rule found for host=%s", entry.Host) return nil } diff --git a/pkg/cli/mcp_safe_update_cache.go b/pkg/cli/mcp_safe_update_cache.go index 856830bb489..a776f985cbb 100644 --- a/pkg/cli/mcp_safe_update_cache.go +++ b/pkg/cli/mcp_safe_update_cache.go @@ -59,6 +59,7 @@ func CollectLockFileManifests(workflowsDir string) map[string]*workflow.GHAWMani // WritePriorManifestFile serialises the manifest cache to a temporary JSON file and // returns its path. The caller is responsible for removing the file when done. func WritePriorManifestFile(cache map[string]*workflow.GHAWManifest) (string, error) { + mcpLog.Printf("Writing prior manifest cache to temp file: %d entries", len(cache)) data, err := json.Marshal(cache) if err != nil { return "", fmt.Errorf("marshal manifest cache: %w", err) @@ -75,5 +76,6 @@ func WritePriorManifestFile(cache map[string]*workflow.GHAWManifest) (string, er return "", fmt.Errorf("write manifest cache file: %w", err) } + mcpLog.Printf("Prior manifest cache written to: %s (%d bytes)", f.Name(), len(data)) return f.Name(), nil } diff --git a/pkg/cli/workflows.go b/pkg/cli/workflows.go index 4fc716008d5..0efdb3e66e7 100644 --- a/pkg/cli/workflows.go +++ b/pkg/cli/workflows.go @@ -242,8 +242,11 @@ func suggestWorkflowNames(target string) []string { // Normalize target: strip .md extension and get basename if it's a path normalizedTarget := strings.TrimSuffix(filepath.Base(target), ".md") + workflowsLog.Printf("Suggesting workflow names for %q (available: %d)", normalizedTarget, len(availableNames)) // Use the existing FindClosestMatches function from parser package - return parser.FindClosestMatches(normalizedTarget, availableNames, 3) + suggestions := parser.FindClosestMatches(normalizedTarget, availableNames, 3) + workflowsLog.Printf("Found %d suggestion(s) for %q: %v", len(suggestions), normalizedTarget, suggestions) + return suggestions } // isWorkflowFile returns true if the file should be treated as a workflow file. @@ -266,6 +269,8 @@ func getMarkdownWorkflowFiles(workflowDir string) ([]string, error) { workflowsDir = getWorkflowsDir() } + workflowsLog.Printf("Scanning for markdown workflow files in: %s", workflowsDir) + if _, err := os.Stat(workflowsDir); os.IsNotExist(err) { return nil, fmt.Errorf("no %s directory found", workflowsDir) } @@ -279,6 +284,7 @@ func getMarkdownWorkflowFiles(workflowDir string) ([]string, error) { // Filter out README.md files mdFiles = filterWorkflowFiles(mdFiles) + workflowsLog.Printf("Found %d markdown workflow file(s) in %s", len(mdFiles), workflowsDir) return mdFiles, nil }