Skip to content

Make create-pull-request auto-close issue behavior configurable #23736

Description

@abillingsley

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 description

What 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)

  1. Default behavior preserved — When auto_close_issue is not set in config and the workflow is triggered from an issue, verify - Fixes #N is appended to the PR body.
  2. Explicit true — When auto_close_issue: true, same behavior as default.
  3. Explicit false — When auto_close_issue: false and the workflow is triggered from an issue, verify no Fixes #N is appended.
  4. false with existing closing keyword — When auto_close_issue: false and the body already contains Fixes #N, verify neither the existing keyword is removed nor a new one is added.
  5. 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)

  1. Config parsing — Verify auto-close-issue: false is correctly parsed into CreatePullRequestsConfig.AutoCloseIssue.
  2. Config serialization — Verify the config value is passed through to the handler config JSON as auto_close_issue.
  3. Default omission — Verify the field is omitted from compiled output when not set (backward compatibility).

Integration/compilation tests

  1. Compile a workflow with auto-close-issue: false set and verify the compiled lock file includes the config value.
  2. Compile a workflow without the field set and verify the compiled output is unchanged (no regression).

Schema validation

  1. Valid values — true, false accepted.
  2. Invalid values — String, number, etc. rejected by schema validation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions