diff --git a/pkg/workflow/checkout_step_generator.go b/pkg/workflow/checkout_step_generator.go index c70a7e7960..bbbfed0f5f 100644 --- a/pkg/workflow/checkout_step_generator.go +++ b/pkg/workflow/checkout_step_generator.go @@ -11,6 +11,7 @@ import ( // If the repository already ends with ".wiki" it is returned unchanged to prevent double-suffixing. func wikiRepository(repository string) string { if repository == "" { + checkoutManagerLog.Print("Wiki checkout using default current repository") return "${{ github.repository }}.wiki" } if strings.HasSuffix(repository, ".wiki") { @@ -27,6 +28,7 @@ func wikiRepository(repository string) string { // The step ID for each checkout is "checkout-app-token-{index}" where index is // the position in the ordered checkout list. func (cm *CheckoutManager) GenerateCheckoutAppTokenSteps(c *Compiler, permissions *Permissions) []string { + checkoutManagerLog.Printf("Building app token minting steps for %d checkout entries", len(cm.ordered)) var steps []string for i, entry := range cm.ordered { if entry.githubApp == nil { @@ -54,6 +56,7 @@ func (cm *CheckoutManager) GenerateCheckoutAppTokenSteps(c *Compiler, permission // The tokens were minted in the agent job and are referenced via // steps.checkout-app-token-{index}.outputs.token. func (cm *CheckoutManager) GenerateCheckoutAppTokenInvalidationSteps(c *Compiler) []string { + checkoutManagerLog.Printf("Building app token invalidation steps for %d checkout entries", len(cm.ordered)) var steps []string for i, entry := range cm.ordered { if entry.githubApp == nil { @@ -320,8 +323,10 @@ func checkoutStepName(key checkoutKey) string { func fetchRefToRefspec(pattern string) string { switch pattern { case "*": + checkoutManagerLog.Print("Fetch refspec: wildcard expanded to all branches") return "+refs/heads/*:refs/remotes/origin/*" case "refs/pulls/open/*": + checkoutManagerLog.Print("Fetch refspec: open PRs pattern expanded") return "+refs/pull/*/head:refs/remotes/origin/pull/*/head" default: // Treat as branch name or glob: map to remote tracking ref diff --git a/pkg/workflow/mcp_renderer_github.go b/pkg/workflow/mcp_renderer_github.go index 5971fead2b..2c8028ac5d 100644 --- a/pkg/workflow/mcp_renderer_github.go +++ b/pkg/workflow/mcp_renderer_github.go @@ -47,6 +47,7 @@ func (r *MCPConfigRendererUnified) RenderGitHubMCP(yaml *strings.Builder, github githubType, readOnly, lockdown, hasGitHubLockdownExplicitlySet(githubTool), shouldUseStepOutputForGuardPolicy, toolsets, r.options.Format) if r.options.Format == "toml" { + mcpRendererLog.Print("GitHub MCP format=toml, dispatching to renderGitHubTOML") r.renderGitHubTOML(yaml, githubTool, workflowData) return } @@ -55,6 +56,7 @@ func (r *MCPConfigRendererUnified) RenderGitHubMCP(yaml *strings.Builder, github // Check if remote mode is enabled (type: remote) if githubType == "remote" { + mcpRendererLog.Printf("GitHub MCP remote mode selected: copilot_fields=%t", r.options.IncludeCopilotFields) // Determine authorization value based on engine requirements // Copilot uses MCP passthrough syntax: "Bearer \${GITHUB_PERSONAL_ACCESS_TOKEN}" // Other engines use shell variable: "Bearer $GITHUB_MCP_SERVER_TOKEN" @@ -80,6 +82,8 @@ func (r *MCPConfigRendererUnified) RenderGitHubMCP(yaml *strings.Builder, github githubDockerImageVersion := getGitHubDockerImageVersion(githubTool) customArgs := getGitHubCustomArgs(githubTool) + mcpRendererLog.Printf("GitHub MCP local docker mode: image_version=%s, custom_args=%d", githubDockerImageVersion, len(customArgs)) + RenderGitHubMCPDockerConfig(yaml, GitHubMCPDockerOptions{ ReadOnly: readOnly, Lockdown: lockdown, @@ -149,6 +153,7 @@ func (r *MCPConfigRendererUnified) renderGitHubTOML(yaml *strings.Builder, githu // Check if remote mode is enabled if githubType == "remote" { + mcpRendererLog.Printf("GitHub MCP TOML remote mode: readonly_endpoint=%t", readOnly) // Remote mode - use hosted GitHub MCP server with streamable HTTP // Use readonly endpoint if read-only mode is enabled if readOnly { diff --git a/pkg/workflow/side_repo_maintenance.go b/pkg/workflow/side_repo_maintenance.go index 16f405bf5d..4e16ecc087 100644 --- a/pkg/workflow/side_repo_maintenance.go +++ b/pkg/workflow/side_repo_maintenance.go @@ -38,6 +38,7 @@ type SideRepoTarget struct { // preferred over an empty one so that the generated workflow uses the custom // token rather than falling back to GH_AW_GITHUB_TOKEN. func collectSideRepoTargets(workflowDataList []*WorkflowData) []SideRepoTarget { + maintenanceLog.Printf("Scanning %d workflows for side-repo targets", len(workflowDataList)) // Use a map to accumulate the best token seen for each slug. // Order slice preserves first-seen repository discovery order for stable output; // tokens may be upgraded to non-empty values from later occurrences. @@ -99,6 +100,7 @@ func generateAllSideRepoMaintenanceWorkflows( minExpiresDays int, ) error { targets := collectSideRepoTargets(workflowDataList) + maintenanceLog.Printf("Generating maintenance workflows for %d side-repo target(s): hasExpires=%t, minExpiresDays=%d", len(targets), hasExpires, minExpiresDays) // Track which side-repo maintenance files we (re-)generate so we can identify // and remove stale files from previous runs when target repos are renamed or removed. @@ -163,6 +165,7 @@ func generateSideRepoMaintenanceWorkflow( ) error { token := effectiveSideRepoToken(target) repoSlug := target.Repository + maintenanceLog.Printf("Building side-repo workflow content: repo=%s, actionMode=%s, hasExpires=%t", repoSlug, actionMode, hasExpires) var yaml strings.Builder @@ -243,6 +246,7 @@ jobs: // Add close-expired-entities job only when any workflow uses expires. if hasExpires { + maintenanceLog.Printf("Including close-expired-entities job for %s (cron=%s)", repoSlug, cronSchedule) closeExpiredCondition := buildNotForkAndScheduled() yaml.WriteString(` close-expired-entities: if: ${{ ` + RenderCondition(closeExpiredCondition) + ` }} diff --git a/pkg/workflow/strict_mode_network_validation.go b/pkg/workflow/strict_mode_network_validation.go index 4d883b0219..a01cc5679f 100644 --- a/pkg/workflow/strict_mode_network_validation.go +++ b/pkg/workflow/strict_mode_network_validation.go @@ -45,16 +45,19 @@ func (c *Compiler) validateStrictMCPNetwork(frontmatter map[string]any, networkP // Check mcp-servers section (new format) mcpServersValue, exists := frontmatter["mcp-servers"] if !exists { + strictModeValidationLog.Print("No mcp-servers section, skipping MCP network validation") return nil } mcpServersMap, ok := mcpServersValue.(map[string]any) if !ok { + strictModeValidationLog.Print("mcp-servers is not a map, skipping MCP network validation") return nil } // Check if top-level network configuration exists hasTopLevelNetwork := networkPermissions != nil && len(networkPermissions.Allowed) > 0 + strictModeValidationLog.Printf("Checking %d MCP servers for container network requirements: hasTopLevelNetwork=%t", len(mcpServersMap), hasTopLevelNetwork) // Check each MCP server for containers for serverName, serverValue := range mcpServersMap { @@ -88,17 +91,20 @@ func (c *Compiler) validateStrictTools(frontmatter map[string]any) error { // Check tools section toolsValue, exists := frontmatter["tools"] if !exists { + strictModeValidationLog.Print("No tools section, skipping strict tools validation") return nil } toolsMap, ok := toolsValue.(map[string]any) if !ok { + strictModeValidationLog.Print("tools is not a map, skipping strict tools validation") return nil } // Check if cache-memory is configured with scope: repo cacheMemoryValue, hasCacheMemory := toolsMap["cache-memory"] if hasCacheMemory { + strictModeValidationLog.Print("Checking cache-memory scope in strict mode") // Helper function to check scope in a cache entry checkScope := func(cacheMap map[string]any) error { if scope, hasScope := cacheMap["scope"]; hasScope { diff --git a/pkg/workflow/strict_mode_steps_validation.go b/pkg/workflow/strict_mode_steps_validation.go index 964825c550..7e21e7d98f 100644 --- a/pkg/workflow/strict_mode_steps_validation.go +++ b/pkg/workflow/strict_mode_steps_validation.go @@ -32,6 +32,7 @@ import ( // in other fields (run, etc.) are errors. // In non-strict mode a warning is emitted for all secrets. func (c *Compiler) validateStepsSecrets(frontmatter map[string]any) error { + strictModeValidationLog.Printf("Validating secrets across steps sections: strictMode=%t", c.strictMode) for _, sectionName := range []string{"pre-steps", "steps", "pre-agent-steps", "post-steps"} { if err := c.validateStepsSectionSecrets(frontmatter, sectionName); err != nil { return err @@ -62,6 +63,7 @@ func (c *Compiler) validateStepsSectionSecrets(frontmatter map[string]any, secti // Separate secrets found in safe bindings (env: maps, with: maps in uses: // action steps) from secrets found in other fields (unsafe, potential leak). + strictModeValidationLog.Printf("Classifying secrets in %s section: %d step(s)", sectionName, len(steps)) var unsafeSecretRefs []string var safeSecretRefs []string for _, step := range steps { @@ -107,6 +109,7 @@ func (c *Compiler) validateStepsSectionSecrets(frontmatter map[string]any, secti // Non-strict mode: emit a warning for all secrets. allSecretRefs = sliceutil.Deduplicate(allSecretRefs) sort.Strings(allSecretRefs) + strictModeValidationLog.Printf("Emitting non-strict warning for %d unique secret reference(s) in %s section", len(allSecretRefs), sectionName) warningMsg := fmt.Sprintf( "Warning: secrets expressions detected in '%s' section may be leaked to the agent job. Found: %s. "+ "Consider moving operations requiring secrets to a separate job outside the agent job.",