@@ -101,19 +101,39 @@ jobs:
101101 team_reviewers: teamReviewers,
102102 });
103103 } else {
104- // Route to cloud-sdk-nodejs-team members with load balancing
105- console.log("Requesting review from a member of cloud-sdk-nodejs-team using load balancing.") ;
104+ console.log("Fetching .github/CODEOWNERS to identify default reviewers...");
105+ let defaultReviewers = [] ;
106106 try {
107- const { data: members } = await github.rest.teams.listMembersInOrg({
108- org: 'googleapis',
109- team_slug: 'cloud-sdk-nodejs-team',
107+ const { data: codeownersData } = await github.rest.repos.getContent({
108+ owner: context.repo.owner,
109+ repo: context.repo.repo,
110+ path: '.github/CODEOWNERS',
110111 });
111-
112- const memberLogins = members
113- .map(m => m.login)
114- .filter(login => login !== author);
112+ const content = Buffer.from(codeownersData.content, 'base64').toString('utf-8');
113+ const lines = content.split('\n');
114+ for (const line of lines) {
115+ const commentIdx = line.indexOf('#');
116+ const cleanLine = (commentIdx !== -1 ? line.substring(0, commentIdx) : line).trim();
117+ if (!cleanLine) continue;
118+
119+ const parts = cleanLine.split(/\s+/);
120+ const pattern = parts[0];
121+ if (pattern === '*') {
122+ const owners = parts.slice(1);
123+ defaultReviewers = owners
124+ .filter(p => p.startsWith('@') && !p.includes('/'))
125+ .map(p => p.substring(1))
126+ .filter(login => login !== author);
127+ break;
128+ }
129+ }
130+ } catch (err) {
131+ console.error("Failed to read or parse CODEOWNERS:", err);
132+ }
115133
116- if (memberLogins.length > 0) {
134+ if (defaultReviewers.length > 0) {
135+ console.log(`Found default reviewers: ${defaultReviewers.join(', ')}. Load balancing among them.`);
136+ try {
117137 // Retrieve active open PRs to calculate load
118138 const { data: openPRs } = await github.rest.pulls.list({
119139 owner: context.repo.owner,
@@ -123,7 +143,7 @@ jobs:
123143 });
124144
125145 const loadMap = {};
126- for (const member of memberLogins ) {
146+ for (const member of defaultReviewers ) {
127147 loadMap[member] = 0;
128148 }
129149
@@ -151,7 +171,7 @@ jobs:
151171 // Find members with the minimum load
152172 let minLoad = Infinity;
153173 let selectedReviewers = [];
154- for (const member of memberLogins ) {
174+ for (const member of defaultReviewers ) {
155175 const load = loadMap[member];
156176 if (load < minLoad) {
157177 minLoad = load;
@@ -169,25 +189,11 @@ jobs:
169189 repo: context.repo.repo,
170190 pull_number: context.payload.pull_request.number,
171191 reviewers: [leastLoadedReviewer],
172- team_reviewers: ['cloud-sdk-nodejs-team'],
173- });
174- } else {
175- console.log("No other members found in cloud-sdk-nodejs-team. Requesting team review only.");
176- await github.rest.pulls.requestReviewers({
177- owner: context.repo.owner,
178- repo: context.repo.repo,
179- pull_number: context.payload.pull_request.number,
180- team_reviewers: ['cloud-sdk-nodejs-team'],
181192 });
193+ } catch (err) {
194+ console.error("Failed to assign reviewers using load balancing:", err);
182195 }
183- } catch (err) {
184- console.error("Failed to fetch team members or assign reviewers:", err);
185- // Fallback to just requesting the team review
186- await github.rest.pulls.requestReviewers({
187- owner: context.repo.owner,
188- repo: context.repo.repo,
189- pull_number: context.payload.pull_request.number,
190- team_reviewers: ['cloud-sdk-nodejs-team'],
191- });
196+ } else {
197+ console.warn("No default reviewers found in CODEOWNERS (or they only contained teams/author).");
192198 }
193199 }
0 commit comments