From ff013deb96a927f67ecea3ece019c29fd31f3d6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 04:29:11 +0000 Subject: [PATCH 1/3] Initial plan From 649971bbc5743721eacef50d2886ced3304fd029 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 04:43:52 +0000 Subject: [PATCH 2/3] fix: cross-repo create-pull-request checkout uses safe ref without github.ref_name When target-repo is set on create-pull-request, the safe_outputs job checkout step now uses a cross-repo-safe ref expression that omits github.ref_name. This prevents checkout failures when the workflow is dispatched from a branch that does not exist in the target repository. The new fallback for cross-repo checkouts is: ${{ github.base_ref || github.event.pull_request.base.ref || github.event.repository.default_branch }} The original expression (including github.ref_name) is still used when no target-repo is configured (same-repo case). Closes # Agent-Logs-Url: https://github.com/github/gh-aw/sessions/69ba08e1-f5ca-4ff6-a3f0-6174bd25a2c2 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../patch-fix-cross-repo-checkout-ref.md | 5 ++ .../smoke-create-cross-repo-pr.lock.yml | 2 +- pkg/workflow/compiler_safe_outputs_steps.go | 7 +++ .../compiler_safe_outputs_steps_test.go | 63 +++++++++++++++++-- 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 .changeset/patch-fix-cross-repo-checkout-ref.md diff --git a/.changeset/patch-fix-cross-repo-checkout-ref.md b/.changeset/patch-fix-cross-repo-checkout-ref.md new file mode 100644 index 0000000000..98462f60f6 --- /dev/null +++ b/.changeset/patch-fix-cross-repo-checkout-ref.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Fixed `create-pull-request` with `target-repo`: the `safe_outputs` checkout step now uses a cross-repo-safe ref expression (omitting `github.ref_name`) to avoid failures when the triggering branch does not exist in the target repository. diff --git a/.github/workflows/smoke-create-cross-repo-pr.lock.yml b/.github/workflows/smoke-create-cross-repo-pr.lock.yml index 2fed407c03..b3af8e0654 100644 --- a/.github/workflows/smoke-create-cross-repo-pr.lock.yml +++ b/.github/workflows/smoke-create-cross-repo-pr.lock.yml @@ -1455,7 +1455,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: github/gh-aw-side-repo - ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} + ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.event.repository.default_branch }} token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} persist-credentials: false fetch-depth: 1 diff --git a/pkg/workflow/compiler_safe_outputs_steps.go b/pkg/workflow/compiler_safe_outputs_steps.go index f219bf0d65..9211e14f1f 100644 --- a/pkg/workflow/compiler_safe_outputs_steps.go +++ b/pkg/workflow/compiler_safe_outputs_steps.go @@ -72,10 +72,17 @@ func (c *Compiler) buildSharedPRCheckoutSteps(data *WorkflowData) []string { // targeting.  Then a lot of this gnarly event code will be only on the "front end" (prepping the // coding agent) not the "backend" (applying the safe outputs) const baseBranchFallbackExpr = "${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}" + // Cross-repo fallback omits github.ref_name to avoid checking out a branch that only exists in the triggering repo. + const crossRepoFallbackExpr = "${{ github.base_ref || github.event.pull_request.base.ref || github.event.repository.default_branch }}" var checkoutRef string if data.SafeOutputs.CreatePullRequests != nil && data.SafeOutputs.CreatePullRequests.BaseBranch != "" { checkoutRef = data.SafeOutputs.CreatePullRequests.BaseBranch consolidatedSafeOutputsStepsLog.Printf("Using custom base-branch from create-pull-request for checkout ref: %s", checkoutRef) + } else if targetRepoSlug != "" { + // Cross-repo checkout: avoid github.ref_name which refers to the triggering branch, + // not a branch in the target repository. + checkoutRef = crossRepoFallbackExpr + consolidatedSafeOutputsStepsLog.Printf("Using cross-repo fallback base branch expression for checkout ref (no github.ref_name)") } else { checkoutRef = baseBranchFallbackExpr consolidatedSafeOutputsStepsLog.Printf("Using fallback base branch expression for checkout ref") diff --git a/pkg/workflow/compiler_safe_outputs_steps_test.go b/pkg/workflow/compiler_safe_outputs_steps_test.go index 9b280aecbc..212cc50ab0 100644 --- a/pkg/workflow/compiler_safe_outputs_steps_test.go +++ b/pkg/workflow/compiler_safe_outputs_steps_test.go @@ -13,11 +13,12 @@ import ( // TestBuildSharedPRCheckoutSteps tests shared PR checkout step generation func TestBuildSharedPRCheckoutSteps(t *testing.T) { tests := []struct { - name string - safeOutputs *SafeOutputsConfig - trialMode bool - trialRepo string - checkContains []string + name string + safeOutputs *SafeOutputsConfig + trialMode bool + trialRepo string + checkContains []string + checkNotContains []string }{ { name: "create pull request only", @@ -120,6 +121,54 @@ func TestBuildSharedPRCheckoutSteps(t *testing.T) { "token: ${{ secrets.GH_AW_CROSS_REPO_PAT }}", "GIT_TOKEN: ${{ secrets.GH_AW_CROSS_REPO_PAT }}", `REPO_NAME: "org/target-repo"`, + // Cross-repo checkout must not use github.ref_name + "ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.event.repository.default_branch }}", + }, + }, + { + name: "cross-repo without base-branch omits github.ref_name from checkout ref", + safeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + TargetRepoSlug: "org/other-repo", + }, + }, + checkContains: []string{ + "ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.event.repository.default_branch }}", + }, + }, + { + name: "cross-repo without base-branch does not include github.ref_name in checkout ref", + safeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + TargetRepoSlug: "org/other-repo", + }, + }, + checkNotContains: []string{ + "github.ref_name", + }, + }, + { + name: "trial mode cross-repo omits github.ref_name from checkout ref", + trialMode: true, + trialRepo: "org/trial-repo", + safeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{}, + }, + checkContains: []string{ + "repository: org/trial-repo", + "ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.event.repository.default_branch }}", + }, + }, + { + name: "cross-repo with explicit base-branch uses base-branch not cross-repo fallback", + safeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + TargetRepoSlug: "org/other-repo", + BaseBranch: "develop", + }, + }, + checkContains: []string{ + "ref: develop", }, }, { @@ -212,6 +261,10 @@ func TestBuildSharedPRCheckoutSteps(t *testing.T) { for _, expected := range tt.checkContains { assert.Contains(t, stepsContent, expected, "Expected to find: "+expected) } + + for _, notExpected := range tt.checkNotContains { + assert.NotContains(t, stepsContent, notExpected, "Expected NOT to find: "+notExpected) + } }) } } From 4f98142da631f9a950ccb9305ad10dd61d98095f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 04:47:54 +0000 Subject: [PATCH 3/3] refactor: consolidate duplicate cross-repo checkout ref test cases and improve comment Agent-Logs-Url: https://github.com/github/gh-aw/sessions/69ba08e1-f5ca-4ff6-a3f0-6174bd25a2c2 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_safe_outputs_steps.go | 3 ++- pkg/workflow/compiler_safe_outputs_steps_test.go | 10 +--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/pkg/workflow/compiler_safe_outputs_steps.go b/pkg/workflow/compiler_safe_outputs_steps.go index 9211e14f1f..d6fc874c55 100644 --- a/pkg/workflow/compiler_safe_outputs_steps.go +++ b/pkg/workflow/compiler_safe_outputs_steps.go @@ -72,7 +72,8 @@ func (c *Compiler) buildSharedPRCheckoutSteps(data *WorkflowData) []string { // targeting.  Then a lot of this gnarly event code will be only on the "front end" (prepping the // coding agent) not the "backend" (applying the safe outputs) const baseBranchFallbackExpr = "${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}" - // Cross-repo fallback omits github.ref_name to avoid checking out a branch that only exists in the triggering repo. + // Cross-repo fallback omits github.ref_name because it refers to the branch in the triggering repository, + // which may not exist in the target repository (e.g., when triggered via workflow_dispatch from a feature branch). const crossRepoFallbackExpr = "${{ github.base_ref || github.event.pull_request.base.ref || github.event.repository.default_branch }}" var checkoutRef string if data.SafeOutputs.CreatePullRequests != nil && data.SafeOutputs.CreatePullRequests.BaseBranch != "" { diff --git a/pkg/workflow/compiler_safe_outputs_steps_test.go b/pkg/workflow/compiler_safe_outputs_steps_test.go index 212cc50ab0..ac03a2e58b 100644 --- a/pkg/workflow/compiler_safe_outputs_steps_test.go +++ b/pkg/workflow/compiler_safe_outputs_steps_test.go @@ -126,7 +126,7 @@ func TestBuildSharedPRCheckoutSteps(t *testing.T) { }, }, { - name: "cross-repo without base-branch omits github.ref_name from checkout ref", + name: "cross-repo without base-branch uses safe ref omitting github.ref_name", safeOutputs: &SafeOutputsConfig{ CreatePullRequests: &CreatePullRequestsConfig{ TargetRepoSlug: "org/other-repo", @@ -135,14 +135,6 @@ func TestBuildSharedPRCheckoutSteps(t *testing.T) { checkContains: []string{ "ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.event.repository.default_branch }}", }, - }, - { - name: "cross-repo without base-branch does not include github.ref_name in checkout ref", - safeOutputs: &SafeOutputsConfig{ - CreatePullRequests: &CreatePullRequestsConfig{ - TargetRepoSlug: "org/other-repo", - }, - }, checkNotContains: []string{ "github.ref_name", },