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
7 changes: 7 additions & 0 deletions pkg/cli/bootstrap_profile_github_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@ import (

"charm.land/huh/v2"
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/repoutil"
"github.com/github/gh-aw/pkg/workflow"
)

var githubAppBootstrapLog = logger.New("cli:bootstrap_profile_github_app")

func runBootstrapGitHubAppAction(ctx context.Context, repo string, action repositoryPackageBootstrapAction, state *bootstrapProfileExistingState) (*bootstrapCreatedGitHubApp, error) {
_, hasVar := state.variables[action.AppIDVariable]
_, hasSecret := state.secrets[action.PrivateKeySecret]
if hasVar && hasSecret {
githubAppBootstrapLog.Printf("GitHub App already configured: appIDVar=%s privateKeySecret=%s, skipping", action.AppIDVariable, action.PrivateKeySecret)
return nil, nil
}
githubAppBootstrapLog.Printf("Running GitHub App bootstrap action: repo=%s appIDVar=%s", repo, action.AppIDVariable)

overrides, err := loadBootstrapGitHubAppOverrides()
if err != nil {
Expand Down Expand Up @@ -88,6 +93,7 @@ func handleBootstrapGitHubAppExistingFlow(ctx context.Context, repo string, acti
if clientID == "" && privateKey == "" && action.Mode != "existing" && overrides.Mode != "existing" {
return false, nil
}
githubAppBootstrapLog.Printf("Applying existing GitHub App credentials: repo=%s hasClientID=%v hasPrivateKey=%v", repo, clientID != "", privateKey != "")
resolvedClientID, resolvedPrivateKey, err := completeExistingGitHubAppCredentials(clientID, privateKey, action, repo)
if err != nil {
return false, err
Expand Down Expand Up @@ -389,6 +395,7 @@ func waitForBootstrapGitHubAppInstallation(ctx context.Context, repo string, cre
if createdApp == nil || createdApp.InstallURL == "" || createdApp.Slug == "" {
return nil
}
githubAppBootstrapLog.Printf("Waiting for GitHub App installation: repo=%s slug=%s installURL=%s", repo, createdApp.Slug, createdApp.InstallURL)
bootstrapLog.Printf("Polling for GitHub App installation: repo=%s, slug=%s", repo, createdApp.Slug)
deadlineTimer := time.NewTimer(bootstrapProfileManifestTimeout)
defer deadlineTimer.Stop()
Expand Down
14 changes: 13 additions & 1 deletion pkg/intent/policy.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package intent

import "slices"
import (
"slices"

"github.com/github/gh-aw/pkg/logger"
)

var policyLog = logger.New("intent:policy")

// autonomyRank maps autonomy levels to a restriction rank (higher = more restrictive).
// propose_only is the most restrictive (agents may only propose changes, not execute);
Expand Down Expand Up @@ -98,9 +104,11 @@ type PolicyCompiler struct {
// rules are merged with stricter-wins semantics. If no rules match, the safest
// default policy is returned.
func (c PolicyCompiler) Compile(rec IntentRecord, repo RepositoryContext) ExecutionPolicy {
policyLog.Printf("Compiling policy: status=%s rules=%d", rec.Status, len(c.Rules))
// Fail-closed for indeterminate statuses: unlinked and ambiguous records
// must never receive a relaxed policy from a matching wildcard rule.
if rec.Status == AttributionUnlinked || rec.Status == AttributionAmbiguous {
policyLog.Printf("Fail-closed for indeterminate status=%s, returning safest default policy", rec.Status)
return safestDefaultPolicy()
}

Expand All @@ -118,14 +126,18 @@ func (c PolicyCompiler) Compile(rec IntentRecord, repo RepositoryContext) Execut
accumulated = deepCopyPolicy(rule.Set)
accumulated.RuleIDs = []string{rule.ID}
matched = true
policyLog.Printf("First matching rule: id=%s autonomy=%s write_scope=%s", rule.ID, rule.Set.Autonomy, rule.Set.WriteScope)
} else {
accumulated = mergePolicy(accumulated, rule.Set)
accumulated.RuleIDs = append(accumulated.RuleIDs, rule.ID)
policyLog.Printf("Merging additional rule: id=%s", rule.ID)
}
}
if !matched {
policyLog.Print("No rules matched, returning safest default policy")
return safestDefaultPolicy()
}
policyLog.Printf("Compiled policy: autonomy=%s write_scope=%s human_approval=%v matched_rules=%d", accumulated.Autonomy, accumulated.WriteScope, accumulated.HumanApprovalRequired, len(accumulated.RuleIDs))
return accumulated
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/workflow/copilot_inline_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import (
"strings"

"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/logger"
)

var inlineDriverLog = logger.New("workflow:copilot_inline_driver")

const (
inlineCopilotSDKDriverDir = ".gh-aw/copilot-sdk"
inlineCopilotSDKDriverWrapperPath = inlineCopilotSDKDriverDir + "/inline-driver"
Expand Down Expand Up @@ -45,6 +48,7 @@ func (d *InlineEngineDriver) wrapperScript() string {
return ""
}

inlineDriverLog.Printf("Generating wrapper script for runtime=%s", d.Runtime)
sourcePath := d.sourcePath()
switch d.Runtime {
case "node":
Expand Down Expand Up @@ -121,6 +125,7 @@ func buildInlineCopilotSDKDriverWriteStep(workflowData *WorkflowData) GitHubActi
inlineDriver := workflowData.EngineConfig.InlineDriver
sourcePath := inlineDriver.sourcePath()
if sourcePath == "" {
inlineDriverLog.Printf("No source path for inline driver runtime=%s, skipping write step", inlineDriver.Runtime)
return GitHubActionStep{}
}

Expand All @@ -133,6 +138,7 @@ func buildInlineCopilotSDKDriverWriteStep(workflowData *WorkflowData) GitHubActi
workflowData.ParsedFrontmatter.RuntimesTyped.Go.Version != "" {
goVersion = workflowData.ParsedFrontmatter.RuntimesTyped.Go.Version
}
inlineDriverLog.Printf("Building inline Copilot SDK driver write step: runtime=%s source=%s goVersion=%s", inlineDriver.Runtime, sourcePath, goVersion)

step := GitHubActionStep{
" - name: Write Inline Copilot SDK Driver",
Expand Down
6 changes: 6 additions & 0 deletions pkg/workflow/mcp_setup_safe_outputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"fmt"
"strings"

"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/sliceutil"
)

var safeOutputsSetupLog = logger.New("workflow:mcp_setup_safe_outputs")

// safeOutputsSecretEnvPrefix is prepended to secret names when generating step env var names for
// safe-outputs config placeholders. The prefix avoids accidental collisions between a workflow
// secret name and a pre-existing step env var (e.g. a secret named DEBUG or
Expand All @@ -20,9 +23,11 @@ func generateSafeOutputsSetup(c *Compiler, yaml *strings.Builder, safeOutputConf
if !HasSafeOutputsEnabled(workflowData.SafeOutputs) {
return
}
safeOutputsSetupLog.Printf("Generating safe outputs setup: configLen=%d", len(safeOutputConfig))
yaml.WriteString(" - name: Generate Safe Outputs Config\n")
sanitizedConfig, envKeys, envValues := buildSafeOutputsConfigRuntimeData(safeOutputConfig)
if len(envKeys) > 0 {
safeOutputsSetupLog.Printf("Safe outputs config: envVars=%d", len(envKeys))
yaml.WriteString(" env:\n")
writeStepEnvVars(yaml, envKeys, envValues)
}
Expand Down Expand Up @@ -111,6 +116,7 @@ func buildSafeOutputsConfigRuntimeEnvVars(safeOutputConfig string) ([]string, ma
func buildSafeOutputsConfigRuntimeData(safeOutputConfig string) (string, []string, map[string]string) {
sanitizedConfig := safeOutputConfig
envKeys, envValues := buildSafeOutputsConfigRuntimeEnvVars(safeOutputConfig)
safeOutputsSetupLog.Printf("Building safe outputs config runtime data: envKeys=%d", len(envKeys))
for _, varName := range envKeys {
value := envValues[varName]
sanitizedConfig = strings.ReplaceAll(sanitizedConfig, value, "${"+varName+"}")
Expand Down
8 changes: 8 additions & 0 deletions pkg/workflow/permissions_compiler_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ import (
"fmt"
"os"
"strings"

"github.com/github/gh-aw/pkg/logger"
)

var permissionsCompilerLog = logger.New("workflow:permissions_compiler_validator")

// validatePermissions validates all permission-related configuration: dangerous
// permissions, GitHub App-only constraints, MCP app write restrictions, workflow_run
// branch security, GitHub MCP toolset permissions, and the id-token write warning.
Expand Down Expand Up @@ -204,6 +208,8 @@ func (c *Compiler) repositoryOwnerIsIndividualUser() bool {
ownerType = strings.TrimSpace(string(output))
c.ownerTypeCache[owner] = ownerType
workflowLog.Printf("Owner type for %q: %s", owner, ownerType)
} else {
permissionsCompilerLog.Printf("Owner type cache hit: owner=%s type=%q", owner, ownerType)
}
return ownerType == "User"
}
Expand Down Expand Up @@ -243,6 +249,8 @@ func validateOIDCPermissions(workflowData *WorkflowData, workflowPermissions *Pe
return nil
}

permissionsCompilerLog.Printf("OIDC permission check: requiresIDTokenWrite=true prefix=%q", errorPrefix)

if workflowPermissions == nil {
return errors.New(errorPrefix + " requires permissions.id-token: write")
}
Expand Down