Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Every argument is optional.
| [exempt-all-issue-assignees](#exempt-all-issue-assignees) | Override [exempt-all-assignees](#exempt-all-assignees) for issues only | |
| [exempt-all-pr-assignees](#exempt-all-pr-assignees) | Override [exempt-all-assignees](#exempt-all-assignees) for PRs only | |
| [exempt-draft-pr](#exempt-draft-pr) | Skip the stale action for draft PRs | `false` |
| [only-draft-pr](#only-draft-pr) | Only process draft PRs (skip non-draft PRs) | `false` |
| [enable-statistics](#enable-statistics) | Display statistics in the logs | `true` |
| [ignore-updates](#ignore-updates) | Any update (update/comment) can reset the stale idle time on the issues/PRs | `false` |
| [ignore-issue-updates](#ignore-issue-updates) | Override [ignore-updates](#ignore-updates) for issues only | |
Expand Down Expand Up @@ -523,6 +524,13 @@ If set to `true`, the pull requests currently in draft will not be marked as sta
Default value: `false`
Required Permission: `pull-requests: read`

#### only-draft-pr

If set to `true`, only draft pull requests will be processed. Non-draft pull requests will be skipped.
This is the inverse of [exempt-draft-pr](#exempt-draft-pr).

Default value: `false`

#### enable-statistics

Collects and display statistics at the end of the stale workflow logs to get a summary of what happened during the run.
Expand Down
1 change: 1 addition & 0 deletions __tests__/constants/default-processor-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
ignoreIssueUpdates: undefined,
ignorePrUpdates: undefined,
exemptDraftPr: false,
onlyDraftPr: false,
closeIssueReason: 'not_planned',
includeOnlyAssigned: false
});
177 changes: 177 additions & 0 deletions __tests__/only-draft-pr.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import {Issue} from '../src/classes/issue';
import {IIssue} from '../src/interfaces/issue';
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
import {IPullRequest} from '../src/interfaces/pull-request';
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';

let issuesProcessorBuilder: IssuesProcessorBuilder;
let issuesProcessor: IssuesProcessorMock;

describe('only-draft-pr option', (): void => {
beforeEach((): void => {
issuesProcessorBuilder = new IssuesProcessorBuilder();
});

describe('when the option "only-draft-pr" is disabled', (): void => {
beforeEach((): void => {
issuesProcessorBuilder.processAllPrs();
});

test('should stale the non-draft pull request', async (): Promise<void> => {
expect.assertions(1);
issuesProcessor = issuesProcessorBuilder
.toStalePrs([
{
draft: false,
number: 10
}
])
.build();

await issuesProcessor.processIssues();

expect(issuesProcessor.staleIssues).toHaveLength(1);
});

test('should stale the draft pull request', async (): Promise<void> => {
expect.assertions(1);
issuesProcessor = issuesProcessorBuilder
.toStalePrs([
{
draft: true,
number: 20
}
])
.build();

await issuesProcessor.processIssues();

expect(issuesProcessor.staleIssues).toHaveLength(1);
});
});

describe('when the option "only-draft-pr" is enabled', (): void => {
beforeEach((): void => {
issuesProcessorBuilder.onlyDraftPr();
});

test('should not stale the non-draft pull request', async (): Promise<void> => {
expect.assertions(1);
issuesProcessor = issuesProcessorBuilder
.toStalePrs([
{
draft: false,
number: 30
}
])
.build();

await issuesProcessor.processIssues();

expect(issuesProcessor.staleIssues).toHaveLength(0);
});

test('should stale the draft pull request', async (): Promise<void> => {
expect.assertions(1);
issuesProcessor = issuesProcessorBuilder
.toStalePrs([
{
draft: true,
number: 40
}
])
.build();

await issuesProcessor.processIssues();

expect(issuesProcessor.staleIssues).toHaveLength(1);
});
});
});

class IssuesProcessorBuilder {
private _options: IIssuesProcessorOptions = {
...DefaultProcessorOptions
};
private _issues: Issue[] = [];

processAllPrs(): IssuesProcessorBuilder {
this._options.onlyDraftPr = false;

return this;
}

onlyDraftPr(): IssuesProcessorBuilder {
this._options.onlyDraftPr = true;

return this;
}

issuesOrPrs(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
this._issues = issues.map(
(issue: Readonly<Partial<IIssue>>, index: Readonly<number>): Issue =>
generateIssue(
this._options,
issue.number ?? index,
issue.title ?? 'dummy-title',
issue.updated_at ?? new Date().toDateString(),
issue.created_at ?? new Date().toDateString(),
!!issue.draft,
!!issue.pull_request,
issue.labels ? issue.labels.map(label => label.name || '') : []
)
);

return this;
}

prs(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
this.issuesOrPrs(
issues.map((issue: Readonly<Partial<IIssue>>): Partial<IIssue> => {
return {
...issue,
pull_request: {key: 'value'}
};
})
);

return this;
}

toStalePrs(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
this.prs(
issues.map((issue: Readonly<Partial<IIssue>>): Partial<IIssue> => {
return {
...issue,
updated_at: '2020-01-01T17:00:00Z',
created_at: '2020-01-01T17:00:00Z'
};
})
);

return this;
}

build(): IssuesProcessorMock {
return new IssuesProcessorMock(
this._options,
alwaysFalseStateMock,
async p => (p === 1 ? this._issues : []),
async () => [],
async () => new Date().toDateString(),
async (): Promise<IPullRequest> => {
return Promise.resolve({
number: 0,
draft: true,
head: {
ref: 'ref',
repo: null
}
});
}
);
}
}
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ inputs:
description: 'Exempt draft pull requests from being marked as stale. Default to false.'
default: 'false'
required: false
only-draft-pr:
description: 'Only process draft pull requests (skip non-draft PRs). Default to false.'
default: 'false'
required: false
enable-statistics:
description: 'Display some statistics at the end regarding the stale workflow (only when the logs are enabled).'
default: 'true'
Expand Down
47 changes: 47 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ const words_to_list_1 = __nccwpck_require__(1883);
const assignees_1 = __nccwpck_require__(7236);
const ignore_updates_1 = __nccwpck_require__(2935);
const exempt_draft_pull_request_1 = __nccwpck_require__(854);
const only_draft_pull_request_1 = __nccwpck_require__(4525);
const issue_1 = __nccwpck_require__(4783);
const issue_logger_1 = __nccwpck_require__(2984);
const logger_1 = __nccwpck_require__(6212);
Expand Down Expand Up @@ -621,6 +622,12 @@ class IssuesProcessor {
IssuesProcessor._endIssueProcessing(issue);
return; // Don't process draft PR
}
// Skip non-draft PRs if only-draft-prs option is enabled
const onlyDraftPullRequest = new only_draft_pull_request_1.OnlyDraftPullRequest(this.options, issue);
if (onlyDraftPullRequest.shouldSkipNonDraftPullRequest()) {
IssuesProcessor._endIssueProcessing(issue);
return; // Only process draft PRs
}
// Determine if this issue needs to be marked stale first
if (!issue.isStale) {
issueLogger.info(`This $$type is not stale`);
Expand Down Expand Up @@ -1500,6 +1507,44 @@ class Milestones {
exports.Milestones = Milestones;


/***/ }),

/***/ 4525:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.OnlyDraftPullRequest = void 0;
const option_1 = __nccwpck_require__(5931);
const logger_service_1 = __nccwpck_require__(1973);
const issue_logger_1 = __nccwpck_require__(2984);
class OnlyDraftPullRequest {
constructor(options, issue) {
this._options = options;
this._issue = issue;
this._issueLogger = new issue_logger_1.IssueLogger(issue);
}
shouldSkipNonDraftPullRequest() {
var _a;
if (this._issue.isPullRequest) {
if (this._options.onlyDraftPr) {
this._issueLogger.info(`The option ${this._issueLogger.createOptionLink(option_1.Option.OnlyDraftPr)} is enabled`);
if (((_a = this._issue) === null || _a === void 0 ? void 0 : _a.draft) !== true) {
this._issueLogger.info(logger_service_1.LoggerService.white('└──'), `Skip this $$type because it is not a draft and only draft PRs should be processed`);
return true;
}
else {
this._issueLogger.info(logger_service_1.LoggerService.white('└──'), `Continuing the process for this $$type because it is a draft`);
}
}
}
return false;
}
}
exports.OnlyDraftPullRequest = OnlyDraftPullRequest;


/***/ }),

/***/ 7957:
Expand Down Expand Up @@ -2257,6 +2302,7 @@ var Option;
Option["IgnoreIssueUpdates"] = "ignore-issue-updates";
Option["IgnorePrUpdates"] = "ignore-pr-updates";
Option["ExemptDraftPr"] = "exempt-draft-pr";
Option["OnlyDraftPr"] = "only-draft-pr";
Option["CloseIssueReason"] = "close-issue-reason";
Option["OnlyIssueTypes"] = "only-issue-types";
})(Option || (exports.Option = Option = {}));
Expand Down Expand Up @@ -2623,6 +2669,7 @@ function _getAndValidateArgs() {
ignoreIssueUpdates: _toOptionalBoolean('ignore-issue-updates'),
ignorePrUpdates: _toOptionalBoolean('ignore-pr-updates'),
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true',
onlyDraftPr: core.getInput('only-draft-pr') === 'true',
closeIssueReason: core.getInput('close-issue-reason'),
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true',
onlyIssueTypes: core.getInput('only-issue-types')
Expand Down
9 changes: 0 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/classes/issue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe('Issue', (): void => {
ignoreIssueUpdates: undefined,
ignorePrUpdates: undefined,
exemptDraftPr: false,
onlyDraftPr: false,
closeIssueReason: '',
includeOnlyAssigned: false
};
Expand Down
Loading
Loading