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
65 changes: 49 additions & 16 deletions .github/workflows/gemini-scheduled-stale-pr-closer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,56 @@ jobs:

// 1. Fetch maintainers for verification
let maintainerLogins = new Set();
let teamFetchSucceeded = false;
try {
const members = await github.paginate(github.rest.teams.listMembersInOrg, {
org: context.repo.owner,
team_slug: 'gemini-cli-maintainers'
});
maintainerLogins = new Set(members.map(m => m.login.toLowerCase()));
teamFetchSucceeded = true;
core.info(`Successfully fetched ${maintainerLogins.size} team members from gemini-cli-maintainers`);
} catch (e) {
core.warning(`Failed to fetch team members from gemini-cli-maintainers: ${e.message}. Falling back to author_association only.`);
const teams = ['gemini-cli-maintainers', 'gemini-cli-askmode-approvers', 'gemini-cli-docs'];

for (const team_slug of teams) {
try {
const members = await github.paginate(github.rest.teams.listMembersInOrg, {
org: context.repo.owner,
team_slug: team_slug
});
for (const m of members) maintainerLogins.add(m.login.toLowerCase());
core.info(`Successfully fetched ${members.length} team members from ${team_slug}`);
} catch (e) {
core.warning(`Failed to fetch team members from ${team_slug}: ${e.message}`);
}
}

const isMaintainer = (login, assoc) => {
const isGooglerCache = new Map();
const isGoogler = async (login) => {
if (isGooglerCache.has(login)) return isGooglerCache.get(login);

try {
// Check membership in 'googlers' or 'google' orgs
const orgs = ['googlers', 'google'];
for (const org of orgs) {
try {
await github.rest.orgs.checkMembershipForUser({
org: org,
username: login
});
core.info(`User ${login} is a member of ${org} organization.`);
isGooglerCache.set(login, true);
return true;
} catch (e) {
// 404 just means they aren't a member, which is fine
if (e.status !== 404) throw e;
}
}
} catch (e) {
core.warning(`Failed to check org membership for ${login}: ${e.message}`);
}

isGooglerCache.set(login, false);
return false;
};

const isMaintainer = async (login, assoc) => {
const isTeamMember = maintainerLogins.has(login.toLowerCase());
const isRepoMaintainer = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(assoc);
return isTeamMember || isRepoMaintainer;
if (isTeamMember || isRepoMaintainer) return true;

return await isGoogler(login);
};

// 2. Determine which PRs to check
Expand All @@ -81,7 +114,7 @@ jobs:
}

for (const pr of prs) {
const maintainerPr = isMaintainer(pr.user.login, pr.author_association);
const maintainerPr = await isMaintainer(pr.user.login, pr.author_association);
const isBot = pr.user.type === 'Bot' || pr.user.login.endsWith('[bot]');

// Detection Logic for Linked Issues
Expand Down Expand Up @@ -175,7 +208,7 @@ jobs:
pull_number: pr.number
});
for (const r of reviews) {
if (isMaintainer(r.user.login, r.author_association)) {
if (await isMaintainer(r.user.login, r.author_association)) {
const d = new Date(r.submitted_at || r.updated_at);
if (d > lastActivity) lastActivity = d;
}
Expand All @@ -186,7 +219,7 @@ jobs:
issue_number: pr.number
});
for (const c of comments) {
if (isMaintainer(c.user.login, c.author_association)) {
if (await isMaintainer(c.user.login, c.author_association)) {
const d = new Date(c.updated_at);
if (d > lastActivity) lastActivity = d;
}
Expand Down
26 changes: 24 additions & 2 deletions .github/workflows/pr-contribution-guidelines-notifier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,31 @@ jobs:
const pr_number = context.payload.pull_request.number;

// 1. Check if the PR author is a maintainer
const isGoogler = async (login) => {
try {
const orgs = ['googlers', 'google'];
for (const org of orgs) {
try {
await github.rest.orgs.checkMembershipForUser({
org: org,
username: login
});
return true;
} catch (e) {
if (e.status !== 404) throw e;
}
}
} catch (e) {
core.warning(`Failed to check org membership for ${login}: ${e.message}`);
}
return false;
};

const authorAssociation = context.payload.pull_request.author_association;
if (['OWNER', 'MEMBER', 'COLLABORATOR'].includes(authorAssociation)) {
core.info(`${username} is a maintainer (Association: ${authorAssociation}). No notification needed.`);
const isRepoMaintainer = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(authorAssociation);

if (isRepoMaintainer || await isGoogler(username)) {
core.info(`${username} is a maintainer or Googler. No notification needed.`);
return;
}

Expand Down
Loading