Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
39 changes: 37 additions & 2 deletions pkg/workflow/compiler_safe_outputs_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,14 @@ func (c *Compiler) buildSharedPRCheckoutSteps(data *WorkflowData) []string {
consolidatedSafeOutputsStepsLog.Print("Building shared PR checkout steps")
var steps []string
fetchDepth := 1
var sparsePatterns []string

if defaultCheckout := NewCheckoutManager(data.CheckoutConfigs).GetDefaultCheckoutOverride(); defaultCheckout != nil && defaultCheckout.fetchDepth != nil {
// Build a single CheckoutManager so we can query both the default and cross-repo entries.
checkoutMgr := NewCheckoutManager(data.CheckoutConfigs)

// Same-repo fallback: use the default (workspace-root) checkout's fetch-depth.
// Overridden below for cross-repo targets once targetRepoSlug is known.
if defaultCheckout := checkoutMgr.GetDefaultCheckoutOverride(); defaultCheckout != nil && defaultCheckout.fetchDepth != nil {
fetchDepth = *defaultCheckout.fetchDepth
consolidatedSafeOutputsStepsLog.Printf("Using custom checkout fetch-depth for safe_outputs: %d", fetchDepth)
Comment thread
dsyme marked this conversation as resolved.
Outdated
}
Expand Down Expand Up @@ -90,6 +96,24 @@ func (c *Compiler) buildSharedPRCheckoutSteps(data *WorkflowData) []string {
consolidatedSafeOutputsStepsLog.Printf("Using trialLogicalRepoSlug: %s", targetRepoSlug)
}

// For cross-repo targets, override fetch-depth and sparse-checkout patterns
// from the checkout: config entry that targets the same repository. The agent
// job already uses these values; the safe_outputs job must mirror them so that
// (a) large repos are not checked out in full unnecessarily and (b) the working
// tree is consistent with what the agent operated on.
if targetRepoSlug != "" {
if targetEntry := checkoutMgr.GetCheckoutForRepository(targetRepoSlug); targetEntry != nil {
if targetEntry.fetchDepth != nil {
fetchDepth = *targetEntry.fetchDepth
consolidatedSafeOutputsStepsLog.Printf("Using checkout fetch-depth for cross-repo target %s: %d", targetRepoSlug, fetchDepth)
}
if len(targetEntry.sparsePatterns) > 0 {
sparsePatterns = targetEntry.sparsePatterns
consolidatedSafeOutputsStepsLog.Printf("Using %d sparse-checkout pattern(s) for cross-repo target %s", len(sparsePatterns), targetRepoSlug)
}
}
}

// Determine the ref (branch) to checkout
// Priority: create-pull-request base-branch > extracted base-branch from agent output > fallback expression
// This is critical: we must checkout the base branch, not github.sha (the triggering commit),
Expand Down Expand Up @@ -149,6 +173,12 @@ func (c *Compiler) buildSharedPRCheckoutSteps(data *WorkflowData) []string {
steps = append(steps, fmt.Sprintf(" token: %s\n", checkoutToken))
steps = append(steps, " persist-credentials: false\n")
steps = append(steps, fmt.Sprintf(" fetch-depth: %d\n", fetchDepth))
if len(sparsePatterns) > 0 {
steps = append(steps, " sparse-checkout: |\n")
for _, pattern := range sparsePatterns {
steps = append(steps, fmt.Sprintf(" %s\n", strings.TrimSpace(pattern)))
}
}
}

// Step 1b: Checkout repository with conditional execution
Expand All @@ -172,6 +202,12 @@ func (c *Compiler) buildSharedPRCheckoutSteps(data *WorkflowData) []string {
steps = append(steps, fmt.Sprintf(" token: %s\n", checkoutToken))
steps = append(steps, " persist-credentials: false\n")
steps = append(steps, fmt.Sprintf(" fetch-depth: %d\n", fetchDepth))
if len(sparsePatterns) > 0 {
steps = append(steps, " sparse-checkout: |\n")
for _, pattern := range sparsePatterns {
steps = append(steps, fmt.Sprintf(" %s\n", strings.TrimSpace(pattern)))
}
}

// Step 2: Configure Git credentials with conditional execution
// Security: Pass GitHub token through environment variable to prevent template injection
Expand Down Expand Up @@ -207,7 +243,6 @@ func (c *Compiler) buildSharedPRCheckoutSteps(data *WorkflowData) []string {
// Without this, applyBundleToBranch must fall back to per-SHA git fetch (prerequisite
// recovery), which requires uploadpack.allowReachableSHA1InWant on the server.
if targetRepoSlug != "" {
checkoutMgr := NewCheckoutManager(data.CheckoutConfigs)
if matchedEntry := checkoutMgr.GetCheckoutForRepository(targetRepoSlug); matchedEntry != nil && len(matchedEntry.fetchRefs) > 0 {
consolidatedSafeOutputsStepsLog.Printf("Adding fetch refs step for cross-repo target %s (%d refs)", targetRepoSlug, len(matchedEntry.fetchRefs))
if fetchStep := buildSafeOutputsFetchRefsStep(targetRepoSlug, checkoutToken, matchedEntry.fetchRefs, RenderCondition(condition)); fetchStep != "" {
Expand Down
65 changes: 65 additions & 0 deletions pkg/workflow/compiler_safe_outputs_steps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,71 @@ func TestBuildSharedPRCheckoutSteps(t *testing.T) {
"contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')",
},
},
{
name: "cross-repo with sparse-checkout patterns propagates them to safe_outputs checkout",
safeOutputs: &SafeOutputsConfig{
CreatePullRequests: &CreatePullRequestsConfig{
BaseSafeOutputConfig: BaseSafeOutputConfig{
GitHubToken: "${{ secrets.CROSS_PAT }}",
},
TargetRepoSlug: "org/monorepo",
BaseBranch: "main",
},
},
checkoutConfigs: []*CheckoutConfig{
{
Repository: "org/monorepo",
SparseCheckout: ".github\nscripts\ntest",
},
},
checkContains: []string{
"sparse-checkout: |",
" .github",
" scripts",
" test",
},
},
{
name: "cross-repo without sparse-checkout does not emit sparse-checkout block",
safeOutputs: &SafeOutputsConfig{
CreatePullRequests: &CreatePullRequestsConfig{
TargetRepoSlug: "org/full-repo",
},
},
checkoutConfigs: []*CheckoutConfig{
{
Repository: "org/full-repo",
},
},
checkNotContains: []string{
"sparse-checkout:",
},
},
Comment thread
dsyme marked this conversation as resolved.
{
name: "cross-repo fetch-depth is read from checkout config for target repo, not default override",
safeOutputs: &SafeOutputsConfig{
CreatePullRequests: &CreatePullRequestsConfig{
TargetRepoSlug: "org/target",
},
},
checkoutConfigs: []*CheckoutConfig{
{
// The "default" checkout (empty repo/path) has a different fetch-depth.
// The cross-repo entry's fetch-depth should win for the safe_outputs checkout.
FetchDepth: func() *int { d := 0; return &d }(),
},
{
Repository: "org/target",
FetchDepth: func() *int { d := 10; return &d }(),
},
},
checkContains: []string{
"fetch-depth: 10",
},
checkNotContains: []string{
"fetch-depth: 0",
},
},
}

for _, tt := range tests {
Expand Down
Loading