Auto-complete Linked Issues Workflow
Quick Reference
- Workflow file:
.github/workflows/auto-complete-issues.yml - Workflow name:
Auto-complete linked issues - Triggers:
pull_request_target(closed),workflow_dispatch - Runner:
ubuntu-latest - Permissions:
issues: write,pull-requests: read - Primary action:
actions/github-script@v9
Purpose
auto-complete-issues.yml automates issue lifecycle cleanup when a pull request is merged.
When conditions are met, the workflow:
- Finds linked issues for the merged PR.
- Adds a completion label based on issue title prefix:
[Bug]->fixed[Feature Request]->completed
- Closes the issue (if still open).
- Removes all assignees.
This implements the behavior requested in issue #3550.
Workflow File
- Path:
.github/workflows/auto-complete-issues.yml - Workflow name:
Auto-complete linked issues - Runtime:
ubuntu-latest - Engine:
actions/github-script@v9
Triggers
Automatic trigger
on:
pull_request_target:
types: [closed]
The workflow starts when a PR is closed, then exits early unless:
pr.merged == true
Manual trigger
on:
workflow_dispatch:
inputs:
pr_number:
type: number
dry_run:
type: boolean
default: true
Manual runs fetch PR metadata via pulls.get and apply the same merged-PR validation.
Permissions and Security Model
Permissions requested
permissions:
issues: write
pull-requests: read
These are the minimum permissions needed to:
- Read PR metadata and body
- Read and update issues
- Add/remove labels
- Remove assignees
Why pull_request_target is used
pull_request_target executes in the context of the base repository (not fork workflow files), which allows write operations using GITHUB_TOKEN.
Risk is controlled because this workflow:
- does not check out code
- does not execute scripts from PR contents
- only acts on GitHub API metadata
- restricts operations to merged PR events only
Processing Pipeline
The script follows a deterministic pipeline:
- Load event context
- Detect whether run is automatic or
workflow_dispatch. - Parse
dry_run.
- Detect whether run is automatic or
- Load PR
- Automatic: use
context.payload.pull_request. - Manual: fetch PR by
pr_number.
- Automatic: use
- Gate checks
- Require merged PR.
- Use actual PR
head/baserefs from event/API metadata (no hardcoded branch gate).
- Discover linked issues
- Primary: GraphQL
closingIssuesReferences(first: 100). - Fallback: regex scan of PR body for closing keywords.
- Primary: GraphQL
- Process each issue
- Fetch issue by number.
- Ignore references that are actually pull requests.
- Derive completion label from title prefix.
- Add label if needed.
- Close issue if open.
- Remove assignees if present.
- Log outcomes
- Success and skip paths are logged explicitly.
- Partial failures are logged as warnings; processing continues.
Linked Issue Discovery Details
Primary method: GraphQL closing references
The workflow queries:
repository.pullRequest(number: X).closingIssuesReferences
This is the most reliable way to honor GitHub-native closing semantics.
Fallback method: regex in PR body
If GraphQL returns no issues (or fails), a fallback regex scans PR body text:
\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)\b
Matches examples like:
close #123closes #123fixed #123resolves #123
Numbers are deduplicated with a Set.
Labeling Rules
Label assignment is title-prefix based and case-insensitive:
/^\[bug\]:?/i-> addfixed/^\[feature request\]:?/i-> addcompleted
Notes:
- If title does not match either pattern, no completion label is added.
- If target label already exists, no duplicate add is attempted.
- Missing labels produce warning logs and do not fail the entire workflow.
Dry Run Behavior
dry_run is only honored for workflow_dispatch runs.
dry_run=true:- No mutations are performed.
- Logs emit explicit
"[dry-run] Would ..."messages.
dry_run=false:- Mutations are executed (label/close/unassign).
Automatic PR-close events always run in live mode.
Operational Runbook
Manual validation before enabling broader usage
- Open Actions -> Auto-complete linked issues.
- Run workflow with:
pr_number: a known merged PRdry_run:true
- Verify logs:
- branch-direction gate result
- issue detection output
- expected label/close/unassign actions
- Re-run with
dry_run=falsefor an approved test PR.
Expected skip conditions
- PR closed without merge
- No linked issues detected
- Referenced number is a PR, not an issue
These are healthy no-op states, not failures.
Troubleshooting
No issues were processed
Check:
- PR actually merged
- PR has closing references (
Fixes #...) or native linked issues
Labels were not added
Check:
- Label names exist exactly as
fixedandcompleted - Issue title prefix is present:
[Bug][Feature Request]
Assignees not removed
Check:
- Issue had assignees at processing time
- Run was not
dry_run=true
Manual run failed with invalid PR input
Check:
pr_numberis numeric and corresponds to an existing PR in this repository
Known Constraints
- Only up to first 100 GraphQL closing references are queried.
- Regex fallback only parses issue references in PR body, not comments.
- Completion label is inferred from title convention, not issue form metadata.
Maintenance Guidance
If label taxonomy changes
Update mapping logic:
[Bug]-> replacement forfixed[Feature Request]-> replacement forcompleted
If issue title conventions change
Update regex checks for title prefixes to match new conventions.
Suggested Future Enhancements
- Move expected branch names to repository variables for zero-code reconfiguration.
- Add optional comment on closed issue linking back to merged PR.
- Add metric-style summary output (counts of labeled/closed/unassigned/skipped).
- Add support for additional title conventions (
[Other Issues],[Question]) if policy requires.
Quick Reference
- Workflow file:
.github/workflows/auto-complete-issues.yml - Manual test mode:
workflow_dispatch+dry_run=true - Branch context source: PR metadata (
head/baserefs) - Completion labels:
[Bug]->fixed[Feature Request]->completed