feat(close-label): automatically remove close-label when no longer closed nor locked (#334)
* feat(assignees): add new option to avoid stale for assignees closes #271 * test: add more coverage * docs: fix readme format issue * docs: reorder and enhance typo * docs(contributing): add more information about the npm scripts * docs(readme): update the default values to reflect the real applied ones * feat(close-label): automatically remove it when no longer closed nor locked closes #278
This commit is contained in:
parent
ec96ff65b0
commit
836169b81a
|
@ -17,10 +17,10 @@ Warns and then closes issues and PRs that have had no activity for a specified a
|
||||||
| `stale-pr-message` | Message to post on the stale PR. | Optional |
|
| `stale-pr-message` | Message to post on the stale PR. | Optional |
|
||||||
| `close-issue-message` | Message to post on the stale issue while closing it. | Optional |
|
| `close-issue-message` | Message to post on the stale issue while closing it. | Optional |
|
||||||
| `close-pr-message` | Message to post on the stale PR while closing it. | Optional |
|
| `close-pr-message` | Message to post on the stale PR while closing it. | Optional |
|
||||||
| `stale-issue-label` | Label to apply on the stale issue. _Defaults to **stale**_ | Optional |
|
| `stale-issue-label` | Label to apply on the stale issue. _Defaults to **Stale**_ | Optional |
|
||||||
| `close-issue-label` | Label to apply on closing issue. | Optional |
|
| `close-issue-label` | Label to apply on closing issue (automatically removed if no longer closed nor locked). | Optional |
|
||||||
| `stale-pr-label` | Label to apply on the stale PR. | Optional |
|
| `stale-pr-label` | Label to apply on the stale PR. _Defaults to **Stale**_ | Optional |
|
||||||
| `close-pr-label` | Label to apply on the closing PR. | Optional |
|
| `close-pr-label` | Label to apply on the closing PR (automatically removed if no longer closed nor locked). | Optional |
|
||||||
| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional |
|
| `exempt-issue-labels` | Labels on an issue exempted from being marked as stale. | Optional |
|
||||||
| `exempt-pr-labels` | Labels on the PR exempted from being marked as stale. | Optional |
|
| `exempt-pr-labels` | Labels on the PR exempted from being marked as stale. | Optional |
|
||||||
| `only-labels` | Only labels checked for stale issue/PR. | Optional |
|
| `only-labels` | Only labels checked for stale issue/PR. | Optional |
|
||||||
|
|
|
@ -2112,3 +2112,111 @@ test('processing a pull request opened since 2 days and with the option "daysBef
|
||||||
expect(processor.staleIssues.length).toEqual(1);
|
expect(processor.staleIssues.length).toEqual(1);
|
||||||
expect(processor.closedIssues.length).toEqual(0);
|
expect(processor.closedIssues.length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('processing a previously closed issue with a close label will remove the close label', async () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const opts: IIssuesProcessorOptions = {
|
||||||
|
...DefaultProcessorOptions,
|
||||||
|
closeIssueLabel: 'close',
|
||||||
|
staleIssueLabel: 'stale'
|
||||||
|
};
|
||||||
|
const now: Date = new Date();
|
||||||
|
const oneWeekAgo: Date = new Date(now.getDate() - 7);
|
||||||
|
const TestIssueList: Issue[] = [
|
||||||
|
generateIssue(
|
||||||
|
opts,
|
||||||
|
1,
|
||||||
|
'An opened issue with a close label',
|
||||||
|
oneWeekAgo.toDateString(),
|
||||||
|
now.toDateString(),
|
||||||
|
false,
|
||||||
|
['close'],
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
];
|
||||||
|
const processor = new IssuesProcessor(
|
||||||
|
opts,
|
||||||
|
async () => 'abot',
|
||||||
|
async p => (p === 1 ? TestIssueList : []),
|
||||||
|
async () => [],
|
||||||
|
async () => new Date().toDateString()
|
||||||
|
);
|
||||||
|
|
||||||
|
// process our fake issue list
|
||||||
|
await processor.processIssues(1);
|
||||||
|
|
||||||
|
expect(processor.removedLabelIssues).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('processing a closed issue with a close label will not remove the close label', async () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const opts: IIssuesProcessorOptions = {
|
||||||
|
...DefaultProcessorOptions,
|
||||||
|
closeIssueLabel: 'close',
|
||||||
|
staleIssueLabel: 'stale'
|
||||||
|
};
|
||||||
|
const now: Date = new Date();
|
||||||
|
const oneWeekAgo: Date = new Date(now.getDate() - 7);
|
||||||
|
const TestIssueList: Issue[] = [
|
||||||
|
generateIssue(
|
||||||
|
opts,
|
||||||
|
1,
|
||||||
|
'A closed issue with a close label',
|
||||||
|
oneWeekAgo.toDateString(),
|
||||||
|
now.toDateString(),
|
||||||
|
false,
|
||||||
|
['close'],
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
];
|
||||||
|
const processor = new IssuesProcessor(
|
||||||
|
opts,
|
||||||
|
async () => 'abot',
|
||||||
|
async p => (p === 1 ? TestIssueList : []),
|
||||||
|
async () => [],
|
||||||
|
async () => new Date().toDateString()
|
||||||
|
);
|
||||||
|
|
||||||
|
// process our fake issue list
|
||||||
|
await processor.processIssues(1);
|
||||||
|
|
||||||
|
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('processing a locked issue with a close label will not remove the close label', async () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const opts: IIssuesProcessorOptions = {
|
||||||
|
...DefaultProcessorOptions,
|
||||||
|
closeIssueLabel: 'close',
|
||||||
|
staleIssueLabel: 'stale'
|
||||||
|
};
|
||||||
|
const now: Date = new Date();
|
||||||
|
const oneWeekAgo: Date = new Date(now.getDate() - 7);
|
||||||
|
const TestIssueList: Issue[] = [
|
||||||
|
generateIssue(
|
||||||
|
opts,
|
||||||
|
1,
|
||||||
|
'A closed issue with a close label',
|
||||||
|
oneWeekAgo.toDateString(),
|
||||||
|
now.toDateString(),
|
||||||
|
false,
|
||||||
|
['close'],
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
];
|
||||||
|
const processor = new IssuesProcessor(
|
||||||
|
opts,
|
||||||
|
async () => 'abot',
|
||||||
|
async p => (p === 1 ? TestIssueList : []),
|
||||||
|
async () => [],
|
||||||
|
async () => new Date().toDateString()
|
||||||
|
);
|
||||||
|
|
||||||
|
// process our fake issue list
|
||||||
|
await processor.processIssues(1);
|
||||||
|
|
||||||
|
expect(processor.removedLabelIssues).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
|
@ -299,6 +299,8 @@ class IssuesProcessor {
|
||||||
issueLogger.info(`Skipping $$type because it is locked`);
|
issueLogger.info(`Skipping $$type because it is locked`);
|
||||||
continue; // don't process locked issues
|
continue; // don't process locked issues
|
||||||
}
|
}
|
||||||
|
// Try to remove the close label when not close/locked issue or PR
|
||||||
|
yield this._removeCloseLabel(issue, closeLabel);
|
||||||
if (this.options.startDate) {
|
if (this.options.startDate) {
|
||||||
const startDate = new Date(this.options.startDate);
|
const startDate = new Date(this.options.startDate);
|
||||||
const createdAt = new Date(issue.created_at);
|
const createdAt = new Date(issue.created_at);
|
||||||
|
@ -677,10 +679,24 @@ class IssuesProcessor {
|
||||||
_removeStaleLabel(issue, staleLabel) {
|
_removeStaleLabel(issue, staleLabel) {
|
||||||
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);
|
||||||
issueLogger.info(`$$type is no longer stale. Removing stale label.`);
|
issueLogger.info(`The $$type is no longer stale. Removing the stale label...`);
|
||||||
return this._removeLabel(issue, staleLabel);
|
return this._removeLabel(issue, staleLabel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
_removeCloseLabel(issue, closeLabel) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const issueLogger = new issue_logger_1.IssueLogger(issue);
|
||||||
|
issueLogger.info(`The $$type is not closed nor locked. Trying to remove the close label...`);
|
||||||
|
if (!closeLabel) {
|
||||||
|
issueLogger.info(`There is no close label on this $$type. Skip`);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
if (is_labeled_1.isLabeled(issue, closeLabel)) {
|
||||||
|
issueLogger.info(`The $$type has a close label "${closeLabel}". Removing the close label...`);
|
||||||
|
return this._removeLabel(issue, closeLabel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.IssuesProcessor = IssuesProcessor;
|
exports.IssuesProcessor = IssuesProcessor;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ export class Issue implements IIssue {
|
||||||
updated_at: IsoDateString;
|
updated_at: IsoDateString;
|
||||||
readonly labels: ILabel[];
|
readonly labels: ILabel[];
|
||||||
readonly pull_request: Object | null | undefined;
|
readonly pull_request: Object | null | undefined;
|
||||||
readonly state: string;
|
readonly state: string | 'closed' | 'open';
|
||||||
readonly locked: boolean;
|
readonly locked: boolean;
|
||||||
readonly milestone: IMilestone | undefined;
|
readonly milestone: IMilestone | undefined;
|
||||||
readonly assignees: IAssignee[];
|
readonly assignees: IAssignee[];
|
||||||
|
|
|
@ -140,6 +140,9 @@ export class IssuesProcessor {
|
||||||
continue; // don't process locked issues
|
continue; // don't process locked issues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to remove the close label when not close/locked issue or PR
|
||||||
|
await this._removeCloseLabel(issue, closeLabel);
|
||||||
|
|
||||||
if (this.options.startDate) {
|
if (this.options.startDate) {
|
||||||
const startDate: Date = new Date(this.options.startDate);
|
const startDate: Date = new Date(this.options.startDate);
|
||||||
const createdAt: Date = new Date(issue.created_at);
|
const createdAt: Date = new Date(issue.created_at);
|
||||||
|
@ -663,8 +666,35 @@ export class IssuesProcessor {
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const issueLogger: IssueLogger = new IssueLogger(issue);
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
issueLogger.info(`$$type is no longer stale. Removing stale label.`);
|
issueLogger.info(
|
||||||
|
`The $$type is no longer stale. Removing the stale label...`
|
||||||
|
);
|
||||||
|
|
||||||
return this._removeLabel(issue, staleLabel);
|
return this._removeLabel(issue, staleLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _removeCloseLabel(
|
||||||
|
issue: Issue,
|
||||||
|
closeLabel: Readonly<string | undefined>
|
||||||
|
): Promise<void> {
|
||||||
|
const issueLogger: IssueLogger = new IssueLogger(issue);
|
||||||
|
|
||||||
|
issueLogger.info(
|
||||||
|
`The $$type is not closed nor locked. Trying to remove the close label...`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!closeLabel) {
|
||||||
|
issueLogger.info(`There is no close label on this $$type. Skip`);
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLabeled(issue, closeLabel)) {
|
||||||
|
issueLogger.info(
|
||||||
|
`The $$type has a close label "${closeLabel}". Removing the close label...`
|
||||||
|
);
|
||||||
|
|
||||||
|
return this._removeLabel(issue, closeLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue