Skip to content

Automate setting closed issue milestones#37872

Merged
roji merged 2 commits intodotnet:mainfrom
roji:ChangeMilestone
Mar 10, 2026
Merged

Automate setting closed issue milestones#37872
roji merged 2 commits intodotnet:mainfrom
roji:ChangeMilestone

Conversation

@roji
Copy link
Member

@roji roji commented Mar 6, 2026

Continues the previous work for applying preview version labels: this now also sets the closed issue's milestone, for both vnext on main (e.g. 11.0.0) and for release (servicing) PRs.

@roji roji requested a review from a team as a code owner March 6, 2026 13:26
Copilot AI review requested due to automatic review settings March 6, 2026 13:26
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the existing GitHub Actions automation which labels issues closed by merged PRs, extending it to also move those closed issues from the Backlog milestone (or no milestone) to the milestone for the current major version derived from eng/Versions.props.

Changes:

  • Read VersionPrefix from eng/Versions.props on the PR target branch to determine the major-version milestone (e.g. 11.0.0).
  • Query closing issues via GraphQL including their milestone, and update milestones when needed.
  • Improve logging and aggregate per-issue failures into a single workflow failure.
Comments suppressed due to low confidence (3)

.github/workflows/label-and-milestone-issues.yml:118

  • The workflow now throws when no matching preview/RC release/* branches are found (or when the latest major has no preview/RC branches). Since this job runs on every merged PR to main, this change can turn a previously benign 'skip' case into a failing workflow run, creating noisy failures during periods without preview/RC branches. Consider logging and returning (skip) instead of throwing here, unless a failing workflow run is explicitly desired.
    .github/workflows/label-and-milestone-issues.yml:168
  • listMilestones is called with state: 'open' and only the first page (per_page: 100) is fetched. This can cause the workflow to fail for servicing branches where the ${majorVersion}.0.0 milestone may already be closed (or if there are >100 milestones), even though GitHub still allows assigning a closed milestone to issues. Consider listing milestones with state: 'all' and paginating (or using github.paginate) before searching by title.
    .github/workflows/label-and-milestone-issues.yml:50
  • parseInt(versionPrefixMatch[1]) should pass a radix (e.g. 10) for consistent parsing and to avoid edge cases if the string ever includes a leading zero or other prefix.

You can also share your feedback on Copilot code review. Take the survey.

@roji roji force-pushed the ChangeMilestone branch from b375a49 to cc26d1c Compare March 6, 2026 15:44
@roji roji enabled auto-merge (squash) March 6, 2026 15:45
@AndriySvyryd AndriySvyryd disabled auto-merge March 6, 2026 19:19
Copilot AI review requested due to automatic review settings March 7, 2026 17:09
@roji roji force-pushed the ChangeMilestone branch from cc26d1c to 46a5381 Compare March 7, 2026 17:09
@roji
Copy link
Member Author

roji commented Mar 7, 2026

@AndriySvyryd I did a bit of a bigger rewrite/simplification here - we now simply always look at eng/Versions.props to know which version the PR is going into (both for release and for preview/RC versions - no more of the version sniffing via which branches exist).

Importantly we now always assign the milestone based on this - even if a milestone is already assigned (so we clobber any existing milestone assignment). I think this should be safe: the contents of Versions.props for the PR's merge commit should always determine the milestone and preview/RC label.

So maybe take another quick look if you want and let me know if you see a problem.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


You can also share your feedback on Copilot code review. Take the survey.

@roji roji force-pushed the ChangeMilestone branch from 46a5381 to 2c88822 Compare March 7, 2026 17:16
@AndriySvyryd
Copy link
Member

AndriySvyryd commented Mar 9, 2026

Importantly we now always assign the milestone based on this - even if a milestone is already assigned (so we clobber any existing milestone assignment). I think this should be safe: the contents of Versions.props for the PR's merge commit should always determine the milestone and preview/RC label.

That means that if an issue was fixed in servicing and then merged to main it will be marked as fixed in 11. It should set the Min of the current and existing version.

Copilot AI review requested due to automatic review settings March 9, 2026 22:04
@roji
Copy link
Member Author

roji commented Mar 9, 2026

@AndriySvyryd good catch, pushed an update to do this. Existing milestones which don't parse as an X.Y.Z version (e.g. Backlog, MQ...) are still always clobbered.

Take a look, obviously we'll need to pay close attention on the first few invocations to make sure this is doing the right thing, but seems legit.

@roji roji requested a review from AndriySvyryd March 9, 2026 22:05
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +104 to +122
// Look up the target milestone (e.g. "11.0.0", "10.0.5") via GraphQL
const targetMilestoneName = versionPrefix;
const milestoneResult = await github.graphql(`
query($owner: String!, $repo: String!, $title: String!) {
repository(owner: $owner, name: $repo) {
milestones(query: $title, first: 1) {
nodes {
number
title
}
}
}
}
`, { owner, repo, title: targetMilestoneName });
const milestoneNode = milestoneResult.repository.milestones.nodes
.find(m => m.title === targetMilestoneName);
if (!milestoneNode) {
throw new Error(`Milestone '${targetMilestoneName}' not found`);
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Milestone lookup can miss the exact milestone and fail the workflow: the GraphQL query requests only first: 1, but query: $title is a search and may return a different milestone (e.g. a milestone whose title contains the version string) as the first result. Fetch more results (or paginate) and then select the exact title match; also consider including both OPEN and CLOSED milestones so the lookup still works if the target milestone was closed.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +53
query($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 50) {
nodes {
number
milestone {
title
}
}
}
}
}
}
`;

const result = await github.graphql(query, { owner, repo, prNumber });
const closingIssues = result.repository.pullRequest.closingIssuesReferences.nodes;

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

closingIssuesReferences(first: 50) will only process the first 50 issues closed by a PR; any additional linked closing issues won’t get labeled/milestoned. Consider paginating this connection (e.g. request pageInfo { hasNextPage endCursor } and loop) so all closing issues are handled.

Suggested change
query($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 50) {
nodes {
number
milestone {
title
}
}
}
}
}
}
`;
const result = await github.graphql(query, { owner, repo, prNumber });
const closingIssues = result.repository.pullRequest.closingIssuesReferences.nodes;
query($owner: String!, $repo: String!, $prNumber: Int!, $after: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 50, after: $after) {
nodes {
number
milestone {
title
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
`;
let closingIssues = [];
let hasNextPage = true;
let cursor = null;
while (hasNextPage) {
const result = await github.graphql(query, { owner, repo, prNumber, after: cursor });
const page = result.repository.pullRequest.closingIssuesReferences;
closingIssues = closingIssues.concat(page.nodes);
hasNextPage = page.pageInfo.hasNextPage;
cursor = page.pageInfo.endCursor;
}

Copilot uses AI. Check for mistakes.
@roji roji merged commit 79c8ce7 into dotnet:main Mar 10, 2026
14 checks passed
@roji roji deleted the ChangeMilestone branch March 10, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants