Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/workflow/checkout_step_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions pkg/workflow/mcp_renderer_github.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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"
Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions pkg/workflow/side_repo_maintenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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) + ` }}
Expand Down
6 changes: 6 additions & 0 deletions pkg/workflow/strict_mode_network_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions pkg/workflow/strict_mode_steps_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.",
Expand Down