Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ng-dev/pr/common/fetch-pull-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
PullRequestState,
StatusState,
CommentAuthorAssociation,
IssueState,
} from '@octokit/graphql-schema';
import {getPendingPrs, getPr, getPrFiles, getPrComments} from '../../utils/github.js';
import {alias, types as graphqlTypes, onUnion, optional, params} from 'typed-graphqlify';
Expand Down Expand Up @@ -133,6 +134,17 @@ export const PR_SCHEMA = {
author: {
login: graphqlTypes.string,
},
closingIssuesReferences: params(
{first: 100},
{
nodes: [
{
number: graphqlTypes.number,
state: graphqlTypes.custom<IssueState>(),
},
],
},
),
};

export type PullRequestFromGithub = typeof PR_SCHEMA;
Expand Down
12 changes: 11 additions & 1 deletion ng-dev/pr/merge/pull-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ import {TEMP_PR_HEAD_BRANCH} from './strategies/strategy.js';
import {mergeLabels} from '../common/labels/merge.js';
import {PullRequestValidationFailure} from '../common/validation/validation-failure.js';
import {AuthenticatedGitClient} from '../../utils/git/authenticated-git-client.js';
import {GithubConfig, NgDevConfig, CaretakerConfig, GoogleSyncConfig} from '../../utils/config.js';
import {GithubConfig, NgDevConfig} from '../../utils/config.js';
import {PullRequestConfig, PullRequestValidationConfig} from '../config/index.js';
import {targetLabels} from '../common/labels/target.js';
import {IssueState} from '@octokit/graphql-schema';

/** Interface describing a pull request's closing issue references. */
export interface PullRequestClosingIssuesReferences {
number: number;
state: IssueState;
}

/** Interface that describes a pull request. */
export interface PullRequest {
Expand Down Expand Up @@ -52,6 +59,8 @@ export interface PullRequest {
validationFailures: PullRequestValidationFailure[];
/** The SHA for the latest commit in the pull request. */
headSha: string;
/** A list of issues that will be closed by the pull request. */
closingIssuesReferences: PullRequestClosingIssuesReferences[];
}

/**
Expand Down Expand Up @@ -155,5 +164,6 @@ export async function loadAndValidatePullRequest(
title: prData.title,
commitCount: prData.commits.totalCount,
headSha: prData.headRefOid,
closingIssuesReferences: prData.closingIssuesReferences.nodes,
};
}
7 changes: 7 additions & 0 deletions ng-dev/pr/merge/strategies/api-merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ export class GithubApiMergeStrategy extends AutosquashMergeStrategy {
);
}

// When a pull request is merged, GitHub automatically closes any linked issues.
// This is not the case when the pull request is not merged into the main branch.
// This is why we need to manually close the linked issues.
if (githubTargetBranch !== this.git.mainBranchName) {
await this.closeLinkedIssues(pullRequest);
}

// Workaround for fatal: refusing to fetch into branch 'refs/heads/merge_pr_target_main' checked out at ...
// Cannot find where but `merge_pr_target_main` is being set as the current branch.
// TODO: remove after finding the root cause.
Expand Down
5 changes: 5 additions & 0 deletions ng-dev/pr/merge/strategies/autosquash-merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ export class AutosquashMergeStrategy extends MergeStrategy {
pull_number: pullRequest.prNumber,
state: 'closed',
});

// When a pull request is merged, GitHub automatically closes any linked issues.
// This is not the case when the pull request is not merged into the main branch.
// This is why we need to manually close the linked issues.
await this.closeLinkedIssues(pullRequest);
}
}
}
Expand Down
33 changes: 33 additions & 0 deletions ng-dev/pr/merge/strategies/strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,37 @@ export abstract class MergeStrategy {
`${banchesAndSha.map(([branch, sha]) => `- ${branch}: ${sha}`).join('\n')}`,
});
}

/**
* Manually closes issues linked to a merged Pull Request.
*
* This function addresses the scenario where **GitHub's automatic issue closure feature is skipped**
* because the PR's target branch is *not* the repository's main branch.
*
* It updates any open linked issues to a `closed` state with a `completed` reason.
*
* @note The method is a **no-op** if the PR target is the main branch,
* as GitHub handles issue closure automatically in that case.
*/
protected async closeLinkedIssues({
closingIssuesReferences,
githubTargetBranch,
}: PullRequest): Promise<void> {
if (githubTargetBranch === this.git.mainBranchName) {
return;
}

for (const {number: issue_number, state} of closingIssuesReferences) {
if (state === 'CLOSED') {
continue;
}

await this.git.github.issues.update({
...this.git.remoteParams,
issue_number,
state_reason: 'completed',
state: 'closed',
});
}
}
}
Loading