mirror of
https://github.com/actions/stale.git
synced 2025-12-30 11:58:18 +00:00
feat(options): add new option ignore-updates to stale even with activity (#540)
* chore(assignees): add logs * docs(readme): use the override syntax to simplify the reading * docs(readme): add missing default options * docs(readme): add 3 new options to ignore activity before stale * chore(action): add 3 new options * fix(removeStaleWhenUpdated): use the value of the action config as expected Fixes #451 * chore(main): add 3 new options * feat(ignore): add new class to ignore all activities before stale * feat(option): add new options to ignore all activities before stale * chore(index): update index file * docs(readme): fix typo * docs(readme): add missing empty row * chore(rebase): fix logger issues due to rebase * chore: aplly changes due to rebase * refactor(naming): change the name of the options as suggested * chore(logs): reverse the logs as well * docs(readme): format the table of options * refactor(naming): rename the the options * style(rename): rename more updates wording to activities * build(ci): run the test step as expected for a CI instead of using a real linter with auto fix and the tests verbose as fuck * chore: handle breaking changes due to new changes * refactor(naming): rename and reverse the options * style(tests): use plural for some describe * docs(days-before-stale): list the new option * chore(index): update index file * chore: keep static methods on top * chore(logs): remove useless log
This commit is contained in:
committed by
GitHub
parent
002bc97450
commit
1cdda06bb3
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import deburr from 'lodash.deburr';
|
||||
import {Option} from '../enums/option';
|
||||
import {wordsToList} from '../functions/words-to-list';
|
||||
import {IAssignee} from '../interfaces/assignee';
|
||||
import {Assignee} from '../interfaces/assignee';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
import {IssueLogger} from './loggers/issue-logger';
|
||||
@@ -10,10 +10,6 @@ import {LoggerService} from '../services/logger.service';
|
||||
type CleanAssignee = string;
|
||||
|
||||
export class Assignees {
|
||||
private static _cleanAssignee(assignee: Readonly<string>): CleanAssignee {
|
||||
return deburr(assignee.toLowerCase());
|
||||
}
|
||||
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
private readonly _issue: Issue;
|
||||
private readonly _issueLogger: IssueLogger;
|
||||
@@ -24,6 +20,10 @@ export class Assignees {
|
||||
this._issueLogger = new IssueLogger(issue);
|
||||
}
|
||||
|
||||
private static _cleanAssignee(assignee: Readonly<string>): CleanAssignee {
|
||||
return deburr(assignee.toLowerCase());
|
||||
}
|
||||
|
||||
shouldExemptAssignees(): boolean {
|
||||
if (!this._issue.hasAssignees) {
|
||||
this._issueLogger.info('This $$type has no assignee');
|
||||
@@ -195,7 +195,7 @@ export class Assignees {
|
||||
const cleanAssignee: CleanAssignee = Assignees._cleanAssignee(assignee);
|
||||
|
||||
return this._issue.assignees.some(
|
||||
(issueAssignee: Readonly<IAssignee>): boolean => {
|
||||
(issueAssignee: Readonly<Assignee>): boolean => {
|
||||
const isSameAssignee: boolean =
|
||||
cleanAssignee === Assignees._cleanAssignee(issueAssignee.login);
|
||||
|
||||
|
||||
251
src/classes/ignore-updates.spec.ts
Normal file
251
src/classes/ignore-updates.spec.ts
Normal file
@@ -0,0 +1,251 @@
|
||||
import {DefaultProcessorOptions} from '../../__tests__/constants/default-processor-options';
|
||||
import {generateIIssue} from '../../__tests__/functions/generate-iissue';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {IgnoreUpdates} from './ignore-updates';
|
||||
import {Issue} from './issue';
|
||||
|
||||
describe('IgnoreUpdates', (): void => {
|
||||
let ignoreUpdates: IgnoreUpdates;
|
||||
let optionsInterface: IIssuesProcessorOptions;
|
||||
let issue: Issue;
|
||||
let issueInterface: IIssue;
|
||||
|
||||
beforeEach((): void => {
|
||||
optionsInterface = {
|
||||
...DefaultProcessorOptions,
|
||||
ignoreIssueUpdates: true
|
||||
};
|
||||
issueInterface = generateIIssue();
|
||||
});
|
||||
|
||||
describe('shouldIgnoreUpdates()', (): void => {
|
||||
describe('when the given issue is not a pull request', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.pull_request = undefined;
|
||||
});
|
||||
|
||||
describe('when the given options are configured to reset the stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignoreUpdates = false;
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to reset the issue stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
delete optionsInterface.ignoreIssueUpdates;
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to reset the issue stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignoreIssueUpdates = false;
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to not reset the issue stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignoreIssueUpdates = true;
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to reset the stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignoreUpdates = true;
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to reset the issue stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
delete optionsInterface.ignoreIssueUpdates;
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to reset the issue stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignoreIssueUpdates = false;
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to not reset the issue stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignoreIssueUpdates = true;
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given issue is a pull request', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.pull_request = {};
|
||||
});
|
||||
|
||||
describe('when the given options are configured to reset the stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignoreUpdates = false;
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to reset the pull request stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
delete optionsInterface.ignorePrUpdates;
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to reset the pull request stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignorePrUpdates = false;
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to not reset the pull request stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignorePrUpdates = true;
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to not reset the stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignoreUpdates = true;
|
||||
});
|
||||
|
||||
describe('when the given options are not configured to reset the pull request stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
delete optionsInterface.ignorePrUpdates;
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to reset the pull request stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignorePrUpdates = false;
|
||||
});
|
||||
|
||||
it('should return false', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the given options are configured to not reset the pull request stale on updates', (): void => {
|
||||
beforeEach((): void => {
|
||||
optionsInterface.ignorePrUpdates = true;
|
||||
});
|
||||
|
||||
it('should return true', (): void => {
|
||||
expect.assertions(1);
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
ignoreUpdates = new IgnoreUpdates(optionsInterface, issue);
|
||||
|
||||
const result = ignoreUpdates.shouldIgnoreUpdates();
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
90
src/classes/ignore-updates.ts
Normal file
90
src/classes/ignore-updates.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import {Option} from '../enums/option';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {Issue} from './issue';
|
||||
import {IssueLogger} from './loggers/issue-logger';
|
||||
|
||||
export class IgnoreUpdates {
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
private readonly _issue: Issue;
|
||||
private readonly _issueLogger: IssueLogger;
|
||||
|
||||
constructor(options: Readonly<IIssuesProcessorOptions>, issue: Issue) {
|
||||
this._options = options;
|
||||
this._issue = issue;
|
||||
this._issueLogger = new IssueLogger(issue);
|
||||
}
|
||||
|
||||
shouldIgnoreUpdates(): boolean {
|
||||
return this._shouldIgnoreUpdates();
|
||||
}
|
||||
|
||||
private _shouldIgnoreUpdates(): boolean {
|
||||
return this._issue.isPullRequest
|
||||
? this._shouldIgnorePullRequestUpdates()
|
||||
: this._shouldIgnoreIssueUpdates();
|
||||
}
|
||||
|
||||
private _shouldIgnorePullRequestUpdates(): boolean {
|
||||
if (this._options.ignorePrUpdates === true) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.IgnorePrUpdates
|
||||
)} is enabled. The stale counter will ignore any updates or comments on this $$type and will use the creation date as a reference ignoring any kind of update`
|
||||
);
|
||||
|
||||
return true;
|
||||
} else if (this._options.ignorePrUpdates === false) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.IgnorePrUpdates
|
||||
)} is disabled. The stale counter will take into account updates and comments on this $$type to avoid to stale when there is some update`
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._logIgnoreUpdates();
|
||||
|
||||
return this._options.ignoreUpdates;
|
||||
}
|
||||
|
||||
private _shouldIgnoreIssueUpdates(): boolean {
|
||||
if (this._options.ignoreIssueUpdates === true) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.IgnoreIssueUpdates
|
||||
)} is enabled. The stale counter will ignore any updates or comments on this $$type and will use the creation date as a reference ignoring any kind of update`
|
||||
);
|
||||
|
||||
return true;
|
||||
} else if (this._options.ignoreIssueUpdates === false) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.IgnoreIssueUpdates
|
||||
)} is disabled. The stale counter will take into account updates and comments on this $$type to avoid to stale when there is some update`
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this._logIgnoreUpdates();
|
||||
|
||||
return this._options.ignoreUpdates;
|
||||
}
|
||||
|
||||
private _logIgnoreUpdates(): void {
|
||||
if (this._options.ignoreUpdates) {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.IgnoreUpdates
|
||||
)} is enabled. The stale counter will ignore any updates or comments on this $$type and will use the creation date as a reference ignoring any kind of update`
|
||||
);
|
||||
} else {
|
||||
this._issueLogger.info(
|
||||
`The option ${this._issueLogger.createOptionLink(
|
||||
Option.IgnoreUpdates
|
||||
)} is disabled. The stale counter will take into account updates and comments on this $$type to avoid to stale when there is some update`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {IAssignee} from '../interfaces/assignee';
|
||||
import {IUserAssignee} from '../interfaces/assignee';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
@@ -57,7 +57,10 @@ describe('Issue', (): void => {
|
||||
exemptAllPrAssignees: undefined,
|
||||
enableStatistics: false,
|
||||
labelsToRemoveWhenUnstale: '',
|
||||
labelsToAddWhenUnstale: ''
|
||||
labelsToAddWhenUnstale: '',
|
||||
ignoreUpdates: false,
|
||||
ignoreIssueUpdates: undefined,
|
||||
ignorePrUpdates: undefined
|
||||
};
|
||||
issueInterface = {
|
||||
title: 'dummy-title',
|
||||
@@ -77,7 +80,8 @@ describe('Issue', (): void => {
|
||||
},
|
||||
assignees: [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
login: 'dummy-login',
|
||||
type: 'User'
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -150,8 +154,9 @@ describe('Issue', (): void => {
|
||||
|
||||
expect(issue.assignees).toStrictEqual([
|
||||
{
|
||||
login: 'dummy-login'
|
||||
} as IAssignee
|
||||
login: 'dummy-login',
|
||||
type: 'User'
|
||||
} as IUserAssignee
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -272,7 +277,8 @@ describe('Issue', (): void => {
|
||||
beforeEach((): void => {
|
||||
issueInterface.assignees = [
|
||||
{
|
||||
login: 'dummy-login'
|
||||
login: 'dummy-login',
|
||||
type: 'User'
|
||||
}
|
||||
];
|
||||
issue = new Issue(optionsInterface, issueInterface);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {isLabeled} from '../functions/is-labeled';
|
||||
import {isPullRequest} from '../functions/is-pull-request';
|
||||
import {IAssignee} from '../interfaces/assignee';
|
||||
import {Assignee} from '../interfaces/assignee';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
import {IIssuesProcessorOptions} from '../interfaces/issues-processor-options';
|
||||
import {ILabel} from '../interfaces/label';
|
||||
@@ -9,7 +9,6 @@ import {IsoDateString} from '../types/iso-date-string';
|
||||
import {Operations} from './operations';
|
||||
|
||||
export class Issue implements IIssue {
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
readonly title: string;
|
||||
readonly number: number;
|
||||
created_at: IsoDateString;
|
||||
@@ -19,21 +18,10 @@ export class Issue implements IIssue {
|
||||
readonly state: string | 'closed' | 'open';
|
||||
readonly locked: boolean;
|
||||
readonly milestone: IMilestone | undefined;
|
||||
readonly assignees: IAssignee[];
|
||||
readonly assignees: Assignee[];
|
||||
isStale: boolean;
|
||||
operations = new Operations();
|
||||
|
||||
get isPullRequest(): boolean {
|
||||
return isPullRequest(this);
|
||||
}
|
||||
|
||||
get staleLabel(): string {
|
||||
return this._getStaleLabel();
|
||||
}
|
||||
|
||||
get hasAssignees(): boolean {
|
||||
return this.assignees.length > 0;
|
||||
}
|
||||
private readonly _options: IIssuesProcessorOptions;
|
||||
|
||||
constructor(
|
||||
options: Readonly<IIssuesProcessorOptions>,
|
||||
@@ -50,10 +38,21 @@ export class Issue implements IIssue {
|
||||
this.locked = issue.locked;
|
||||
this.milestone = issue.milestone;
|
||||
this.assignees = issue.assignees;
|
||||
|
||||
this.isStale = isLabeled(this, this.staleLabel);
|
||||
}
|
||||
|
||||
get isPullRequest(): boolean {
|
||||
return isPullRequest(this);
|
||||
}
|
||||
|
||||
get staleLabel(): string {
|
||||
return this._getStaleLabel();
|
||||
}
|
||||
|
||||
get hasAssignees(): boolean {
|
||||
return this.assignees.length > 0;
|
||||
}
|
||||
|
||||
private _getStaleLabel(): string {
|
||||
return this.isPullRequest
|
||||
? this._options.stalePrLabel
|
||||
|
||||
@@ -12,11 +12,11 @@ import {cleanLabel} from '../functions/clean-label';
|
||||
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 {Assignees} from './assignees';
|
||||
import {IgnoreUpdates} from './ignore-updates';
|
||||
import {Issue} from './issue';
|
||||
import {IssueLogger} from './loggers/issue-logger';
|
||||
import {Logger} from './loggers/logger';
|
||||
@@ -24,6 +24,7 @@ import {Milestones} from './milestones';
|
||||
import {StaleOperations} from './stale-operations';
|
||||
import {Statistics} from './statistics';
|
||||
import {LoggerService} from '../services/logger.service';
|
||||
import {IIssue} from '../interfaces/issue';
|
||||
|
||||
/***
|
||||
* Handle processing of issues for staleness/closure.
|
||||
@@ -53,22 +54,12 @@ export class IssuesProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static _getStaleMessageUsedOptionName(
|
||||
issue: Readonly<Issue>
|
||||
): Option.StalePrMessage | Option.StaleIssueMessage {
|
||||
return issue.isPullRequest
|
||||
? Option.StalePrMessage
|
||||
: Option.StaleIssueMessage;
|
||||
}
|
||||
|
||||
private static _getCloseLabelUsedOptionName(
|
||||
issue: Readonly<Issue>
|
||||
): Option.ClosePrLabel | Option.CloseIssueLabel {
|
||||
return issue.isPullRequest ? Option.ClosePrLabel : Option.CloseIssueLabel;
|
||||
}
|
||||
|
||||
private readonly _logger: Logger = new Logger();
|
||||
private readonly _statistics: Statistics | undefined;
|
||||
readonly operations: StaleOperations;
|
||||
readonly client: InstanceType<typeof GitHub>;
|
||||
readonly options: IIssuesProcessorOptions;
|
||||
@@ -77,6 +68,8 @@ export class IssuesProcessor {
|
||||
readonly deletedBranchIssues: Issue[] = [];
|
||||
readonly removedLabelIssues: Issue[] = [];
|
||||
readonly addedLabelIssues: Issue[] = [];
|
||||
private readonly _logger: Logger = new Logger();
|
||||
private readonly _statistics: Statistics | undefined;
|
||||
|
||||
constructor(options: IIssuesProcessorOptions) {
|
||||
this.options = options;
|
||||
@@ -404,23 +397,46 @@ export class IssuesProcessor {
|
||||
return; // Don't process exempt assignees
|
||||
}
|
||||
|
||||
// Should this issue be marked stale?
|
||||
const shouldBeStale = !IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeStale
|
||||
);
|
||||
|
||||
// Determine if this issue needs to be marked stale first
|
||||
if (!issue.isStale) {
|
||||
issueLogger.info(`This $$type is not stale`);
|
||||
const updatedAtDate: Date = new Date(issue.updated_at);
|
||||
const shouldIgnoreUpdates: boolean = new IgnoreUpdates(
|
||||
this.options,
|
||||
issue
|
||||
).shouldIgnoreUpdates();
|
||||
|
||||
// Should this issue be marked as stale?
|
||||
let shouldBeStale: boolean;
|
||||
|
||||
// Ignore the last update and only use the creation date
|
||||
if (shouldIgnoreUpdates) {
|
||||
shouldBeStale = !IssuesProcessor._updatedSince(
|
||||
issue.created_at,
|
||||
daysBeforeStale
|
||||
);
|
||||
}
|
||||
// Use the last update to check if we need to stale
|
||||
else {
|
||||
shouldBeStale = !IssuesProcessor._updatedSince(
|
||||
issue.updated_at,
|
||||
daysBeforeStale
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldBeStale) {
|
||||
issueLogger.info(
|
||||
`This $$type should be stale based on the last update date the ${getHumanizedDate(
|
||||
updatedAtDate
|
||||
)} (${LoggerService.cyan(issue.updated_at)})`
|
||||
);
|
||||
if (shouldIgnoreUpdates) {
|
||||
issueLogger.info(
|
||||
`This $$type should be stale based on the creation date the ${getHumanizedDate(
|
||||
new Date(issue.created_at)
|
||||
)} (${LoggerService.cyan(issue.created_at)})`
|
||||
);
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`This $$type should be stale based on the last update date the ${getHumanizedDate(
|
||||
new Date(issue.updated_at)
|
||||
)} (${LoggerService.cyan(issue.updated_at)})`
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldMarkAsStale) {
|
||||
issueLogger.info(
|
||||
@@ -439,11 +455,19 @@ export class IssuesProcessor {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`This $$type should not be stale based on the last update date the ${getHumanizedDate(
|
||||
updatedAtDate
|
||||
)} (${LoggerService.cyan(issue.updated_at)})`
|
||||
);
|
||||
if (shouldIgnoreUpdates) {
|
||||
issueLogger.info(
|
||||
`This $$type should not be stale based on the creation date the ${getHumanizedDate(
|
||||
new Date(issue.created_at)
|
||||
)} (${LoggerService.cyan(issue.created_at)})`
|
||||
);
|
||||
} else {
|
||||
issueLogger.info(
|
||||
`This $$type should not be stale based on the last update date the ${getHumanizedDate(
|
||||
new Date(issue.updated_at)
|
||||
)} (${LoggerService.cyan(issue.updated_at)})`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,5 +42,8 @@ export enum Option {
|
||||
ExemptAllPrAssignees = 'exempt-all-pr-assignees',
|
||||
EnableStatistics = 'enable-statistics',
|
||||
LabelsToRemoveWhenUnstale = 'labels-to-remove-when-unstale',
|
||||
LabelsToAddWhenUnstale = 'labels-to-add-when-unstale'
|
||||
LabelsToAddWhenUnstale = 'labels-to-add-when-unstale',
|
||||
IgnoreUpdates = 'ignore-updates',
|
||||
IgnoreIssueUpdates = 'ignore-issue-updates',
|
||||
IgnorePrUpdates = 'ignore-pr-updates'
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
export interface IAssignee {
|
||||
login: string;
|
||||
}
|
||||
// @todo improve to include the notion of team?
|
||||
interface IAssignee {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface IUserAssignee extends IAssignee {
|
||||
login: string;
|
||||
type: 'User' | string;
|
||||
}
|
||||
|
||||
export type Assignee = IUserAssignee;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import {IsoDateString} from '../types/iso-date-string';
|
||||
import {IAssignee} from './assignee';
|
||||
import {ILabel} from './label';
|
||||
import {IMilestone} from './milestone';
|
||||
|
||||
export interface IIssue {
|
||||
title: string;
|
||||
number: number;
|
||||
created_at: IsoDateString;
|
||||
updated_at: IsoDateString;
|
||||
labels: ILabel[];
|
||||
pull_request: Object | null | undefined;
|
||||
state: string;
|
||||
locked: boolean;
|
||||
milestone: IMilestone | undefined;
|
||||
assignees: IAssignee[];
|
||||
}
|
||||
import {IsoDateString} from '../types/iso-date-string';
|
||||
import {Assignee} from './assignee';
|
||||
import {ILabel} from './label';
|
||||
import {IMilestone} from './milestone';
|
||||
|
||||
export interface IIssue {
|
||||
title: string;
|
||||
number: number;
|
||||
created_at: IsoDateString;
|
||||
updated_at: IsoDateString;
|
||||
labels: ILabel[];
|
||||
pull_request: Object | null | undefined;
|
||||
state: string;
|
||||
locked: boolean;
|
||||
milestone: IMilestone | undefined;
|
||||
assignees: Assignee[];
|
||||
}
|
||||
|
||||
@@ -47,4 +47,7 @@ export interface IIssuesProcessorOptions {
|
||||
enableStatistics: boolean;
|
||||
labelsToRemoveWhenUnstale: string;
|
||||
labelsToAddWhenUnstale: string;
|
||||
ignoreUpdates: boolean;
|
||||
ignoreIssueUpdates: boolean | undefined;
|
||||
ignorePrUpdates: boolean | undefined;
|
||||
}
|
||||
|
||||
20
src/main.ts
20
src/main.ts
@@ -57,10 +57,10 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
core.getInput('remove-stale-when-updated') === 'false'
|
||||
),
|
||||
removeIssueStaleWhenUpdated: _toOptionalBoolean(
|
||||
core.getInput('remove-issue-stale-when-updated')
|
||||
'remove-issue-stale-when-updated'
|
||||
),
|
||||
removePrStaleWhenUpdated: _toOptionalBoolean(
|
||||
core.getInput('remove-pr-stale-when-updated')
|
||||
'remove-pr-stale-when-updated'
|
||||
),
|
||||
debugOnly: core.getInput('debug-only') === 'true',
|
||||
ascending: core.getInput('ascending') === 'true',
|
||||
@@ -83,7 +83,10 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
exemptAllPrAssignees: _toOptionalBoolean('exempt-all-pr-assignees'),
|
||||
enableStatistics: core.getInput('enable-statistics') === 'true',
|
||||
labelsToRemoveWhenUnstale: core.getInput('labels-to-remove-when-unstale'),
|
||||
labelsToAddWhenUnstale: core.getInput('labels-to-add-when-unstale')
|
||||
labelsToAddWhenUnstale: core.getInput('labels-to-add-when-unstale'),
|
||||
ignoreUpdates: core.getInput('ignore-updates') === 'true',
|
||||
ignoreIssueUpdates: _toOptionalBoolean('ignore-issue-updates'),
|
||||
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates')
|
||||
};
|
||||
|
||||
for (const numberInput of [
|
||||
@@ -120,6 +123,17 @@ async function processOutput(
|
||||
core.setOutput('closed-issues-prs', JSON.stringify(closedIssues));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description
|
||||
* From an argument name, get the value as an optional boolean
|
||||
* This is very useful for all the arguments that override others
|
||||
* It will allow us to easily use the original one when the return value is `undefined`
|
||||
* Which is different from `true` or `false` that consider the argument as set
|
||||
*
|
||||
* @param {Readonly<string>} argumentName The name of the argument to check
|
||||
*
|
||||
* @returns {boolean | undefined} The value matching the given argument name
|
||||
*/
|
||||
function _toOptionalBoolean(
|
||||
argumentName: Readonly<string>
|
||||
): boolean | undefined {
|
||||
|
||||
Reference in New Issue
Block a user