Skip to content
Open
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
3 changes: 2 additions & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax


# The yoshi-nodejs team is the default owner for nodejs repositories.
# The cloud sdk nodejs team is the default owner for nodejs repositories.
# team_members: @pearigee @feywind @djbruce @shivanee-p
* @googleapis/cloud-sdk-nodejs-team
/handwritten/bigquery @googleapis/bigquery-team
/handwritten/cloud-profiler @googleapis/cloud-profiler-team
Expand Down
89 changes: 52 additions & 37 deletions .github/workflows/assign-reviewers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,29 +91,58 @@ jobs:
}
}

let assigned = false;
if (assignedTeams.size > 0) {
const teamReviewers = Array.from(assignedTeams);
console.log(`PR contains changes matching specific routes. Requesting review from: ${teamReviewers.join(', ')}`);
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
team_reviewers: teamReviewers,
});
} else {
// Route to cloud-sdk-nodejs-team members with load balancing
console.log("Requesting review from a member of cloud-sdk-nodejs-team using load balancing.");
try {
const { data: members } = await github.rest.teams.listMembersInOrg({
org: 'googleapis',
team_slug: 'cloud-sdk-nodejs-team',
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
team_reviewers: teamReviewers,
});

const memberLogins = members
.map(m => m.login)
.filter(login => login !== author);
assigned = true;
} catch (err) {
console.error(`Failed to assign route-specific team reviewers (${teamReviewers.join(', ')}):`, err.message || err);
console.log("Falling back to default team members.");
}
}

if (!assigned) {
console.log("Fetching .github/CODEOWNERS to identify default reviewers...");
let defaultReviewers = [];
try {
const { data: codeownersData } = await github.rest.repos.getContent({
owner: context.repo.owner,
repo: context.repo.repo,
path: '.github/CODEOWNERS',
ref: context.payload.pull_request.head.sha,
});
const content = Buffer.from(codeownersData.content, 'base64').toString('utf-8');
const lines = content.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('#')) {
const commentContent = trimmed.substring(1).trim();
if (commentContent.startsWith('team_members:')) {
const listPart = commentContent.substring('team_members:'.length).trim();
const parts = listPart.split(/\s+/);
defaultReviewers = parts
.filter(p => p.startsWith('@'))
.map(p => p.substring(1))
.filter(login => login !== author);
break;
}
}
}
} catch (err) {
console.error("Failed to read or parse CODEOWNERS:", err);
}

if (memberLogins.length > 0) {
if (defaultReviewers.length > 0) {
console.log(`Found default reviewers: ${defaultReviewers.join(', ')}. Load balancing among them.`);
try {
// Retrieve active open PRs to calculate load
const { data: openPRs } = await github.rest.pulls.list({
owner: context.repo.owner,
Expand All @@ -123,7 +152,7 @@ jobs:
});

const loadMap = {};
for (const member of memberLogins) {
for (const member of defaultReviewers) {
loadMap[member] = 0;
}

Expand Down Expand Up @@ -151,7 +180,7 @@ jobs:
// Find members with the minimum load
let minLoad = Infinity;
let selectedReviewers = [];
for (const member of memberLogins) {
for (const member of defaultReviewers) {
const load = loadMap[member];
if (load < minLoad) {
minLoad = load;
Expand All @@ -169,25 +198,11 @@ jobs:
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
reviewers: [leastLoadedReviewer],
team_reviewers: ['cloud-sdk-nodejs-team'],
});
} else {
console.log("No other members found in cloud-sdk-nodejs-team. Requesting team review only.");
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
team_reviewers: ['cloud-sdk-nodejs-team'],
});
} catch (err) {
console.error("Failed to assign reviewers using load balancing:", err);
}
} catch (err) {
console.error("Failed to fetch team members or assign reviewers:", err);
// Fallback to just requesting the team review
await github.rest.pulls.requestReviewers({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
team_reviewers: ['cloud-sdk-nodejs-team'],
});
} else {
console.warn("No default reviewers found in CODEOWNERS (or they only contained teams/author).");
}
}
Loading