diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 642af919ab0..ec4f6026ebd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -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 diff --git a/.github/workflows/assign-reviewers.yml b/.github/workflows/assign-reviewers.yml index 71b55547acf..0c5f64dbd0a 100644 --- a/.github/workflows/assign-reviewers.yml +++ b/.github/workflows/assign-reviewers.yml @@ -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, @@ -123,7 +152,7 @@ jobs: }); const loadMap = {}; - for (const member of memberLogins) { + for (const member of defaultReviewers) { loadMap[member] = 0; } @@ -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; @@ -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)."); } }