Skip to content

Commit 9ebd208

Browse files
Augment "Schedule Monthly" open issues assigned to non-team members (#8175)
* initial commit * Addition of `nonTeamWIthOpen` * separate Skills Issue list from inactiveWithOpenSkills * Split out inactiveSkillsIssue, ref to getOpenAssignedIssues * Update get-open-assigned-issues.js tweaks to getOpenAssignedIssues * insert a missing semi-colon * add check for unique values inActiveMemberOpenIssue * Update trim-inactive-members.js, `removeInactiveMembers()` * Update trim-inactive-members.js * Update trim-inactive-members.js fix misplaced `!` * Update get-contributors-data.js correct capitalization of 'GitHub' * delete unused parameter cannotRemoveYet
1 parent 700edd9 commit 9ebd208

File tree

4 files changed

+120
-43
lines changed

4 files changed

+120
-43
lines changed

github-actions/trigger-schedule/list-inactive-members/create-new-issue.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ async function main({ g, c }) {
2323
const filepath = 'github-actions/utils/_data/inactive-members.json';
2424
const rawData = fs.readFileSync(filepath, 'utf8');
2525
let inactiveLists = JSON.parse(rawData);
26-
const inactiveWithOpen = parseInactiveOpen(inactiveLists['cannotRemoveYet']);
26+
const inactiveWithOpen = parseInactiveOpen(inactiveLists['inactiveOpenIssue']);
27+
const nonTeamWithOpen = parseNonTeamOpen(inactiveLists['nonTeamOpenIssue']);
2728

2829
const owner = context.repo.owner;
2930
const repo = context.repo.repo;
@@ -33,7 +34,7 @@ async function main({ g, c }) {
3334
const issueNumber = issue.number;
3435

3536
// Add issue number used to reference the issue and comment on the `Dev/PM Agenda and Notes`
36-
const commentBody = `**Review Inactive Team Members:** #` + issueNumber + inactiveWithOpen;
37+
const commentBody = `**Review Inactive Team Members:** #` + issueNumber + inactiveWithOpen + nonTeamWithOpen;
3738
await postComment(AGENDA_ISSUE_NUM, commentBody, github, context);
3839
}
3940

@@ -84,15 +85,31 @@ const createIssue = async (owner, repo, inactiveLists) => {
8485
};
8586

8687
const parseInactiveOpen = (inactiveOpens) => {
87-
if(Object.keys(inactiveOpens).length === 0){
88+
if (Object.keys(inactiveOpens).length === 0) {
8889
return '';
8990
} else {
9091
let inactiveOpen = '\r\n\nInactive members with open issues:\r\n';
91-
for(const [key, value] of Object.entries(inactiveOpens)){
92-
inactiveOpen += ' - ' + key + ': #' + value + '\r\n';
92+
for (const [user, issues] of Object.entries(inactiveOpens)) {
93+
for (const issue of issues) {
94+
inactiveOpen += ` - ${user}: #${issue}\r\n`;
95+
}
9396
}
9497
return inactiveOpen;
9598
}
9699
};
97100

101+
const parseNonTeamOpen = (nonTeamOpens) => {
102+
if (Object.keys(nonTeamOpens).length === 0) {
103+
return '';
104+
} else {
105+
let nonTeamOpen = '\r\n\nNon-team members with open issues:\r\n';
106+
for (const [user, issues] of Object.entries(nonTeamOpens)) {
107+
for (const issue of issues) {
108+
nonTeamOpen += ` - ${user}: #${issue}\r\n`;
109+
}
110+
}
111+
return nonTeamOpen;
112+
}
113+
};
114+
98115
module.exports = main;

github-actions/trigger-schedule/list-inactive-members/get-contributors-data.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,25 @@ let dates = [oneMonthAgo, twoMonthsAgo];
2727

2828
/**
2929
* Main function
30-
* @param {Object} g - github object from actions/github-script
30+
* @param {Object} g - GitHub object from actions/github-script
3131
* @param {Object} c - context object from actions/github-script
3232
* @return {Object} results - object to use in `trim-inactive-members.js`
3333
*/
3434
async function main({ g, c }) {
3535
github = g;
3636
context = c;
3737

38-
const [contributorsOneMonthAgo, contributorsTwoMonthsAgo, inactiveWithOpenIssue] = await fetchContributors(dates);
38+
const [contributorsOneMonthAgo, contributorsTwoMonthsAgo, inactiveWithOpenSkills,inactiveWithOpenIssue] = await fetchContributors(dates);
3939
console.log(`-`.repeat(60));
4040
console.log('List of active contributors since ' + dates[0].slice(0, 10) + ':');
4141
console.log(contributorsOneMonthAgo);
4242

4343
return {
4444
recentContributors: contributorsOneMonthAgo,
4545
previousContributors: contributorsTwoMonthsAgo,
46-
inactiveWithOpenIssue: inactiveWithOpenIssue,
47-
dates: dates,
46+
inactiveWithOpenSkills,
47+
inactiveWithOpenIssue,
48+
dates,
4849
};
4950
};
5051

@@ -58,6 +59,7 @@ async function main({ g, c }) {
5859
async function fetchContributors(dates){
5960
let allContributorsSinceOneMonthAgo = {};
6061
let allContributorsSinceTwoMonthsAgo = {};
62+
let inactiveWithOpenSkills = {};
6163
let inactiveWithOpenIssue = {};
6264

6365
// Members of 'website-maintain' team are considered permanent members
@@ -76,7 +78,7 @@ async function fetchContributors(dates){
7678
let pageNum = 1;
7779
let result = [];
7880

79-
// Since Github only allows to fetch max 100 items per request, we need to 'flip' pages
81+
// Since GitHub only allows to fetch max 100 items per request, we need to 'flip' pages
8082
while(true){
8183
// Fetch 100 items per each page (`pageNum`)
8284
const contributors = await github.request(api, {
@@ -128,9 +130,9 @@ async function fetchContributors(dates){
128130
else if (date === dates[1]) {
129131
const regex = /Pre-work checklist|Skills Issue/i;
130132
if (regex.test(contributorInfo.title)) {
131-
inactiveWithOpenIssue[assignee] = [issueNum, true];
133+
inactiveWithOpenSkills[assignee] = [issueNum];
132134
} else {
133-
inactiveWithOpenIssue[assignee] = [issueNum, false];
135+
inactiveWithOpenIssue[assignee] = [issueNum];
134136
}
135137
}
136138
}
@@ -151,7 +153,7 @@ async function fetchContributors(dates){
151153
allContributorsSinceTwoMonthsAgo = allContributorsSince;
152154
}
153155
}
154-
return [allContributorsSinceOneMonthAgo, allContributorsSinceTwoMonthsAgo, inactiveWithOpenIssue];
156+
return [allContributorsSinceOneMonthAgo, allContributorsSinceTwoMonthsAgo, inactiveWithOpenSkills, inactiveWithOpenIssue];
155157
}
156158

157159

github-actions/trigger-schedule/list-inactive-members/trim-inactive-members.js

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const fs = require('fs');
33
const getTeamMembers = require('../../utils/get-team-members');
44
const addTeamMember = require('../../utils/add-team-member');
5+
const getOpenAssignedIssues = require('../../utils/get-open-assigned-issues');
56

67
// Global variables
78
var github;
@@ -10,6 +11,7 @@ var context;
1011
const baseTeam = 'website';
1112
const writeTeam = 'website-write';
1213
const mergeTeam = 'website-merge';
14+
const PMTeam = 'website-pm';
1315

1416

1517

@@ -21,10 +23,11 @@ const mergeTeam = 'website-merge';
2123
* @param {Object} c - context object from actions/github-script
2224
* @param {Object} recentContributors - recentContributors since the dates[0] = recent cutoff
2325
* @param {Object} previousContributors - previousContributors since the dates[1] = previous cutoff
26+
* @param {Object} inactiveWithOpenSkills- Inactive contributors that have open Skills issues
2427
* @param {Object} inactiveWithOpenIssue - Inactive contributors that have open issues
2528
* @param {Object} dates - [recent, previous] dates of oneMonthAgo, twoMonthsAgo
2629
*/
27-
async function main({ g, c }, { recentContributors, previousContributors, inactiveWithOpenIssue, dates }) {
30+
async function main({ g, c }, { recentContributors, previousContributors, inactiveWithOpenSkills, inactiveWithOpenIssue, dates }) {
2831
github = g;
2932
context = c;
3033

@@ -33,69 +36,71 @@ async function main({ g, c }, { recentContributors, previousContributors, inacti
3336
console.log('Current members of ' + writeTeam + ':');
3437
console.log(currentTeamMembers);
3538

36-
const [removedContributors, cannotRemoveYet] = await removeInactiveMembers(previousContributors, inactiveWithOpenIssue, currentTeamMembers);
39+
const removedContributors = await removeInactiveMembers(previousContributors, inactiveWithOpenSkills, inactiveWithOpenIssue, currentTeamMembers);
3740
console.log(`-`.repeat(60));
3841
console.log('Removed members from ' + writeTeam + ' inactive since ' + dates[1].slice(0, 10) + ':');
3942
console.log(removedContributors);
4043

41-
console.log(`-`.repeat(60));
42-
console.log('Members inactive since ' + dates[1].slice(0, 10) + ' with open issues preventing removal:');
43-
console.log(cannotRemoveYet);
44-
4544
// Repeat getTeamMembers() after removedContributors to compare with recentContributors
4645
const updatedTeamMembers = await getTeamMembers(github, context, writeTeam);
4746
const notifiedContributors = await notifyInactiveMembers(updatedTeamMembers, recentContributors);
4847
console.log(`-`.repeat(60));
4948
console.log('Notified members from ' + writeTeam + ' inactive since ' + dates[0].slice(0, 10) + ':');
5049
console.log(notifiedContributors);
5150

52-
writeData(removedContributors, notifiedContributors, cannotRemoveYet);
51+
// Final pass to get all open, assigned issues to check whether assignee either isn't a team member or is inactive
52+
const currentPMTeam = await getTeamMembers(github, context, PMTeam);
53+
const writeAndPMTeam = { ...updatedTeamMembers, ...currentPMTeam };
54+
const [nonTeamOpenIssue, inactiveOpenIssue] = await getOpenAssignedIssues(github, context, writeAndPMTeam, inactiveWithOpenIssue);
55+
console.log(`-`.repeat(60));
56+
console.log('Members inactive since ' + dates[1].slice(0, 10) + ' with open issues preventing removal:');
57+
console.log(inactiveOpenIssue);
58+
59+
writeData(removedContributors, notifiedContributors, nonTeamOpenIssue, inactiveOpenIssue);
5360
};
5461

5562

5663

5764
/**
5865
* Remove contributors that were last active **before** the previous (twoMonthsAgo) date
5966
* @param {Object} previousContributors - List of contributors active since previous date
67+
* @param {Object} inactiveWithOpenSkills - Inactive members with open Skills Issue
6068
* @param {Object} inactiveWithOpenIssue - Inactive members with open issues
61-
* @returns {Array} removedMembers - List of members that were removed
62-
* @returns {Object} cannotRemoveYet - List of members that cannot be removed due to open issues
69+
* @param {Object} currentTeamMembers - Current team members
70+
* @returns {Object} removedMembers - List of members that were removed
6371
*/
64-
async function removeInactiveMembers(previousContributors, inactiveWithOpenIssue, currentTeamMembers){
72+
async function removeInactiveMembers(previousContributors, inactiveWithOpenSkills, inactiveWithOpenIssue, currentTeamMembers) {
6573
const removedMembers = [];
66-
const cannotRemoveYet = {};
6774
const previouslyNotified = await readPreviousNotifyList();
6875

69-
// Loop over team members and remove them from the team if they are not in previousContributors list
70-
for(const username in currentTeamMembers){
71-
if (!previousContributors[username]){
76+
// Loop over team members and remove them from the team if they
77+
// are not in either previousContributors or inactiveWithOpenIssue
78+
for (const username in currentTeamMembers) {
79+
if ((!previousContributors[username]) || !(username in inactiveWithOpenIssue)) {
7280
// Prior to deletion, confirm that member is on the baseTeam
7381
await addTeamMember(github, context, baseTeam, username);
74-
// But if member has an open issue or was not on the previouslyNotified list, do not remove yet
75-
if(username in inactiveWithOpenIssue && inactiveWithOpenIssue[username][1] === false){
76-
cannotRemoveYet[username] = inactiveWithOpenIssue[username][0];
77-
} else if((previouslyNotified.length > 0) && !(previouslyNotified.includes(username))){
78-
console.log('Member was not on last month\'s \'Inactive Members\' list, do not remove: ' + username);
82+
// If member was not on the previouslyNotified list, do not remove yet
83+
if ((previouslyNotified.length > 0) && !(previouslyNotified.includes(username))) {
84+
console.log(`Member was not on last month's 'Inactive Members' list, do not remove: ${username}`);
7985
} else {
8086
// Remove member from all teams (except baseTeam)
81-
const teams = [writeTeam, mergeTeam];
82-
for(const team of teams){
87+
for (const team of [writeTeam, mergeTeam]) {
8388
// https://docs.github.com/en/rest/teams/members?apiVersion=2022-11-28#remove-team-membership-for-a-user
8489
await github.request('DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}', {
8590
org: context.repo.owner,
8691
team_slug: team,
87-
username: username,
92+
username,
8893
});
8994
}
9095
removedMembers.push(username);
9196
// After removal, close member's "Skills Issue", if open
92-
if(username in inactiveWithOpenIssue && inactiveWithOpenIssue[username][1] === true){
93-
closePrework(username, inactiveWithOpenIssue[username][0]);
97+
if (username in inactiveWithOpenSkills) {
98+
closePrework(username, inactiveWithOpenSkills[username]);
9499
}
95100
}
96101
}
97102
}
98-
return [removedMembers, cannotRemoveYet];
103+
return removedMembers;
99104
}
100105

101106

@@ -200,14 +205,15 @@ async function checkMemberIsNotNew(member){
200205

201206
/**
202207
* Function to save inactive members list to local repo for use in next job
203-
* @param {Array} removedContributors - List of contributors that were removed
204-
* @param {Array} notifiedContributors - List of contributors to be notified
205-
* @param {Array} cannotRemoveYet - List of contributors that can't be removed yet
208+
* @param {Array} removedContributors - List of contributors that were removed
209+
* @param {Array} notifiedContributors - List of contributors to be notified
210+
* @param {Array} nonTeamOpenIssue - List of non-team members with open issues
211+
* @param {Array} inactiveOpenIssue - List of inactive members that can't be removed yet
206212
*/
207-
function writeData(removedContributors, notifiedContributors, cannotRemoveYet){
213+
function writeData(removedContributors, notifiedContributors, nonTeamOpenIssue, inactiveOpenIssue) {
208214

209215
const filepath = 'github-actions/utils/_data/inactive-members.json';
210-
const inactiveMemberLists = { removedContributors, notifiedContributors, cannotRemoveYet };
216+
const inactiveMemberLists = { removedContributors, notifiedContributors, nonTeamOpenIssue, inactiveOpenIssue };
211217

212218
fs.writeFile(filepath, JSON.stringify(inactiveMemberLists, null, 2), (err) => {
213219
if (err) throw err;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Function to get all repo issues that either are not assigned to a currentTeam member, or are assigned to
3+
* inactive members so that leadership can be made aware that the issue does not have an active team member assigned.
4+
* @param {Object} currentTeam - currentTeam members (optional)
5+
* @param {Object} inactiveMemberOpenIssue - inactive team members assigned to an open issue (optional)
6+
* @return {Object} nonTeamMemberOpenIssue - non-team members assigned to open issues
7+
* @return {Object} inactiveMemberOpenIssue - inactive team members, all assignments to open issues
8+
*/
9+
async function getOpenAssignedIssues(github, context, currentTeam = {}, inactiveMemberOpenIssue = {}) {
10+
let nonTeamMemberOpenIssue = {};
11+
let pageNum = 1;
12+
let result = [];
13+
14+
// Since Github only allows to fetch max 100 items per request, we need to 'flip' pages
15+
while (true) {
16+
// Fetch 100 items per each page (`pageNum`)
17+
// https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues
18+
const openIssues = await github.request('GET /repos/{owner}/{repo}/issues', {
19+
owner: context.repo.owner,
20+
repo: context.repo.repo,
21+
assignee: '*',
22+
per_page: 100,
23+
page: pageNum
24+
});
25+
26+
// If the API call returns an empty array, break out of loop- there is no additional data.
27+
// Else if data is returned, push it to `result` and increase the page number (`pageNum`)
28+
if (!openIssues.data.length) {
29+
break;
30+
} else {
31+
result = result.concat(openIssues.data);
32+
pageNum++;
33+
}
34+
}
35+
36+
// Loop through each result individually
37+
for (const contributorInfo of result) {
38+
let assignee = contributorInfo.assignee.login;
39+
let issueNum = contributorInfo.number;
40+
// Check if assignee is not a currentTeam member, then find their other open issues
41+
// Else if assignee is on the inactiveMember list, find all of their open issues
42+
if (!(assignee in currentTeam)) {
43+
(nonTeamMemberOpenIssue[assignee] ??= []).push(issueNum);
44+
} else if (assignee in inactiveMemberOpenIssue && !inactiveMemberOpenIssue[assignee].includes(issueNum)) {
45+
inactiveMemberOpenIssue[assignee].push(issueNum);
46+
}
47+
}
48+
49+
return [nonTeamMemberOpenIssue, inactiveMemberOpenIssue];
50+
}
51+
52+
module.exports = getOpenAssignedIssues;

0 commit comments

Comments
 (0)