Compare commits

..

2 Commits

Author SHA1 Message Date
chiranjib-swain
ab3a99355d build(deps): update undici to 6.23.0, fast-xml-parser to 5.3.4, and @actions/http-client to 3.0.2 2026-02-05 12:01:13 +05:30
dependabot[bot]
012207ad41 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-05 06:18:35 +00:00
10 changed files with 54999 additions and 450 deletions

View File

@@ -1,6 +1,6 @@
--- ---
name: "@actions/http-client" name: "@actions/http-client"
version: 3.0.1 version: 3.0.2
type: npm type: npm
summary: Actions Http Client summary: Actions Http Client
homepage: https://github.com/actions/toolkit/tree/main/packages/http-client homepage: https://github.com/actions/toolkit/tree/main/packages/http-client

View File

@@ -1,6 +1,6 @@
--- ---
name: fast-xml-parser name: fast-xml-parser
version: 5.3.3 version: 5.3.4
type: npm type: npm
summary: Validate XML, Parse XML, Build XML without C/C++ based libraries summary: Validate XML, Parse XML, Build XML without C/C++ based libraries
homepage: homepage:

34
.licenses/npm/undici-6.23.0.dep.yml generated Normal file
View File

@@ -0,0 +1,34 @@
---
name: undici
version: 6.23.0
type: npm
summary: An HTTP/1.1 client, written from scratch for Node.js
homepage: https://undici.nodejs.org
license: mit
licenses:
- sources: LICENSE
text: |
MIT License
Copyright (c) Matteo Collina and Undici contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- sources: README.md
text: MIT
notices: []

View File

@@ -4,7 +4,6 @@ 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(
@@ -18,15 +17,7 @@ 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);
@@ -40,21 +31,7 @@ export class IssuesProcessorMock extends IssuesProcessor {
} }
if (getLabelCreationDate) { if (getLabelCreationDate) {
this.getLabelCreationDate = async ( this.getLabelCreationDate = getLabelCreationDate;
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) {

View File

@@ -129,7 +129,6 @@ 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,

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);
});
});

54961
dist/index.js vendored

File diff suppressed because one or more lines are too long

56
package-lock.json generated
View File

@@ -92,13 +92,13 @@
} }
}, },
"node_modules/@actions/cache/node_modules/@actions/http-client": { "node_modules/@actions/cache/node_modules/@actions/http-client": {
"version": "3.0.1", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==", "integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tunnel": "^0.0.6", "tunnel": "^0.0.6",
"undici": "^5.28.5" "undici": "^6.23.0"
} }
}, },
"node_modules/@actions/cache/node_modules/@actions/io": { "node_modules/@actions/cache/node_modules/@actions/io": {
@@ -115,6 +115,15 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/@actions/cache/node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/@actions/core": { "node_modules/@actions/core": {
"version": "1.11.1", "version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
@@ -148,13 +157,22 @@
} }
}, },
"node_modules/@actions/github/node_modules/@actions/http-client": { "node_modules/@actions/github/node_modules/@actions/http-client": {
"version": "3.0.1", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz",
"integrity": "sha512-SbGS8c/vySbNO3kjFgSW77n83C4MQx/Yoe+b1hAdpuvfHxnkHzDq2pWljUpAA56Si1Gae/7zjeZsV0CYjmLo/w==", "integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tunnel": "^0.0.6", "tunnel": "^0.0.6",
"undici": "^5.28.5" "undici": "^6.23.0"
}
},
"node_modules/@actions/github/node_modules/@actions/http-client/node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
} }
}, },
"node_modules/@actions/glob": { "node_modules/@actions/glob": {
@@ -1137,10 +1155,11 @@
} }
}, },
"node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
"version": "3.14.1", "version": "3.14.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"argparse": "^1.0.7", "argparse": "^1.0.7",
"esprima": "^4.0.0" "esprima": "^4.0.0"
@@ -3909,9 +3928,9 @@
"dev": true "dev": true
}, },
"node_modules/fast-xml-parser": { "node_modules/fast-xml-parser": {
"version": "5.3.3", "version": "5.3.4",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz",
"integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==", "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -5631,10 +5650,11 @@
} }
}, },
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/lodash.deburr": { "node_modules/lodash.deburr": {
"version": "4.1.0", "version": "4.1.0",

View File

@@ -608,7 +608,7 @@ export class IssuesProcessor {
async getLabelCreationDate( async getLabelCreationDate(
issue: Issue, issue: Issue,
label: string label: string
): Promise<{creationDate?: string; events: IIssueEvent[]}> { ): Promise<string | undefined> {
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,7 +623,6 @@ 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(
@@ -634,51 +633,10 @@ export class IssuesProcessor {
if (!staleLabeledEvent) { if (!staleLabeledEvent) {
// Must be old rather than labeled // Must be old rather than labeled
return {creationDate: undefined, events}; return undefined;
} }
return {creationDate: staleLabeledEvent.created_at, events}; return staleLabeledEvent.created_at;
}
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> {
@@ -733,11 +691,8 @@ export class IssuesProcessor {
closeLabel?: string closeLabel?: string
) { ) {
const issueLogger: IssueLogger = new IssueLogger(issue); const issueLogger: IssueLogger = new IssueLogger(issue);
const {creationDate, events} = await this.getLabelCreationDate( const markedStaleOn: string =
issue, (await this.getLabelCreationDate(issue, staleLabel)) || issue.updated_at;
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)}`
); );
@@ -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) // 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)
let issueHasUpdateSinceStale = isDateMoreRecentThan( const 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