mirror of
https://github.com/actions/stale.git
synced 2025-12-11 04:32:53 +00:00
committed by
GitHub
parent
9d6f46564a
commit
e96f31f877
@@ -1,11 +1,12 @@
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
import {IMilestone} from '../interfaces/milestone';
|
||||
import {IssueProcessorOptions, Label} from '../IssueProcessor';
|
||||
import {Issue} from './issue';
|
||||
|
||||
describe('Issue', (): void => {
|
||||
let issue: Issue;
|
||||
let optionsInterface: IssueProcessorOptions;
|
||||
let optionsInterface: IIssuesProcessorOptions;
|
||||
let issueInterface: IIssue;
|
||||
|
||||
beforeEach((): void => {
|
||||
@@ -91,7 +92,7 @@ describe('Issue', (): void => {
|
||||
expect(issue.labels).toStrictEqual([
|
||||
{
|
||||
name: 'dummy-name'
|
||||
} as Label
|
||||
} as ILabel
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -193,7 +194,7 @@ describe('Issue', (): void => {
|
||||
issueInterface.labels = [
|
||||
{
|
||||
name: 'dummy-stale-issue-label'
|
||||
} as Label
|
||||
} as ILabel
|
||||
];
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import {isLabeled} from '../functions/is-labeled';
|
||||
import {isPullRequest} from '../functions/is-pull-request';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
import {IMilestone} from '../interfaces/milestone';
|
||||
import {IssueProcessorOptions, Label} from '../IssueProcessor';
|
||||
import {IsoDateString} from '../types/iso-date-string';
|
||||
|
||||
export class Issue implements IIssue {
|
||||
private readonly _options: IssueProcessorOptions;
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
readonly title: string;
|
||||
readonly number: number;
|
||||
created_at: IsoDateString;
|
||||
updated_at: IsoDateString;
|
||||
readonly labels: Label[];
|
||||
readonly labels: ILabel[];
|
||||
readonly pull_request: Object | null | undefined;
|
||||
readonly state: string;
|
||||
readonly locked: boolean;
|
||||
@@ -21,7 +22,7 @@ export class Issue implements IIssue {
|
||||
isStale: boolean;
|
||||
|
||||
constructor(
|
||||
options: Readonly<IssueProcessorOptions>,
|
||||
options: Readonly<IIssuesProcessorOptions>,
|
||||
issue: Readonly<IIssue>
|
||||
) {
|
||||
this._options = options;
|
||||
|
||||
@@ -1,84 +1,29 @@
|
||||
import {context, getOctokit} from '@actions/github';
|
||||
import {GitHub} from '@actions/github/lib/utils';
|
||||
import {GetResponseTypeFromEndpointMethod} from '@octokit/types';
|
||||
import {Issue} from './classes/issue';
|
||||
import {IssueLogger} from './classes/loggers/issue-logger';
|
||||
import {Logger} from './classes/loggers/logger';
|
||||
import {Milestones} from './classes/milestones';
|
||||
import {IssueType} from './enums/issue-type';
|
||||
import {getHumanizedDate} from './functions/dates/get-humanized-date';
|
||||
import {isDateMoreRecentThan} from './functions/dates/is-date-more-recent-than';
|
||||
import {isValidDate} from './functions/dates/is-valid-date';
|
||||
import {getIssueType} from './functions/get-issue-type';
|
||||
import {isLabeled} from './functions/is-labeled';
|
||||
import {isPullRequest} from './functions/is-pull-request';
|
||||
import {shouldMarkWhenStale} from './functions/should-mark-when-stale';
|
||||
import {IsoOrRfcDateString} from './types/iso-or-rfc-date-string';
|
||||
import {wordsToList} from './functions/words-to-list';
|
||||
import {IIssue} from './interfaces/issue';
|
||||
|
||||
export interface PullRequest {
|
||||
number: number;
|
||||
head: {
|
||||
ref: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface User {
|
||||
type: string;
|
||||
login: string;
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface IssueEvent {
|
||||
created_at: string;
|
||||
event: string;
|
||||
label: Label;
|
||||
}
|
||||
|
||||
export interface Label {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IssueProcessorOptions {
|
||||
repoToken: string;
|
||||
staleIssueMessage: string;
|
||||
stalePrMessage: string;
|
||||
closeIssueMessage: string;
|
||||
closePrMessage: string;
|
||||
daysBeforeStale: number;
|
||||
daysBeforeIssueStale: number; // Could be NaN
|
||||
daysBeforePrStale: number; // Could be NaN
|
||||
daysBeforeClose: number;
|
||||
daysBeforeIssueClose: number; // Could be NaN
|
||||
daysBeforePrClose: number; // Could be NaN
|
||||
staleIssueLabel: string;
|
||||
closeIssueLabel: string;
|
||||
exemptIssueLabels: string;
|
||||
stalePrLabel: string;
|
||||
closePrLabel: string;
|
||||
exemptPrLabels: string;
|
||||
onlyLabels: string;
|
||||
operationsPerRun: number;
|
||||
removeStaleWhenUpdated: boolean;
|
||||
debugOnly: boolean;
|
||||
ascending: boolean;
|
||||
skipStaleIssueMessage: boolean;
|
||||
skipStalePrMessage: boolean;
|
||||
deleteBranch: boolean;
|
||||
startDate: IsoOrRfcDateString | undefined; // Should be ISO 8601 or RFC 2822
|
||||
exemptMilestones: string;
|
||||
exemptIssueMilestones: string;
|
||||
exemptPrMilestones: string;
|
||||
}
|
||||
import {IssueType} from '../enums/issue-type';
|
||||
import {getHumanizedDate} from '../functions/dates/get-humanized-date';
|
||||
import {isDateMoreRecentThan} from '../functions/dates/is-date-more-recent-than';
|
||||
import {isValidDate} from '../functions/dates/is-valid-date';
|
||||
import {getIssueType} from '../functions/get-issue-type';
|
||||
import {isLabeled} from '../functions/is-labeled';
|
||||
import {isPullRequest} from '../functions/is-pull-request';
|
||||
import {shouldMarkWhenStale} from '../functions/should-mark-when-stale';
|
||||
import {wordsToList} from '../functions/words-to-list';
|
||||
import {IComment} from '../interfaces/comment';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssueEvent} from '../interfaces/issue-event';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {IPullRequest} from '../interfaces/pull-request';
|
||||
import {Issue} from './issue';
|
||||
import {IssueLogger} from './loggers/issue-logger';
|
||||
import {Logger} from './loggers/logger';
|
||||
import {Milestones} from './milestones';
|
||||
|
||||
/***
|
||||
* Handle processing of issues for staleness/closure.
|
||||
*/
|
||||
export class IssueProcessor {
|
||||
export class IssuesProcessor {
|
||||
private static _updatedSince(timestamp: string, num_days: number): boolean {
|
||||
const daysInMillis = 1000 * 60 * 60 * 24 * num_days;
|
||||
const millisSinceLastUpdated =
|
||||
@@ -90,20 +35,20 @@ export class IssueProcessor {
|
||||
private readonly _logger: Logger = new Logger();
|
||||
private _operationsLeft = 0;
|
||||
readonly client: InstanceType<typeof GitHub>;
|
||||
readonly options: IssueProcessorOptions;
|
||||
readonly options: IIssuesProcessorOptions;
|
||||
readonly staleIssues: Issue[] = [];
|
||||
readonly closedIssues: Issue[] = [];
|
||||
readonly deletedBranchIssues: Issue[] = [];
|
||||
readonly removedLabelIssues: Issue[] = [];
|
||||
|
||||
constructor(
|
||||
options: IssueProcessorOptions,
|
||||
options: IIssuesProcessorOptions,
|
||||
getActor?: () => Promise<string>,
|
||||
getIssues?: (page: number) => Promise<Issue[]>,
|
||||
listIssueComments?: (
|
||||
issueNumber: number,
|
||||
sinceDate: string
|
||||
) => Promise<Comment[]>,
|
||||
) => Promise<IComment[]>,
|
||||
getLabelCreationDate?: (
|
||||
issue: Issue,
|
||||
label: string
|
||||
@@ -271,7 +216,7 @@ export class IssueProcessor {
|
||||
}
|
||||
|
||||
// should this issue be marked stale?
|
||||
const shouldBeStale = !IssueProcessor._updatedSince(
|
||||
const shouldBeStale = !IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeStale
|
||||
);
|
||||
@@ -350,7 +295,7 @@ export class IssueProcessor {
|
||||
issueLogger.info(`Days before issue close: ${daysBeforeClose}`);
|
||||
}
|
||||
|
||||
const issueHasUpdate: boolean = IssueProcessor._updatedSince(
|
||||
const issueHasUpdate: boolean = IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeClose
|
||||
);
|
||||
@@ -423,7 +368,7 @@ export class IssueProcessor {
|
||||
private async _listIssueComments(
|
||||
issueNumber: number,
|
||||
sinceDate: string
|
||||
): Promise<Comment[]> {
|
||||
): Promise<IComment[]> {
|
||||
// find any comments since date on the given issue
|
||||
try {
|
||||
const comments = await this.client.issues.listComments({
|
||||
@@ -586,7 +531,7 @@ export class IssueProcessor {
|
||||
|
||||
private async _getPullRequest(
|
||||
issue: Issue
|
||||
): Promise<PullRequest | undefined> {
|
||||
): Promise<IPullRequest | undefined> {
|
||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||
this._operationsLeft -= 1;
|
||||
|
||||
@@ -692,7 +637,7 @@ export class IssueProcessor {
|
||||
issue_number: issue.number
|
||||
});
|
||||
|
||||
const events: IssueEvent[] = await this.client.paginate(options);
|
||||
const events: IIssueEvent[] = await this.client.paginate(options);
|
||||
const reversedEvents = events.reverse();
|
||||
|
||||
const staleLabeledEvent = reversedEvents.find(
|
||||
@@ -1,11 +1,11 @@
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IssueProcessorOptions} from '../IssueProcessor';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
import {Milestones} from './milestones';
|
||||
|
||||
describe('Milestones', (): void => {
|
||||
let milestones: Milestones;
|
||||
let optionsInterface: IssueProcessorOptions;
|
||||
let optionsInterface: IIssuesProcessorOptions;
|
||||
let issue: Issue;
|
||||
let issueInterface: IIssue;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import deburr from 'lodash.deburr';
|
||||
import {wordsToList} from '../functions/words-to-list';
|
||||
import {IssueProcessorOptions} from '../IssueProcessor';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
|
||||
type CleanMilestone = string;
|
||||
@@ -10,10 +10,10 @@ export class Milestones {
|
||||
return deburr(label.toLowerCase());
|
||||
}
|
||||
|
||||
private readonly _options: IssueProcessorOptions;
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
private readonly _issue: Issue;
|
||||
|
||||
constructor(options: Readonly<IssueProcessorOptions>, issue: Issue) {
|
||||
constructor(options: Readonly<IIssuesProcessorOptions>, issue: Issue) {
|
||||
this._options = options;
|
||||
this._issue = issue;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import deburr from 'lodash.deburr';
|
||||
import {Issue} from '../classes/issue';
|
||||
import {Label} from '../IssueProcessor';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
import {CleanLabel} from '../types/clean-label';
|
||||
|
||||
/**
|
||||
@@ -16,7 +16,7 @@ export function isLabeled(
|
||||
issue: Readonly<Issue>,
|
||||
label: Readonly<string>
|
||||
): boolean {
|
||||
return !!issue.labels.find((issueLabel: Readonly<Label>): boolean => {
|
||||
return !!issue.labels.find((issueLabel: Readonly<ILabel>): boolean => {
|
||||
return cleanLabel(label) === cleanLabel(issueLabel.name);
|
||||
});
|
||||
}
|
||||
|
||||
5
src/interfaces/comment.ts
Normal file
5
src/interfaces/comment.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import {IUser} from './user';
|
||||
|
||||
export interface IComment {
|
||||
user: IUser;
|
||||
}
|
||||
7
src/interfaces/issue-event.ts
Normal file
7
src/interfaces/issue-event.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {ILabel} from './label';
|
||||
|
||||
export interface IIssueEvent {
|
||||
created_at: string;
|
||||
event: string;
|
||||
label: ILabel;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Label} from '../IssueProcessor';
|
||||
import {IsoDateString} from '../types/iso-date-string';
|
||||
import {ILabel} from './label';
|
||||
import {IMilestone} from './milestone';
|
||||
|
||||
export interface IIssue {
|
||||
@@ -7,7 +7,7 @@ export interface IIssue {
|
||||
number: number;
|
||||
created_at: IsoDateString;
|
||||
updated_at: IsoDateString;
|
||||
labels: Label[];
|
||||
labels: ILabel[];
|
||||
pull_request: Object | null | undefined;
|
||||
state: string;
|
||||
locked: boolean;
|
||||
|
||||
33
src/interfaces/issues-processor-options.ts
Normal file
33
src/interfaces/issues-processor-options.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import {IsoOrRfcDateString} from '../types/iso-or-rfc-date-string';
|
||||
|
||||
export interface IIssuesProcessorOptions {
|
||||
repoToken: string;
|
||||
staleIssueMessage: string;
|
||||
stalePrMessage: string;
|
||||
closeIssueMessage: string;
|
||||
closePrMessage: string;
|
||||
daysBeforeStale: number;
|
||||
daysBeforeIssueStale: number; // Could be NaN
|
||||
daysBeforePrStale: number; // Could be NaN
|
||||
daysBeforeClose: number;
|
||||
daysBeforeIssueClose: number; // Could be NaN
|
||||
daysBeforePrClose: number; // Could be NaN
|
||||
staleIssueLabel: string;
|
||||
closeIssueLabel: string;
|
||||
exemptIssueLabels: string;
|
||||
stalePrLabel: string;
|
||||
closePrLabel: string;
|
||||
exemptPrLabels: string;
|
||||
onlyLabels: string;
|
||||
operationsPerRun: number;
|
||||
removeStaleWhenUpdated: boolean;
|
||||
debugOnly: boolean;
|
||||
ascending: boolean;
|
||||
skipStaleIssueMessage: boolean;
|
||||
skipStalePrMessage: boolean;
|
||||
deleteBranch: boolean;
|
||||
startDate: IsoOrRfcDateString | undefined; // Should be ISO 8601 or RFC 2822
|
||||
exemptMilestones: string;
|
||||
exemptIssueMilestones: string;
|
||||
exemptPrMilestones: string;
|
||||
}
|
||||
3
src/interfaces/label.ts
Normal file
3
src/interfaces/label.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface ILabel {
|
||||
name: string;
|
||||
}
|
||||
6
src/interfaces/pull-request.ts
Normal file
6
src/interfaces/pull-request.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface IPullRequest {
|
||||
number: number;
|
||||
head: {
|
||||
ref: string;
|
||||
};
|
||||
}
|
||||
4
src/interfaces/user.ts
Normal file
4
src/interfaces/user.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface IUser {
|
||||
type: string;
|
||||
login: string;
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as core from '@actions/core';
|
||||
import {isValidDate} from './functions/dates/is-valid-date';
|
||||
import {IssueProcessor, IssueProcessorOptions} from './IssueProcessor';
|
||||
import {IssuesProcessor} from './classes/issues-processor';
|
||||
import {IIssuesProcessorOptions} from './interfaces/issues-processor-options';
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
const args = getAndValidateArgs();
|
||||
|
||||
const processor: IssueProcessor = new IssueProcessor(args);
|
||||
const processor: IssuesProcessor = new IssuesProcessor(args);
|
||||
await processor.processIssues();
|
||||
} catch (error) {
|
||||
core.error(error);
|
||||
@@ -14,8 +15,8 @@ async function run(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function getAndValidateArgs(): IssueProcessorOptions {
|
||||
const args: IssueProcessorOptions = {
|
||||
function getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
const args: IIssuesProcessorOptions = {
|
||||
repoToken: core.getInput('repo-token'),
|
||||
staleIssueMessage: core.getInput('stale-issue-message'),
|
||||
stalePrMessage: core.getInput('stale-pr-message'),
|
||||
|
||||
Reference in New Issue
Block a user