Skip to content

Commit

Permalink
Merge pull request #243 from aws-actions/upgrade-octokit
Browse files Browse the repository at this point in the history
Upgrade octokit
  • Loading branch information
alikulka authored Jan 17, 2025
2 parents f92752a + eb56b93 commit 86a7b74
Show file tree
Hide file tree
Showing 9 changed files with 810 additions and 1,019 deletions.
917 changes: 298 additions & 619 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@
"homepage": "https://github.com/aws-actions/stale-issue-cleanup/#readme",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^2.2.0",
"@actions/github": "^6.0.0",
"dateformat": "^4.4.1"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@octokit/types": "^13.7.0",
"@fetch-mock/vitest": "^0.2.6",
"@octokit/plugin-rest-endpoint-methods": "^13.3.0",
"@types/dateformat": "^5.0.3",
"@types/node": "^22.10.7",
"@vercel/ncc": "^0.38.3",
"@vitest/coverage-v8": "^2.1.8",
"dotenv": "^16.0.3",
"nock": "^13.3.0",
"typescript": "^5.7.3",
"vitest": "^2.1.8"
}
Expand Down
6 changes: 3 additions & 3 deletions src/entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export function getAndValidateInputs(): Inputs {
return args;
}

export async function processIssues(client: github.GitHub, args: Inputs) {
export async function processIssues(client: ReturnType<typeof github.getOctokit>, args: Inputs) {
const uniqueIssues = await getIssues(client, args);

for await (const _ of uniqueIssues.map(async (issue) => {
Expand Down Expand Up @@ -219,12 +219,12 @@ export async function processIssues(client: github.GitHub, args: Inputs) {
}));
}

export async function run(): Promise<void> {
export async function run(fetchImpl?: typeof globalThis.fetch): Promise<void> {
try {
core.info('Starting issue processing');
const args = getAndValidateInputs();
core.debug(JSON.stringify(args, null, 2));
const client = new github.GitHub({ auth: args.repoToken, userAgent: 'GHA Stale Issue' });
const client = github.getOctokit(args.repoToken, { request: { fetch: fetchImpl || globalThis.fetch } });
await processIssues(client, args);
core.info('Labelled issue processing complete');
process.exitCode = 0;
Expand Down
122 changes: 57 additions & 65 deletions src/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,160 +5,152 @@ import type { issueTimelineEventsType, issueType } from './utils';

const MS_PER_DAY = 86400000;

export async function closeIssue(client: github.GitHub, issue: issueType, cfsLabel: string) {
export async function closeIssue(client: ReturnType<typeof github.getOctokit>, issue: issueType, cfsLabel: string) {
core.debug(`closing issue #${issue.number} for staleness`);
if (cfsLabel && cfsLabel !== '') {
await client.issues.addLabels({
await client.rest.issues.addLabels({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
labels: [cfsLabel],
});
}
await client.issues.update({
await client.rest.issues.update({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
state: 'closed',
});
}

export async function removeLabel(client: github.GitHub, issue: issueType, label: string) {
export async function removeLabel(client: ReturnType<typeof github.getOctokit>, issue: issueType, label: string) {
core.debug(`removing label ${label} from issue #${issue.number}`);
await client.issues.removeLabel({
await client.rest.issues.removeLabel({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
name: label,
});
}

export async function markStale(client: github.GitHub, issue: issueType, staleMessage: string, staleLabel: string) {
export async function markStale(
client: ReturnType<typeof github.getOctokit>,
issue: issueType,
staleMessage: string,
staleLabel: string,
) {
core.debug(`marking issue #${issue.number} as stale`);
await client.issues.createComment({
await client.rest.issues.createComment({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
body: staleMessage,
});
await client.issues.addLabels({
await client.rest.issues.addLabels({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issue.number,
labels: [staleLabel],
});
}

export async function getTimelineEvents(client: github.GitHub, issue: issueType): Promise<issueTimelineEventsType[]> {
const options = client.issues.listEventsForTimeline.endpoint.merge({
export async function getTimelineEvents(
client: ReturnType<typeof github.getOctokit>,
issue: issueType,
): Promise<issueTimelineEventsType[]> {
return client.paginate(client.rest.issues.listEventsForTimeline, {
issue_number: issue.number,
owner: github.context.repo.owner,
repo: github.context.repo.repo,
per_page: 100,
});
return client.paginate(options);
}

export async function getIssues(client: github.GitHub, args: Inputs): Promise<Array<issueType>> {
let responseIssues = [];
let staleIssues = [];
let stalePrs = [];
let ancientIssues = [];

let options = client.issues.listForRepo.endpoint.merge({
export async function getIssues(client: ReturnType<typeof github.getOctokit>, args: Inputs): Promise<Array<issueType>> {
const responseIssues: issueType[] = await client.paginate(client.rest.issues.listForRepo, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
state: 'open',
labels: args.responseRequestedLabel,
per_page: 100,
});
responseIssues = await client.paginate(options);
core.debug(`found ${responseIssues.length} response-requested issues`);

const staleIssues: issueType[] = [];
if (args.staleIssueMessage && args.staleIssueMessage !== '') {
options = client.issues.listForRepo.endpoint.merge({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
state: 'open',
labels: args.staleIssueLabel,
per_page: 100,
});
staleIssues = await client.paginate(options);
staleIssues.push(
...(await client.paginate(client.rest.issues.listForRepo, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
state: 'open',
labels: args.staleIssueLabel,
per_page: 100,
})),
);
core.debug(`found ${staleIssues.length} stale issues`);
} else {
core.debug('skipping stale issues due to empty message');
}

const stalePrs: issueType[] = [];
if (args.stalePrMessage && args.stalePrMessage !== '') {
options = client.issues.listForRepo.endpoint.merge({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
state: 'open',
labels: args.stalePrLabel,
per_page: 100,
});
stalePrs = await client.paginate(options);
stalePrs.push(
...(await client.paginate(client.rest.issues.listForRepo, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
state: 'open',
labels: args.stalePrLabel,
per_page: 100,
})),
);
core.debug(`found ${stalePrs.length} stale PRs`);
} else {
core.debug('skipping stale PRs due to empty message');
}

const ancientIssues: issueType[] = [];
if (args.ancientIssueMessage && args.ancientIssueMessage !== '') {
core.debug(
`using issue ${args.useCreatedDateForAncient ? 'created date' : 'last updated'} to determine for getting ancient issues.`,
);
options = client.issues.listForRepo.endpoint.merge({
const ancientResults = await client.paginate(client.rest.issues.listForRepo, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
state: 'open',
per_page: 100,
sort: 'updated',
direction: 'asc',
});
const ancientResults = await client.paginate(options);
ancientIssues = ancientResults.filter(
(issue) =>
(args.useCreatedDateForAncient ? new Date(issue.created_at) : new Date(issue.updated_at)) <
new Date(Date.now() - MS_PER_DAY * args.daysBeforeAncient),
);
ancientResults
.filter(
(issue) =>
(args.useCreatedDateForAncient ? new Date(issue.created_at) : new Date(issue.updated_at)) <
new Date(Date.now() - MS_PER_DAY * args.daysBeforeAncient),
)
.map((i) => ancientIssues.push(i));
core.debug(`found ${ancientIssues.length} ancient issues`);
} else {
core.debug('skipping ancient issues due to empty message');
}

const issues = [...responseIssues, ...staleIssues, ...stalePrs, ...ancientIssues];
return Object.values(
issues.reduce((unique, item) => {
unique[`${item.id}`] = item;
return unique;
}, []),
);
// Dedupe issues based on id
const ids = new Set();
return issues.filter((issue) => (ids.has(issue.id) ? false : ids.add(issue.id)));
}

export async function hasEnoughUpvotes(
client: github.GitHub,
client: ReturnType<typeof github.getOctokit>,
issueNumber: number,
upvoteCount: number,
): Promise<boolean> {
const options = client.reactions.listForIssue.endpoint.merge({
const reactions = await client.paginate(client.rest.reactions.listForIssue, {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
issue_number: issueNumber,
mediaType: { previews: ['squirrel-girl-preview'] },
// The squirrel-girl preview is no longer needed in newer versions
per_page: 100,
});
let reactions = await client.paginate(options);
//for some reason, the client.paginate thing returns an Array containing an Array containing reactions.
//we only want the inner Array.
reactions = reactions[0];
if (reactions && reactions.length > 0) {
const upvotes = reactions.reduce((acc: number, cur: { content: string }) => {
if (cur.content === '+1' || cur.content === 'heart' || cur.content === 'hooray' || cur.content === 'rocket') {
return acc + 1;
}
return acc;
}, 0);
return upvotes >= upvoteCount;
}
return false;
const upvotes = reactions.reduce((acc, cur) => (cur.content.match(/\+1|heart|hooray|rocket/) ? acc + 1 : acc), 0);
return upvotes >= upvoteCount;
}
9 changes: 5 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as core from '@actions/core';
import type { Endpoints } from '@octokit/types';
import type { RestEndpointMethodTypes } from '@octokit/plugin-rest-endpoint-methods';
import dateformat from 'dateformat';

export type issueType = Endpoints['GET /repos/{owner}/{repo}/issues']['response']['data'][0];
export type issueTimelineEventsType =
Endpoints['GET /repos/{owner}/{repo}/issues/{issue_number}/timeline']['response']['data'][0];
export type issueType =
| RestEndpointMethodTypes['issues']['get']['response']['data']
| RestEndpointMethodTypes['pulls']['get']['response']['data'];
export type issueTimelineEventsType = RestEndpointMethodTypes['issues']['listEventsForTimeline']['response']['data'][0];

export function isLabeled(issue: issueType, label: string) {
if ('labels' in issue) {
Expand Down
Loading

0 comments on commit 86a7b74

Please sign in to comment.