From 6fb4509c4bca84f2e1d0a274bf3062cfdc2f9122 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 10:27:49 +0000 Subject: [PATCH 1/4] Initial plan From b59aff787211fc6f87518bd049bb6cd6a3c99bc7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 10:33:20 +0000 Subject: [PATCH 2/4] chore: outline plan for raw log linter fixes Agent-Logs-Url: https://github.com/github/gh-aw/sessions/25ef1873-b711-46a4-bce0-fb37054323df Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .github/workflows/smoke-otel-backends.lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-otel-backends.lock.yml b/.github/workflows/smoke-otel-backends.lock.yml index a581ff20194..c5f41ee2366 100644 --- a/.github/workflows/smoke-otel-backends.lock.yml +++ b/.github/workflows/smoke-otel-backends.lock.yml @@ -773,7 +773,7 @@ jobs: "url": "https://mcp.datadoghq.com/api/unstable/mcp-server/mcp?toolsets=core", "headers": { "DD_API_KEY": "\${DD_API_KEY}", - "DD_APPLICATION_KEY": "\${DD_APP_KEY}", + "DD_APPLICATION_KEY": "\${DD_APPLICATION_KEY}", "DD_SITE": "\${DD_SITE}" }, "tools": [ From fdbf72d75559a21537a6a2d11ba15edfb927c1cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 10:46:03 +0000 Subject: [PATCH 3/4] fix: replace raw log usage identifiers in workflow parser and actionpins Agent-Logs-Url: https://github.com/github/gh-aw/sessions/25ef1873-b711-46a4-bce0-fb37054323df Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .../workflows/smoke-otel-backends.lock.yml | 2 +- pkg/actionpins/actionpins.go | 50 ++++++++-------- pkg/parser/content_extractor.go | 8 +-- pkg/parser/frontmatter.go | 2 +- pkg/parser/frontmatter_content.go | 24 ++++---- pkg/parser/import_bfs.go | 40 ++++++------- pkg/parser/import_field_extractor.go | 48 +++++++-------- pkg/parser/schedule_cron_detection.go | 12 ++-- pkg/parser/schema_errors.go | 20 +++---- pkg/parser/tools_merger.go | 8 +-- pkg/workflow/compiler.go | 50 ++++++++-------- pkg/workflow/compiler_jobs.go | 6 +- pkg/workflow/compiler_main_job.go | 2 +- pkg/workflow/compiler_orchestrator_engine.go | 4 +- .../compiler_orchestrator_frontmatter.go | 6 +- pkg/workflow/compiler_orchestrator_tools.go | 6 +- pkg/workflow/compiler_string_api.go | 4 +- pkg/workflow/compiler_validators.go | 58 +++++++++---------- pkg/workflow/config_helpers.go | 30 +++++----- pkg/workflow/create_discussion.go | 6 +- pkg/workflow/engine_firewall_support.go | 4 +- pkg/workflow/missing_issue_reporting.go | 18 +++--- pkg/workflow/parse_helpers.go | 8 +-- .../permissions_compiler_validator.go | 18 +++--- pkg/workflow/project_config_parsing.go | 6 +- pkg/workflow/runtime_detection.go | 4 +- pkg/workflow/runtime_step_generator.go | 2 +- pkg/workflow/templatables.go | 12 ++-- pkg/workflow/update_entity_helpers.go | 2 +- pkg/workflow/validation_helpers.go | 6 +- 30 files changed, 233 insertions(+), 233 deletions(-) diff --git a/.github/workflows/smoke-otel-backends.lock.yml b/.github/workflows/smoke-otel-backends.lock.yml index c5f41ee2366..a581ff20194 100644 --- a/.github/workflows/smoke-otel-backends.lock.yml +++ b/.github/workflows/smoke-otel-backends.lock.yml @@ -773,7 +773,7 @@ jobs: "url": "https://mcp.datadoghq.com/api/unstable/mcp-server/mcp?toolsets=core", "headers": { "DD_API_KEY": "\${DD_API_KEY}", - "DD_APPLICATION_KEY": "\${DD_APPLICATION_KEY}", + "DD_APPLICATION_KEY": "\${DD_APP_KEY}", "DD_SITE": "\${DD_SITE}" }, "tools": [ diff --git a/pkg/actionpins/actionpins.go b/pkg/actionpins/actionpins.go index 461074fbfcc..829b65a2986 100644 --- a/pkg/actionpins/actionpins.go +++ b/pkg/actionpins/actionpins.go @@ -22,7 +22,7 @@ import ( "github.com/github/gh-aw/pkg/semverutil" ) -var log = logger.New("workflow:action_pins") +var actionPinsLog = logger.New("actionpins:actionpins") //go:embed data/action_pins.json var actionPinsJSON []byte @@ -109,16 +109,16 @@ var ( func getActionPins() []ActionPin { actionPinsOnce.Do(func() { - log.Print("Unmarshaling action pins from embedded JSON (first call, will be cached)") + actionPinsLog.Print("Unmarshaling action pins from embedded JSON (first call, will be cached)") var data ActionPinsData if err := json.Unmarshal(actionPinsJSON, &data); err != nil { - log.Printf("Failed to unmarshal action pins JSON: %v", err) + actionPinsLog.Printf("Failed to unmarshal action pins JSON: %v", err) panic(fmt.Sprintf("failed to load action pins: %v", err)) } if n := countPinKeyMismatches(data.Entries); n > 0 { - log.Printf("Found %d key/version mismatches in action_pins.json", n) + actionPinsLog.Printf("Found %d key/version mismatches in action_pins.json", n) } pins := slices.Collect(maps.Values(data.Entries)) @@ -130,17 +130,17 @@ func getActionPins() []ActionPin { return cmp.Compare(a.Repo, b.Repo) }) - log.Printf("Successfully unmarshaled and sorted %d action pins from JSON", len(pins)) + actionPinsLog.Printf("Successfully unmarshaled and sorted %d action pins from JSON", len(pins)) cachedActionPins = pins cachedActionPinsByRepo = buildByRepoIndex(pins) - log.Printf("Built per-repo action pin index for %d repos", len(cachedActionPinsByRepo)) + actionPinsLog.Printf("Built per-repo action pin index for %d repos", len(cachedActionPinsByRepo)) cachedContainerPins = data.Containers if cachedContainerPins == nil { cachedContainerPins = make(map[string]ContainerPin) } - log.Printf("Loaded %d container pins from JSON", len(cachedContainerPins)) + actionPinsLog.Printf("Loaded %d container pins from JSON", len(cachedContainerPins)) }) return cachedActionPins @@ -156,7 +156,7 @@ func countPinKeyMismatches(entries map[string]ActionPin) int { if keyVersion != pin.Version { count++ shortSHA := pin.SHA[:min(len(pin.SHA), 8)] - log.Printf("WARNING: Key/version mismatch in action_pins.json: key=%s has version=%s but pin.Version=%s (sha=%s)", + actionPinsLog.Printf("WARNING: Key/version mismatch in action_pins.json: key=%s has version=%s but pin.Version=%s (sha=%s)", key, keyVersion, pin.Version, shortSHA) } } @@ -309,40 +309,40 @@ func ResolveActionPin(actionRepo, version string, ctx *PinContext) (string, erro if ctx == nil { ctx = &PinContext{} } - log.Printf("Resolving action pin: repo=%s, version=%s, strict_mode=%t", actionRepo, version, ctx.StrictMode) + actionPinsLog.Printf("Resolving action pin: repo=%s, version=%s, strict_mode=%t", actionRepo, version, ctx.StrictMode) isAlreadySHA := isValidFullSHA(version) if ctx.Resolver != nil && !isAlreadySHA { - log.Printf("Attempting dynamic resolution for %s@%s", actionRepo, version) + actionPinsLog.Printf("Attempting dynamic resolution for %s@%s", actionRepo, version) sha, err := ctx.Resolver.ResolveSHA(cmp.Or(ctx.Ctx, context.Background()), actionRepo, version) if err == nil && sha != "" { - log.Printf("Dynamic resolution succeeded: %s@%s → %s", actionRepo, version, sha) + actionPinsLog.Printf("Dynamic resolution succeeded: %s@%s → %s", actionRepo, version, sha) resolvedVersion := findVersionBySHA(actionRepo, sha) result := formatPinnedActionWithResolution(actionRepo, sha, version, resolvedVersion) - log.Printf("Returning pinned reference: %s", result) + actionPinsLog.Printf("Returning pinned reference: %s", result) return result, nil } - log.Printf("Dynamic resolution failed for %s@%s: %v", actionRepo, version, err) + actionPinsLog.Printf("Dynamic resolution failed for %s@%s: %v", actionRepo, version, err) } else { if isAlreadySHA { - log.Printf("Version is already a SHA, skipping dynamic resolution") + actionPinsLog.Printf("Version is already a SHA, skipping dynamic resolution") } else { - log.Printf("No action resolver available, skipping dynamic resolution") + actionPinsLog.Printf("No action resolver available, skipping dynamic resolution") } } - log.Printf("Falling back to hardcoded pins for %s@%s", actionRepo, version) + actionPinsLog.Printf("Falling back to hardcoded pins for %s@%s", actionRepo, version) matchingPins := GetActionPinsByRepo(actionRepo) if len(matchingPins) == 0 { - log.Printf("No hardcoded pins found for %s", actionRepo) + actionPinsLog.Printf("No hardcoded pins found for %s", actionRepo) } else { - log.Printf("Found %d hardcoded pin(s) for %s", len(matchingPins), actionRepo) + actionPinsLog.Printf("Found %d hardcoded pin(s) for %s", len(matchingPins), actionRepo) for _, pin := range matchingPins { if pin.Version == version { - log.Printf("Exact version match: requested=%s, found=%s", version, pin.Version) + actionPinsLog.Printf("Exact version match: requested=%s, found=%s", version, pin.Version) return FormatPinnedActionReference(actionRepo, pin.SHA, pin.Version), nil } } @@ -350,21 +350,21 @@ func ResolveActionPin(actionRepo, version string, ctx *PinContext) (string, erro if isAlreadySHA { for _, pin := range matchingPins { if pin.SHA == version { - log.Printf("Exact SHA match: requested=%s, found version=%s", version, pin.Version) + actionPinsLog.Printf("Exact SHA match: requested=%s, found version=%s", version, pin.Version) return FormatPinnedActionReference(actionRepo, pin.SHA, pin.Version), nil } } - log.Printf("SHA %s not found in hardcoded pins, returning as-is", version) + actionPinsLog.Printf("SHA %s not found in hardcoded pins, returning as-is", version) return FormatPinnedActionReference(actionRepo, version, version), nil } if !ctx.StrictMode && len(matchingPins) > 0 { selectedPin, foundCompatible := findCompatiblePin(matchingPins, version) if foundCompatible { - log.Printf("No exact match for version %s, using highest semver-compatible version: %s", version, selectedPin.Version) + actionPinsLog.Printf("No exact match for version %s, using highest semver-compatible version: %s", version, selectedPin.Version) } else { selectedPin = matchingPins[0] - log.Printf("No exact match for version %s, no semver-compatible versions found, using highest available: %s", version, selectedPin.Version) + actionPinsLog.Printf("No exact match for version %s, no semver-compatible versions found, using highest available: %s", version, selectedPin.Version) } if !isAlreadySHA { @@ -377,14 +377,14 @@ func ResolveActionPin(actionRepo, version string, ctx *PinContext) (string, erro ctx.Warnings[cacheKey] = true } } - log.Printf("Using version in non-strict mode: %s@%s (requested) → %s@%s (used)", + actionPinsLog.Printf("Using version in non-strict mode: %s@%s (requested) → %s@%s (used)", actionRepo, version, actionRepo, selectedPin.Version) return formatPinnedActionWithResolution(actionRepo, selectedPin.SHA, version, selectedPin.Version), nil } } if isAlreadySHA { - log.Printf("SHA %s not found in hardcoded pins, returning as-is", version) + actionPinsLog.Printf("SHA %s not found in hardcoded pins, returning as-is", version) return FormatPinnedActionReference(actionRepo, version, version), nil } diff --git a/pkg/parser/content_extractor.go b/pkg/parser/content_extractor.go index d7e6bc501d8..1dc1e6a5300 100644 --- a/pkg/parser/content_extractor.go +++ b/pkg/parser/content_extractor.go @@ -13,10 +13,10 @@ var contentExtractorLog = logger.New("parser:content_extractor") // extractToolsFromContent extracts tools and mcp-servers sections from frontmatter as JSON string func extractToolsFromContent(content string) (string, error) { - log.Printf("Extracting tools from content: size=%d bytes", len(content)) + parserLog.Printf("Extracting tools from content: size=%d bytes", len(content)) result, err := ExtractFrontmatterFromContent(content) if err != nil { - log.Printf("Failed to extract frontmatter: %v", err) + parserLog.Printf("Failed to extract frontmatter: %v", err) return "{}", nil // Return empty object on error to match bash behavior } @@ -40,11 +40,11 @@ func extractToolsFromContent(content string) (string, error) { // If nothing was extracted, return empty object if len(extracted) == 0 { - log.Print("No tools or mcp-servers found in content") + parserLog.Print("No tools or mcp-servers found in content") return "{}", nil } - log.Printf("Extracted %d tool/server configurations", len(extracted)) + parserLog.Printf("Extracted %d tool/server configurations", len(extracted)) // Convert to JSON string extractedJSON, err := json.Marshal(extracted) if err != nil { diff --git a/pkg/parser/frontmatter.go b/pkg/parser/frontmatter.go index 082fa356a92..05fe606e2ec 100644 --- a/pkg/parser/frontmatter.go +++ b/pkg/parser/frontmatter.go @@ -4,4 +4,4 @@ import ( "github.com/github/gh-aw/pkg/logger" ) -var log = logger.New("parser:frontmatter") +var parserLog = logger.New("parser:frontmatter") diff --git a/pkg/parser/frontmatter_content.go b/pkg/parser/frontmatter_content.go index 5145d6fc825..93988df56cb 100644 --- a/pkg/parser/frontmatter_content.go +++ b/pkg/parser/frontmatter_content.go @@ -23,7 +23,7 @@ type FrontmatterResult struct { // ExtractFrontmatterFromContent parses YAML frontmatter from markdown content string func ExtractFrontmatterFromContent(content string) (*FrontmatterResult, error) { - log.Printf("Extracting frontmatter from content: size=%d bytes", len(content)) + parserLog.Printf("Extracting frontmatter from content: size=%d bytes", len(content)) // Fast-path: inspect only the first line to determine whether frontmatter exists. firstNewline := strings.IndexByte(content, '\n') firstLine := content @@ -33,7 +33,7 @@ func ExtractFrontmatterFromContent(content string) (*FrontmatterResult, error) { // Check if file starts with frontmatter delimiter. if !isFrontmatterDelimiterLine(firstLine) { - log.Print("No frontmatter delimiter found, returning content as markdown") + parserLog.Print("No frontmatter delimiter found, returning content as markdown") // No frontmatter, return entire content as markdown return &FrontmatterResult{ Frontmatter: make(map[string]any), @@ -113,7 +113,7 @@ func ExtractFrontmatterFromContent(content string) (*FrontmatterResult, error) { markdown = content[markdownStart:] } - log.Printf("Successfully extracted frontmatter: fields=%d, markdown_size=%d bytes", len(frontmatter), len(markdown)) + parserLog.Printf("Successfully extracted frontmatter: fields=%d, markdown_size=%d bytes", len(frontmatter), len(markdown)) return &FrontmatterResult{ Frontmatter: frontmatter, Markdown: strings.TrimSpace(markdown), @@ -190,7 +190,7 @@ func ExtractFrontmatterFromBuiltinFile(path string, content []byte) (*Frontmatte // ExtractMarkdownSection extracts a specific section from markdown content // Supports H1-H3 headers and proper nesting (matches bash implementation) func ExtractMarkdownSection(content, sectionName string) (string, error) { - log.Printf("Extracting markdown section: section=%s, content_size=%d bytes", sectionName, len(content)) + parserLog.Printf("Extracting markdown section: section=%s, content_size=%d bytes", sectionName, len(content)) scanner := bufio.NewScanner(strings.NewReader(content)) var sectionContent bytes.Buffer inSection := false @@ -225,12 +225,12 @@ func ExtractMarkdownSection(content, sectionName string) (string, error) { } if !inSection { - log.Printf("Section not found: %s", sectionName) + parserLog.Printf("Section not found: %s", sectionName) return "", fmt.Errorf("section '%s' not found", sectionName) } extractedContent := strings.TrimSpace(sectionContent.String()) - log.Printf("Successfully extracted section: size=%d bytes", len(extractedContent)) + parserLog.Printf("Successfully extracted section: size=%d bytes", len(extractedContent)) return extractedContent, nil } @@ -269,15 +269,15 @@ func findH1WorkflowName(markdownBody string) string { // avoids the redundant file-read and YAML-parse that those functions perform when the caller // already holds the parsed FrontmatterResult. func ExtractWorkflowNameFromMarkdownBody(markdownBody string, virtualPath string) (string, error) { - log.Printf("Extracting workflow name from markdown body: virtualPath=%s, size=%d bytes", virtualPath, len(markdownBody)) + parserLog.Printf("Extracting workflow name from markdown body: virtualPath=%s, size=%d bytes", virtualPath, len(markdownBody)) if name := findH1WorkflowName(markdownBody); name != "" { - log.Printf("Found workflow name from H1 header: %s", name) + parserLog.Printf("Found workflow name from H1 header: %s", name) return name, nil } defaultName := generateDefaultWorkflowName(virtualPath) - log.Printf("No H1 header found, using default name: %s", defaultName) + parserLog.Printf("No H1 header found, using default name: %s", defaultName) return defaultName, nil } @@ -285,7 +285,7 @@ func ExtractWorkflowNameFromMarkdownBody(markdownBody string, virtualPath string // This is the in-memory equivalent of ExtractWorkflowNameFromMarkdown, used by Wasm builds // where filesystem access is unavailable. func ExtractWorkflowNameFromContent(content string, virtualPath string) (string, error) { - log.Printf("Extracting workflow name from content: virtualPath=%s, size=%d bytes", virtualPath, len(content)) + parserLog.Printf("Extracting workflow name from content: virtualPath=%s, size=%d bytes", virtualPath, len(content)) markdownContent, err := ExtractMarkdownContent(content) if err != nil { @@ -293,12 +293,12 @@ func ExtractWorkflowNameFromContent(content string, virtualPath string) (string, } if name := findH1WorkflowName(markdownContent); name != "" { - log.Printf("Found workflow name from H1 header: %s", name) + parserLog.Printf("Found workflow name from H1 header: %s", name) return name, nil } defaultName := generateDefaultWorkflowName(virtualPath) - log.Printf("No H1 header found, using default name: %s", defaultName) + parserLog.Printf("No H1 header found, using default name: %s", defaultName) return defaultName, nil } diff --git a/pkg/parser/import_bfs.go b/pkg/parser/import_bfs.go index 1be6c744010..a18cce9e310 100644 --- a/pkg/parser/import_bfs.go +++ b/pkg/parser/import_bfs.go @@ -25,7 +25,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a return &ImportsResult{}, nil } - log.Print("Processing imports from frontmatter with recursive BFS") + parserLog.Print("Processing imports from frontmatter with recursive BFS") // Parse imports field - can be array of strings or objects with path and inputs, // or an object with an 'aw' (agentic workflow paths) subfield. @@ -68,7 +68,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a return &ImportsResult{}, nil } - log.Printf("Found %d direct imports to process", len(importSpecs)) + parserLog.Printf("Found %d direct imports to process", len(importSpecs)) // Initialize BFS queue and visited set for cycle detection var queue []importQueueItem @@ -87,7 +87,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a // Check if this is a repository-only import (owner/repo@ref without file path) if isRepositoryImport(importPath) { - log.Printf("Detected repository import: %s", importPath) + parserLog.Printf("Detected repository import: %s", importPath) acc.repositoryImports = append(acc.repositoryImports, importPath) // Repository imports don't need further processing - they're handled at runtime continue @@ -160,13 +160,13 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a inputs: importSpec.Inputs, remoteOrigin: origin, }) - log.Printf("Queued import: %s (resolved to %s)", importPath, fullPath) + parserLog.Printf("Queued import: %s (resolved to %s)", importPath, fullPath) } else { // Same file imported again - verify the 'with' values are identical if err := checkImportInputsConsistency(importPath, visitedInputs[fullPath], importSpec.Inputs); err != nil { return nil, err } - log.Printf("Skipping duplicate import: %s (already visited)", importPath) + parserLog.Printf("Skipping duplicate import: %s (already visited)", importPath) } } @@ -176,7 +176,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a item := queue[0] queue = queue[1:] - log.Printf("Processing import from queue: %s", item.fullPath) + parserLog.Printf("Processing import from queue: %s", item.fullPath) // Merge inputs from this import into the aggregated inputs map maps.Copy(acc.importInputs, item.inputs) @@ -191,7 +191,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a if isAgentFile { if acc.agentFile != "" { // Multiple agent files found - error - log.Printf("Multiple agent files found: %s and %s", acc.agentFile, item.importPath) + parserLog.Printf("Multiple agent files found: %s and %s", acc.agentFile, item.importPath) return nil, fmt.Errorf("multiple agent files found in imports: '%s' and '%s'. Only one agent file is allowed per workflow", acc.agentFile, item.importPath) } // Extract relative path from repository root (from .github/ onwards) @@ -204,22 +204,22 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a acc.agentFile = fullPathSlash importRelPath = fullPathSlash } - log.Printf("Found agent file: %s (resolved to: %s)", item.fullPath, acc.agentFile) + parserLog.Printf("Found agent file: %s (resolved to: %s)", item.fullPath, acc.agentFile) // Store the original import specification for remote agents // This allows runtime detection and .github folder merging acc.agentImportSpec = item.importPath - log.Printf("Agent import specification: %s", acc.agentImportSpec) + parserLog.Printf("Agent import specification: %s", acc.agentImportSpec) // Track import path for runtime-import macro generation (only if no inputs) // Imports with inputs must be inlined for compile-time substitution if len(item.inputs) == 0 { // No inputs - use runtime-import macro acc.importPaths = append(acc.importPaths, importRelPath) - log.Printf("Added agent import path for runtime-import: %s", importRelPath) + parserLog.Printf("Added agent import path for runtime-import: %s", importRelPath) } else { // Has inputs - must inline for compile-time substitution - log.Printf("Agent file has inputs - will be inlined instead of runtime-imported") + parserLog.Printf("Agent file has inputs - will be inlined instead of runtime-imported") // For agent files, extract markdown content (only when inputs are present) markdownContent, err := processIncludedFileWithVisited(item.fullPath, item.sectionName, false, visited) @@ -245,7 +245,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a // Check if this is a YAML workflow file (not .lock.yml) if isYAMLWorkflowFile(item.fullPath) { - log.Printf("Detected YAML workflow file: %s", item.fullPath) + parserLog.Printf("Detected YAML workflow file: %s", item.fullPath) // Process YAML workflow import to extract jobs/steps and services // Special case: copilot-setup-steps.yml returns steps YAML instead of jobs JSON @@ -260,13 +260,13 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a // Add to CopilotSetupSteps instead of MergedSteps (inserted at start of workflow) if jobsOrStepsData != "" { acc.copilotSetupStepsBuilder.WriteString(jobsOrStepsData + "\n") - log.Printf("Added copilot-setup steps (will be inserted at start): %s", item.importPath) + parserLog.Printf("Added copilot-setup steps (will be inserted at start): %s", item.importPath) } } else { // For regular YAML workflows, jobsOrStepsData contains jobs in JSON format if jobsOrStepsData != "" && jobsOrStepsData != "{}" { acc.jobsBuilder.WriteString(jobsOrStepsData + "\n") - log.Printf("Added jobs from YAML workflow: %s", item.importPath) + parserLog.Printf("Added jobs from YAML workflow: %s", item.importPath) } } @@ -280,7 +280,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a servicesYAML, err := yaml.Marshal(servicesWrapper) if err == nil { acc.servicesBuilder.WriteString(string(servicesYAML) + "\n") - log.Printf("Added services from YAML workflow: %s", item.importPath) + parserLog.Printf("Added services from YAML workflow: %s", item.importPath) } } } @@ -327,7 +327,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a } if err != nil { // If frontmatter extraction fails, continue with other processing - log.Printf("Failed to extract frontmatter from %s: %v", item.fullPath, err) + parserLog.Printf("Failed to extract frontmatter from %s: %v", item.fullPath, err) } else if result.Frontmatter != nil { // Check for nested imports field type nestedImportEntry struct { @@ -483,13 +483,13 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a inputs: nestedEntry.inputs, remoteOrigin: nestedRemoteOrigin, }) - log.Printf("Discovered nested import: %s -> %s (queued)", item.fullPath, nestedFullPath) + parserLog.Printf("Discovered nested import: %s -> %s (queued)", item.fullPath, nestedFullPath) } else { // Same file re-imported from a different path - verify inputs match if err := checkImportInputsConsistency(nestedImportPath, visitedInputs[nestedFullPath], nestedEntry.inputs); err != nil { return nil, err } - log.Printf("Skipping already visited nested import: %s (cycle detected)", nestedFullPath) + parserLog.Printf("Skipping already visited nested import: %s (cycle detected)", nestedFullPath) } } } @@ -500,7 +500,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a } } - log.Printf("Completed BFS traversal. Processed %d imports in total", len(processedOrder)) + parserLog.Printf("Completed BFS traversal. Processed %d imports in total", len(processedOrder)) // Sort imports in topological order (roots first, dependencies before dependents) // Returns an error if a circular import is detected @@ -508,7 +508,7 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a if err != nil { return nil, err } - log.Printf("Sorted imports in topological order: %v", topologicalOrder) + parserLog.Printf("Sorted imports in topological order: %v", topologicalOrder) return acc.toImportsResult(topologicalOrder), nil } diff --git a/pkg/parser/import_field_extractor.go b/pkg/parser/import_field_extractor.go index 3e0e4013f9b..1a7907bfe84 100644 --- a/pkg/parser/import_field_extractor.go +++ b/pkg/parser/import_field_extractor.go @@ -100,7 +100,7 @@ func newImportAccumulator() *importAccumulator { // skip-roles, skip-bots, pre-steps, pre-agent-steps, post-steps, labels, cache, and features. // The work is delegated to focused helper methods, each handling one logical phase. func (acc *importAccumulator) extractAllImportFields(content []byte, item importQueueItem, visited map[string]bool) error { - log.Printf("Extracting all import fields: path=%s, section=%s, inputs=%d, content_size=%d bytes", item.fullPath, item.sectionName, len(item.inputs), len(content)) + parserLog.Printf("Extracting all import fields: path=%s, section=%s, inputs=%d, content_size=%d bytes", item.fullPath, item.sectionName, len(item.inputs), len(content)) // Phase 1: Parse, apply defaults, substitute inputs, extract tools and markdown. origFm, fm, err := acc.prepareFrontmatter(content, item, visited) @@ -204,7 +204,7 @@ func (acc *importAccumulator) prepareFrontmatter(content []byte, item importQueu for _, w := range agentWarnings { msg := fmt.Sprintf("import '%s': %s", item.importPath, w) acc.warnings = append(acc.warnings, msg) - log.Printf("%s", msg) + parserLog.Printf("%s", msg) } // Extract tools from imported file. @@ -234,12 +234,12 @@ func (acc *importAccumulator) prepareFrontmatter(content []byte, item importQueu if !wasSubstituted && !strings.HasPrefix(importRelPath, BuiltinPathPrefix) { // No substitution happened and not a builtin - use runtime-import macro acc.importPaths = append(acc.importPaths, importRelPath) - log.Printf("Added import path for runtime-import: %s", importRelPath) + parserLog.Printf("Added import path for runtime-import: %s", importRelPath) } else if wasSubstituted { // Content was modified by substitution - inline for compile-time substitution. // Extract markdown from the already-substituted content so that import-inputs // expressions embedded in the markdown body are resolved here. - log.Printf("Import %s has substituted inputs - will be inlined for compile-time substitution", importRelPath) + parserLog.Printf("Import %s has substituted inputs - will be inlined for compile-time substitution", importRelPath) markdownContent, merr := ExtractMarkdownContent(rawContent) if merr != nil { return nil, nil, fmt.Errorf("failed to extract markdown from imported file '%s': %w", item.fullPath, merr) @@ -286,7 +286,7 @@ func (acc *importAccumulator) extractEngineConfig(fm map[string]any, fullPath st if !hasEngine { return } - log.Printf("Found engine config in import: %s", fullPath) + parserLog.Printf("Found engine config in import: %s", fullPath) switch v := engineVal.(type) { case string: @@ -303,14 +303,14 @@ func (acc *importAccumulator) extractEngineConfig(fm map[string]any, fullPath st if acc.mergedEngineMCPToolTimeout == "" { if ttStr, ok := mcpMap["tool-timeout"].(string); ok && ttStr != "" { acc.mergedEngineMCPToolTimeout = ttStr - log.Printf("Extracted engine.mcp.tool-timeout from import %s: %s", fullPath, ttStr) + parserLog.Printf("Extracted engine.mcp.tool-timeout from import %s: %s", fullPath, ttStr) } } // Extract session-timeout (first-wins across all imports) if acc.mergedEngineMCPSessionTimeout == "" { if stStr, ok := mcpMap["session-timeout"].(string); ok && stStr != "" { acc.mergedEngineMCPSessionTimeout = stStr - log.Printf("Extracted engine.mcp.session-timeout from import %s: %s", fullPath, stStr) + parserLog.Printf("Extracted engine.mcp.session-timeout from import %s: %s", fullPath, stStr) } } } @@ -332,7 +332,7 @@ func (acc *importAccumulator) extractEngineConfig(fm map[string]any, fullPath st if modelStr, ok := v["model"].(string); ok && modelStr != "" { if acc.mergedEngineModel == "" { acc.mergedEngineModel = modelStr - log.Printf("Extracted engine.model preference from import %s: %s", fullPath, modelStr) + parserLog.Printf("Extracted engine.model preference from import %s: %s", fullPath, modelStr) } } } @@ -357,7 +357,7 @@ func (acc *importAccumulator) extractConfigFields(fm map[string]any, fullPath st if maxRunsJSON, merr := extractFieldJSONFromMap(fm, "max-runs", ""); merr == nil && maxRunsJSON != "" && maxRunsJSON != "null" { acc.mergedMaxRuns = maxRunsJSON - log.Printf("Extracted max-runs from import: %s", fullPath) + parserLog.Printf("Extracted max-runs from import: %s", fullPath) } } @@ -366,7 +366,7 @@ func (acc *importAccumulator) extractConfigFields(fm map[string]any, fullPath st if maxTokensJSON, merr := extractFieldJSONFromMap(fm, "max-effective-tokens", ""); merr == nil && maxTokensJSON != "" && maxTokensJSON != "null" { acc.mergedMaxEffectiveTokens = maxTokensJSON - log.Printf("Extracted max-effective-tokens from import: %s", fullPath) + parserLog.Printf("Extracted max-effective-tokens from import: %s", fullPath) } } @@ -458,7 +458,7 @@ func (acc *importAccumulator) extractActivationFields(fm map[string]any, item im if acc.skipIfMatch == "" { if skipJSON, skipErr := extractOnSectionAnyFieldFromMap(fm, "skip-if-match"); skipErr == nil && skipJSON != "" && skipJSON != "null" { acc.skipIfMatch = skipJSON - log.Printf("Extracted on.skip-if-match from import: %s", item.fullPath) + parserLog.Printf("Extracted on.skip-if-match from import: %s", item.fullPath) } } @@ -466,7 +466,7 @@ func (acc *importAccumulator) extractActivationFields(fm map[string]any, item im if acc.skipIfNoMatch == "" { if skipJSON, skipErr := extractOnSectionAnyFieldFromMap(fm, "skip-if-no-match"); skipErr == nil && skipJSON != "" && skipJSON != "null" { acc.skipIfNoMatch = skipJSON - log.Printf("Extracted on.skip-if-no-match from import: %s", item.fullPath) + parserLog.Printf("Extracted on.skip-if-no-match from import: %s", item.fullPath) } } @@ -476,7 +476,7 @@ func (acc *importAccumulator) extractActivationFields(fm map[string]any, item im var token string if jsonErr := json.Unmarshal([]byte(tokenJSON), &token); jsonErr == nil && token != "" { acc.activationGitHubToken = token - log.Printf("Extracted on.github-token from import: %s", item.fullPath) + parserLog.Printf("Extracted on.github-token from import: %s", item.fullPath) } } } @@ -486,7 +486,7 @@ func (acc *importAccumulator) extractActivationFields(fm map[string]any, item im if appJSON, appErr := extractOnSectionAnyFieldFromMap(fm, "github-app"); appErr == nil { if validated := validateGitHubAppJSON(appJSON); validated != "" { acc.activationGitHubApp = validated - log.Printf("Extracted on.github-app from import: %s", item.fullPath) + parserLog.Printf("Extracted on.github-app from import: %s", item.fullPath) } } } @@ -496,7 +496,7 @@ func (acc *importAccumulator) extractActivationFields(fm map[string]any, item im if appJSON, appErr := extractFieldJSONFromMap(fm, "github-app", ""); appErr == nil { if validated := validateGitHubAppJSON(appJSON); validated != "" { acc.topLevelGitHubApp = validated - log.Printf("Extracted top-level github-app from import: %s", item.fullPath) + parserLog.Printf("Extracted top-level github-app from import: %s", item.fullPath) } } } @@ -506,7 +506,7 @@ func (acc *importAccumulator) extractActivationFields(fm map[string]any, item im // for later parsing by the compiler. if checkoutJSON, checkoutErr := extractFieldJSONFromMap(fm, "checkout", ""); checkoutErr == nil && checkoutJSON != "" && checkoutJSON != "null" && checkoutJSON != "false" { acc.checkouts = append(acc.checkouts, checkoutJSON) - log.Printf("Extracted checkout from import: %s", item.fullPath) + parserLog.Printf("Extracted checkout from import: %s", item.fullPath) } } @@ -586,7 +586,7 @@ func (acc *importAccumulator) extractFeatureAndObservabilityFields(fm map[string var featuresMap map[string]any if jsonErr := json.Unmarshal([]byte(featuresContent), &featuresMap); jsonErr == nil { acc.features = append(acc.features, featuresMap) - log.Printf("Extracted features from import: %d entries", len(featuresMap)) + parserLog.Printf("Extracted features from import: %d entries", len(featuresMap)) } } @@ -608,7 +608,7 @@ func (acc *importAccumulator) extractFeatureAndObservabilityFields(fm map[string } if len(modelsMap) > 0 { acc.models = append(acc.models, modelsMap) - log.Printf("Extracted model aliases from import: %d entries", len(modelsMap)) + parserLog.Printf("Extracted model aliases from import: %d entries", len(modelsMap)) } } } @@ -620,7 +620,7 @@ func (acc *importAccumulator) extractFeatureAndObservabilityFields(fm map[string if rsAny, hasRS := fm["run-install-scripts"]; hasRS { if rsBool, ok := rsAny.(bool); ok && rsBool { acc.runInstallScripts = true - log.Printf("Extracted run-install-scripts: true from import: %s", fullPath) + parserLog.Printf("Extracted run-install-scripts: true from import: %s", fullPath) } } // Also check runtimes.node.run-install-scripts @@ -631,7 +631,7 @@ func (acc *importAccumulator) extractFeatureAndObservabilityFields(fm map[string if rsAny, hasRS := nodeMap["run-install-scripts"]; hasRS { if rsBool, ok := rsAny.(bool); ok && rsBool { acc.runInstallScripts = true - log.Printf("Extracted runtimes.node.run-install-scripts: true from import: %s", fullPath) + parserLog.Printf("Extracted runtimes.node.run-install-scripts: true from import: %s", fullPath) } } } @@ -645,14 +645,14 @@ func (acc *importAccumulator) extractFeatureAndObservabilityFields(fm map[string // single array happens in toImportsResult. if obsContent, obsErr := extractFieldJSONFromMap(fm, "observability", "{}"); obsErr == nil && obsContent != "" && obsContent != "{}" { acc.observabilityConfigs = append(acc.observabilityConfigs, obsContent) - log.Printf("Extracted observability from import: %s", fullPath) + parserLog.Printf("Extracted observability from import: %s", fullPath) } } // toImportsResult converts the accumulated state to a final ImportsResult. // topologicalOrder is the result from topologicalSortImports. func (acc *importAccumulator) toImportsResult(topologicalOrder []string) *ImportsResult { - log.Printf("Building ImportsResult: importedFiles=%d, importPaths=%d, engines=%d, bots=%d, labels=%d", + parserLog.Printf("Building ImportsResult: importedFiles=%d, importPaths=%d, engines=%d, bots=%d, labels=%d", len(topologicalOrder), len(acc.importPaths), len(acc.engines), len(acc.bots), len(acc.labels)) return &ImportsResult{ MergedTools: acc.toolsBuilder.String(), @@ -782,7 +782,7 @@ func mergeObservabilityConfigs(configs []string) string { } var obs map[string]any if err := json.Unmarshal([]byte(cfgJSON), &obs); err != nil { - log.Printf("Failed to unmarshal observability config from import %d during merge: %v", i, err) + parserLog.Printf("Failed to unmarshal observability config from import %d during merge: %v", i, err) continue } for _, e := range extractOTLPEndpointsFromObsMap(obs) { @@ -806,7 +806,7 @@ func mergeObservabilityConfigs(configs []string) string { } b, err := json.Marshal(merged) if err != nil { - log.Printf("Failed to marshal %d merged OTLP endpoints: %v", len(allEndpoints), err) + parserLog.Printf("Failed to marshal %d merged OTLP endpoints: %v", len(allEndpoints), err) return "" } return string(b) diff --git a/pkg/parser/schedule_cron_detection.go b/pkg/parser/schedule_cron_detection.go index 38b226e5d27..9b252e089f5 100644 --- a/pkg/parser/schedule_cron_detection.go +++ b/pkg/parser/schedule_cron_detection.go @@ -42,7 +42,7 @@ func IsDailyCron(cron string) bool { result := fields[2] == "*" && fields[3] == "*" && fields[4] == "*" if result { - log.Printf("Cron expression classified as daily: %q (minute=%s, hour=%s)", cron, minute, hour) + parserLog.Printf("Cron expression classified as daily: %q (minute=%s, hour=%s)", cron, minute, hour) } return result } @@ -76,7 +76,7 @@ func IsHourlyCron(cron string) bool { // Check remaining fields are wildcards result := fields[2] == "*" && fields[3] == "*" && fields[4] == "*" if result { - log.Printf("Cron expression classified as hourly: %q (minute=%s, hour=%s)", cron, minute, hour) + parserLog.Printf("Cron expression classified as hourly: %q (minute=%s, hour=%s)", cron, minute, hour) } return result } @@ -122,7 +122,7 @@ func IsWeeklyCron(cron string) bool { } } - log.Printf("Cron expression classified as weekly: %q (minute=%s, hour=%s, dow=%s)", cron, minute, hour, dow) + parserLog.Printf("Cron expression classified as weekly: %q (minute=%s, hour=%s, dow=%s)", cron, minute, hour, dow) return true } @@ -137,18 +137,18 @@ func IsCronExpression(input string) bool { // A cron expression has exactly 5 fields fields := strings.Fields(input) if len(fields) != 5 { - log.Printf("Input is not a cron expression (expected 5 fields, got %d): %q", len(fields), input) + parserLog.Printf("Input is not a cron expression (expected 5 fields, got %d): %q", len(fields), input) return false } // Each field should match cron syntax (numbers, *, /, -, ,) for _, field := range fields { if !cronFieldPattern.MatchString(field) { - log.Printf("Cron field %q contains invalid characters in expression: %q", field, input) + parserLog.Printf("Cron field %q contains invalid characters in expression: %q", field, input) return false } } - log.Printf("Input recognized as valid cron expression: %q", input) + parserLog.Printf("Input recognized as valid cron expression: %q", input) return true } diff --git a/pkg/parser/schema_errors.go b/pkg/parser/schema_errors.go index ab77ad92bac..58edc3c9fa0 100644 --- a/pkg/parser/schema_errors.go +++ b/pkg/parser/schema_errors.go @@ -23,11 +23,11 @@ var maxConstraintPattern = regexp.MustCompile(`^maximum: got (-?\d+(?:\.\d+)?), // - "maximum: got 120, want 60" → "must be at most 60 (got 120)" func translateSchemaConstraintMessage(message string) string { if m := minConstraintPattern.FindStringSubmatch(message); len(m) == 3 { - log.Printf("Translating minimum constraint message: got=%s want=%s", m[1], m[2]) + parserLog.Printf("Translating minimum constraint message: got=%s want=%s", m[1], m[2]) return fmt.Sprintf("must be at least %s (got %s)", m[2], m[1]) } if m := maxConstraintPattern.FindStringSubmatch(message); len(m) == 3 { - log.Printf("Translating maximum constraint message: got=%s want=%s", m[1], m[2]) + parserLog.Printf("Translating maximum constraint message: got=%s want=%s", m[1], m[2]) return fmt.Sprintf("must be at most %s (got %s)", m[2], m[1]) } return message @@ -35,7 +35,7 @@ func translateSchemaConstraintMessage(message string) string { // cleanJSONSchemaErrorMessage removes unhelpful prefixes from jsonschema validation errors func cleanJSONSchemaErrorMessage(errorMsg string) string { - log.Printf("Cleaning JSON schema error message (%d chars)", len(errorMsg)) + parserLog.Printf("Cleaning JSON schema error message (%d chars)", len(errorMsg)) // Split the error message into lines lines := strings.Split(errorMsg, "\n") @@ -86,7 +86,7 @@ func cleanOneOfMessage(message string) string { return message } - log.Printf("Simplifying oneOf error message (%d lines)", len(strings.Split(message, "\n"))) + parserLog.Printf("Simplifying oneOf error message (%d lines)", len(strings.Split(message, "\n"))) lines := strings.Split(message, "\n") var meaningful []string @@ -250,7 +250,7 @@ func stripAtPathPrefix(line string) string { // findFrontmatterBounds finds the start and end indices of frontmatter in file lines // Returns: startIdx (-1 if not found), endIdx (-1 if not found), frontmatterContent func findFrontmatterBounds(lines []string) (startIdx int, endIdx int, frontmatterContent string) { - log.Printf("Finding frontmatter bounds in %d lines", len(lines)) + parserLog.Printf("Finding frontmatter bounds in %d lines", len(lines)) startIdx = -1 endIdx = -1 @@ -269,7 +269,7 @@ func findFrontmatterBounds(lines []string) (startIdx int, endIdx int, frontmatte } if startIdx == -1 { - log.Print("No frontmatter opening delimiter found") + parserLog.Print("No frontmatter opening delimiter found") return -1, -1, "" } @@ -284,10 +284,10 @@ func findFrontmatterBounds(lines []string) (startIdx int, endIdx int, frontmatte if endIdx == -1 { // No closing "---" found - log.Print("No frontmatter closing delimiter found") + parserLog.Print("No frontmatter closing delimiter found") return -1, -1, "" } - log.Printf("Found frontmatter bounds: start=%d end=%d", startIdx, endIdx) + parserLog.Printf("Found frontmatter bounds: start=%d end=%d", startIdx, endIdx) // Extract frontmatter content between the markers frontmatterLines := lines[startIdx+1 : endIdx] @@ -341,7 +341,7 @@ func appendKnownFieldValidValuesHint(message string, jsonPath string) (string, b if !strings.Contains(strings.ToLower(message), "unknown propert") { return message, false } - log.Printf("Appending known field hint for path: %s", jsonPath) + parserLog.Printf("Appending known field hint for path: %s", jsonPath) // Find the best matching known path: exact match first, then the longest matching parent. hint, hintOK := knownFieldValidValues[jsonPath] @@ -425,7 +425,7 @@ func rewriteAdditionalPropertiesError(message string) string { if len(match) >= 2 { properties := normalizeAdditionalPropertyList(match[1]) - log.Printf("Rewriting additional properties error: %s", properties) + parserLog.Printf("Rewriting additional properties error: %s", properties) if strings.Contains(properties, ",") { return "Unknown properties: " + properties diff --git a/pkg/parser/tools_merger.go b/pkg/parser/tools_merger.go index 4f1078f2f51..de2ffddb2a5 100644 --- a/pkg/parser/tools_merger.go +++ b/pkg/parser/tools_merger.go @@ -13,7 +13,7 @@ var toolsMergerLog = logger.New("parser:tools_merger") // mergeToolsFromJSON merges multiple JSON tool objects from content func mergeToolsFromJSON(content string) (string, error) { - log.Printf("Merging tools from JSON: content_size=%d bytes", len(content)) + parserLog.Printf("Merging tools from JSON: content_size=%d bytes", len(content)) // Clean up the content first content = strings.TrimSpace(content) @@ -53,11 +53,11 @@ func mergeToolsFromJSON(content string) (string, error) { // If no valid objects found, return empty if len(jsonObjects) == 0 { - log.Print("No valid JSON objects found in content, returning empty object") + parserLog.Print("No valid JSON objects found in content, returning empty object") return "{}", nil } - log.Printf("Found %d JSON objects to merge", len(jsonObjects)) + parserLog.Printf("Found %d JSON objects to merge", len(jsonObjects)) // Merge all objects merged := make(map[string]any) for _, obj := range jsonObjects { @@ -81,7 +81,7 @@ func mergeToolsFromJSON(content string) (string, error) { // Only supports merging arrays and maps for neutral tools (bash, web-fetch, web-search, edit, mcp-*). // Removes all legacy Claude tool merging logic. func MergeTools(base, additional map[string]any) (map[string]any, error) { - log.Printf("Merging tools: base_keys=%d, additional_keys=%d", len(base), len(additional)) + parserLog.Printf("Merging tools: base_keys=%d, additional_keys=%d", len(base), len(additional)) result := make(map[string]any) // Copy base diff --git a/pkg/workflow/compiler.go b/pkg/workflow/compiler.go index e03ace1547a..f1a9df799df 100644 --- a/pkg/workflow/compiler.go +++ b/pkg/workflow/compiler.go @@ -18,7 +18,7 @@ import ( "github.com/goccy/go-yaml" ) -var log = logger.New("workflow:compiler") +var workflowLog = logger.New("workflow:compiler") const ( // MaxLockFileSize is the maximum allowed size for generated lock workflow files (500KB) @@ -62,7 +62,7 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error { c.markdownPath = markdownPath // Parse the markdown file - log.Printf("Parsing workflow file") + workflowLog.Printf("Parsing workflow file") workflowData, err := c.ParseWorkflowFile(markdownPath) if err != nil { // ParseWorkflowFile already returns formatted compiler errors; pass them through. @@ -128,7 +128,7 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP // Always validate expression sizes - this is a hard limit from GitHub Actions (21KB) // that cannot be bypassed, so we validate it unconditionally - log.Print("Validating expression sizes") + workflowLog.Print("Validating expression sizes") if err := c.validateExpressionSizes(yamlContent); err != nil { // Store error first so we can write invalid YAML before returning formattedErr := formatCompilerError(markdownPath, "error", fmt.Sprintf("expression size validation failed: %v", err), err) @@ -156,7 +156,7 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP if needsSchemaCheck { // Schema validation requires parsed YAML; parse once and share with the // template injection validator below. - log.Print("Parsing compiled YAML for validation") + workflowLog.Print("Parsing compiled YAML for validation") if parseErr := yaml.Unmarshal([]byte(yamlContent), &parsedWorkflow); parseErr != nil { // If parsing fails here the subsequent validators would also fail; keep going // so we surface the root error from the right validator. @@ -177,7 +177,7 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP // Validate against GitHub Actions schema (unless skipped) if needsSchemaCheck { - log.Print("Validating workflow against GitHub Actions schema") + workflowLog.Print("Validating workflow against GitHub Actions schema") var schemaErr error if parsedWorkflow != nil { schemaErr = c.validateGitHubActionsSchemaFromParsed(parsedWorkflow) @@ -208,7 +208,7 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP } // Validate container images used in MCP configurations - log.Print("Validating container images") + workflowLog.Print("Validating container images") if err := c.validateContainerImages(workflowData); err != nil { // Treat container image validation failures as warnings, not errors // This is because validation may fail due to auth issues locally (e.g., private registries) @@ -217,19 +217,19 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP } // Validate runtime packages (npx, uv) - log.Print("Validating runtime packages") + workflowLog.Print("Validating runtime packages") if err := c.validateRuntimePackages(workflowData); err != nil { return "", nil, nil, formatCompilerError(markdownPath, "error", fmt.Sprintf("runtime package validation failed: %v", err), err) } // Validate firewall configuration (log-level enum) - log.Print("Validating firewall configuration") + workflowLog.Print("Validating firewall configuration") if err := c.validateFirewallConfig(workflowData); err != nil { return "", nil, nil, formatCompilerError(markdownPath, "error", fmt.Sprintf("firewall configuration validation failed: %v", err), err) } // Validate repository features (discussions, issues) - log.Print("Validating repository features") + workflowLog.Print("Validating repository features") if err := c.validateRepositoryFeatures(workflowData); err != nil { return "", nil, nil, formatCompilerError(markdownPath, "error", fmt.Sprintf("repository feature validation failed: %v", err), err) } @@ -246,9 +246,9 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP func (c *Compiler) writeWorkflowOutput(lockFile, yamlContent string, markdownPath string) error { // Write to lock file (unless noEmit is enabled) if c.noEmit { - log.Print("Validation completed - no lock file generated (--no-emit enabled)") + workflowLog.Print("Validation completed - no lock file generated (--no-emit enabled)") } else { - log.Printf("Writing output to: %s", lockFile) + workflowLog.Printf("Writing output to: %s", lockFile) // Check if content has actually changed contentUnchanged := false @@ -256,7 +256,7 @@ func (c *Compiler) writeWorkflowOutput(lockFile, yamlContent string, markdownPat if normalizeHeredocDelimiters(string(existingContent)) == normalizeHeredocDelimiters(yamlContent) { // Content is identical (modulo random heredoc tokens) - skip write to preserve timestamp contentUnchanged = true - log.Print("Lock file content unchanged - skipping write to preserve timestamp") + workflowLog.Print("Lock file content unchanged - skipping write to preserve timestamp") } } @@ -265,7 +265,7 @@ func (c *Compiler) writeWorkflowOutput(lockFile, yamlContent string, markdownPat if err := os.WriteFile(lockFile, []byte(yamlContent), constants.FilePermPublic); err != nil { return formatCompilerError(lockFile, "error", fmt.Sprintf("failed to write lock file: %v", err), err) } - log.Print("Lock file written successfully") + workflowLog.Print("Lock file written successfully") } // Validate file size after writing @@ -316,7 +316,7 @@ func (c *Compiler) validateTemplateInjection(yamlContent, lockFile, markdownPath // Path A: YAML was already parsed for schema validation; reuse it. // Walking the pre-parsed tree (run: block values only) is faster than // scanning the full YAML string. - log.Print("Validating for template injection vulnerabilities") + workflowLog.Print("Validating for template injection vulnerabilities") templateErr = validateNoTemplateInjectionFromParsed(parsedWorkflow) if templateErr == nil { templateErr = validateNoGitHubExpressionsInRunScriptsFromParsed(parsedWorkflow) @@ -327,7 +327,7 @@ func (c *Compiler) validateTemplateInjection(yamlContent, lockFile, markdownPath // unsafe) appear inside a run: block before paying the cost of a full // yaml.Unmarshal for layered security + regression validation. if hasAnyExpressionInRunContent(yamlContent) { - log.Print("Validating for template injection vulnerabilities") + workflowLog.Print("Validating for template injection vulnerabilities") var reparsed map[string]any if err := yaml.Unmarshal([]byte(yamlContent), &reparsed); err != nil { // Malformed YAML: skip validation (compilation would have surfaced this elsewhere). @@ -386,7 +386,7 @@ func (c *Compiler) CompileWorkflowData(workflowData *WorkflowData, markdownPath // Track compilation time for performance monitoring startTime := time.Now() defer func() { - log.Printf("Compilation completed in %v", time.Since(startTime)) + workflowLog.Printf("Compilation completed in %v", time.Since(startTime)) }() // Reset the step order tracker for this compilation @@ -421,7 +421,7 @@ func (c *Compiler) CompileWorkflowData(workflowData *WorkflowData, markdownPath // Sanitize the lock file path to prevent path traversal attacks lockFile = filepath.Clean(lockFile) - log.Printf("Starting compilation: %s -> %s", markdownPath, lockFile) + workflowLog.Printf("Starting compilation: %s -> %s", markdownPath, lockFile) // Read the existing lock file to extract the previous gh-aw-manifest for safe update // enforcement. @@ -439,32 +439,32 @@ func (c *Compiler) CompileWorkflowData(workflowData *WorkflowData, markdownPath if cached != nil { secretCount = len(cached.Secrets) } - log.Printf("Using pre-cached gh-aw-manifest for %s: %d secret(s)", lockFile, secretCount) + workflowLog.Printf("Using pre-cached gh-aw-manifest for %s: %d secret(s)", lockFile, secretCount) } else if committedContent, readErr := c.readLockFileFromHEAD(lockFile); readErr == nil { if m, parseErr := ExtractGHAWManifestFromLockFile(committedContent); parseErr == nil { oldManifest = m if oldManifest != nil { - log.Printf("Loaded committed gh-aw-manifest from HEAD: %d secret(s)", len(oldManifest.Secrets)) + workflowLog.Printf("Loaded committed gh-aw-manifest from HEAD: %d secret(s)", len(oldManifest.Secrets)) } } else { - log.Printf("Failed to parse committed gh-aw-manifest: %v. Safe update enforcement will proceed without baseline comparison (all secrets will be considered new).", parseErr) + workflowLog.Printf("Failed to parse committed gh-aw-manifest: %v. Safe update enforcement will proceed without baseline comparison (all secrets will be considered new).", parseErr) } } else { - log.Printf("Lock file %s not found in HEAD commit (%v); falling back to filesystem read.", lockFile, readErr) + workflowLog.Printf("Lock file %s not found in HEAD commit (%v); falling back to filesystem read.", lockFile, readErr) if existingContent, fsErr := os.ReadFile(lockFile); fsErr == nil { if m, parseErr := ExtractGHAWManifestFromLockFile(string(existingContent)); parseErr == nil { oldManifest = m if oldManifest != nil { - log.Printf("Loaded gh-aw-manifest from filesystem: %d secret(s)", len(oldManifest.Secrets)) + workflowLog.Printf("Loaded gh-aw-manifest from filesystem: %d secret(s)", len(oldManifest.Secrets)) } } else { - log.Printf("Failed to parse filesystem gh-aw-manifest: %v. Safe update enforcement will treat as empty manifest.", parseErr) + workflowLog.Printf("Failed to parse filesystem gh-aw-manifest: %v. Safe update enforcement will treat as empty manifest.", parseErr) } } else { // No lock file anywhere — this is a brand-new workflow. Use an empty // (non-nil) manifest so EnforceSafeUpdate applies enforcement and flags // any newly introduced secrets or actions for review. - log.Printf("Lock file %s not found (new workflow). Safe update enforcement will use an empty baseline.", lockFile) + workflowLog.Printf("Lock file %s not found (new workflow). Safe update enforcement will use an empty baseline.", lockFile) oldManifest = &GHAWManifest{Version: currentGHAWManifestVersion} } } @@ -491,7 +491,7 @@ func (c *Compiler) CompileWorkflowData(workflowData *WorkflowData, markdownPath } // Note: Markdown content size is now handled by splitting into multiple steps in generatePrompt - log.Printf("Workflow: %s, Tools: %d", workflowData.Name, len(workflowData.Tools)) + workflowLog.Printf("Workflow: %s, Tools: %d", workflowData.Name, len(workflowData.Tools)) // Note: compute-text functionality is now inlined directly in the task job // instead of using a shared action file diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index 0b16136064d..153976e779c 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -1012,17 +1012,17 @@ func (c *Compiler) extractPinnedJobSteps(fieldName string, jobName string, confi func (c *Compiler) shouldAddCheckoutStep(data *WorkflowData) bool { // If checkout was explicitly disabled via checkout: false, skip it if data.CheckoutDisabled { - log.Print("Skipping checkout step: checkout disabled via checkout: false") + workflowLog.Print("Skipping checkout step: checkout disabled via checkout: false") return false } // If custom steps already contain checkout, don't add another one if data.CustomSteps != "" && ContainsCheckout(data.CustomSteps) { - log.Print("Skipping checkout step: custom steps already contain checkout") + workflowLog.Print("Skipping checkout step: custom steps already contain checkout") return false } // Always add checkout to ensure agent has repository access - log.Print("Adding checkout step: agent job requires repository access") + workflowLog.Print("Adding checkout step: agent job requires repository access") return true } diff --git a/pkg/workflow/compiler_main_job.go b/pkg/workflow/compiler_main_job.go index 7f284c8dc7e..96d9121660b 100644 --- a/pkg/workflow/compiler_main_job.go +++ b/pkg/workflow/compiler_main_job.go @@ -24,7 +24,7 @@ func isBuiltinJobName(jobName string) bool { // buildMainJob creates the main agent job that runs the AI agent with the configured engine and tools. // This job depends on the activation job if it exists, and handles the main workflow logic. func (c *Compiler) buildMainJob(data *WorkflowData, activationJobCreated bool) (*Job, error) { - log.Printf("Building main job for workflow: %s", data.Name) + workflowLog.Printf("Building main job for workflow: %s", data.Name) var steps []string // Add setup action steps at the beginning of the job diff --git a/pkg/workflow/compiler_orchestrator_engine.go b/pkg/workflow/compiler_orchestrator_engine.go index 1637e0334e5..903bd2888ea 100644 --- a/pkg/workflow/compiler_orchestrator_engine.go +++ b/pkg/workflow/compiler_orchestrator_engine.go @@ -264,7 +264,7 @@ func (c *Compiler) setupEngineAndImports(result *parser.FrontmatterResult, clean if engineSetting == "" { defaultEngine := c.engineRegistry.GetDefaultEngine() engineSetting = defaultEngine.GetID() - log.Printf("No 'engine:' setting found, defaulting to: %s", engineSetting) + workflowLog.Printf("No 'engine:' setting found, defaulting to: %s", engineSetting) // Create a default EngineConfig with the default engine ID if not already set if engineConfig == nil { engineConfig = &EngineConfig{ID: engineSetting} @@ -352,7 +352,7 @@ func (c *Compiler) setupEngineAndImports(result *parser.FrontmatterResult, clean return nil, fmt.Errorf("engine %s RenderConfig failed: %w", engineSetting, err) } - log.Printf("AI engine: %s (%s)", agenticEngine.GetDisplayName(), engineSetting) + workflowLog.Printf("AI engine: %s (%s)", agenticEngine.GetDisplayName(), engineSetting) if agenticEngine.IsExperimental() && c.verbose { fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Using experimental engine: "+agenticEngine.GetDisplayName())) c.IncrementWarningCount() diff --git a/pkg/workflow/compiler_orchestrator_frontmatter.go b/pkg/workflow/compiler_orchestrator_frontmatter.go index 15944107958..7ab45fff854 100644 --- a/pkg/workflow/compiler_orchestrator_frontmatter.go +++ b/pkg/workflow/compiler_orchestrator_frontmatter.go @@ -36,7 +36,7 @@ type frontmatterParseResult struct { // isRedirectOnly is set to true with the redirect target in redirectTarget. func (c *Compiler) parseFrontmatterSection(markdownPath string) (*frontmatterParseResult, error) { orchestratorFrontmatterLog.Printf("Starting frontmatter parsing: %s", markdownPath) - log.Printf("Reading file: %s", markdownPath) + workflowLog.Printf("Reading file: %s", markdownPath) // Clean the path to prevent path traversal issues (gosec G304) // filepath.Clean removes ".." and other problematic path elements @@ -51,7 +51,7 @@ func (c *Compiler) parseFrontmatterSection(markdownPath string) (*frontmatterPar } contentString := string(content) - log.Printf("File size: %d bytes", len(content)) + workflowLog.Printf("File size: %d bytes", len(content)) // Parse frontmatter and markdown orchestratorFrontmatterLog.Printf("Parsing frontmatter from file: %s", cleanPath) @@ -181,7 +181,7 @@ func (c *Compiler) parseFrontmatterSection(markdownPath string) (*frontmatterPar c.IncrementWarningCount() } - log.Printf("Frontmatter: %d chars, Markdown: %d chars", len(result.Frontmatter), len(result.Markdown)) + workflowLog.Printf("Frontmatter: %d chars, Markdown: %d chars", len(result.Frontmatter), len(result.Markdown)) return &frontmatterParseResult{ cleanPath: cleanPath, diff --git a/pkg/workflow/compiler_orchestrator_tools.go b/pkg/workflow/compiler_orchestrator_tools.go index f789d9b7966..19abb57a05a 100644 --- a/pkg/workflow/compiler_orchestrator_tools.go +++ b/pkg/workflow/compiler_orchestrator_tools.go @@ -50,7 +50,7 @@ func (c *Compiler) processToolsAndMarkdown(result *parser.FrontmatterResult, cle agenticEngine CodingAgentEngine, engineSetting string, importsResult *parser.ImportsResult) (*toolsProcessingResult, error) { orchestratorToolsLog.Printf("Processing tools and markdown") - log.Print("Processing tools and includes...") + workflowLog.Print("Processing tools and includes...") // Extract inline sub-agents from the markdown body before any other processing. // This strips sub-agent sections from the effective markdown so they do not affect @@ -278,7 +278,7 @@ func (c *Compiler) processToolsAndMarkdown(result *parser.FrontmatterResult, cle orchestratorToolsLog.Print("No imported markdown with inputs") } - log.Print("Expanded includes in markdown content") + workflowLog.Print("Expanded includes in markdown content") // Combine all included files (from tools and markdown) // Use a map to deduplicate files @@ -317,7 +317,7 @@ func (c *Compiler) processToolsAndMarkdown(result *parser.FrontmatterResult, cle // Extract emoji from frontmatter for use in footers and UI frontmatterEmoji := extractStringFromMap(result.Frontmatter, "emoji", nil) - log.Printf("Extracted workflow name: '%s'", workflowName) + workflowLog.Printf("Extracted workflow name: '%s'", workflowName) // Check if the markdown content uses the text output OR if the workflow is triggered by // events that have content (issues, discussions, PRs, comments). The sanitized step should diff --git a/pkg/workflow/compiler_string_api.go b/pkg/workflow/compiler_string_api.go index 1c77249caf0..7de6fe7caa7 100644 --- a/pkg/workflow/compiler_string_api.go +++ b/pkg/workflow/compiler_string_api.go @@ -25,7 +25,7 @@ func (c *Compiler) CompileToYAML(workflowData *WorkflowData, markdownPath string startTime := time.Now() defer func() { - log.Printf("CompileToYAML completed in %v", time.Since(startTime)) + workflowLog.Printf("CompileToYAML completed in %v", time.Since(startTime)) }() c.stepOrderTracker = NewStepOrderTracker() @@ -55,7 +55,7 @@ func (c *Compiler) CompileToYAML(workflowData *WorkflowData, markdownPath string // This is the primary entry point for Wasm/browser usage where filesystem access is unavailable. // The virtualPath is used for error messages and lock file naming (e.g., "workflow.md"). func (c *Compiler) ParseWorkflowString(content string, virtualPath string) (*WorkflowData, error) { - log.Printf("ParseWorkflowString: parsing %d bytes with virtual path %s", len(content), virtualPath) + workflowLog.Printf("ParseWorkflowString: parsing %d bytes with virtual path %s", len(content), virtualPath) cleanPath := filepath.Clean(virtualPath) diff --git a/pkg/workflow/compiler_validators.go b/pkg/workflow/compiler_validators.go index 0b5511335fc..e9a131c74ef 100644 --- a/pkg/workflow/compiler_validators.go +++ b/pkg/workflow/compiler_validators.go @@ -17,7 +17,7 @@ import ( func (c *Compiler) validateExpressions(workflowData *WorkflowData, markdownPath string) error { // Validate expression safety - check that all GitHub Actions expressions are in the allowed list if strings.Contains(workflowData.MarkdownContent, "${{") { - log.Printf("Validating expression safety") + workflowLog.Printf("Validating expression safety") if err := validateExpressionSafety(workflowData.MarkdownContent); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } @@ -25,7 +25,7 @@ func (c *Compiler) validateExpressions(workflowData *WorkflowData, markdownPath // Validate expressions in runtime-import files at compile time if strings.Contains(workflowData.MarkdownContent, "{{#runtime-import") { - log.Printf("Validating runtime-import files") + workflowLog.Printf("Validating runtime-import files") // Go up from .github/workflows/file.md to repo root workflowDir := filepath.Dir(markdownPath) // .github/workflows githubDir := filepath.Dir(workflowDir) // .github @@ -50,7 +50,7 @@ func (c *Compiler) validateExpressions(workflowData *WorkflowData, markdownPath // and applies any action-mode override specified via the "action-mode" feature flag. func (c *Compiler) validateFeatureConfig(workflowData *WorkflowData, markdownPath string) error { // Validate feature flags - log.Printf("Validating feature flags") + workflowLog.Printf("Validating feature flags") if err := validateFeatures(workflowData); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } @@ -69,7 +69,7 @@ func (c *Compiler) validateFeatureConfig(workflowData *WorkflowData, markdownPat if !mode.IsValid() { return formatCompilerError(markdownPath, "error", fmt.Sprintf("invalid action-mode feature flag '%s'. Must be 'dev', 'release', or 'script'", actionModeStr), nil) } - log.Printf("Overriding action mode from feature flag: %s", mode) + workflowLog.Printf("Overriding action mode from feature flag: %s", mode) c.SetActionMode(mode) } } @@ -85,26 +85,26 @@ func (c *Compiler) validateFeatureConfig(workflowData *WorkflowData, markdownPat // workflowPermissions is the *Permissions value returned by validatePermissions. func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdownPath string, workflowPermissions *Permissions) error { // Validate agent file exists if specified in engine config - log.Printf("Validating agent file if specified") + workflowLog.Printf("Validating agent file if specified") if err := c.validateAgentFile(workflowData, markdownPath); err != nil { // validateAgentFile always returns formatCompilerError results; pass through directly. return err } // Validate sandbox configuration - log.Printf("Validating sandbox configuration") + workflowLog.Printf("Validating sandbox configuration") if err := validateSandboxConfig(workflowData); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate safe-outputs target configuration - log.Printf("Validating safe-outputs target fields") + workflowLog.Printf("Validating safe-outputs target fields") if err := validateSafeOutputsTarget(workflowData.SafeOutputs); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate safe-outputs max configuration - log.Printf("Validating safe-outputs max fields") + workflowLog.Printf("Validating safe-outputs max fields") if err := validateSafeOutputsMax(workflowData.SafeOutputs); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } @@ -112,7 +112,7 @@ func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdow // Validate safe-outputs steps for dangerous shell expansion patterns. // In strict mode this is a hard error; in non-strict mode it is a warning // so that existing workflows continue to compile while authors migrate them. - log.Printf("Validating safe-outputs steps for shell expansion patterns") + workflowLog.Printf("Validating safe-outputs steps for shell expansion patterns") if err := validateSafeOutputsStepsShellExpansion(workflowData.SafeOutputs); err != nil { if c.strictMode { return formatCompilerError(markdownPath, "error", err.Error(), err) @@ -122,77 +122,77 @@ func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdow } // Validate safe-outputs allowed-domains configuration - log.Printf("Validating safe-outputs allowed-domains") + workflowLog.Printf("Validating safe-outputs allowed-domains") if err := c.validateSafeOutputsAllowedDomains(workflowData.SafeOutputs); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate safe-outputs merge-pull-request configuration - log.Printf("Validating safe-outputs merge-pull-request") + workflowLog.Printf("Validating safe-outputs merge-pull-request") if err := validateSafeOutputsMergePullRequest(workflowData.SafeOutputs); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate safe-outputs needs declarations - log.Printf("Validating safe-outputs needs declarations") + workflowLog.Printf("Validating safe-outputs needs declarations") if err := validateSafeOutputsNeeds(workflowData); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate on.needs declarations and on.github-app needs expressions - log.Printf("Validating on.needs declarations") + workflowLog.Printf("Validating on.needs declarations") if err := c.validateOnNeeds(workflowData); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate safe-job needs: declarations against known generated job IDs - log.Printf("Validating safe-job needs declarations") + workflowLog.Printf("Validating safe-job needs declarations") if err := validateSafeJobNeeds(workflowData); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Emit warnings for push-to-pull-request-branch misconfiguration - log.Printf("Validating push-to-pull-request-branch configuration") + workflowLog.Printf("Validating push-to-pull-request-branch configuration") c.validatePushToPullRequestBranchWarnings(workflowData.SafeOutputs, workflowData.CheckoutConfigs) // Reject bare "*" in allowed-labels (CTR-015) - log.Printf("Validating safe-outputs allowed-labels glob scope") + workflowLog.Printf("Validating safe-outputs allowed-labels glob scope") if err := c.validateSafeOutputsAllowedLabelsGlobScope(workflowData.SafeOutputs); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate network allowed domains configuration - log.Printf("Validating network allowed domains") + workflowLog.Printf("Validating network allowed domains") if err := c.validateNetworkAllowedDomains(workflowData.NetworkPermissions); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate network firewall configuration - log.Printf("Validating network firewall configuration") + workflowLog.Printf("Validating network firewall configuration") if err := validateNetworkFirewallConfig(workflowData.NetworkPermissions); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate safe-outputs allow-workflows requires GitHub App - log.Printf("Validating safe-outputs allow-workflows") + workflowLog.Printf("Validating safe-outputs allow-workflows") if err := validateSafeOutputsAllowWorkflows(workflowData.SafeOutputs); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate labels configuration - log.Printf("Validating labels") + workflowLog.Printf("Validating labels") if err := validateLabels(workflowData); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate workflow_dispatch required inputs with slash/label command triggers. - log.Printf("Validating workflow_dispatch input requirements for command triggers") + workflowLog.Printf("Validating workflow_dispatch input requirements for command triggers") if err := validateCommandWorkflowDispatchInputs(workflowData); err != nil { return formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate workflow-level concurrency group expression - log.Printf("Validating workflow-level concurrency configuration") + workflowLog.Printf("Validating workflow-level concurrency configuration") if workflowData.Concurrency != "" { // Use the cached validation result from applyDefaults to avoid re-running the // expensive ExpressionParser (regex + tokenize + parse) on every validateWorkflowData call. @@ -221,7 +221,7 @@ func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdow } // Validate engine-level concurrency group expression - log.Printf("Validating engine-level concurrency configuration") + workflowLog.Printf("Validating engine-level concurrency configuration") if workflowData.EngineConfig != nil && workflowData.EngineConfig.Concurrency != "" { // Extract the group expression from the engine concurrency YAML groupExpr := extractConcurrencyGroupFromYAML(workflowData.EngineConfig.Concurrency) @@ -338,7 +338,7 @@ func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdow } // Validate GitHub tools against enabled toolsets - log.Printf("Validating GitHub tools against enabled toolsets") + workflowLog.Printf("Validating GitHub tools against enabled toolsets") if workflowData.ParsedTools != nil && workflowData.ParsedTools.GitHub != nil { // Extract allowed tools and reuse the cached parsed toolsets from applyDefaults to // avoid a redundant ParseGitHubToolsets call on every validateWorkflowData iteration. @@ -365,7 +365,7 @@ func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdow } // Validate permissions for agentic-workflows tool - log.Printf("Validating permissions for agentic-workflows tool") + workflowLog.Printf("Validating permissions for agentic-workflows tool") if _, hasAgenticWorkflows := workflowData.Tools["agentic-workflows"]; hasAgenticWorkflows { // Check if actions: read permission exists actionsLevel, hasActions := workflowPermissions.Get(PermissionActions) @@ -383,7 +383,7 @@ func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdow } // Validate resources field — GitHub Actions expression syntax is not allowed. - log.Printf("Validating resources field") + workflowLog.Printf("Validating resources field") if workflowData.ParsedFrontmatter != nil { for _, r := range workflowData.ParsedFrontmatter.Resources { if strings.Contains(r, "${{") { @@ -394,19 +394,19 @@ func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdow } // Validate dispatch-workflow configuration (independent of agentic-workflows tool) - log.Print("Validating dispatch-workflow configuration") + workflowLog.Print("Validating dispatch-workflow configuration") if err := c.validateDispatchWorkflow(workflowData, markdownPath); err != nil { return formatCompilerError(markdownPath, "error", fmt.Sprintf("dispatch-workflow validation failed: %v", err), err) } // Validate dispatch_repository configuration (independent of agentic-workflows tool) - log.Print("Validating dispatch_repository configuration") + workflowLog.Print("Validating dispatch_repository configuration") if err := c.validateDispatchRepository(workflowData, markdownPath); err != nil { return formatCompilerError(markdownPath, "error", fmt.Sprintf("dispatch_repository validation failed: %v", err), err) } // Validate call-workflow configuration (independent of agentic-workflows tool) - log.Print("Validating call-workflow configuration") + workflowLog.Print("Validating call-workflow configuration") if err := c.validateCallWorkflow(workflowData, markdownPath); err != nil { return formatCompilerError(markdownPath, "error", fmt.Sprintf("call-workflow validation failed: %v", err), err) } diff --git a/pkg/workflow/config_helpers.go b/pkg/workflow/config_helpers.go index 5a7ae10c302..fbd21f549ff 100644 --- a/pkg/workflow/config_helpers.go +++ b/pkg/workflow/config_helpers.go @@ -50,15 +50,15 @@ var configHelpersLog = logger.New("workflow:config_helpers") func ParseStringArrayFromConfig(m map[string]any, key string, log *logger.Logger) []string { if value, exists := m[key]; exists { if log != nil { - log.Printf("Parsing %s from config", key) + workflowLog.Printf("Parsing %s from config", key) } if strings := parseStringSliceAny(value, log); strings != nil { // Return the slice even if empty (to distinguish from not provided) if len(strings) == 0 && log != nil { - log.Printf("No valid %s strings found, returning empty array", key) + workflowLog.Printf("No valid %s strings found, returning empty array", key) } if log != nil { - log.Printf("Parsed %d %s from config", len(strings), key) + workflowLog.Printf("Parsed %d %s from config", len(strings), key) } return strings } @@ -76,29 +76,29 @@ func ParseStringArrayFromConfig(m map[string]any, key string, log *logger.Logger func ParseStringArrayOrExprFromConfig(m map[string]any, key string, log *logger.Logger) []string { if value, exists := m[key]; exists { if log != nil { - log.Printf("Parsing %s from config", key) + workflowLog.Printf("Parsing %s from config", key) } // Accept a GitHub Actions expression string: wrap it in a single-element slice. if s, ok := value.(string); ok { if isExpression(s) { if log != nil { - log.Printf("Field %s is a GitHub Actions expression, wrapping in single-element array", key) + workflowLog.Printf("Field %s is a GitHub Actions expression, wrapping in single-element array", key) } return []string{s} } // Non-expression string is invalid for an array field. if log != nil { - log.Printf("Field %q must be an array or a GitHub Actions expression, ignoring non-expression string: %q", key, s) + workflowLog.Printf("Field %q must be an array or a GitHub Actions expression, ignoring non-expression string: %q", key, s) } return nil } // Handle arrays (existing logic). if strings := parseStringSliceAny(value, log); strings != nil { if len(strings) == 0 && log != nil { - log.Printf("No valid %s strings found, returning empty array", key) + workflowLog.Printf("No valid %s strings found, returning empty array", key) } if log != nil { - log.Printf("Parsed %d %s from config", len(strings), key) + workflowLog.Printf("Parsed %d %s from config", len(strings), key) } return strings } @@ -113,7 +113,7 @@ func extractStringFromMap(m map[string]any, key string, log *logger.Logger) stri if value, exists := m[key]; exists { if valueStr, ok := value.(string); ok { if log != nil { - log.Printf("Parsed %s from config: %s", key, valueStr) + workflowLog.Printf("Parsed %s from config: %s", key, valueStr) } return valueStr } @@ -166,7 +166,7 @@ func preprocessExpiresField(configData map[string]any, log *logger.Logger) bool configData["expires"] = 0 } if log != nil { - log.Printf("Parsed expires value %v to %d hours (disabled=%t)", expires, expiresInt, expiresDisabled) + workflowLog.Printf("Parsed expires value %v to %d hours (disabled=%t)", expires, expiresInt, expiresDisabled) } } } @@ -178,11 +178,11 @@ func preprocessExpiresField(configData map[string]any, log *logger.Logger) bool // If log is provided, it will log the extracted value for debugging. func ParseBoolFromConfig(m map[string]any, key string, log *logger.Logger) bool { if log != nil { - log.Printf("Parsing %s from config", key) + workflowLog.Printf("Parsing %s from config", key) } result := typeutil.ParseBool(m, key) if log != nil { - log.Printf("Parsed %s from config: %t", key, result) + workflowLog.Printf("Parsed %s from config: %t", key, result) } return result } @@ -215,7 +215,7 @@ func unmarshalConfig(m map[string]any, key string, target any, log *logger.Logge } if log != nil { - log.Printf("Unmarshaling config for key %q into typed struct", key) + workflowLog.Printf("Unmarshaling config for key %q into typed struct", key) } // Marshal the config data back to YAML bytes @@ -230,7 +230,7 @@ func unmarshalConfig(m map[string]any, key string, target any, log *logger.Logge } if log != nil { - log.Printf("Successfully unmarshaled config for key %q", key) + workflowLog.Printf("Successfully unmarshaled config for key %q", key) } return nil @@ -273,7 +273,7 @@ func parseConfigScaffold[T any]( if _, exists := outputMap[key]; !exists { return nil } - log.Printf("Parsing %s configuration", key) + workflowLog.Printf("Parsing %s configuration", key) var config T if err := unmarshalConfig(outputMap, key, &config, log); err != nil { return onError(err) diff --git a/pkg/workflow/create_discussion.go b/pkg/workflow/create_discussion.go index 96d1ab7f249..ffe23764146 100644 --- a/pkg/workflow/create_discussion.go +++ b/pkg/workflow/create_discussion.go @@ -151,12 +151,12 @@ func normalizeDiscussionCategory(category string, log *logger.Logger, markdownPa if corrected, exists := categoryCorrections[category]; exists { message = fmt.Sprintf("Discussion category %q normalized to lowercase: %q", category, corrected) if log != nil { - log.Printf("Normalized discussion category %q to lowercase: %q", category, corrected) + workflowLog.Printf("Normalized discussion category %q to lowercase: %q", category, corrected) } } else { message = fmt.Sprintf("Discussion category %q normalized to lowercase: %q", category, normalizedCategory) if log != nil { - log.Printf("Normalized discussion category %q to lowercase: %q", category, normalizedCategory) + workflowLog.Printf("Normalized discussion category %q to lowercase: %q", category, normalizedCategory) } } @@ -172,7 +172,7 @@ func normalizeDiscussionCategory(category string, log *logger.Logger, markdownPa if plural, isSingular := singularToPlural[normalizedCategory]; isSingular { if log != nil { - log.Printf("⚠ Discussion category %q is singular; consider using plural form %q for consistency", normalizedCategory, plural) + workflowLog.Printf("⚠ Discussion category %q is singular; consider using plural form %q for consistency", normalizedCategory, plural) } } diff --git a/pkg/workflow/engine_firewall_support.go b/pkg/workflow/engine_firewall_support.go index 04f789a5ef5..0c9788f5336 100644 --- a/pkg/workflow/engine_firewall_support.go +++ b/pkg/workflow/engine_firewall_support.go @@ -152,7 +152,7 @@ func defaultGetSquidLogsSteps(workflowData *WorkflowData, log *logger.Logger) [] // Only add upload and parsing steps if firewall is enabled if isFirewallEnabled(workflowData) { - log.Printf("Adding Squid logs upload and parsing steps for workflow: %s", workflowData.Name) + workflowLog.Printf("Adding Squid logs upload and parsing steps for workflow: %s", workflowData.Name) squidLogsUpload := generateSquidLogsUploadStep(workflowData.Name) steps = append(steps, squidLogsUpload) @@ -161,7 +161,7 @@ func defaultGetSquidLogsSteps(workflowData *WorkflowData, log *logger.Logger) [] firewallLogParsing := generateFirewallLogParsingStep(workflowData.Name) steps = append(steps, firewallLogParsing) } else { - log.Print("Firewall disabled, skipping Squid logs upload") + workflowLog.Print("Firewall disabled, skipping Squid logs upload") } return steps diff --git a/pkg/workflow/missing_issue_reporting.go b/pkg/workflow/missing_issue_reporting.go index 411f869d748..1173b63e5cd 100644 --- a/pkg/workflow/missing_issue_reporting.go +++ b/pkg/workflow/missing_issue_reporting.go @@ -62,7 +62,7 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, // Explicitly disabled: missing-data: false if configBool, ok := configData.(bool); ok && !configBool { - log.Printf("%s configuration explicitly disabled", yamlKey) + workflowLog.Printf("%s configuration explicitly disabled", yamlKey) return nil } @@ -70,7 +70,7 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, // Enabled with no value: missing-data: (nil) if configData == nil { - log.Printf("%s configuration enabled with defaults", yamlKey) + workflowLog.Printf("%s configuration enabled with defaults", yamlKey) createIssueStr := strconv.FormatBool(defaultCreateIssue) cfg.CreateIssue = &createIssueStr if parseReportAsFailure { @@ -83,19 +83,19 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, } if configMap, ok := configData.(map[string]any); ok { - log.Printf("Parsing %s configuration from map", yamlKey) + workflowLog.Printf("Parsing %s configuration from map", yamlKey) c.parseBaseSafeOutputConfig(configMap, &cfg.BaseSafeOutputConfig, 0) // Pre-process create-issue to support literal booleans and GitHub Actions expressions. if err := preprocessBoolFieldAsString(configMap, "create-issue", log); err != nil { - log.Printf("Invalid create-issue value for %s: %v", yamlKey, err) + workflowLog.Printf("Invalid create-issue value for %s: %v", yamlKey, err) return nil } if createIssueVal, exists := configMap["create-issue"]; exists { if createIssueStr, ok := createIssueVal.(string); ok { cfg.CreateIssue = &createIssueStr - log.Printf("create-issue: %s", createIssueStr) + workflowLog.Printf("create-issue: %s", createIssueStr) } } else { createIssueStr := strconv.FormatBool(defaultCreateIssue) @@ -105,13 +105,13 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, // Parse report-as-failure field (only for missing-tool and missing-data, not report-incomplete) if parseReportAsFailure { if err := preprocessBoolFieldAsString(configMap, "report-as-failure", log); err != nil { - log.Printf("Invalid report-as-failure value for %s: %v", yamlKey, err) + workflowLog.Printf("Invalid report-as-failure value for %s: %v", yamlKey, err) return nil } if reportAsFailureVal, exists := configMap["report-as-failure"]; exists { if reportAsFailureStr, ok := reportAsFailureVal.(string); ok { cfg.ReportAsFailure = &reportAsFailureStr - log.Printf("report-as-failure: %s", reportAsFailureStr) + workflowLog.Printf("report-as-failure: %s", reportAsFailureStr) } } else { trueVal := "true" @@ -122,7 +122,7 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, if titlePrefix, exists := configMap["title-prefix"]; exists { if titlePrefixStr, ok := titlePrefix.(string); ok { cfg.TitlePrefix = titlePrefixStr - log.Printf("title-prefix: %s", titlePrefixStr) + workflowLog.Printf("title-prefix: %s", titlePrefixStr) } } else { cfg.TitlePrefix = defaultTitle @@ -130,7 +130,7 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, if _, exists := configMap["labels"]; exists { cfg.Labels = ParseStringArrayFromConfig(configMap, "labels", log) - log.Printf("labels: %v", cfg.Labels) + workflowLog.Printf("labels: %v", cfg.Labels) } else { cfg.Labels = []string{} } diff --git a/pkg/workflow/parse_helpers.go b/pkg/workflow/parse_helpers.go index b65feb7a4e4..05e3177f267 100644 --- a/pkg/workflow/parse_helpers.go +++ b/pkg/workflow/parse_helpers.go @@ -47,12 +47,12 @@ func preprocessProtectedFilesField(configData map[string]any, log *logger.Logger if policy, ok := pfMap["policy"].(string); ok && policy != "" { configData["protected-files"] = policy if log != nil { - log.Printf("protected-files object form: policy=%s", policy) + workflowLog.Printf("protected-files object form: policy=%s", policy) } } else { delete(configData, "protected-files") if log != nil { - log.Print("protected-files object form: no policy, using default") + workflowLog.Print("protected-files object form: no policy, using default") } } return parseStringSliceAny(pfMap["exclude"], log) @@ -87,13 +87,13 @@ func parseStringSliceAny(raw any, log *logger.Logger) []string { if s, ok := item.(string); ok { result = append(result, s) } else if log != nil { - log.Printf("parseStringSliceAny: skipping non-string item: %T", item) + workflowLog.Printf("parseStringSliceAny: skipping non-string item: %T", item) } } return result default: if log != nil { - log.Printf("parseStringSliceAny: unexpected type %T, ignoring", raw) + workflowLog.Printf("parseStringSliceAny: unexpected type %T, ignoring", raw) } return nil } diff --git a/pkg/workflow/permissions_compiler_validator.go b/pkg/workflow/permissions_compiler_validator.go index d7b992edd93..5472bf8056e 100644 --- a/pkg/workflow/permissions_compiler_validator.go +++ b/pkg/workflow/permissions_compiler_validator.go @@ -59,19 +59,19 @@ func (c *Compiler) validatePermissions(workflowData *WorkflowData, markdownPath } // Validate dangerous permissions - log.Printf("Validating dangerous permissions") + workflowLog.Printf("Validating dangerous permissions") if err := validateDangerousPermissions(workflowData, workflowPermissions); err != nil { return nil, formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate GitHub App-only permissions require a GitHub App to be configured - log.Printf("Validating GitHub App-only permissions") + workflowLog.Printf("Validating GitHub App-only permissions") if err := validateGitHubAppOnlyPermissions(workflowData, workflowPermissions); err != nil { return nil, formatCompilerError(markdownPath, "error", err.Error(), err) } // Validate tools.github.github-app.permissions does not use "write" - log.Printf("Validating GitHub MCP app permissions (no write)") + workflowLog.Printf("Validating GitHub MCP app permissions (no write)") if err := validateGitHubMCPAppPermissionsNoWrite(workflowData); err != nil { return nil, formatCompilerError(markdownPath, "error", err.Error(), err) } @@ -80,31 +80,31 @@ func (c *Compiler) validatePermissions(workflowData *WorkflowData, markdownPath warnGitHubAppPermissionsUnsupportedContexts(workflowData) // Validate workflow_run triggers have branch restrictions - log.Printf("Validating workflow_run triggers for branch restrictions") + workflowLog.Printf("Validating workflow_run triggers for branch restrictions") if err := c.validateWorkflowRunBranches(workflowData, markdownPath); err != nil { return nil, err } // Validate pull_request_target trigger security - log.Printf("Validating pull_request_target trigger security") + workflowLog.Printf("Validating pull_request_target trigger security") if err := c.validatePullRequestTargetTrigger(workflowData, markdownPath); err != nil { return nil, err } // Validate permissions against GitHub MCP toolsets - log.Printf("Validating permissions for GitHub MCP toolsets") + workflowLog.Printf("Validating permissions for GitHub MCP toolsets") if workflowData.ParsedTools != nil && workflowData.ParsedTools.GitHub != nil { // Check if GitHub tool was explicitly configured in frontmatter // If permissions exist but tools.github was NOT explicitly configured, // skip validation and let the GitHub MCP server handle permission issues hasPermissions := workflowData.Permissions != "" - log.Printf("Permission validation check: hasExplicitGitHubTool=%v, hasPermissions=%v", + workflowLog.Printf("Permission validation check: hasExplicitGitHubTool=%v, hasPermissions=%v", workflowData.HasExplicitGitHubTool, hasPermissions) // Skip validation if permissions exist but GitHub tool was auto-added (not explicit) if hasPermissions && !workflowData.HasExplicitGitHubTool { - log.Printf("Skipping permission validation: permissions exist but tools.github not explicitly configured") + workflowLog.Printf("Skipping permission validation: permissions exist but tools.github not explicitly configured") } else { // Validate permissions using the typed GitHub tool configuration. // Pass the cached parsed toolsets from applyDefaults to avoid a redundant @@ -141,7 +141,7 @@ func (c *Compiler) validatePermissions(workflowData *WorkflowData, markdownPath } // Emit warning if id-token: write permission is detected - log.Printf("Checking for id-token: write permission") + workflowLog.Printf("Checking for id-token: write permission") if level, exists := workflowPermissions.Get(PermissionIdToken); exists && level == PermissionWrite { warningMsg := `This workflow grants id-token: write permission OIDC tokens can authenticate to cloud providers (AWS, Azure, GCP). diff --git a/pkg/workflow/project_config_parsing.go b/pkg/workflow/project_config_parsing.go index 59e70164b55..67fc933d38f 100644 --- a/pkg/workflow/project_config_parsing.go +++ b/pkg/workflow/project_config_parsing.go @@ -64,9 +64,9 @@ func parseProjectViews(configMap map[string]any, log *logger.Logger) []ProjectVi // Only add view if it has required fields if view.Name != "" && view.Layout != "" { views = append(views, view) - log.Printf("Parsed view %d: %s (%s)", i+1, view.Name, view.Layout) + workflowLog.Printf("Parsed view %d: %s (%s)", i+1, view.Name, view.Layout) } else { - log.Printf("Skipping invalid view %d: missing required fields", i+1) + workflowLog.Printf("Skipping invalid view %d: missing required fields", i+1) } } return views @@ -123,7 +123,7 @@ func parseProjectFieldDefinitions(configMap map[string]any, log *logger.Logger) if field.Name != "" && field.DataType != "" { fields = append(fields, field) - log.Printf("Parsed field definition %d: %s (%s)", i+1, field.Name, field.DataType) + workflowLog.Printf("Parsed field definition %d: %s (%s)", i+1, field.Name, field.DataType) } } return fields diff --git a/pkg/workflow/runtime_detection.go b/pkg/workflow/runtime_detection.go index 459dcb8b63e..69d26a88219 100644 --- a/pkg/workflow/runtime_detection.go +++ b/pkg/workflow/runtime_detection.go @@ -112,7 +112,7 @@ func requiresNodeForEngineHarness(workflowData *WorkflowData) bool { // detectFromCustomSteps scans custom steps YAML for runtime commands func detectFromCustomSteps(customSteps string, requirements map[string]*RuntimeRequirement) { - log.Print("Scanning custom steps for runtime commands") + workflowLog.Print("Scanning custom steps for runtime commands") lines := strings.SplitSeq(customSteps, "\n") for line := range lines { // Look for run: commands @@ -189,7 +189,7 @@ func detectFromMCPConfigs(tools *ToolsConfig, requirements map[string]*RuntimeRe } allTools := tools.ToMap() - log.Printf("Scanning %d MCP configurations for runtime commands", len(allTools)) + workflowLog.Printf("Scanning %d MCP configurations for runtime commands", len(allTools)) // Scan custom MCP tools for runtime commands // Skip containerized MCP servers as they don't need host runtime setup diff --git a/pkg/workflow/runtime_step_generator.go b/pkg/workflow/runtime_step_generator.go index d88465939f4..5db38d8880f 100644 --- a/pkg/workflow/runtime_step_generator.go +++ b/pkg/workflow/runtime_step_generator.go @@ -161,7 +161,7 @@ func generateSetupStep(req *RuntimeRequirement) GitHubActionStep { sort.Strings(allKeys) for _, key := range allKeys { step = append(step, fmt.Sprintf(" %s: %s", key, allExtraFields[key])) - log.Printf(" Added extra field to runtime setup: %s = %s", key, allExtraFields[key]) + workflowLog.Printf(" Added extra field to runtime setup: %s = %s", key, allExtraFields[key]) } return step diff --git a/pkg/workflow/templatables.go b/pkg/workflow/templatables.go index 3ac05512f56..293573b19a5 100644 --- a/pkg/workflow/templatables.go +++ b/pkg/workflow/templatables.go @@ -157,7 +157,7 @@ func preprocessBoolFieldAsString(configData map[string]any, fieldName string, lo configData[fieldName] = "false" } if log != nil { - log.Printf("Converted %s bool to string before unmarshaling", fieldName) + workflowLog.Printf("Converted %s bool to string before unmarshaling", fieldName) } case string: if !isExpression(v) { @@ -227,22 +227,22 @@ func preprocessIntFieldAsString(configData map[string]any, fieldName string, log case int: configData[fieldName] = strconv.Itoa(v) if log != nil { - log.Printf("Converted %s int to string before unmarshaling", fieldName) + workflowLog.Printf("Converted %s int to string before unmarshaling", fieldName) } case int64: configData[fieldName] = strconv.FormatInt(v, 10) if log != nil { - log.Printf("Converted %s int64 to string before unmarshaling", fieldName) + workflowLog.Printf("Converted %s int64 to string before unmarshaling", fieldName) } case float64: configData[fieldName] = strconv.Itoa(int(v)) if log != nil { - log.Printf("Converted %s float64 to string before unmarshaling", fieldName) + workflowLog.Printf("Converted %s float64 to string before unmarshaling", fieldName) } case uint64: configData[fieldName] = strconv.FormatUint(v, 10) if log != nil { - log.Printf("Converted %s uint64 to string before unmarshaling", fieldName) + workflowLog.Printf("Converted %s uint64 to string before unmarshaling", fieldName) } case string: if !isExpression(v) { @@ -287,7 +287,7 @@ func preprocessStringArrayFieldAsTemplatable(configData map[string]any, fieldNam // can receive it after YAML marshaling/unmarshaling. configData[fieldName] = []string{s} if log != nil { - log.Printf("Wrapped %s expression string in single-element array before unmarshaling", fieldName) + workflowLog.Printf("Wrapped %s expression string in single-element array before unmarshaling", fieldName) } } // Arrays ([]string, []any) are left unchanged for YAML unmarshal to handle. diff --git a/pkg/workflow/update_entity_helpers.go b/pkg/workflow/update_entity_helpers.go index ce2a17a8084..8bbb0d6c3fb 100644 --- a/pkg/workflow/update_entity_helpers.go +++ b/pkg/workflow/update_entity_helpers.go @@ -276,7 +276,7 @@ func parseUpdateEntityStringBoolField(configMap map[string]any, fieldName string } if err := preprocessBoolFieldAsString(configMap, fieldName, log); err != nil { if log != nil { - log.Printf("Invalid %s value: %v", fieldName, err) + workflowLog.Printf("Invalid %s value: %v", fieldName, err) } return nil } diff --git a/pkg/workflow/validation_helpers.go b/pkg/workflow/validation_helpers.go index 0715411cf31..adf7cc8e9ac 100644 --- a/pkg/workflow/validation_helpers.go +++ b/pkg/workflow/validation_helpers.go @@ -164,7 +164,7 @@ func validateStringEnumField(configData map[string]any, fieldName string, allowe strVal, ok := val.(string) if !ok { if log != nil { - log.Printf("Invalid %s value %v (must be one of %v), ignoring", fieldName, val, allowed) + workflowLog.Printf("Invalid %s value %v (must be one of %v), ignoring", fieldName, val, allowed) } delete(configData, fieldName) return @@ -172,13 +172,13 @@ func validateStringEnumField(configData map[string]any, fieldName string, allowe // GitHub Actions expressions are validated at runtime by the handler. if containsExpression(strVal) { if log != nil { - log.Printf("%s value is a GitHub Actions expression, skipping compile-time enum validation", fieldName) + workflowLog.Printf("%s value is a GitHub Actions expression, skipping compile-time enum validation", fieldName) } return } if !slices.Contains(allowed, strVal) { if log != nil { - log.Printf("Invalid %s value %v (must be one of %v), ignoring", fieldName, val, allowed) + workflowLog.Printf("Invalid %s value %v (must be one of %v), ignoring", fieldName, val, allowed) } delete(configData, fieldName) } From 3fcfafbdf0caac25a3487859c4e92453a37104ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 11:04:16 +0000 Subject: [PATCH 4/4] fix: preserve caller logger namespaces in workflow helper functions Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/config_helpers.go | 80 ++++++++++++------------- pkg/workflow/create_discussion.go | 14 ++--- pkg/workflow/engine_firewall_support.go | 6 +- pkg/workflow/missing_issue_reporting.go | 26 ++++---- pkg/workflow/parse_helpers.go | 24 ++++---- pkg/workflow/project_config_parsing.go | 10 ++-- pkg/workflow/templatables.go | 30 +++++----- pkg/workflow/update_entity_helpers.go | 8 +-- pkg/workflow/validation_helpers.go | 14 ++--- 9 files changed, 106 insertions(+), 106 deletions(-) diff --git a/pkg/workflow/config_helpers.go b/pkg/workflow/config_helpers.go index fbd21f549ff..fc38ce22bcd 100644 --- a/pkg/workflow/config_helpers.go +++ b/pkg/workflow/config_helpers.go @@ -47,18 +47,18 @@ var configHelpersLog = logger.New("workflow:config_helpers") // ParseStringArrayFromConfig is a generic helper that extracts and validates a string array from a map // Returns a slice of strings, or nil if not present or invalid // If log is provided, it will log the extracted values for debugging -func ParseStringArrayFromConfig(m map[string]any, key string, log *logger.Logger) []string { +func ParseStringArrayFromConfig(m map[string]any, key string, debugLog *logger.Logger) []string { if value, exists := m[key]; exists { - if log != nil { - workflowLog.Printf("Parsing %s from config", key) + if debugLog != nil { + debugLog.Printf("Parsing %s from config", key) } - if strings := parseStringSliceAny(value, log); strings != nil { + if strings := parseStringSliceAny(value, debugLog); strings != nil { // Return the slice even if empty (to distinguish from not provided) - if len(strings) == 0 && log != nil { - workflowLog.Printf("No valid %s strings found, returning empty array", key) + if len(strings) == 0 && debugLog != nil { + debugLog.Printf("No valid %s strings found, returning empty array", key) } - if log != nil { - workflowLog.Printf("Parsed %d %s from config", len(strings), key) + if debugLog != nil { + debugLog.Printf("Parsed %d %s from config", len(strings), key) } return strings } @@ -73,32 +73,32 @@ func ParseStringArrayFromConfig(m map[string]any, key string, log *logger.Logger // a JSON array. // // Non-expression bare strings are treated as invalid and nil is returned. -func ParseStringArrayOrExprFromConfig(m map[string]any, key string, log *logger.Logger) []string { +func ParseStringArrayOrExprFromConfig(m map[string]any, key string, debugLog *logger.Logger) []string { if value, exists := m[key]; exists { - if log != nil { - workflowLog.Printf("Parsing %s from config", key) + if debugLog != nil { + debugLog.Printf("Parsing %s from config", key) } // Accept a GitHub Actions expression string: wrap it in a single-element slice. if s, ok := value.(string); ok { if isExpression(s) { - if log != nil { - workflowLog.Printf("Field %s is a GitHub Actions expression, wrapping in single-element array", key) + if debugLog != nil { + debugLog.Printf("Field %s is a GitHub Actions expression, wrapping in single-element array", key) } return []string{s} } // Non-expression string is invalid for an array field. - if log != nil { - workflowLog.Printf("Field %q must be an array or a GitHub Actions expression, ignoring non-expression string: %q", key, s) + if debugLog != nil { + debugLog.Printf("Field %q must be an array or a GitHub Actions expression, ignoring non-expression string: %q", key, s) } return nil } // Handle arrays (existing logic). - if strings := parseStringSliceAny(value, log); strings != nil { - if len(strings) == 0 && log != nil { - workflowLog.Printf("No valid %s strings found, returning empty array", key) + if strings := parseStringSliceAny(value, debugLog); strings != nil { + if len(strings) == 0 && debugLog != nil { + debugLog.Printf("No valid %s strings found, returning empty array", key) } - if log != nil { - workflowLog.Printf("Parsed %d %s from config", len(strings), key) + if debugLog != nil { + debugLog.Printf("Parsed %d %s from config", len(strings), key) } return strings } @@ -109,11 +109,11 @@ func ParseStringArrayOrExprFromConfig(m map[string]any, key string, log *logger. // extractStringFromMap is a generic helper that extracts and validates a string value from a map // Returns the string value, or empty string if not present or invalid // If log is provided, it will log the extracted value for debugging -func extractStringFromMap(m map[string]any, key string, log *logger.Logger) string { +func extractStringFromMap(m map[string]any, key string, debugLog *logger.Logger) string { if value, exists := m[key]; exists { if valueStr, ok := value.(string); ok { - if log != nil { - workflowLog.Printf("Parsed %s from config: %s", key, valueStr) + if debugLog != nil { + debugLog.Printf("Parsed %s from config: %s", key, valueStr) } return valueStr } @@ -148,7 +148,7 @@ func parseTargetRepoWithValidation(configMap map[string]any) (string, bool) { // // Returns true if expires was explicitly disabled with false, false otherwise. // This helper consolidates duplicate preprocessing logic used in parseCreateIssuesConfig and parseCreateDiscussionsConfig. -func preprocessExpiresField(configData map[string]any, log *logger.Logger) bool { +func preprocessExpiresField(configData map[string]any, debugLog *logger.Logger) bool { expiresDisabled := false if configData != nil { if expires, exists := configData["expires"]; exists { @@ -165,8 +165,8 @@ func preprocessExpiresField(configData map[string]any, log *logger.Logger) bool // Invalid or missing - set to 0 configData["expires"] = 0 } - if log != nil { - workflowLog.Printf("Parsed expires value %v to %d hours (disabled=%t)", expires, expiresInt, expiresDisabled) + if debugLog != nil { + debugLog.Printf("Parsed expires value %v to %d hours (disabled=%t)", expires, expiresInt, expiresDisabled) } } } @@ -176,13 +176,13 @@ func preprocessExpiresField(configData map[string]any, log *logger.Logger) bool // ParseBoolFromConfig is a generic helper that extracts and validates a boolean value from a map. // Returns the boolean value, or false if not present or invalid. // If log is provided, it will log the extracted value for debugging. -func ParseBoolFromConfig(m map[string]any, key string, log *logger.Logger) bool { - if log != nil { - workflowLog.Printf("Parsing %s from config", key) +func ParseBoolFromConfig(m map[string]any, key string, debugLog *logger.Logger) bool { + if debugLog != nil { + debugLog.Printf("Parsing %s from config", key) } result := typeutil.ParseBool(m, key) - if log != nil { - workflowLog.Printf("Parsed %s from config: %t", key, result) + if debugLog != nil { + debugLog.Printf("Parsed %s from config: %t", key, result) } return result } @@ -194,7 +194,7 @@ func ParseBoolFromConfig(m map[string]any, key string, log *logger.Logger) bool // Example usage: // // var config CreateIssuesConfig -// if err := unmarshalConfig(outputMap, "create-issue", &config, log); err != nil { +// if err := unmarshalConfig(outputMap, "create-issue", &config, debugLog); err != nil { // return nil, err // } // @@ -203,7 +203,7 @@ func ParseBoolFromConfig(m map[string]any, key string, log *logger.Logger) bool // 2. Marshals it to YAML bytes (preserving structure) // 3. Unmarshals the YAML into the typed struct (using struct tags for field mapping) // 4. Validates that all fields are properly typed -func unmarshalConfig(m map[string]any, key string, target any, log *logger.Logger) error { +func unmarshalConfig(m map[string]any, key string, target any, debugLog *logger.Logger) error { configData, exists := m[key] if !exists { return fmt.Errorf("config key %q not found", key) @@ -214,8 +214,8 @@ func unmarshalConfig(m map[string]any, key string, target any, log *logger.Logge configData = map[string]any{} } - if log != nil { - workflowLog.Printf("Unmarshaling config for key %q into typed struct", key) + if debugLog != nil { + debugLog.Printf("Unmarshaling config for key %q into typed struct", key) } // Marshal the config data back to YAML bytes @@ -229,8 +229,8 @@ func unmarshalConfig(m map[string]any, key string, target any, log *logger.Logge return fmt.Errorf("failed to unmarshal config for %q: %w", key, err) } - if log != nil { - workflowLog.Printf("Successfully unmarshaled config for key %q", key) + if debugLog != nil { + debugLog.Printf("Successfully unmarshaled config for key %q", key) } return nil @@ -267,15 +267,15 @@ func unmarshalConfig(m map[string]any, key string, target any, log *logger.Logge func parseConfigScaffold[T any]( outputMap map[string]any, key string, - log *logger.Logger, + debugLog *logger.Logger, onError func(err error) *T, ) *T { if _, exists := outputMap[key]; !exists { return nil } - workflowLog.Printf("Parsing %s configuration", key) + debugLog.Printf("Parsing %s configuration", key) var config T - if err := unmarshalConfig(outputMap, key, &config, log); err != nil { + if err := unmarshalConfig(outputMap, key, &config, debugLog); err != nil { return onError(err) } return &config diff --git a/pkg/workflow/create_discussion.go b/pkg/workflow/create_discussion.go index ffe23764146..2c576e88497 100644 --- a/pkg/workflow/create_discussion.go +++ b/pkg/workflow/create_discussion.go @@ -124,7 +124,7 @@ func (c *Compiler) parseCreateDiscussionsConfig(outputMap map[string]any) *Creat } // Returns normalized category (or original if it's a category ID) -func normalizeDiscussionCategory(category string, log *logger.Logger, markdownPath string) string { +func normalizeDiscussionCategory(category string, debugLog *logger.Logger, markdownPath string) string { // Empty category is allowed (GitHub Discussions will use default) if category == "" { return category @@ -150,13 +150,13 @@ func normalizeDiscussionCategory(category string, log *logger.Logger, markdownPa // Check if we have a known correction if corrected, exists := categoryCorrections[category]; exists { message = fmt.Sprintf("Discussion category %q normalized to lowercase: %q", category, corrected) - if log != nil { - workflowLog.Printf("Normalized discussion category %q to lowercase: %q", category, corrected) + if debugLog != nil { + debugLog.Printf("Normalized discussion category %q to lowercase: %q", category, corrected) } } else { message = fmt.Sprintf("Discussion category %q normalized to lowercase: %q", category, normalizedCategory) - if log != nil { - workflowLog.Printf("Normalized discussion category %q to lowercase: %q", category, normalizedCategory) + if debugLog != nil { + debugLog.Printf("Normalized discussion category %q to lowercase: %q", category, normalizedCategory) } } @@ -171,8 +171,8 @@ func normalizeDiscussionCategory(category string, log *logger.Logger, markdownPa } if plural, isSingular := singularToPlural[normalizedCategory]; isSingular { - if log != nil { - workflowLog.Printf("⚠ Discussion category %q is singular; consider using plural form %q for consistency", normalizedCategory, plural) + if debugLog != nil { + debugLog.Printf("⚠ Discussion category %q is singular; consider using plural form %q for consistency", normalizedCategory, plural) } } diff --git a/pkg/workflow/engine_firewall_support.go b/pkg/workflow/engine_firewall_support.go index 0c9788f5336..c049b0864d4 100644 --- a/pkg/workflow/engine_firewall_support.go +++ b/pkg/workflow/engine_firewall_support.go @@ -147,12 +147,12 @@ func generateFirewallLogParsingStep(workflowName string) GitHubActionStep { // defaultGetSquidLogsSteps returns the steps for uploading and parsing Squid logs after // secret redaction. It is shared across engines (Claude, Codex, Copilot) whose // GetSquidLogsSteps implementations are otherwise identical save for the logger used. -func defaultGetSquidLogsSteps(workflowData *WorkflowData, log *logger.Logger) []GitHubActionStep { +func defaultGetSquidLogsSteps(workflowData *WorkflowData, debugLog *logger.Logger) []GitHubActionStep { var steps []GitHubActionStep // Only add upload and parsing steps if firewall is enabled if isFirewallEnabled(workflowData) { - workflowLog.Printf("Adding Squid logs upload and parsing steps for workflow: %s", workflowData.Name) + debugLog.Printf("Adding Squid logs upload and parsing steps for workflow: %s", workflowData.Name) squidLogsUpload := generateSquidLogsUploadStep(workflowData.Name) steps = append(steps, squidLogsUpload) @@ -161,7 +161,7 @@ func defaultGetSquidLogsSteps(workflowData *WorkflowData, log *logger.Logger) [] firewallLogParsing := generateFirewallLogParsingStep(workflowData.Name) steps = append(steps, firewallLogParsing) } else { - workflowLog.Print("Firewall disabled, skipping Squid logs upload") + debugLog.Print("Firewall disabled, skipping Squid logs upload") } return steps diff --git a/pkg/workflow/missing_issue_reporting.go b/pkg/workflow/missing_issue_reporting.go index 1173b63e5cd..ccb74934e61 100644 --- a/pkg/workflow/missing_issue_reporting.go +++ b/pkg/workflow/missing_issue_reporting.go @@ -54,7 +54,7 @@ func (c *Compiler) parseReportIncompleteConfig(outputMap map[string]any) *Report return c.parseIssueReportingConfig(outputMap, "report-incomplete", "[incomplete]", true, false, reportIncompleteLog) } -func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, defaultTitle string, defaultCreateIssue bool, parseReportAsFailure bool, log *logger.Logger) *IssueReportingConfig { +func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, defaultTitle string, defaultCreateIssue bool, parseReportAsFailure bool, debugLog *logger.Logger) *IssueReportingConfig { configData, exists := outputMap[yamlKey] if !exists { return nil @@ -62,7 +62,7 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, // Explicitly disabled: missing-data: false if configBool, ok := configData.(bool); ok && !configBool { - workflowLog.Printf("%s configuration explicitly disabled", yamlKey) + debugLog.Printf("%s configuration explicitly disabled", yamlKey) return nil } @@ -70,7 +70,7 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, // Enabled with no value: missing-data: (nil) if configData == nil { - workflowLog.Printf("%s configuration enabled with defaults", yamlKey) + debugLog.Printf("%s configuration enabled with defaults", yamlKey) createIssueStr := strconv.FormatBool(defaultCreateIssue) cfg.CreateIssue = &createIssueStr if parseReportAsFailure { @@ -83,19 +83,19 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, } if configMap, ok := configData.(map[string]any); ok { - workflowLog.Printf("Parsing %s configuration from map", yamlKey) + debugLog.Printf("Parsing %s configuration from map", yamlKey) c.parseBaseSafeOutputConfig(configMap, &cfg.BaseSafeOutputConfig, 0) // Pre-process create-issue to support literal booleans and GitHub Actions expressions. - if err := preprocessBoolFieldAsString(configMap, "create-issue", log); err != nil { - workflowLog.Printf("Invalid create-issue value for %s: %v", yamlKey, err) + if err := preprocessBoolFieldAsString(configMap, "create-issue", debugLog); err != nil { + debugLog.Printf("Invalid create-issue value for %s: %v", yamlKey, err) return nil } if createIssueVal, exists := configMap["create-issue"]; exists { if createIssueStr, ok := createIssueVal.(string); ok { cfg.CreateIssue = &createIssueStr - workflowLog.Printf("create-issue: %s", createIssueStr) + debugLog.Printf("create-issue: %s", createIssueStr) } } else { createIssueStr := strconv.FormatBool(defaultCreateIssue) @@ -104,14 +104,14 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, // Parse report-as-failure field (only for missing-tool and missing-data, not report-incomplete) if parseReportAsFailure { - if err := preprocessBoolFieldAsString(configMap, "report-as-failure", log); err != nil { - workflowLog.Printf("Invalid report-as-failure value for %s: %v", yamlKey, err) + if err := preprocessBoolFieldAsString(configMap, "report-as-failure", debugLog); err != nil { + debugLog.Printf("Invalid report-as-failure value for %s: %v", yamlKey, err) return nil } if reportAsFailureVal, exists := configMap["report-as-failure"]; exists { if reportAsFailureStr, ok := reportAsFailureVal.(string); ok { cfg.ReportAsFailure = &reportAsFailureStr - workflowLog.Printf("report-as-failure: %s", reportAsFailureStr) + debugLog.Printf("report-as-failure: %s", reportAsFailureStr) } } else { trueVal := "true" @@ -122,15 +122,15 @@ func (c *Compiler) parseIssueReportingConfig(outputMap map[string]any, yamlKey, if titlePrefix, exists := configMap["title-prefix"]; exists { if titlePrefixStr, ok := titlePrefix.(string); ok { cfg.TitlePrefix = titlePrefixStr - workflowLog.Printf("title-prefix: %s", titlePrefixStr) + debugLog.Printf("title-prefix: %s", titlePrefixStr) } } else { cfg.TitlePrefix = defaultTitle } if _, exists := configMap["labels"]; exists { - cfg.Labels = ParseStringArrayFromConfig(configMap, "labels", log) - workflowLog.Printf("labels: %v", cfg.Labels) + cfg.Labels = ParseStringArrayFromConfig(configMap, "labels", debugLog) + debugLog.Printf("labels: %v", cfg.Labels) } else { cfg.Labels = []string{} } diff --git a/pkg/workflow/parse_helpers.go b/pkg/workflow/parse_helpers.go index 05e3177f267..1b85ffa4918 100644 --- a/pkg/workflow/parse_helpers.go +++ b/pkg/workflow/parse_helpers.go @@ -30,7 +30,7 @@ import "github.com/github/gh-aw/pkg/logger" // // When the string form is encountered the field is left unchanged and nil is returned. // The log parameter is optional; pass nil to suppress debug output. -func preprocessProtectedFilesField(configData map[string]any, log *logger.Logger) []string { +func preprocessProtectedFilesField(configData map[string]any, debugLog *logger.Logger) []string { if configData == nil { return nil } @@ -46,16 +46,16 @@ func preprocessProtectedFilesField(configData map[string]any, log *logger.Logger // Object form: extract policy and exclude if policy, ok := pfMap["policy"].(string); ok && policy != "" { configData["protected-files"] = policy - if log != nil { - workflowLog.Printf("protected-files object form: policy=%s", policy) + if debugLog != nil { + debugLog.Printf("protected-files object form: policy=%s", policy) } } else { delete(configData, "protected-files") - if log != nil { - workflowLog.Print("protected-files object form: no policy, using default") + if debugLog != nil { + debugLog.Print("protected-files object form: no policy, using default") } } - return parseStringSliceAny(pfMap["exclude"], log) + return parseStringSliceAny(pfMap["exclude"], debugLog) } // parseStringSliceAny coerces a raw any value into a []string. @@ -72,8 +72,8 @@ func preprocessProtectedFilesField(configData map[string]any, log *logger.Logger // before calling this function: // // if s, ok := raw.(string); ok { return []string{s} } -// return parseStringSliceAny(raw, log) -func parseStringSliceAny(raw any, log *logger.Logger) []string { +// return parseStringSliceAny(raw, debugLog) +func parseStringSliceAny(raw any, debugLog *logger.Logger) []string { if raw == nil { return nil } @@ -86,14 +86,14 @@ func parseStringSliceAny(raw any, log *logger.Logger) []string { for _, item := range v { if s, ok := item.(string); ok { result = append(result, s) - } else if log != nil { - workflowLog.Printf("parseStringSliceAny: skipping non-string item: %T", item) + } else if debugLog != nil { + debugLog.Printf("parseStringSliceAny: skipping non-string item: %T", item) } } return result default: - if log != nil { - workflowLog.Printf("parseStringSliceAny: unexpected type %T, ignoring", raw) + if debugLog != nil { + debugLog.Printf("parseStringSliceAny: unexpected type %T, ignoring", raw) } return nil } diff --git a/pkg/workflow/project_config_parsing.go b/pkg/workflow/project_config_parsing.go index 67fc933d38f..2ed6b6f702b 100644 --- a/pkg/workflow/project_config_parsing.go +++ b/pkg/workflow/project_config_parsing.go @@ -4,7 +4,7 @@ import "github.com/github/gh-aw/pkg/logger" // parseProjectViews parses the "views" list from a project config map. // Only views with both name and layout fields are included; invalid ones are skipped. -func parseProjectViews(configMap map[string]any, log *logger.Logger) []ProjectView { +func parseProjectViews(configMap map[string]any, debugLog *logger.Logger) []ProjectView { viewsData, exists := configMap["views"] if !exists { return nil @@ -64,9 +64,9 @@ func parseProjectViews(configMap map[string]any, log *logger.Logger) []ProjectVi // Only add view if it has required fields if view.Name != "" && view.Layout != "" { views = append(views, view) - workflowLog.Printf("Parsed view %d: %s (%s)", i+1, view.Name, view.Layout) + debugLog.Printf("Parsed view %d: %s (%s)", i+1, view.Name, view.Layout) } else { - workflowLog.Printf("Skipping invalid view %d: missing required fields", i+1) + debugLog.Printf("Skipping invalid view %d: missing required fields", i+1) } } return views @@ -74,7 +74,7 @@ func parseProjectViews(configMap map[string]any, log *logger.Logger) []ProjectVi // parseProjectFieldDefinitions parses the "field-definitions" (or "field_definitions") list // from a project config map. Only fields with both name and data-type are included. -func parseProjectFieldDefinitions(configMap map[string]any, log *logger.Logger) []ProjectFieldDefinition { +func parseProjectFieldDefinitions(configMap map[string]any, debugLog *logger.Logger) []ProjectFieldDefinition { fieldsData, hasFields := configMap["field-definitions"] if !hasFields { // Allow underscore variant as well @@ -123,7 +123,7 @@ func parseProjectFieldDefinitions(configMap map[string]any, log *logger.Logger) if field.Name != "" && field.DataType != "" { fields = append(fields, field) - workflowLog.Printf("Parsed field definition %d: %s (%s)", i+1, field.Name, field.DataType) + debugLog.Printf("Parsed field definition %d: %s (%s)", i+1, field.Name, field.DataType) } } return fields diff --git a/pkg/workflow/templatables.go b/pkg/workflow/templatables.go index 293573b19a5..44103d69c1b 100644 --- a/pkg/workflow/templatables.go +++ b/pkg/workflow/templatables.go @@ -144,7 +144,7 @@ func (t *TemplatableInt32) Ptr() *TemplatableInt32 { // If the value is a string it must be a GitHub Actions expression (starts // with "${{" and ends with "}}"); any other free-form string is rejected // and an error is returned. -func preprocessBoolFieldAsString(configData map[string]any, fieldName string, log *logger.Logger) error { +func preprocessBoolFieldAsString(configData map[string]any, fieldName string, debugLog *logger.Logger) error { if configData == nil { return nil } @@ -156,8 +156,8 @@ func preprocessBoolFieldAsString(configData map[string]any, fieldName string, lo } else { configData[fieldName] = "false" } - if log != nil { - workflowLog.Printf("Converted %s bool to string before unmarshaling", fieldName) + if debugLog != nil { + debugLog.Printf("Converted %s bool to string before unmarshaling", fieldName) } case string: if !isExpression(v) { @@ -218,7 +218,7 @@ func (b *handlerConfigBuilder) AddTemplatableBool(key string, value *string) *ha // If the value is a string it must be a GitHub Actions expression (starts // with "${{" and ends with "}}"); any other free-form string is rejected // and an error is returned. -func preprocessIntFieldAsString(configData map[string]any, fieldName string, log *logger.Logger) error { +func preprocessIntFieldAsString(configData map[string]any, fieldName string, debugLog *logger.Logger) error { if configData == nil { return nil } @@ -226,23 +226,23 @@ func preprocessIntFieldAsString(configData map[string]any, fieldName string, log switch v := val.(type) { case int: configData[fieldName] = strconv.Itoa(v) - if log != nil { - workflowLog.Printf("Converted %s int to string before unmarshaling", fieldName) + if debugLog != nil { + debugLog.Printf("Converted %s int to string before unmarshaling", fieldName) } case int64: configData[fieldName] = strconv.FormatInt(v, 10) - if log != nil { - workflowLog.Printf("Converted %s int64 to string before unmarshaling", fieldName) + if debugLog != nil { + debugLog.Printf("Converted %s int64 to string before unmarshaling", fieldName) } case float64: configData[fieldName] = strconv.Itoa(int(v)) - if log != nil { - workflowLog.Printf("Converted %s float64 to string before unmarshaling", fieldName) + if debugLog != nil { + debugLog.Printf("Converted %s float64 to string before unmarshaling", fieldName) } case uint64: configData[fieldName] = strconv.FormatUint(v, 10) - if log != nil { - workflowLog.Printf("Converted %s uint64 to string before unmarshaling", fieldName) + if debugLog != nil { + debugLog.Printf("Converted %s uint64 to string before unmarshaling", fieldName) } case string: if !isExpression(v) { @@ -265,7 +265,7 @@ func preprocessIntFieldAsString(configData map[string]any, fieldName string, log // // Free-form strings that are not GitHub Actions expressions are rejected with an error. // Array values ([]string, []any) are left untouched for the normal YAML unmarshal path. -func preprocessStringArrayFieldAsTemplatable(configData map[string]any, fieldName string, log *logger.Logger) error { +func preprocessStringArrayFieldAsTemplatable(configData map[string]any, fieldName string, debugLog *logger.Logger) error { if configData == nil { return nil } @@ -286,8 +286,8 @@ func preprocessStringArrayFieldAsTemplatable(configData map[string]any, fieldNam // Wrap the expression in a single-element slice so the []string struct field // can receive it after YAML marshaling/unmarshaling. configData[fieldName] = []string{s} - if log != nil { - workflowLog.Printf("Wrapped %s expression string in single-element array before unmarshaling", fieldName) + if debugLog != nil { + debugLog.Printf("Wrapped %s expression string in single-element array before unmarshaling", fieldName) } } // Arrays ([]string, []any) are left unchanged for YAML unmarshal to handle. diff --git a/pkg/workflow/update_entity_helpers.go b/pkg/workflow/update_entity_helpers.go index 8bbb0d6c3fb..abd4ad0c885 100644 --- a/pkg/workflow/update_entity_helpers.go +++ b/pkg/workflow/update_entity_helpers.go @@ -267,16 +267,16 @@ func parseUpdateEntityBoolField(configMap map[string]any, fieldName string, mode // parseUpdateEntityStringBoolField parses a FieldParsingTemplatableBool field from a config map. // It pre-processes the value to normalise literal booleans to strings, then returns the value // as *string. Returns nil when the field is absent. -func parseUpdateEntityStringBoolField(configMap map[string]any, fieldName string, log *logger.Logger) *string { +func parseUpdateEntityStringBoolField(configMap map[string]any, fieldName string, debugLog *logger.Logger) *string { if configMap == nil { return nil } if _, exists := configMap[fieldName]; !exists { return nil } - if err := preprocessBoolFieldAsString(configMap, fieldName, log); err != nil { - if log != nil { - workflowLog.Printf("Invalid %s value: %v", fieldName, err) + if err := preprocessBoolFieldAsString(configMap, fieldName, debugLog); err != nil { + if debugLog != nil { + debugLog.Printf("Invalid %s value: %v", fieldName, err) } return nil } diff --git a/pkg/workflow/validation_helpers.go b/pkg/workflow/validation_helpers.go index adf7cc8e9ac..ff6d9da0ed8 100644 --- a/pkg/workflow/validation_helpers.go +++ b/pkg/workflow/validation_helpers.go @@ -153,7 +153,7 @@ func parseMountEntry(mount string) (mountParts, mountValidationKind) { // GitHub Actions expression strings (e.g. "${{ inputs.policy }}") are accepted // without enum validation and passed through unchanged; the resolved value is // validated at runtime by the safe-output handler. -func validateStringEnumField(configData map[string]any, fieldName string, allowed []string, log *logger.Logger) { +func validateStringEnumField(configData map[string]any, fieldName string, allowed []string, debugLog *logger.Logger) { if configData == nil { return } @@ -163,22 +163,22 @@ func validateStringEnumField(configData map[string]any, fieldName string, allowe } strVal, ok := val.(string) if !ok { - if log != nil { - workflowLog.Printf("Invalid %s value %v (must be one of %v), ignoring", fieldName, val, allowed) + if debugLog != nil { + debugLog.Printf("Invalid %s value %v (must be one of %v), ignoring", fieldName, val, allowed) } delete(configData, fieldName) return } // GitHub Actions expressions are validated at runtime by the handler. if containsExpression(strVal) { - if log != nil { - workflowLog.Printf("%s value is a GitHub Actions expression, skipping compile-time enum validation", fieldName) + if debugLog != nil { + debugLog.Printf("%s value is a GitHub Actions expression, skipping compile-time enum validation", fieldName) } return } if !slices.Contains(allowed, strVal) { - if log != nil { - workflowLog.Printf("Invalid %s value %v (must be one of %v), ignoring", fieldName, val, allowed) + if debugLog != nil { + debugLog.Printf("Invalid %s value %v (must be one of %v), ignoring", fieldName, val, allowed) } delete(configData, fieldName) }