Compare commits

..

2 Commits

Author SHA1 Message Date
chiranjib-swain
950f866ef5 chore: add license files for undici and http-client dependencies 2026-02-03 17:31:26 +05:30
dependabot[bot]
fa53299cb8 build(deps-dev): bump lodash from 4.17.21 to 4.17.23
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-02 17:25:45 +00:00
6 changed files with 122 additions and 494 deletions

View File

@@ -4,7 +4,6 @@ import {IComment} from '../../src/interfaces/comment';
import {IIssuesProcessorOptions} from '../../src/interfaces/issues-processor-options';
import {IPullRequest} from '../../src/interfaces/pull-request';
import {IState} from '../../src/interfaces/state/state';
import {IIssueEvent} from '../../src/interfaces/issue-event';
export class IssuesProcessorMock extends IssuesProcessor {
constructor(
@@ -18,15 +17,7 @@ export class IssuesProcessorMock extends IssuesProcessor {
getLabelCreationDate?: (
issue: Issue,
label: string
) =>
| Promise<string | undefined>
| Promise<{creationDate?: string; events: IIssueEvent[]}>,
hasOnlyStaleLabelingEventsSince?: (
issue: Issue,
sinceDate: string,
staleLabel: string,
events: IIssueEvent[]
) => Promise<boolean>,
) => Promise<string | undefined>,
getPullRequest?: (issue: Issue) => Promise<IPullRequest | undefined | void>
) {
super(options, state);
@@ -40,21 +31,7 @@ export class IssuesProcessorMock extends IssuesProcessor {
}
if (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;
this.getLabelCreationDate = getLabelCreationDate;
}
if (getPullRequest) {

View File

@@ -129,7 +129,6 @@ class IssuesProcessorBuilder {
async p => (p === 1 ? this._issues : []),
async () => [],
async () => new Date().toDateString(),
undefined,
async (): Promise<IPullRequest> => {
return Promise.resolve({
number: 0,

View File

@@ -1,288 +0,0 @@
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
View File

@@ -736,35 +736,9 @@ class IssuesProcessor {
(0, clean_label_1.cleanLabel)(event.label.name) === (0, clean_label_1.cleanLabel)(label));
if (!staleLabeledEvent) {
// Must be old rather than labeled
return { creationDate: undefined, events };
return undefined;
}
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);
});
return staleLabeledEvent.created_at;
});
}
getPullRequest(issue) {
@@ -809,8 +783,7 @@ class IssuesProcessor {
_processStaleIssue(issue, staleLabel, staleMessage, labelsToAddWhenUnstale, labelsToRemoveWhenUnstale, labelsToRemoveWhenStale, closeMessage, closeLabel) {
return __awaiter(this, void 0, void 0, function* () {
const issueLogger = new issue_logger_1.IssueLogger(issue);
const { creationDate, events } = yield this.getLabelCreationDate(issue, staleLabel);
const markedStaleOn = creationDate || issue.updated_at;
const markedStaleOn = (yield this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
issueLogger.info(`$$type marked stale on: ${logger_service_1.LoggerService.cyan(markedStaleOn)}`);
const issueHasCommentsSinceStale = yield this._hasCommentsSince(issue, markedStaleOn, staleMessage);
issueLogger.info(`$$type has been commented on: ${logger_service_1.LoggerService.cyan(issueHasCommentsSinceStale)}`);
@@ -832,17 +805,7 @@ class IssuesProcessor {
}
// 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)
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`);
}
}
const issueHasUpdateSinceStale = (0, is_date_more_recent_than_1.isDateMoreRecentThan)(new Date(issue.updated_at), new Date(markedStaleOn), 15);
issueLogger.info(`$$type has been updated since it was marked stale: ${logger_service_1.LoggerService.cyan(issueHasUpdateSinceStale)}`);
// Should we un-stale this issue?
if (shouldRemoveStaleWhenUpdated &&

177
package-lock.json generated
View File

@@ -1058,10 +1058,11 @@
}
},
"node_modules/@eslint/js": {
"version": "8.55.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz",
"integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==",
"version": "8.57.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
"integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
@@ -1075,13 +1076,15 @@
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
"integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
"deprecated": "Use @eslint/config-array instead",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanwhocodes/object-schema": "^2.0.1",
"debug": "^4.1.1",
"@humanwhocodes/object-schema": "^2.0.3",
"debug": "^4.3.1",
"minimatch": "^3.0.5"
},
"engines": {
@@ -1102,10 +1105,12 @@
}
},
"node_modules/@humanwhocodes/object-schema": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
"dev": true
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"deprecated": "Use @eslint/object-schema instead",
"dev": true,
"license": "BSD-3-Clause"
},
"node_modules/@hutson/parse-repository-url": {
"version": "3.0.2",
@@ -2026,16 +2031,17 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.2.tgz",
"integrity": "sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
"integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "6.13.2",
"@typescript-eslint/type-utils": "6.13.2",
"@typescript-eslint/utils": "6.13.2",
"@typescript-eslint/visitor-keys": "6.13.2",
"@typescript-eslint/scope-manager": "6.21.0",
"@typescript-eslint/type-utils": "6.21.0",
"@typescript-eslint/utils": "6.21.0",
"@typescript-eslint/visitor-keys": "6.21.0",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@@ -2061,15 +2067,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.2.tgz",
"integrity": "sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/scope-manager": "6.13.2",
"@typescript-eslint/types": "6.13.2",
"@typescript-eslint/typescript-estree": "6.13.2",
"@typescript-eslint/visitor-keys": "6.13.2",
"@typescript-eslint/scope-manager": "6.21.0",
"@typescript-eslint/types": "6.21.0",
"@typescript-eslint/typescript-estree": "6.21.0",
"@typescript-eslint/visitor-keys": "6.21.0",
"debug": "^4.3.4"
},
"engines": {
@@ -2089,13 +2096,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.2.tgz",
"integrity": "sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
"integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "6.13.2",
"@typescript-eslint/visitor-keys": "6.13.2"
"@typescript-eslint/types": "6.21.0",
"@typescript-eslint/visitor-keys": "6.21.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -2106,13 +2114,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.2.tgz",
"integrity": "sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
"integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "6.13.2",
"@typescript-eslint/utils": "6.13.2",
"@typescript-eslint/typescript-estree": "6.21.0",
"@typescript-eslint/utils": "6.21.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
@@ -2133,10 +2142,11 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.2.tgz",
"integrity": "sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
"integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
@@ -2146,16 +2156,18 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.2.tgz",
"integrity": "sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
"integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/types": "6.13.2",
"@typescript-eslint/visitor-keys": "6.13.2",
"@typescript-eslint/types": "6.21.0",
"@typescript-eslint/visitor-keys": "6.21.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "9.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
@@ -2172,18 +2184,45 @@
}
}
},
"node_modules/@typescript-eslint/utils": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.2.tgz",
"integrity": "sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==",
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
"integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.13.2",
"@typescript-eslint/types": "6.13.2",
"@typescript-eslint/typescript-estree": "6.13.2",
"@typescript-eslint/scope-manager": "6.21.0",
"@typescript-eslint/types": "6.21.0",
"@typescript-eslint/typescript-estree": "6.21.0",
"semver": "^7.5.4"
},
"engines": {
@@ -2198,12 +2237,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "6.13.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.2.tgz",
"integrity": "sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==",
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
"integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "6.13.2",
"@typescript-eslint/types": "6.21.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
@@ -3435,16 +3475,18 @@
}
},
"node_modules/eslint": {
"version": "8.55.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz",
"integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==",
"version": "8.57.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.4",
"@eslint/js": "8.55.0",
"@humanwhocodes/config-array": "^0.11.13",
"@eslint/js": "8.57.1",
"@humanwhocodes/config-array": "^0.13.0",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"@ungap/structured-clone": "^1.2.0",
@@ -3525,10 +3567,11 @@
}
},
"node_modules/eslint-plugin-jest": {
"version": "27.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz",
"integrity": "sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng==",
"version": "27.9.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz",
"integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/utils": "^5.10.0"
},
@@ -3536,7 +3579,7 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
},
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0",
"@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0",
"eslint": "^7.0.0 || ^8.0.0",
"jest": "*"
},
@@ -5650,9 +5693,9 @@
}
},
"node_modules/lodash": {
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"dev": true,
"license": "MIT"
},

View File

@@ -608,7 +608,7 @@ export class IssuesProcessor {
async getLabelCreationDate(
issue: Issue,
label: string
): Promise<{creationDate?: string; events: IIssueEvent[]}> {
): Promise<string | undefined> {
const issueLogger: IssueLogger = new IssueLogger(issue);
issueLogger.info(`Checking for label on this $$type`);
@@ -623,7 +623,6 @@ export class IssuesProcessor {
});
const events: IIssueEvent[] = await this.client.paginate(options);
const reversedEvents = events.reverse();
const staleLabeledEvent = reversedEvents.find(
@@ -634,51 +633,10 @@ export class IssuesProcessor {
if (!staleLabeledEvent) {
// Must be old rather than labeled
return {creationDate: undefined, events};
return undefined;
}
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);
});
return staleLabeledEvent.created_at;
}
async getPullRequest(issue: Issue): Promise<IPullRequest | undefined | void> {
@@ -733,11 +691,8 @@ export class IssuesProcessor {
closeLabel?: string
) {
const issueLogger: IssueLogger = new IssueLogger(issue);
const {creationDate, events} = await this.getLabelCreationDate(
issue,
staleLabel
);
const markedStaleOn: string = creationDate || issue.updated_at;
const markedStaleOn: string =
(await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
issueLogger.info(
`$$type marked stale on: ${LoggerService.cyan(markedStaleOn)}`
);
@@ -789,33 +744,12 @@ export class IssuesProcessor {
// 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)
let issueHasUpdateSinceStale = isDateMoreRecentThan(
const issueHasUpdateSinceStale = 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 = 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(
`$$type has been updated since it was marked stale: ${LoggerService.cyan(
issueHasUpdateSinceStale