Skip to content

Commit 0566e3f

Browse files
authored
feat(ng-dev): automatically close linked issues for non-main branches
When a pull request is merged into a non-main branch, GitHub does not automatically close the linked issues. This change ensures that linked issues are closed when a PR is merged into any branch, not just the main branch.
1 parent 43cd9ca commit 0566e3f

File tree

7 files changed

+98
-4
lines changed

7 files changed

+98
-4
lines changed

.github/local-actions/branch-manager/main.js

Lines changed: 29 additions & 3 deletions
Large diffs are not rendered by default.

ng-dev/pr/common/fetch-pull-request.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
PullRequestState,
1414
StatusState,
1515
CommentAuthorAssociation,
16+
IssueState,
1617
} from '@octokit/graphql-schema';
1718
import {getPendingPrs, getPr, getPrFiles, getPrComments} from '../../utils/github.js';
1819
import {alias, types as graphqlTypes, onUnion, optional, params} from 'typed-graphqlify';
@@ -133,6 +134,17 @@ export const PR_SCHEMA = {
133134
author: {
134135
login: graphqlTypes.string,
135136
},
137+
closingIssuesReferences: params(
138+
{first: 100},
139+
{
140+
nodes: [
141+
{
142+
number: graphqlTypes.number,
143+
state: graphqlTypes.custom<IssueState>(),
144+
},
145+
],
146+
},
147+
),
136148
};
137149

138150
export type PullRequestFromGithub = typeof PR_SCHEMA;

ng-dev/pr/merge/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ ts_project(
2121
"//ng-dev:__subpackages__",
2222
],
2323
deps = [
24+
"//ng-dev:node_modules/@octokit/graphql-schema",
2425
"//ng-dev:node_modules/@octokit/plugin-rest-endpoint-methods",
2526
"//ng-dev:node_modules/@octokit/rest",
2627
"//ng-dev:node_modules/@types/node",

ng-dev/pr/merge/pull-request.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {IssueState} from '@octokit/graphql-schema';
910
import {
1011
getTargetBranchesAndLabelForPullRequest,
1112
PullRequestTarget,
@@ -18,10 +19,16 @@ import {TEMP_PR_HEAD_BRANCH} from './strategies/strategy.js';
1819
import {mergeLabels} from '../common/labels/merge.js';
1920
import {PullRequestValidationFailure} from '../common/validation/validation-failure.js';
2021
import {AuthenticatedGitClient} from '../../utils/git/authenticated-git-client.js';
21-
import {GithubConfig, NgDevConfig, CaretakerConfig, GoogleSyncConfig} from '../../utils/config.js';
22+
import {GithubConfig, NgDevConfig} from '../../utils/config.js';
2223
import {PullRequestConfig, PullRequestValidationConfig} from '../config/index.js';
2324
import {targetLabels} from '../common/labels/target.js';
2425

26+
/** Interface describing a pull request's closing issue references. */
27+
export interface PullRequestClosingIssuesReferences {
28+
number: number;
29+
state: IssueState;
30+
}
31+
2532
/** Interface that describes a pull request. */
2633
export interface PullRequest {
2734
/** URL to the pull request. */
@@ -52,6 +59,8 @@ export interface PullRequest {
5259
validationFailures: PullRequestValidationFailure[];
5360
/** The SHA for the latest commit in the pull request. */
5461
headSha: string;
62+
/** A list of issues that will be closed by the pull request. */
63+
closingIssuesReferences: PullRequestClosingIssuesReferences[];
5564
}
5665

5766
/**
@@ -155,5 +164,6 @@ export async function loadAndValidatePullRequest(
155164
title: prData.title,
156165
commitCount: prData.commits.totalCount,
157166
headSha: prData.headRefOid,
167+
closingIssuesReferences: prData.closingIssuesReferences.nodes,
158168
};
159169
}

ng-dev/pr/merge/strategies/api-merge.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ export class GithubApiMergeStrategy extends AutosquashMergeStrategy {
152152
);
153153
}
154154

155+
// When a pull request is merged, GitHub automatically closes any linked issues.
156+
// This is not the case when the pull request is not merged into the main branch.
157+
// This is why we need to manually close the linked issues.
158+
if (githubTargetBranch !== this.git.mainBranchName) {
159+
await this.closeLinkedIssues(pullRequest);
160+
}
161+
155162
// Workaround for fatal: refusing to fetch into branch 'refs/heads/merge_pr_target_main' checked out at ...
156163
// Cannot find where but `merge_pr_target_main` is being set as the current branch.
157164
// TODO: remove after finding the root cause.

ng-dev/pr/merge/strategies/autosquash-merge.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ export class AutosquashMergeStrategy extends MergeStrategy {
100100
pull_number: pullRequest.prNumber,
101101
state: 'closed',
102102
});
103+
104+
// When a pull request is merged, GitHub automatically closes any linked issues.
105+
// This is not the case when the pull request is not merged into the main branch.
106+
// This is why we need to manually close the linked issues.
107+
await this.closeLinkedIssues(pullRequest);
103108
}
104109
}
105110
}

ng-dev/pr/merge/strategies/strategy.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,37 @@ export abstract class MergeStrategy {
226226
`${banchesAndSha.map(([branch, sha]) => `- ${branch}: ${sha}`).join('\n')}`,
227227
});
228228
}
229+
230+
/**
231+
* Manually closes issues linked to a merged Pull Request.
232+
*
233+
* This function addresses the scenario where **GitHub's automatic issue closure feature is skipped**
234+
* because the PR's target branch is *not* the repository's main branch.
235+
*
236+
* It updates any open linked issues to a `closed` state with a `completed` reason.
237+
*
238+
* @note The method is a **no-op** if the PR target is the main branch,
239+
* as GitHub handles issue closure automatically in that case.
240+
*/
241+
protected async closeLinkedIssues({
242+
closingIssuesReferences,
243+
githubTargetBranch,
244+
}: PullRequest): Promise<void> {
245+
if (githubTargetBranch === this.git.mainBranchName) {
246+
return;
247+
}
248+
249+
for (const {number: issue_number, state} of closingIssuesReferences) {
250+
if (state === 'CLOSED') {
251+
continue;
252+
}
253+
254+
await this.git.github.issues.update({
255+
...this.git.remoteParams,
256+
issue_number,
257+
state_reason: 'completed',
258+
state: 'closed',
259+
});
260+
}
261+
}
229262
}

0 commit comments

Comments
 (0)