diff --git a/actions/setup/md/safe_outputs_push_to_pr_branch.md b/actions/setup/md/safe_outputs_push_to_pr_branch.md index 6960d764128..b554e29c520 100644 --- a/actions/setup/md/safe_outputs_push_to_pr_branch.md +++ b/actions/setup/md/safe_outputs_push_to_pr_branch.md @@ -6,6 +6,10 @@ To push changes to the branch of a pull request: 2. Add and commit your changes to the local copy of the pull request branch. Be careful to add exactly the files you intend, and verify you haven't deleted or changed any files you didn't intend to. 3. Push the branch to the repo by using the push_to_pull_request_branch tool from safeoutputs. +**Multi-checkout workflows (`checkout:` with multiple repositories):** +- `push_to_pull_request_branch` operates on the checkout for the target repository (the directory matching the `path:` value in your workflow's checkout step). +- Run all `git` commands from that checkout directory before calling the tool. Use a subshell (`(cd && git ...)`) or `pushd`/`popd` to avoid changing the working directory for subsequent commands in the same step. +- If needed, check out the PR branch locally from `origin/` first. **Important constraints:** - This tool is **append-only**: it adds new commits on top of the existing PR branch. Force-push is NOT supported. - Do NOT use `git merge` to bring another branch (e.g., `main`) into the PR branch — merge commits cannot be signed; the action will attempt to squash them into a single linear commit before pushing, but this rewrites history. Use `git rebase` instead (e.g., `git rebase origin/main`) to avoid the rewrite. diff --git a/pkg/workflow/unified_prompt_step_test.go b/pkg/workflow/unified_prompt_step_test.go index 519d6260a11..a85c7228cf1 100644 --- a/pkg/workflow/unified_prompt_step_test.go +++ b/pkg/workflow/unified_prompt_step_test.go @@ -523,3 +523,86 @@ func TestCollectPromptSections_CliProxy(t *testing.T) { } }) } + +func TestCollectPromptSections_PRCommentPushToPRBranchGuidance(t *testing.T) { + compiler := &Compiler{} + + t.Run("includes guidance when PR comment triggers and push-to-pr-branch is configured", func(t *testing.T) { + data := &WorkflowData{ + On: "issue_comment", + Permissions: "contents: read", + SafeOutputs: &SafeOutputsConfig{ + PushToPullRequestBranch: &PushToPullRequestBranchConfig{}, + }, + } + + sections := compiler.collectPromptSections(data) + + var guidanceSection *PromptSection + for i := range sections { + if sections[i].IsFile && sections[i].Content == prContextPushToPRBranchGuidanceFile { + guidanceSection = §ions[i] + break + } + } + + require.NotNil(t, guidanceSection, "Should include push-to-PR-branch guidance for PR comment workflows") + assert.NotEmpty(t, guidanceSection.ShellCondition, "Guidance should be conditionally injected for PR-comment events") + assert.Contains(t, guidanceSection.ShellCondition, "issue_comment") + assert.Equal(t, "${{ github.event.issue.pull_request && 'true' || '' }}", guidanceSection.EnvVars["GH_AW_IS_PR_COMMENT"]) + }) + + t.Run("includes guidance when pull_request_review_comment triggers and push-to-pr-branch is configured", func(t *testing.T) { + data := &WorkflowData{ + On: "pull_request_review_comment", + Permissions: "contents: read", + SafeOutputs: &SafeOutputsConfig{ + PushToPullRequestBranch: &PushToPullRequestBranchConfig{}, + }, + } + + sections := compiler.collectPromptSections(data) + + var guidanceSection *PromptSection + for i := range sections { + if sections[i].IsFile && sections[i].Content == prContextPushToPRBranchGuidanceFile { + guidanceSection = §ions[i] + break + } + } + + require.NotNil(t, guidanceSection, "Should include push-to-PR-branch guidance for pull_request_review_comment workflows") + assert.NotEmpty(t, guidanceSection.ShellCondition, "Guidance should be conditionally injected for PR-comment events") + assert.Contains(t, guidanceSection.ShellCondition, "pull_request_review_comment") + }) + + t.Run("does not include guidance when push-to-pr-branch is not configured", func(t *testing.T) { + data := &WorkflowData{ + On: "issue_comment", + Permissions: "contents: read", + SafeOutputs: &SafeOutputsConfig{}, + } + + sections := compiler.collectPromptSections(data) + + for _, section := range sections { + assert.NotEqual(t, prContextPushToPRBranchGuidanceFile, section.Content, + "Should not include push-to-PR-branch guidance unless tool is configured") + } + }) + + t.Run("does not include guidance when SafeOutputs is nil", func(t *testing.T) { + data := &WorkflowData{ + On: "issue_comment", + Permissions: "contents: read", + SafeOutputs: nil, + } + + sections := compiler.collectPromptSections(data) + + for _, section := range sections { + assert.NotEqual(t, prContextPushToPRBranchGuidanceFile, section.Content, + "Should not include push-to-PR-branch guidance when SafeOutputs is nil") + } + }) +}