@@ -25237,10 +25237,12 @@ const github = __importStar(__nccwpck_require__(5438));
25237
25237
const moment_1 = __importDefault(__nccwpck_require__(9623));
25238
25238
const fs_1 = __nccwpck_require__(7147);
25239
25239
const artifact = __importStar(__nccwpck_require__(2605));
25240
+ const request_error_1 = __nccwpck_require__(537);
25240
25241
function getInputs() {
25241
25242
const result = {};
25242
25243
result.token = core.getInput('github-token');
25243
25244
result.org = core.getInput('organization');
25245
+ result.enterprise = core.getInput('enterprise');
25244
25246
result.removeInactive = core.getBooleanInput('remove');
25245
25247
result.removefromTeam = core.getBooleanInput('remove-from-team');
25246
25248
result.inactiveDays = parseInt(core.getInput('inactive-days'));
@@ -25251,89 +25253,160 @@ function getInputs() {
25251
25253
exports.getInputs = getInputs;
25252
25254
const run = () => __awaiter(void 0, void 0, void 0, function* () {
25253
25255
const input = getInputs();
25256
+ let organizations = [];
25257
+ let hasNextPage = false;
25258
+ let afterCursor = undefined;
25259
+ let allInactiveSeats = [];
25260
+ let allRemovedSeatsCount = 0;
25261
+ let allSeatsCount = 0;
25254
25262
const octokit = github.getOctokit(input.token);
25255
- const seats = yield core.group('Fetching GitHub Copilot seats', () => __awaiter(void 0, void 0, void 0, function* ( ) {
25256
- let _seats = [], totalSeats = 0, page = 1 ;
25263
+ if (input.enterprise && input.enterprise !== null ) {
25264
+ core.info(`Fetching all organizations for ${input.enterprise}...`) ;
25257
25265
do {
25258
- const response = yield octokit.request(`GET /orgs/{org}/copilot/billing/seats?per_page=100&page=${page}`, {
25259
- org: input.org
25260
- });
25261
- totalSeats = response.data.total_seats;
25262
- _seats = _seats.concat(response.data.seats);
25263
- page++;
25264
- } while (_seats.length < totalSeats);
25265
- core.info(`Found ${_seats.length} seats`);
25266
- core.info(JSON.stringify(_seats, null, 2));
25267
- return _seats;
25268
- }));
25269
- const msToDays = (d) => Math.ceil(d / (1000 * 3600 * 24));
25270
- const now = new Date();
25271
- const inactiveSeats = seats.filter(seat => {
25272
- if (seat.last_activity_at === null || seat.last_activity_at === undefined) {
25273
- const created = new Date(seat.created_at);
25274
- const diff = now.getTime() - created.getTime();
25266
+ const query = `
25267
+ query ($enterprise: String!, $after: String) {
25268
+ enterprise(slug: $enterprise) {
25269
+ organizations(first: 100, after: $after) {
25270
+ pageInfo {
25271
+ endCursor
25272
+ hasNextPage
25273
+ }
25274
+ nodes {
25275
+ login
25276
+ }
25277
+ }
25278
+ }
25279
+ }
25280
+ `;
25281
+ const variables = { "enterprise": input.enterprise, "after": afterCursor };
25282
+ const response = yield octokit.graphql(query, variables);
25283
+ organizations = organizations.concat(response.enterprise.organizations.nodes.map(org => org.login));
25284
+ hasNextPage = response.enterprise.organizations.pageInfo.hasNextPage;
25285
+ afterCursor = response.enterprise.organizations.pageInfo.endCursor;
25286
+ } while (hasNextPage);
25287
+ core.info(`Found ${organizations.length} organizations: ${organizations.join(', ')}`);
25288
+ }
25289
+ else {
25290
+ organizations = input.org.split(',').map(org => org.trim());
25291
+ }
25292
+ for (const org of organizations) {
25293
+ const seats = yield core.group('Fetching GitHub Copilot seats for ' + org, () => __awaiter(void 0, void 0, void 0, function* () {
25294
+ let _seats = [], totalSeats = 0, page = 1;
25295
+ do {
25296
+ try {
25297
+ const response = yield octokit.request(`GET /orgs/{org}/copilot/billing/seats?per_page=100&page=${page}`, {
25298
+ org: org
25299
+ });
25300
+ totalSeats = response.data.total_seats;
25301
+ _seats = _seats.concat(response.data.seats);
25302
+ page++;
25303
+ }
25304
+ catch (error) {
25305
+ if (error instanceof request_error_1.RequestError && error.message === "Copilot Business is not enabled for this organization.") {
25306
+ core.error(error.message + ` (${org})`);
25307
+ break;
25308
+ }
25309
+ else if (error instanceof request_error_1.RequestError && error.status === 404) {
25310
+ core.error(error.message + ` (${org}). Please ensure that the organization has GitHub Copilot enabled and you are an org owner.`);
25311
+ break;
25312
+ }
25313
+ else {
25314
+ throw error;
25315
+ }
25316
+ }
25317
+ } while (_seats.length < totalSeats);
25318
+ core.info(`Found ${_seats.length} seats`);
25319
+ core.info(JSON.stringify(_seats, null, 2));
25320
+ return _seats;
25321
+ }));
25322
+ const msToDays = (d) => Math.ceil(d / (1000 * 3600 * 24));
25323
+ const now = new Date();
25324
+ const inactiveSeats = seats.filter(seat => {
25325
+ if (seat.last_activity_at === null || seat.last_activity_at === undefined) {
25326
+ const created = new Date(seat.created_at);
25327
+ const diff = now.getTime() - created.getTime();
25328
+ return msToDays(diff) > input.inactiveDays;
25329
+ }
25330
+ const lastActive = new Date(seat.last_activity_at);
25331
+ const diff = now.getTime() - lastActive.getTime();
25275
25332
return msToDays(diff) > input.inactiveDays;
25333
+ }).sort((a, b) => (a.last_activity_at === null || a.last_activity_at === undefined || b.last_activity_at === null || b.last_activity_at === undefined ?
25334
+ -1 : new Date(a.last_activity_at).getTime() - new Date(b.last_activity_at).getTime()));
25335
+ const inactiveSeatsWithOrg = inactiveSeats.map(seat => (Object.assign(Object.assign({}, seat), { organization: org })));
25336
+ allInactiveSeats = [...allInactiveSeats, ...inactiveSeatsWithOrg];
25337
+ allSeatsCount += seats.length;
25338
+ if (input.removeInactive) {
25339
+ const inactiveSeatsAssignedIndividually = inactiveSeats.filter(seat => !seat.assigning_team);
25340
+ if (inactiveSeatsAssignedIndividually.length > 0) {
25341
+ core.group('Removing inactive seats', () => __awaiter(void 0, void 0, void 0, function* () {
25342
+ const response = yield octokit.request(`DELETE /orgs/{org}/copilot/billing/selected_users`, {
25343
+ org: org,
25344
+ selected_usernames: inactiveSeatsAssignedIndividually.map(seat => seat.assignee.login),
25345
+ });
25346
+ core.info(`Removed ${response.data.seats_cancelled} seats`);
25347
+ console.log(typeof response.data.seats_cancelled);
25348
+ allRemovedSeatsCount += response.data.seats_cancelled;
25349
+ }));
25350
+ }
25276
25351
}
25277
- const lastActive = new Date(seat.last_activity_at);
25278
- const diff = now.getTime() - lastActive.getTime();
25279
- return msToDays(diff) > input.inactiveDays;
25280
- }).sort((a, b) => (a.last_activity_at === null || a.last_activity_at === undefined || b.last_activity_at === null || b.last_activity_at === undefined ?
25281
- -1 : new Date(a.last_activity_at).getTime() - new Date(b.last_activity_at).getTime()));
25282
- core.setOutput('inactive-seats', JSON.stringify(inactiveSeats));
25283
- core.setOutput('inactive-seat-count', inactiveSeats.length.toString());
25284
- core.setOutput('seat-count', seats.length.toString());
25285
- if (input.removeInactive) {
25286
- const inactiveSeatsAssignedIndividually = inactiveSeats.filter(seat => !seat.assigning_team);
25287
- if (inactiveSeatsAssignedIndividually.length > 0) {
25288
- core.group('Removing inactive seats', () => __awaiter(void 0, void 0, void 0, function* () {
25289
- const response = yield octokit.request(`DELETE /orgs/{org}/copilot/billing/selected_users`, {
25290
- org: input.org,
25291
- selected_usernames: inactiveSeatsAssignedIndividually.map(seat => seat.assignee.login),
25292
- });
25293
- core.info(`Removed ${response.data.seats_cancelled} seats`);
25294
- core.setOutput('removed-seats', response.data.seats_cancelled);
25352
+ if (input.removefromTeam) {
25353
+ const inactiveSeatsAssignedByTeam = inactiveSeats.filter(seat => seat.assigning_team);
25354
+ core.group('Removing inactive seats from team', () => __awaiter(void 0, void 0, void 0, function* () {
25355
+ for (const seat of inactiveSeatsAssignedByTeam) {
25356
+ if (!seat.assigning_team || typeof (seat.assignee.login) !== 'string')
25357
+ continue;
25358
+ yield octokit.request('DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}', {
25359
+ org: org,
25360
+ team_slug: seat.assigning_team.slug,
25361
+ username: seat.assignee.login
25362
+ });
25363
+ }
25295
25364
}));
25296
25365
}
25297
- }
25298
- if (input.removefromTeam) {
25299
- const inactiveSeatsAssignedByTeam = inactiveSeats.filter(seat => seat.assigning_team);
25300
- core.group('Removing inactive seats from team', () => __awaiter(void 0, void 0, void 0, function* () {
25301
- for (const seat of inactiveSeatsAssignedByTeam) {
25302
- if (!seat.assigning_team || typeof (seat.assignee.login) !== 'string')
25303
- continue;
25304
- yield octokit.request('DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}', {
25305
- org: input.org,
25306
- team_slug: seat.assigning_team.slug,
25307
- username: seat.assignee.login
25308
- });
25366
+ if (input.jobSummary) {
25367
+ yield core.summary
25368
+ .addHeading(`${org} - Inactive Seats: ${inactiveSeats.length.toString()} / ${seats.length.toString()}`);
25369
+ if (seats.length > 0) {
25370
+ core.summary.addTable([
25371
+ [
25372
+ { data: 'Avatar', header: true },
25373
+ { data: 'Login', header: true },
25374
+ { data: 'Last Activity', header: true },
25375
+ { data: 'Last Editor Used', header: true }
25376
+ ],
25377
+ ...inactiveSeats.sort((a, b) => {
25378
+ const loginA = (a.assignee.login || 'Unknown');
25379
+ const loginB = (b.assignee.login || 'Unknown');
25380
+ return loginA.localeCompare(loginB);
25381
+ }).map(seat => [
25382
+ `<img src="${seat.assignee.avatar_url}" width="33" />`,
25383
+ seat.assignee.login || 'Unknown',
25384
+ seat.last_activity_at === null ? 'No activity' : (0, moment_1.default)(seat.last_activity_at).fromNow(),
25385
+ seat.last_activity_editor || 'Unknown'
25386
+ ])
25387
+ ]);
25309
25388
}
25310
- }));
25311
- }
25312
- if (input.jobSummary) {
25313
- yield core.summary
25314
- .addHeading(`Inactive Seats: ${inactiveSeats.length.toString()} / ${seats.length.toString()}`)
25315
- .addTable([
25316
- [
25317
- { data: 'Avatar', header: true },
25318
- { data: 'Login', header: true },
25319
- { data: 'Last Activity', header: true },
25320
- { data: 'Last Editor Used', header: true }
25321
- ],
25322
- ...inactiveSeats.map(seat => [
25323
- `<img src="${seat.assignee.avatar_url}" width="33" />`,
25324
- seat.assignee.login || 'Unknown',
25325
- seat.last_activity_at === null ? 'No activity' : (0, moment_1.default)(seat.last_activity_at).fromNow(),
25326
- seat.last_activity_editor || 'Unknown'
25327
- ])
25328
- ])
25329
- .addLink('Manage GitHub Copilot seats', `https://github.com/organizations/${input.org}/settings/copilot/seat_management`)
25330
- .write();
25389
+ core.summary.addLink('Manage GitHub Copilot seats', `https://github.com/organizations/${org}/settings/copilot/seat_management`)
25390
+ .write();
25391
+ }
25331
25392
}
25332
25393
if (input.csv) {
25333
25394
core.group('Writing CSV', () => __awaiter(void 0, void 0, void 0, function* () {
25395
+ const sortedSeats = allInactiveSeats.sort((a, b) => {
25396
+ if (a.organization < b.organization)
25397
+ return -1;
25398
+ if (a.organization > b.organization)
25399
+ return 1;
25400
+ if (a.assignee.login < b.assignee.login)
25401
+ return -1;
25402
+ if (a.assignee.login > b.assignee.login)
25403
+ return 1;
25404
+ return 0;
25405
+ });
25334
25406
const csv = [
25335
- ['Login', 'Last Activity', 'Last Editor Used'],
25336
- ...inactiveSeats.map(seat => [
25407
+ ['Organization', 'Login', 'Last Activity', 'Last Editor Used'],
25408
+ ...sortedSeats.map(seat => [
25409
+ seat.organization,
25337
25410
seat.assignee.login,
25338
25411
seat.last_activity_at === null ? 'No activity' : (0, moment_1.default)(seat.last_activity_at).fromNow(),
25339
25412
seat.last_activity_editor || '-'
@@ -25344,6 +25417,10 @@ const run = () => __awaiter(void 0, void 0, void 0, function* () {
25344
25417
yield artifactClient.uploadArtifact('inactive-seats', ['inactive-seats.csv'], '.');
25345
25418
}));
25346
25419
}
25420
+ core.setOutput('inactive-seats', JSON.stringify(allInactiveSeats));
25421
+ core.setOutput('inactive-seat-count', allInactiveSeats.length.toString());
25422
+ core.setOutput('seat-count', allSeatsCount.toString());
25423
+ core.setOutput('removed-seats', allRemovedSeatsCount.toString());
25347
25424
});
25348
25425
run();
25349
25426
0 commit comments