diff --git a/pkg/errorutil/errors.go b/pkg/errorutil/errors.go index ad0dd20d18f..afc34f5d95c 100644 --- a/pkg/errorutil/errors.go +++ b/pkg/errorutil/errors.go @@ -2,7 +2,13 @@ // returned by the GitHub API and gh CLI. package errorutil -import "strings" +import ( + "strings" + + "github.com/github/gh-aw/pkg/logger" +) + +var errorutilLog = logger.New("errorutil:errors") // IsNotFoundError reports whether err represents an HTTP 404 / "not found" response. // It returns false when err is nil. @@ -10,7 +16,11 @@ import "strings" // the phrase "not found", which covers all known forms returned by the GitHub API, // the gh CLI, and the go-gh library. func IsNotFoundError(err error) bool { - return containsErrorSubstring(err, "404", "not found") + matched := containsErrorSubstring(err, "404", "not found") + if matched { + errorutilLog.Printf("Classified error as not-found (404): %v", err) + } + return matched } // IsForbiddenError reports whether err represents an HTTP 403 / "forbidden" response. @@ -19,7 +29,11 @@ func IsNotFoundError(err error) bool { // "HTTP 403" or "403 Forbidden", which avoids misclassifying unrelated errors // like "forbidden character". func IsForbiddenError(err error) bool { - return containsHTTPStatusSubstring(err, "403", "forbidden") + matched := containsHTTPStatusSubstring(err, "403", "forbidden") + if matched { + errorutilLog.Printf("Classified error as forbidden (403): %v", err) + } + return matched } // IsGoneError reports whether err represents an HTTP 410 / "gone" response. @@ -28,7 +42,11 @@ func IsForbiddenError(err error) bool { // "HTTP 410" or "410 Gone", which avoids misclassifying unrelated errors like // "connection has gone away". func IsGoneError(err error) bool { - return containsHTTPStatusSubstring(err, "410", "gone") + matched := containsHTTPStatusSubstring(err, "410", "gone") + if matched { + errorutilLog.Printf("Classified error as gone (410): %v", err) + } + return matched } // containsErrorSubstring reports whether err contains any of the provided diff --git a/pkg/jsonutil/json.go b/pkg/jsonutil/json.go index 5b3397469f2..cecf3963ff6 100644 --- a/pkg/jsonutil/json.go +++ b/pkg/jsonutil/json.go @@ -4,8 +4,12 @@ import ( "bytes" "encoding/json" "strings" + + "github.com/github/gh-aw/pkg/logger" ) +var jsonutilLog = logger.New("jsonutil:json") + // MarshalCompactNoHTMLEscape marshals a value to compact JSON without HTML escaping. // It trims the trailing newline emitted by json.Encoder. func MarshalCompactNoHTMLEscape(v any) (string, error) { @@ -13,8 +17,11 @@ func MarshalCompactNoHTMLEscape(v any) (string, error) { encoder := json.NewEncoder(&buf) encoder.SetEscapeHTML(false) if err := encoder.Encode(v); err != nil { + jsonutilLog.Printf("MarshalCompactNoHTMLEscape encode failed: %v", err) return "", err } - return strings.TrimSuffix(buf.String(), "\n"), nil + result := strings.TrimSuffix(buf.String(), "\n") + jsonutilLog.Printf("MarshalCompactNoHTMLEscape produced %d bytes", len(result)) + return result, nil } diff --git a/pkg/syncutil/onceloader.go b/pkg/syncutil/onceloader.go index 758391f2dcd..dcea16c21a6 100644 --- a/pkg/syncutil/onceloader.go +++ b/pkg/syncutil/onceloader.go @@ -1,6 +1,12 @@ package syncutil -import "sync" +import ( + "sync" + + "github.com/github/gh-aw/pkg/logger" +) + +var syncutilLog = logger.New("syncutil:onceloader") // OnceLoader caches the result of a fallible, expensive one-shot fetch. // Safe for concurrent use; loader is invoked at most once. @@ -17,8 +23,14 @@ func (o *OnceLoader[T]) Get(loader func() (T, error)) (T, error) { defer o.mu.Unlock() if !o.done { + syncutilLog.Print("OnceLoader.Get: cache miss, invoking loader") o.result, o.err = loader() o.done = true + if o.err != nil { + syncutilLog.Printf("OnceLoader.Get: loader failed: %v", o.err) + } else { + syncutilLog.Print("OnceLoader.Get: loader succeeded, result cached") + } } return o.result, o.err @@ -29,6 +41,7 @@ func (o *OnceLoader[T]) Reset() { o.mu.Lock() defer o.mu.Unlock() + syncutilLog.Print("OnceLoader.Reset: clearing cached state") var zero T o.result = zero o.err = nil @@ -42,6 +55,7 @@ func (o *OnceLoader[T]) Override(result T, err error) { o.mu.Lock() defer o.mu.Unlock() + syncutilLog.Printf("OnceLoader.Override: storing cached value (err=%v)", err) o.result = result o.err = err o.done = true diff --git a/pkg/workflow/safe_outputs_workflow_helpers.go b/pkg/workflow/safe_outputs_workflow_helpers.go index 732786e1cc9..e3ac4de9e99 100644 --- a/pkg/workflow/safe_outputs_workflow_helpers.go +++ b/pkg/workflow/safe_outputs_workflow_helpers.go @@ -4,9 +4,12 @@ import ( "fmt" "sort" + "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/stringutil" ) +var safeOutputsWorkflowHelpersLog = logger.New("workflow:safe_outputs_workflow_helpers") + type workflowToolDefinitionOptions struct { workflowName string workflowInputs map[string]any @@ -16,6 +19,7 @@ type workflowToolDefinitionOptions struct { func generateWorkflowToolDefinition(opts workflowToolDefinitionOptions) map[string]any { toolName := stringutil.NormalizeSafeOutputIdentifier(opts.workflowName) + safeOutputsWorkflowHelpersLog.Printf("Generating workflow tool definition: workflow=%s, tool=%s, inputs=%d", opts.workflowName, toolName, len(opts.workflowInputs)) description := fmt.Sprintf(opts.descriptionFormat, opts.workflowName) properties, required := buildInputSchema(opts.workflowInputs, func(inputName string) string { return fmt.Sprintf("Input parameter '%s' for workflow %s", inputName, opts.workflowName) @@ -35,6 +39,7 @@ func generateWorkflowToolDefinition(opts workflowToolDefinitionOptions) map[stri if len(required) > 0 { sort.Strings(required) tool["inputSchema"].(map[string]any)["required"] = required + safeOutputsWorkflowHelpersLog.Printf("Workflow tool %s has %d required inputs", toolName, len(required)) } return tool @@ -42,15 +47,19 @@ func generateWorkflowToolDefinition(opts workflowToolDefinitionOptions) map[stri func resolveWorkflowExtension(fileResult *findWorkflowFileResult) (string, bool) { if fileResult.lockExists { + safeOutputsWorkflowHelpersLog.Print("Resolved workflow extension: .lock.yml (lock file exists)") return ".lock.yml", true } if fileResult.ymlExists { + safeOutputsWorkflowHelpersLog.Print("Resolved workflow extension: .yml (yml file exists)") return ".yml", true } if fileResult.mdExists { // .md-only: the workflow is a same-batch compilation target that will produce a .lock.yml + safeOutputsWorkflowHelpersLog.Print("Resolved workflow extension: .lock.yml (md-only, same-batch target)") return ".lock.yml", true } + safeOutputsWorkflowHelpersLog.Print("Failed to resolve workflow extension: no candidate files exist") return "", false } diff --git a/pkg/workflow/workflow_inputs_extractor.go b/pkg/workflow/workflow_inputs_extractor.go index 074f75513f6..2ea0b0979e0 100644 --- a/pkg/workflow/workflow_inputs_extractor.go +++ b/pkg/workflow/workflow_inputs_extractor.go @@ -3,12 +3,17 @@ package workflow import ( "os" + "github.com/github/gh-aw/pkg/logger" "github.com/github/gh-aw/pkg/parser" ) +var workflowInputsExtractorLog = logger.New("workflow:workflow_inputs_extractor") + func extractInputsFromYAML(workflowPath, trigger string) (map[string]any, error) { + workflowInputsExtractorLog.Printf("Extracting inputs from YAML: path=%s, trigger=%s", workflowPath, trigger) workflow, err := readWorkflowYAML(workflowPath) if err != nil { + workflowInputsExtractorLog.Printf("Failed to read workflow YAML: %v", err) return nil, err } @@ -16,13 +21,16 @@ func extractInputsFromYAML(workflowPath, trigger string) (map[string]any, error) } func extractInputsFromMarkdown(mdPath, trigger string) (map[string]any, error) { + workflowInputsExtractorLog.Printf("Extracting inputs from markdown: path=%s, trigger=%s", mdPath, trigger) content, err := os.ReadFile(mdPath) // #nosec G304 -- mdPath is validated via isPathWithinDir in findWorkflowFile if err != nil { + workflowInputsExtractorLog.Printf("Failed to read markdown file: %v", err) return nil, err } result, err := parser.ExtractFrontmatterFromContent(string(content)) if err != nil || result == nil { + workflowInputsExtractorLog.Printf("No frontmatter extracted (err=%v), returning empty inputs", err) return make(map[string]any), nil } @@ -60,5 +68,6 @@ func extractInputsFromParsedWorkflow(workflow map[string]any, trigger string) ma return make(map[string]any) } + workflowInputsExtractorLog.Printf("Found %d inputs for trigger %s", len(inputsMap), trigger) return inputsMap }