-
Notifications
You must be signed in to change notification settings - Fork 327
Make create-pull-request auto-close issue behavior configurable #23736
Description
Summary
When a workflow is triggered from an issue, the create-pull-request safe output handler automatically appends - Fixes #<issue_number> to the PR description if no closing keyword (fixes, closes, resolves) is already present. This causes GitHub to auto-close the triggering issue when the PR is merged.
This behavior is intentional (added in #12823) but is not configurable. Workflows that create PRs related to an issue, but don't intend to fully resolve it, have no way to opt out.
Proposed Changes
Add a new auto-close-issue field to the create-pull-request safe-outputs frontmatter configuration, defaulting to true to preserve backward compatibility.
Frontmatter usage
safe-outputs:
create-pull-request:
auto-close-issue: false # Don't auto-add "Fixes #N" to PR descriptionWhat needs to change
1. Go config struct — pkg/workflow/create_pull_request.go
Add a new field to CreatePullRequestsConfig:
AutoCloseIssue *bool `yaml:"auto-close-issue,omitempty"` // Auto-add "Fixes #N" closing keyword when triggered from an issue (default: true)2. Compiler handler config serialization — wherever the handler config is built for the create_pull_request JS handler, pass the auto_close_issue value through to the runtime config object.
3. JavaScript handler — actions/setup/js/create_pull_request.cjs
Read the config value and gate the auto-append logic:
// ~line 116 area, with the other config reads
const autoCloseIssue = config.auto_close_issue !== false; // Default to true
// ~line 492-501, gate existing behavior
if (triggeringIssueNumber && autoCloseIssue) {
const hasClosingKeyword = /(?:fix|fixes|fixed|close|closes|closed|resolve|resolves|resolved)\s+#\d+/i.test(processedBody);
if (!hasClosingKeyword) {
processedBody = processedBody.trimEnd() + `\n\n- Fixes #${triggeringIssueNumber}`;
core.info(`Auto-added "Fixes #${triggeringIssueNumber}" closing keyword to PR body as bullet point`);
}
} else if (triggeringIssueNumber && !autoCloseIssue) {
core.info(`Skipping auto-close keyword for #${triggeringIssueNumber} (auto-close-issue: false)`);
}4. JSON schema — Update the create-pull-request schema in pkg/parser/schemas/ to document the new field.
5. Documentation — Update docs/src/content/docs/reference/safe-outputs.md to document auto-close-issue.
Why
The current behavior assumes that every PR created from an issue is intended to fully resolve that issue. In practice, several workflows don't fit this pattern:
- Partial work PRs — A workflow that creates a PR addressing part of an issue but not all of it. Merging the PR would prematurely close the issue.
- Investigation/analysis PRs — A workflow that creates a PR with findings or config changes related to an issue but the issue itself requires further action.
- Multi-PR workflows — A workflow that may spawn multiple PRs from a single issue. Only the final PR should close the issue.
- Draft/tracking PRs — PRs created for visibility or review that reference an issue but shouldn't close it on merge.
Today, the only workaround is to instruct the agent to include a closing keyword in the body (which skips the auto-append), but this is fragile — agents don't reliably follow instructions (which is the reason the auto-append was added in the first place), and there's no clean way to say "reference but don't close."
Test Plan
Unit tests (actions/setup/js/create_pull_request.test.cjs)
- Default behavior preserved — When
auto_close_issueis not set in config and the workflow is triggered from an issue, verify- Fixes #Nis appended to the PR body. - Explicit
true— Whenauto_close_issue: true, same behavior as default. - Explicit
false— Whenauto_close_issue: falseand the workflow is triggered from an issue, verify noFixes #Nis appended. falsewith existing closing keyword — Whenauto_close_issue: falseand the body already containsFixes #N, verify neither the existing keyword is removed nor a new one is added.- No triggering issue — When not triggered from an issue, verify the flag has no effect regardless of value.
Go tests (pkg/workflow/create_pull_request_test.go)
- Config parsing — Verify
auto-close-issue: falseis correctly parsed intoCreatePullRequestsConfig.AutoCloseIssue. - Config serialization — Verify the config value is passed through to the handler config JSON as
auto_close_issue. - Default omission — Verify the field is omitted from compiled output when not set (backward compatibility).
Integration/compilation tests
- Compile a workflow with
auto-close-issue: falseset and verify the compiled lock file includes the config value. - Compile a workflow without the field set and verify the compiled output is unchanged (no regression).
Schema validation
- Valid values —
true,falseaccepted. - Invalid values — String, number, etc. rejected by schema validation.