Skip to content
Merged
Changes from 2 commits
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
136 changes: 136 additions & 0 deletions .github/workflows/label-preview-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# This workflow automatically labels issues with the preview/RC version when their fixing PR
# is merged into main. It determines the current preview/RC by inspecting which release branches
# exist (e.g. if release/11.0-preview2 exists but not release/11.0-preview3, the label is preview-3).

name: Label issues with preview/RC version

on:
pull_request_target:
types: [closed]
branches: [main]

permissions:
issues: write
contents: read
pull-requests: read

jobs:
label:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- name: Determine preview/RC version and label issues
uses: actions/github-script@v8
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const prNumber = context.payload.pull_request.number;

// Fetch only release branches using the Git refs API
let releaseRefs = [];
for (let page = 1; ; page++) {
const { data: refs } = await github.rest.git.listMatchingRefs({
owner,
repo,
ref: 'heads/release/',
per_page: 100,
page
});
releaseRefs = releaseRefs.concat(refs);
if (refs.length < 100) break;
}

// Parse release branches to find preview/rc numbers per major version
const previewRegex = /^release\/(\d+)\.0-preview(\d+)$/;
const rcRegex = /^release\/(\d+)\.0-rc(\d+)$/;

// Map: major version -> { previews: Set<number>, rcs: Set<number> }
const versions = new Map();

for (const ref of releaseRefs) {
const branchName = ref.ref.replace('refs/heads/', '');
let match = branchName.match(previewRegex);
if (match) {
const major = parseInt(match[1]);
if (!versions.has(major)) versions.set(major, { previews: new Set(), rcs: new Set() });
versions.get(major).previews.add(parseInt(match[2]));
continue;
}

match = branchName.match(rcRegex);
if (match) {
const major = parseInt(match[1]);
if (!versions.has(major)) versions.set(major, { previews: new Set(), rcs: new Set() });
versions.get(major).rcs.add(parseInt(match[2]));
}
}

if (versions.size === 0) {
console.log('No release branches found, skipping labeling');
return;
}

// Find the highest major version
const maxMajor = Math.max(...versions.keys());
const { previews, rcs } = versions.get(maxMajor);

// Determine the next preview/rc label
let label;
if (rcs.size > 0) {
// Once any rc branch exists, main is targeting preview-1 of the next major version
label = 'preview-1';
} else if (previews.size > 0) {
const maxPreview = Math.max(...previews);
if (maxPreview >= 7) {
label = 'rc-1';
} else {
label = `preview-${maxPreview + 1}`;
}
} else {
console.log('No preview or rc branches found for latest major version, skipping labeling');
return;
}

console.log(`Determined label: ${label}`);

// Find issues closed by this PR using GraphQL
const query = `
query($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 50) {
nodes {
number
}
}
}
}
}
`;

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

if (closingIssues.length === 0) {
console.log('No closing issues linked to this PR, skipping labeling');
return;
}

// Label all closing issues (don't filter by state to avoid race conditions
// where GitHub hasn't closed the issue yet when this workflow runs)
for (const issue of closingIssues) {
console.log(`Adding label '${label}' to issue #${issue.number}`);
try {
await github.rest.issues.addLabels({
owner,
repo,
issue_number: issue.number,
labels: [label]
});
} catch (error) {
console.error(`Failed to add label to issue #${issue.number}: ${error.message}`);
}
}

console.log(`Done. Labeled ${closingIssues.length} issue(s) with '${label}'`);
Loading