mirror of
https://github.com/actions/stale.git
synced 2026-02-07 22:58:17 +00:00
feat: enhance IssuesProcessor to return label creation date and events, add stale label event checks
This commit is contained in:
@@ -4,6 +4,7 @@ import {IComment} from '../../src/interfaces/comment';
|
|||||||
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
|
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
|
||||||
import {IPullRequest} from '../../src/interfaces/pull-request';
|
import {IPullRequest} from '../../src/interfaces/pull-request';
|
||||||
import {IState} from '../../src/interfaces/state/state';
|
import {IState} from '../../src/interfaces/state/state';
|
||||||
|
import {IIssueEvent} from '../../src/interfaces/issue-event';
|
||||||
|
|
||||||
export class IssuesProcessorMock extends IssuesProcessor {
|
export class IssuesProcessorMock extends IssuesProcessor {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -17,7 +18,15 @@ export class IssuesProcessorMock extends IssuesProcessor {
|
|||||||
getLabelCreationDate?: (
|
getLabelCreationDate?: (
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
label: string
|
label: string
|
||||||
) => Promise<string | undefined>,
|
) =>
|
||||||
|
| Promise<string | undefined>
|
||||||
|
| Promise<{creationDate?: string; events: IIssueEvent[]}>,
|
||||||
|
hasOnlyStaleLabelingEventsSince?: (
|
||||||
|
issue: Issue,
|
||||||
|
sinceDate: string,
|
||||||
|
staleLabel: string,
|
||||||
|
events: IIssueEvent[]
|
||||||
|
) => Promise<boolean>,
|
||||||
getPullRequest?: (issue: Issue) => Promise<IPullRequest | undefined | void>
|
getPullRequest?: (issue: Issue) => Promise<IPullRequest | undefined | void>
|
||||||
) {
|
) {
|
||||||
super(options, state);
|
super(options, state);
|
||||||
@@ -31,7 +40,21 @@ export class IssuesProcessorMock extends IssuesProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (getLabelCreationDate) {
|
if (getLabelCreationDate) {
|
||||||
this.getLabelCreationDate = getLabelCreationDate;
|
this.getLabelCreationDate = async (
|
||||||
|
issue: Issue,
|
||||||
|
label: string
|
||||||
|
): Promise<{creationDate?: string; events: IIssueEvent[]}> => {
|
||||||
|
const result = await getLabelCreationDate(issue, label);
|
||||||
|
if (typeof result === 'string' || typeof result === 'undefined') {
|
||||||
|
return {creationDate: result, events: []};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasOnlyStaleLabelingEventsSince) {
|
||||||
|
this.hasOnlyStaleLabelingEventsSince = hasOnlyStaleLabelingEventsSince;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getPullRequest) {
|
if (getPullRequest) {
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ class IssuesProcessorBuilder {
|
|||||||
async p => (p === 1 ? this._issues : []),
|
async p => (p === 1 ? this._issues : []),
|
||||||
async () => [],
|
async () => [],
|
||||||
async () => new Date().toDateString(),
|
async () => new Date().toDateString(),
|
||||||
|
undefined,
|
||||||
async (): Promise<IPullRequest> => {
|
async (): Promise<IPullRequest> => {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
number: 0,
|
number: 0,
|
||||||
|
|||||||
288
__tests__/remove-stale-when-updated-label-events.spec.ts
Normal file
288
__tests__/remove-stale-when-updated-label-events.spec.ts
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
import {Issue} from '../src/classes/issue';
|
||||||
|
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
||||||
|
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||||
|
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||||
|
import {generateIssue} from './functions/generate-issue';
|
||||||
|
import {alwaysFalseStateMock} from './classes/state-mock';
|
||||||
|
import {IState} from '../src/interfaces/state/state';
|
||||||
|
import {IIssueEvent} from '../src/interfaces/issue-event';
|
||||||
|
import {IssuesProcessor} from '../src/classes/issues-processor';
|
||||||
|
|
||||||
|
describe('remove-stale-when-updated with stale label events', (): void => {
|
||||||
|
const markedStaleOn = '2025-01-01T00:00:00Z';
|
||||||
|
const updatedAt = '2025-01-01T00:01:00Z';
|
||||||
|
|
||||||
|
let options: IIssuesProcessorOptions;
|
||||||
|
|
||||||
|
beforeEach((): void => {
|
||||||
|
options = {
|
||||||
|
...DefaultProcessorOptions,
|
||||||
|
removeStaleWhenUpdated: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildIssue = (): Issue =>
|
||||||
|
generateIssue(
|
||||||
|
options,
|
||||||
|
1,
|
||||||
|
'dummy-title',
|
||||||
|
updatedAt,
|
||||||
|
markedStaleOn,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
['Stale']
|
||||||
|
);
|
||||||
|
|
||||||
|
const buildEvents = (): IIssueEvent[] => [
|
||||||
|
{
|
||||||
|
event: 'labeled',
|
||||||
|
created_at: markedStaleOn,
|
||||||
|
label: {name: 'Stale'}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
test('does not remove stale label when only stale label events occurred', async (): Promise<void> => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const issue = buildIssue();
|
||||||
|
|
||||||
|
const processor = new IssuesProcessorMock(
|
||||||
|
options,
|
||||||
|
alwaysFalseStateMock,
|
||||||
|
async p => (p === 1 ? [issue] : []),
|
||||||
|
async () => [],
|
||||||
|
async () => ({creationDate: markedStaleOn, events: buildEvents()}),
|
||||||
|
async () => true
|
||||||
|
);
|
||||||
|
|
||||||
|
await processor.processIssues();
|
||||||
|
|
||||||
|
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removes stale label when updates are not just stale label events', async (): Promise<void> => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const issue = buildIssue();
|
||||||
|
|
||||||
|
const processor = new IssuesProcessorMock(
|
||||||
|
options,
|
||||||
|
alwaysFalseStateMock,
|
||||||
|
async p => (p === 1 ? [issue] : []),
|
||||||
|
async () => [],
|
||||||
|
async () => ({creationDate: markedStaleOn, events: buildEvents()}),
|
||||||
|
async () => false
|
||||||
|
);
|
||||||
|
|
||||||
|
await processor.processIssues();
|
||||||
|
|
||||||
|
expect(processor.removedLabelIssues).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
class TestIssuesProcessor extends IssuesProcessor {
|
||||||
|
constructor(
|
||||||
|
options: IIssuesProcessorOptions,
|
||||||
|
state: IState,
|
||||||
|
events: IIssueEvent[]
|
||||||
|
) {
|
||||||
|
super(options, state);
|
||||||
|
const client = {
|
||||||
|
rest: {
|
||||||
|
issues: {
|
||||||
|
listEvents: {
|
||||||
|
endpoint: {
|
||||||
|
merge: () => ({})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
paginate: {
|
||||||
|
iterator: async function* () {
|
||||||
|
yield {data: events};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(this as any).client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
async callhasOnlyStaleLabelingEventsSince(
|
||||||
|
issue: Issue,
|
||||||
|
sinceDate: string,
|
||||||
|
staleLabel: string,
|
||||||
|
events: IIssueEvent[]
|
||||||
|
): Promise<boolean> {
|
||||||
|
return this.hasOnlyStaleLabelingEventsSince(
|
||||||
|
issue,
|
||||||
|
sinceDate,
|
||||||
|
staleLabel,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('hasOnlyStaleLabelingEventsSince', (): void => {
|
||||||
|
const staleLabel = 'Stale';
|
||||||
|
const sinceDate = '2025-01-01T00:00:00Z';
|
||||||
|
const originalRepo = process.env.GITHUB_REPOSITORY;
|
||||||
|
|
||||||
|
let options: IIssuesProcessorOptions;
|
||||||
|
|
||||||
|
beforeEach((): void => {
|
||||||
|
process.env.GITHUB_REPOSITORY = 'owner/repo';
|
||||||
|
options = {
|
||||||
|
...DefaultProcessorOptions,
|
||||||
|
staleIssueLabel: staleLabel,
|
||||||
|
removeStaleWhenUpdated: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((): void => {
|
||||||
|
if (originalRepo === undefined) {
|
||||||
|
delete process.env.GITHUB_REPOSITORY;
|
||||||
|
} else {
|
||||||
|
process.env.GITHUB_REPOSITORY = originalRepo;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const buildIssue = (): Issue =>
|
||||||
|
generateIssue(
|
||||||
|
options,
|
||||||
|
1,
|
||||||
|
'dummy-title',
|
||||||
|
'2025-01-01T00:02:00Z',
|
||||||
|
sinceDate,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
[staleLabel]
|
||||||
|
);
|
||||||
|
|
||||||
|
test('returns true when only stale label events exist after the since date', async (): Promise<void> => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const issue = buildIssue();
|
||||||
|
const events: IIssueEvent[] = [
|
||||||
|
// Event before the sinceDate should be ignored.
|
||||||
|
{
|
||||||
|
event: 'labeled',
|
||||||
|
created_at: '2024-12-31T23:59:00Z',
|
||||||
|
label: {name: staleLabel}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'labeled',
|
||||||
|
created_at: '2025-01-01T00:00:10Z',
|
||||||
|
label: {name: staleLabel}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const processor = new TestIssuesProcessor(
|
||||||
|
options,
|
||||||
|
alwaysFalseStateMock,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
const result = await processor.callhasOnlyStaleLabelingEventsSince(
|
||||||
|
issue,
|
||||||
|
sinceDate,
|
||||||
|
staleLabel,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns false when a non-stale label event exists after the since date', async (): Promise<void> => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const issue = buildIssue();
|
||||||
|
const events: IIssueEvent[] = [
|
||||||
|
{
|
||||||
|
event: 'labeled',
|
||||||
|
created_at: '2025-01-01T00:00:10Z',
|
||||||
|
label: {name: 'other-label'}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const processor = new TestIssuesProcessor(
|
||||||
|
options,
|
||||||
|
alwaysFalseStateMock,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
const result = await processor.callhasOnlyStaleLabelingEventsSince(
|
||||||
|
issue,
|
||||||
|
sinceDate,
|
||||||
|
staleLabel,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns false when stale label is removed after the since date', async (): Promise<void> => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const issue = buildIssue();
|
||||||
|
const events: IIssueEvent[] = [
|
||||||
|
{
|
||||||
|
event: 'unlabeled',
|
||||||
|
created_at: '2025-01-01T00:00:10Z',
|
||||||
|
label: {name: staleLabel}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const processor = new TestIssuesProcessor(
|
||||||
|
options,
|
||||||
|
alwaysFalseStateMock,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
const result = await processor.callhasOnlyStaleLabelingEventsSince(
|
||||||
|
issue,
|
||||||
|
sinceDate,
|
||||||
|
staleLabel,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns false when a non-label event exists after the since date', async (): Promise<void> => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const issue = buildIssue();
|
||||||
|
const events: IIssueEvent[] = [
|
||||||
|
{
|
||||||
|
event: 'commented',
|
||||||
|
created_at: '2025-01-01T00:00:10Z',
|
||||||
|
label: {name: staleLabel}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const processor = new TestIssuesProcessor(
|
||||||
|
options,
|
||||||
|
alwaysFalseStateMock,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
const result = await processor.callhasOnlyStaleLabelingEventsSince(
|
||||||
|
issue,
|
||||||
|
sinceDate,
|
||||||
|
staleLabel,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('includes events that occur exactly at the since date boundary', async (): Promise<void> => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const issue = buildIssue();
|
||||||
|
const events: IIssueEvent[] = [
|
||||||
|
{
|
||||||
|
event: 'labeled',
|
||||||
|
created_at: sinceDate,
|
||||||
|
label: {name: staleLabel}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const processor = new TestIssuesProcessor(
|
||||||
|
options,
|
||||||
|
alwaysFalseStateMock,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
const result = await processor.callhasOnlyStaleLabelingEventsSince(
|
||||||
|
issue,
|
||||||
|
sinceDate,
|
||||||
|
staleLabel,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
45
dist/index.js
vendored
45
dist/index.js
vendored
@@ -736,9 +736,35 @@ class IssuesProcessor {
|
|||||||
(0, clean_label_1.cleanLabel)(event.label.name) === (0, clean_label_1.cleanLabel)(label));
|
(0, clean_label_1.cleanLabel)(event.label.name) === (0, clean_label_1.cleanLabel)(label));
|
||||||
if (!staleLabeledEvent) {
|
if (!staleLabeledEvent) {
|
||||||
// Must be old rather than labeled
|
// Must be old rather than labeled
|
||||||
return undefined;
|
return { creationDate: undefined, events };
|
||||||
}
|
}
|
||||||
return staleLabeledEvent.created_at;
|
return { creationDate: staleLabeledEvent.created_at, events };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
hasOnlyStaleLabelingEventsSince(issue, sinceDate, staleLabel, events) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
|
issueLogger.info(`Checking if only stale label added events on $$type since: ${logger_service_1.LoggerService.cyan(sinceDate)}`);
|
||||||
|
if (!sinceDate) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const sinceTimestamp = new Date(sinceDate).getTime();
|
||||||
|
if (Number.isNaN(sinceTimestamp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const relevantEvents = events.filter(event => {
|
||||||
|
const eventTimestamp = new Date(event.created_at).getTime();
|
||||||
|
return !Number.isNaN(eventTimestamp) && eventTimestamp >= sinceTimestamp;
|
||||||
|
});
|
||||||
|
if (relevantEvents.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return relevantEvents.every(event => {
|
||||||
|
if (event.event !== 'labeled') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (0, clean_label_1.cleanLabel)(event.label.name) === (0, clean_label_1.cleanLabel)(staleLabel);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getPullRequest(issue) {
|
getPullRequest(issue) {
|
||||||
@@ -783,7 +809,8 @@ class IssuesProcessor {
|
|||||||
_processStaleIssue(issue, staleLabel, staleMessage, labelsToAddWhenUnstale, labelsToRemoveWhenUnstale, labelsToRemoveWhenStale, closeMessage, closeLabel) {
|
_processStaleIssue(issue, staleLabel, staleMessage, labelsToAddWhenUnstale, labelsToRemoveWhenUnstale, labelsToRemoveWhenStale, closeMessage, closeLabel) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
const markedStaleOn = (yield this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
const { creationDate, events } = yield this.getLabelCreationDate(issue, staleLabel);
|
||||||
|
const markedStaleOn = creationDate || issue.updated_at;
|
||||||
issueLogger.info(`$$type marked stale on: ${logger_service_1.LoggerService.cyan(markedStaleOn)}`);
|
issueLogger.info(`$$type marked stale on: ${logger_service_1.LoggerService.cyan(markedStaleOn)}`);
|
||||||
const issueHasCommentsSinceStale = yield this._hasCommentsSince(issue, markedStaleOn, staleMessage);
|
const issueHasCommentsSinceStale = yield this._hasCommentsSince(issue, markedStaleOn, staleMessage);
|
||||||
issueLogger.info(`$$type has been commented on: ${logger_service_1.LoggerService.cyan(issueHasCommentsSinceStale)}`);
|
issueLogger.info(`$$type has been commented on: ${logger_service_1.LoggerService.cyan(issueHasCommentsSinceStale)}`);
|
||||||
@@ -805,7 +832,17 @@ class IssuesProcessor {
|
|||||||
}
|
}
|
||||||
// The issue.updated_at and markedStaleOn are not always exactly in sync (they can be off by a second or 2)
|
// The issue.updated_at and markedStaleOn are not always exactly in sync (they can be off by a second or 2)
|
||||||
// isDateMoreRecentThan makes sure they are not the same date within a certain tolerance (15 seconds in this case)
|
// isDateMoreRecentThan makes sure they are not the same date within a certain tolerance (15 seconds in this case)
|
||||||
const issueHasUpdateSinceStale = (0, is_date_more_recent_than_1.isDateMoreRecentThan)(new Date(issue.updated_at), new Date(markedStaleOn), 15);
|
let issueHasUpdateSinceStale = (0, is_date_more_recent_than_1.isDateMoreRecentThan)(new Date(issue.updated_at), new Date(markedStaleOn), 15);
|
||||||
|
// Check if the only update was the stale label being added
|
||||||
|
if (issueHasUpdateSinceStale &&
|
||||||
|
shouldRemoveStaleWhenUpdated &&
|
||||||
|
!issue.markedStaleThisRun) {
|
||||||
|
const onlyStaleLabelAdded = yield this.hasOnlyStaleLabelingEventsSince(issue, markedStaleOn, staleLabel, events);
|
||||||
|
if (onlyStaleLabelAdded) {
|
||||||
|
issueHasUpdateSinceStale = false;
|
||||||
|
issueLogger.info(`Ignoring $$type update since only the stale label was added`);
|
||||||
|
}
|
||||||
|
}
|
||||||
issueLogger.info(`$$type has been updated since it was marked stale: ${logger_service_1.LoggerService.cyan(issueHasUpdateSinceStale)}`);
|
issueLogger.info(`$$type has been updated since it was marked stale: ${logger_service_1.LoggerService.cyan(issueHasUpdateSinceStale)}`);
|
||||||
// Should we un-stale this issue?
|
// Should we un-stale this issue?
|
||||||
if (shouldRemoveStaleWhenUpdated &&
|
if (shouldRemoveStaleWhenUpdated &&
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ export class IssuesProcessor {
|
|||||||
async getLabelCreationDate(
|
async getLabelCreationDate(
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
label: string
|
label: string
|
||||||
): Promise<string | undefined> {
|
): Promise<{creationDate?: string; events: IIssueEvent[]}> {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
issueLogger.info(`Checking for label on this $$type`);
|
issueLogger.info(`Checking for label on this $$type`);
|
||||||
@@ -623,6 +623,7 @@ export class IssuesProcessor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const events: IIssueEvent[] = await this.client.paginate(options);
|
const events: IIssueEvent[] = await this.client.paginate(options);
|
||||||
|
|
||||||
const reversedEvents = events.reverse();
|
const reversedEvents = events.reverse();
|
||||||
|
|
||||||
const staleLabeledEvent = reversedEvents.find(
|
const staleLabeledEvent = reversedEvents.find(
|
||||||
@@ -633,10 +634,51 @@ export class IssuesProcessor {
|
|||||||
|
|
||||||
if (!staleLabeledEvent) {
|
if (!staleLabeledEvent) {
|
||||||
// Must be old rather than labeled
|
// Must be old rather than labeled
|
||||||
return undefined;
|
return {creationDate: undefined, events};
|
||||||
}
|
}
|
||||||
|
|
||||||
return staleLabeledEvent.created_at;
|
return {creationDate: staleLabeledEvent.created_at, events};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async hasOnlyStaleLabelingEventsSince(
|
||||||
|
issue: Issue,
|
||||||
|
sinceDate: string,
|
||||||
|
staleLabel: string,
|
||||||
|
events: IIssueEvent[]
|
||||||
|
): Promise<boolean> {
|
||||||
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
|
issueLogger.info(
|
||||||
|
`Checking if only stale label added events on $$type since: ${LoggerService.cyan(
|
||||||
|
sinceDate
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!sinceDate) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sinceTimestamp = new Date(sinceDate).getTime();
|
||||||
|
if (Number.isNaN(sinceTimestamp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relevantEvents = events.filter(event => {
|
||||||
|
const eventTimestamp = new Date(event.created_at).getTime();
|
||||||
|
return !Number.isNaN(eventTimestamp) && eventTimestamp >= sinceTimestamp;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (relevantEvents.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return relevantEvents.every(event => {
|
||||||
|
if (event.event !== 'labeled') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanLabel(event.label.name) === cleanLabel(staleLabel);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPullRequest(issue: Issue): Promise<IPullRequest | undefined | void> {
|
async getPullRequest(issue: Issue): Promise<IPullRequest | undefined | void> {
|
||||||
@@ -691,8 +733,11 @@ export class IssuesProcessor {
|
|||||||
closeLabel?: string
|
closeLabel?: string
|
||||||
) {
|
) {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
const markedStaleOn: string =
|
const {creationDate, events} = await this.getLabelCreationDate(
|
||||||
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
|
issue,
|
||||||
|
staleLabel
|
||||||
|
);
|
||||||
|
const markedStaleOn: string = creationDate || issue.updated_at;
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
`$$type marked stale on: ${LoggerService.cyan(markedStaleOn)}`
|
`$$type marked stale on: ${LoggerService.cyan(markedStaleOn)}`
|
||||||
);
|
);
|
||||||
@@ -744,12 +789,33 @@ export class IssuesProcessor {
|
|||||||
|
|
||||||
// The issue.updated_at and markedStaleOn are not always exactly in sync (they can be off by a second or 2)
|
// The issue.updated_at and markedStaleOn are not always exactly in sync (they can be off by a second or 2)
|
||||||
// isDateMoreRecentThan makes sure they are not the same date within a certain tolerance (15 seconds in this case)
|
// isDateMoreRecentThan makes sure they are not the same date within a certain tolerance (15 seconds in this case)
|
||||||
const issueHasUpdateSinceStale = isDateMoreRecentThan(
|
let issueHasUpdateSinceStale = isDateMoreRecentThan(
|
||||||
new Date(issue.updated_at),
|
new Date(issue.updated_at),
|
||||||
new Date(markedStaleOn),
|
new Date(markedStaleOn),
|
||||||
15
|
15
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Check if the only update was the stale label being added
|
||||||
|
if (
|
||||||
|
issueHasUpdateSinceStale &&
|
||||||
|
shouldRemoveStaleWhenUpdated &&
|
||||||
|
!issue.markedStaleThisRun
|
||||||
|
) {
|
||||||
|
const onlyStaleLabelAdded = await this.hasOnlyStaleLabelingEventsSince(
|
||||||
|
issue,
|
||||||
|
markedStaleOn,
|
||||||
|
staleLabel,
|
||||||
|
events
|
||||||
|
);
|
||||||
|
|
||||||
|
if (onlyStaleLabelAdded) {
|
||||||
|
issueHasUpdateSinceStale = false;
|
||||||
|
issueLogger.info(
|
||||||
|
`Ignoring $$type update since only the stale label was added`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
issueLogger.info(
|
issueLogger.info(
|
||||||
`$$type has been updated since it was marked stale: ${LoggerService.cyan(
|
`$$type has been updated since it was marked stale: ${LoggerService.cyan(
|
||||||
issueHasUpdateSinceStale
|
issueHasUpdateSinceStale
|
||||||
|
|||||||
Reference in New Issue
Block a user