Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
88 changes: 88 additions & 0 deletions .github/scripts/triage/check-changes-ownership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import * as utils from './utils.ts';
import { Octokit } from "@octokit/action";

const octokit = new Octokit();

const [owner, repo] = process.env.GITHUB_REPOSITORY!.split("/");
const prNumber: number = +process.env.PR_NUMBER!;
const changes: string[] = process.env.CHANGED_FILES!.split(',');

/**
* Checks if the PR already has the 'triage:accepted:ready' label, meaning the triage checks should be skipped.
* @returns true if the PR has the 'triage:accepted:ready' assigned to it, false otherwise.
*/
async function shouldSkipCheck() {
const result = await octokit.request("GET /repos/{owner}/{repo}/issues/{issue_number}/labels", {
owner: owner,
repo: repo,
issue_number: prNumber
});

return result.data.some(l => l.name === "triage:accepted:ready");
}

function getCommentText(changesWithoutOwners: string[]): string {
return `
This PR contains changes to area(s) that do not have an active SIG/project and will be auto-closed:

- ${changesWithoutOwners.join('\n- ')}

Such changes may be rejected or put on hold until a new SIG/project is established.

Please refer to the [Semantic Convention Areas](https://github.com/open-telemetry/semantic-conventions/blob/main/AREAS.md)
document to see the current active SIGs and also to learn how to kick start a new one.`;
}

async function changesInInactiveAreas(): Promise<boolean> {
// skips enforcing the triage process if the PR has the 'triage:accepted:ready' label on it
// this means maintainers/approvers decided to bypass it for good reasons.
if (await shouldSkipCheck()) {
return false;
}

const areas = utils.getAllAreasMetadata();
const areaOwnersMap = utils.getActiveAreasWithCodeOwners(areas);

// extract only the name after model/ which is the actual area name
const changedAreas = changes.map(folder => folder.split('/')[1]);

let changesWithoutOwners: string[] = [];
changedAreas.forEach(ca => {
if (!areaOwnersMap.has(ca)) {
changesWithoutOwners.push(ca);
}
});

if (changesWithoutOwners.length > 0) {
await octokit.request("POST /repos/{owner}/{repo}/issues/{issue_number}/labels", {
owner: owner,
repo: repo,
issue_number: prNumber,
labels: ['triage:rejected:declined']
});
await octokit.request("POST /repos/{owner}/{repo}/issues/{issue_number}/comments", {
owner: owner,
repo: repo,
issue_number: prNumber,
body: getCommentText(changesWithoutOwners)
});
await octokit.request("PATCH /repos/{owner}/{repo}/issues/{issue_number}", {
owner: owner,
repo: repo,
issue_number: prNumber,
state: 'closed',
state_reason: 'not_planned'
});

return true
}

return false;
}

(async () => {
const result = await changesInInactiveAreas();
if (result) {
process.exit(1);
}
})();
Loading
Loading