Repository: actions/stale
Pull request #338 (2021-02-28 11:16:39)

Bump @octokit/rest from 18.1.1 to 18.3.0
Bumps [@octokit/rest](https://github.com/octokit/rest.js) from 18.1.1 to 18.3.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/octokit/rest.js/releases"><code>@​octokit/rest's releases</code></a>.</em></p>
<blockquote>
<h2>v18.3.0</h2>
<h1><a href="https://github.com/octokit/rest.js/compare/v18.2.1...v18.3.0">18.3.0</a> (2021-02-26)</h1>
<h3>Features</h3>
<ul>
<li><code>octokit.actions.createOrUpdateEnvironmentSecret()</code>, <code>octokit.actions.deleteEnvironmentSecret()</code>, <code>octokit.actions.getEnvironmentPublicKey()</code>, <code>octokit.actions.getEnvironmentSecret()</code>, <code>octokit.actions.getPendingDeploymentsForRun()</code>, <code>octokit.actions.getReviewsForRun()</code>, <code>octokit.actions.listEnvironmentSecrets()</code>, <code>octokit.actions.reviewPendingDeploymentsForRun()</code>, <code>octokit.repos.createAnEnvironment()</code>, <code>octokit.repos.deleteAnEnvironment()</code>, <code>octokit.repos.getAllEnvironments()</code>, <code>octokit.repos.getEnvironment()</code>, <code>octokit.repos.setEnvironmentProtectionRules()</code> (<a href="https://github.com/octokit/rest.js/commit/c36bb4372c42b3de89a8f49bfe09ae26b428b179">c36bb43</a>)</li>
</ul>
<h2>v18.2.1</h2>
<h2><a href="https://github.com/octokit/rest.js/compare/v18.2.0...v18.2.1">18.2.1</a> (2021-02-24)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>typescript:</strong> <code>octokit.repos.createFork()</code> has a <code>never</code> resolve type - via @octokit/plugin-rest-endpoint-methods v4.12.2 (<a href="https://github-redirect.dependabot.com/octokit/rest.js/issues/2024">#2024</a>) (<a href="https://github.com/octokit/rest.js/commit/beed59274f86d07f4d977c2819292ed97660a95d">beed592</a>)</li>
</ul>
<h2>v18.2.0</h2>
<h3>Features</h3>
<ul>
<li><code>octokit.packages.deletePackageForAuthenticatedUser()</code></li>
<li><code>octokit.packages.deletePackageForOrg()</code></li>
<li><code>octokit.packages.deletePackageVersionForAuthenticatedUser()</code></li>
<li><code>octokit.packages.deletePackageVersionForOrg()</code></li>
<li><code>octokit.packages.getAllPackageVersionsForAPackageOwnedByAnOrg()</code></li>
<li><code>octokit.packages.getAllPackageVersionsForAPackageOwnedByTheAuthenticatedUser()</code></li>
<li><code>octokit.packages.getAllPackageVersionsForPackageOwnedByUser()</code></li>
<li><code>octokit.packages.getPackageForAuthenticatedUser()</code></li>
<li><code>octokit.packages.getPackageForOrganization()</code></li>
<li><code>octokit.packages.getPackageForUser()</code></li>
<li><code>octokit.packages.getPackageVersionForAuthenticatedUser()</code></li>
<li><code>octokit.packages.getPackageVersionForOrganization()</code></li>
<li><code>octokit.packages.getPackageVersionForUser()</code></li>
<li><code>octokit.packages.restorePackageForAuthenticatedUser()</code></li>
<li><code>octokit.packages.restorePackageForOrg()</code></li>
<li><code>octokit.packages.restorePackageVersionForAuthenticatedUser()</code></li>
<li><code>octokit.packages.restorePackageVersionForOrg()</code></li>
</ul>
<p>(<a href="https://github-redirect.dependabot.com/octokit/rest.js/issues/2017">#2017</a>) (<a href="https://github.com/octokit/rest.js/commit/40b40f86bd05a2875b0e4e1b010da6ff251a6295">40b40f8</a>)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/octokit/rest.js/commit/c36bb4372c42b3de89a8f49bfe09ae26b428b179"><code>c36bb43</code></a> feat: <code>octokit.actions.createOrUpdateEnvironmentSecret()</code>, `octokit.actions.d...</li>
<li><a href="https://github.com/octokit/rest.js/commit/beed59274f86d07f4d977c2819292ed97660a95d"><code>beed592</code></a> fix(typescript): <code>octokit.repos.createFork()</code> has a <code>never</code> resolve type - vi...</li>
<li><a href="https://github.com/octokit/rest.js/commit/0ec92ba3e77f74b9d8d12435236fb493aa14b296"><code>0ec92ba</code></a> build(deps): update dependency @octokit/plugin-rest-endpoint-methods to v4.12...</li>
<li><a href="https://github.com/octokit/rest.js/commit/40b40f86bd05a2875b0e4e1b010da6ff251a6295"><code>40b40f8</code></a> feat: <code>octokit.packages.deletePackageForAuthenticatedUser()</code>, `octokit.packag...</li>
<li><a href="https://github.com/octokit/rest.js/commit/e80cc8809f651044d2ec92bb2f7fdb53418c92b9"><code>e80cc88</code></a> `octokit.codeScanning.{getAnalysis,getSarif,deleteAnalysis,listAlertsInstance...</li>
<li><a href="https://github.com/octokit/rest.js/commit/776a3da11506315ab2a1e2adc4d8ec8e7950e142"><code>776a3da</code></a> build(deps): lock file maintenance (<a href="https://github-redirect.dependabot.com/octokit/rest.js/issues/2013">#2013</a>)</li>
<li>See full diff in <a href="https://github.com/octokit/rest.js/compare/v18.1.1...v18.3.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@octokit/rest&package-manager=npm_and_yarn&previous-version=18.1.1&new-version=18.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/README.md b/README.md
index 512ed47..97b4557 100644
--- a/README.md
+++ b/README.md
@@ -17,13 +17,15 @@ 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 |
 | `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 |
-| `stale-issue-label`           | Label to apply on the stale issue. _Defaults to **stale**_                                                      | Optional |
-| `close-issue-label`           | Label to apply on closing issue.                                                                                | Optional |
-| `stale-pr-label`              | Label to apply on the stale PR.                                                                                 | Optional |
-| `close-pr-label`              | Label to apply on the closing PR.                                                                               | Optional |
+| `stale-issue-label`           | Label to apply on the stale issue. _Defaults to **Stale**_                                                      | 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. _Defaults to **Stale**_                                                         | 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-pr-labels`            | Labels on the PR exempted from being marked as stale.                                                           | Optional |
 | `only-labels`                 | Only labels checked for stale issue/PR.                                                                         | Optional |
+| `only-issue-labels`           | Only labels checked for stale issue (override `only-labels`).                                                   | Optional |
+| `only-pr-labels`              | Only labels checked for stale PR (override `only-labels`).                                                      | Optional |
 | `operations-per-run`          | Maximum number of operations per run (GitHub API CRUD related). _Defaults to **30**_                            | Optional |
 | `remove-stale-when-updated`   | Remove stale label from issue/PR on updates or comments. _Defaults to **true**_                                 | Optional |
 | `debug-only`                  | Dry-run on action. _Defaults to **false**_                                                                      | Optional |
diff --git a/action.yml b/action.yml
index cbf8ade..211470c 100644
--- a/action.yml
+++ b/action.yml
@@ -88,6 +88,14 @@ inputs:
     description: 'Only issues or pull requests with all of these labels are checked if stale. Defaults to `[]` (disabled) and can be a comma-separated list of labels.'
     default: ''
     required: false
+  only-issue-labels:
+    description: 'Only issues with all of these labels are checked if stale. Defaults to `[]` (disabled) and can be a comma-separated list of labels. Override "only-labels" option regarding only the issues.'
+    default: ''
+    required: false
+  only-pr-labels:
+    description: 'Only pull requests with all of these labels are checked if stale. Defaults to `[]` (disabled) and can be a comma-separated list of labels. Override "only-labels" option regarding only the pull requests.'
+    default: ''
+    required: false
   operations-per-run:
     description: 'The maximum number of operations per run, used to control rate limiting (GitHub API CRUD related).'
     default: '30'
diff --git a/dist/index.js b/dist/index.js
index 9465aef..f1c1bdb 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -285,6 +285,23 @@ class IssuesProcessor {
                 const daysBeforeStale = issue.isPullRequest
                     ? this._getDaysBeforePrStale()
                     : this._getDaysBeforeIssueStale();
+                const onlyLabels = words_to_list_1.wordsToList(this._getOnlyLabels(issue));
+                if (onlyLabels.length > 0) {
+                    issueLogger.info(`The option "onlyLabels" was specified to only processed the issues and pull requests with all those labels (${onlyLabels.length})`);
+                    const hasAllWhitelistedLabels = onlyLabels.every((label) => {
+                        return is_labeled_1.isLabeled(issue, label);
+                    });
+                    if (!hasAllWhitelistedLabels) {
+                        issueLogger.info(`Skipping this $$type because it doesn't have all the required labels`);
+                        continue; // Don't process issues without all of the required labels
+                    }
+                    else {
+                        issueLogger.info(`All the required labels are present on this $$type. Continuing the process`);
+                    }
+                }
+                else {
+                    issueLogger.info(`The option "onlyLabels" was not specified. Continuing the process for this $$type`);
+                }
                 issueLogger.info(`Days before $$type stale: ${daysBeforeStale}`);
                 const shouldMarkAsStale = should_mark_when_stale_1.shouldMarkWhenStale(daysBeforeStale);
                 if (!staleMessage && shouldMarkAsStale) {
@@ -299,6 +316,8 @@ class IssuesProcessor {
                     issueLogger.info(`Skipping $$type because it is locked`);
                     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) {
                     const startDate = new Date(this.options.startDate);
                     const createdAt = new Date(issue.created_at);
@@ -460,7 +479,6 @@ class IssuesProcessor {
                     owner: github_1.context.repo.owner,
                     repo: github_1.context.repo.repo,
                     state: 'open',
-                    labels: this.options.onlyLabels,
                     per_page: 100,
                     direction: this.options.ascending ? 'asc' : 'desc',
                     page
@@ -674,13 +692,40 @@ class IssuesProcessor {
             ? this.options.daysBeforeClose
             : this.options.daysBeforePrClose;
     }
+    _getOnlyLabels(issue) {
+        if (issue.isPullRequest) {
+            if (this.options.onlyPrLabels !== '') {
+                return this.options.onlyPrLabels;
+            }
+        }
+        else {
+            if (this.options.onlyIssueLabels !== '') {
+                return this.options.onlyIssueLabels;
+            }
+        }
+        return this.options.onlyLabels;
+    }
     _removeStaleLabel(issue, staleLabel) {
         return __awaiter(this, void 0, void 0, function* () {
             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);
         });
     }
+    _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;
 
@@ -1154,6 +1199,8 @@ function _getAndValidateArgs() {
         closePrLabel: core.getInput('close-pr-label'),
         exemptPrLabels: core.getInput('exempt-pr-labels'),
         onlyLabels: core.getInput('only-labels'),
+        onlyIssueLabels: core.getInput('only-issue-labels'),
+        onlyPrLabels: core.getInput('only-pr-labels'),
         operationsPerRun: parseInt(core.getInput('operations-per-run', { required: true })),
         removeStaleWhenUpdated: !(core.getInput('remove-stale-when-updated') === 'false'),
         debugOnly: core.getInput('debug-only') === 'true',
@@ -1206,7 +1253,7 @@ function _toOptionalBoolean(argumentName) {
     }
     return undefined;
 }
-_run();
+void _run();
 
 
 /***/ }),
diff --git a/package-lock.json b/package-lock.json
index 1a4162c..095a5a2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1568,9 +1568,9 @@
       }
     },
     "@octokit/openapi-types": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-4.0.3.tgz",
-      "integrity": "sha512-CXCe1Zzc/WhjpuAEgoUktAinZJ02l6W8q1hDkwBtaonEtO6cjo/3Pyb2J1t5y2q/SvjESDJ77xZ9UVHqQVvihw=="
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-5.2.0.tgz",
+      "integrity": "sha512-MInMij2VK5o96Ei6qaHjxBglSZWOXQs9dTZfnGX2Xnr2mhA+yk9L/QCH4RcJGISJJCBclLHuY3ytq+iRgDfX7w=="
     },
     "@octokit/plugin-paginate-rest": {
       "version": "2.2.4",
@@ -1620,14 +1620,14 @@
       }
     },
     "@octokit/rest": {
-      "version": "18.1.1",
-      "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.1.1.tgz",
-      "integrity": "sha512-ZcCHMyfGT1qtJD72usigAfUQ6jU89ZUPFb2AOubR6WZ7/RRFVZUENVm1I2yvJBUicqTujezPW9cY1+o3Mb4rNA==",
+      "version": "18.3.0",
+      "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.3.0.tgz",
+      "integrity": "sha512-R45oBVhnq3HAOGVtC6lHY7LX7TGWqbbcD4KvBHoT4QIjgJzfqKag3m/DUJwLnp8xrokz1spZmspTIXiDeQqJSA==",
       "requires": {
         "@octokit/core": "^3.2.3",
         "@octokit/plugin-paginate-rest": "^2.6.2",
         "@octokit/plugin-request-log": "^1.0.2",
-        "@octokit/plugin-rest-endpoint-methods": "4.10.3"
+        "@octokit/plugin-rest-endpoint-methods": "4.13.0"
       },
       "dependencies": {
         "@octokit/auth-token": {
@@ -1662,19 +1662,19 @@
           }
         },
         "@octokit/plugin-paginate-rest": {
-          "version": "2.9.1",
-          "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.9.1.tgz",
-          "integrity": "sha512-8wnuWGjwDIEobbBet2xAjZwgiMVTgIer5wBsnGXzV3lJ4yqphLU2FEMpkhSrDx7y+WkZDfZ+V+1cFMZ1mAaFag==",
+          "version": "2.11.0",
+          "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.11.0.tgz",
+          "integrity": "sha512-7L9xQank2G3r1dGqrVPo1z62V5utbykOUzlmNHPz87Pww/JpZQ9KyG5CHtUzgmB4n5iDRKYNK/86A8D98HP0yA==",
           "requires": {
-            "@octokit/types": "^6.8.0"
+            "@octokit/types": "^6.11.0"
           }
         },
         "@octokit/plugin-rest-endpoint-methods": {
-          "version": "4.10.3",
-          "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.10.3.tgz",
-          "integrity": "sha512-CsNQeVY34Vs9iea2Z9/TCPlebxv6KpjO9f1BUPz+14qundTSYT9kgf8j5wA1k37VstfBQ4xnuURYdnbGzJBJXw==",
+          "version": "4.13.0",
+          "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.0.tgz",
+          "integrity": "sha512-Ofusy7BwHkU7z4TNsVdf7wm5W3KR625KqlQj4AiWPnBvclmZU0Y2bVK8b8Mz8nW7sEX9TJcCdX6KeaincE/cLw==",
           "requires": {
-            "@octokit/types": "^6.8.3",
+            "@octokit/types": "^6.11.0",
             "deprecation": "^2.3.1"
           }
         },
@@ -1694,12 +1694,11 @@
           }
         },
         "@octokit/types": {
-          "version": "6.8.4",
-          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.8.4.tgz",
-          "integrity": "sha512-iKAMx//mBk74NwXdeKg8kdYO5zb1n6Ypk5uxwM96U35rJQv4Y8lMQxfzhN8QyzQHaZkxQsl28bn+MPEhiqXb7w==",
+          "version": "6.11.0",
+          "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.11.0.tgz",
+          "integrity": "sha512-RMLAmpPZf/a33EsclBazKg02NCCj4rC69V9sUgf0SuWTjmnBD2QC1nIVtJo7RJrGnwG1+aoFBb2yTrWm/8AS7w==",
           "requires": {
-            "@octokit/openapi-types": "^4.0.3",
-            "@types/node": ">= 8"
+            "@octokit/openapi-types": "^5.2.0"
           }
         },
         "is-plain-object": {
diff --git a/package.json b/package.json
index 40ffdca..fdf4d1a 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
   "dependencies": {
     "@actions/core": "^1.2.6",
     "@actions/github": "^4.0.0",
-    "@octokit/rest": "^18.1.1",
+    "@octokit/rest": "^18.3.0",
     "lodash.deburr": "^4.1.0",
     "semver": "^7.3.4"
   },
diff --git a/src/classes/issue.spec.ts b/src/classes/issue.spec.ts
index 8cbd3d9..8d852b2 100644
--- a/src/classes/issue.spec.ts
+++ b/src/classes/issue.spec.ts
@@ -28,6 +28,8 @@ describe('Issue', (): void => {
       exemptIssueLabels: '',
       exemptPrLabels: '',
       onlyLabels: '',
+      onlyIssueLabels: '',
+      onlyPrLabels: '',
       operationsPerRun: 0,
       removeStaleWhenUpdated: false,
       repoToken: '',
diff --git a/src/classes/issue.ts b/src/classes/issue.ts
index 816de1f..d48add6 100644
--- a/src/classes/issue.ts
+++ b/src/classes/issue.ts
@@ -15,7 +15,7 @@ export class Issue implements IIssue {
   updated_at: IsoDateString;
   readonly labels: ILabel[];
   readonly pull_request: Object | null | undefined;
-  readonly state: string;
+  readonly state: string | 'closed' | 'open';
   readonly locked: boolean;
   readonly milestone: IMilestone | undefined;
   readonly assignees: IAssignee[];
diff --git a/src/classes/issues-processor.ts b/src/classes/issues-processor.ts
index 758950b..cbed3f5 100644
--- a/src/classes/issues-processor.ts
+++ b/src/classes/issues-processor.ts
@@ -120,6 +120,34 @@ export class IssuesProcessor {
       const daysBeforeStale: number = issue.isPullRequest
         ? this._getDaysBeforePrStale()
         : this._getDaysBeforeIssueStale();
+      const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue));
+
+      if (onlyLabels.length > 0) {
+        issueLogger.info(
+          `The option "onlyLabels" was specified to only processed the issues and pull requests with all those labels (${onlyLabels.length})`
+        );
+
+        const hasAllWhitelistedLabels: boolean = onlyLabels.every(
+          (label: Readonly<string>): boolean => {
+            return isLabeled(issue, label);
+          }
+        );
+
+        if (!hasAllWhitelistedLabels) {
+          issueLogger.info(
+            `Skipping this $$type because it doesn't have all the required labels`
+          );
+          continue; // Don't process issues without all of the required labels
+        } else {
+          issueLogger.info(
+            `All the required labels are present on this $$type. Continuing the process`
+          );
+        }
+      } else {
+        issueLogger.info(
+          `The option "onlyLabels" was not specified. Continuing the process for this $$type`
+        );
+      }
 
       issueLogger.info(`Days before $$type stale: ${daysBeforeStale}`);
 
@@ -140,6 +168,9 @@ export class IssuesProcessor {
         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) {
         const startDate: Date = new Date(this.options.startDate);
         const createdAt: Date = new Date(issue.created_at);
@@ -395,7 +426,6 @@ export class IssuesProcessor {
           owner: context.repo.owner,
           repo: context.repo.repo,
           state: 'open',
-          labels: this.options.onlyLabels,
           per_page: 100,
           direction: this.options.ascending ? 'asc' : 'desc',
           page
@@ -657,14 +687,55 @@ export class IssuesProcessor {
       : this.options.daysBeforePrClose;
   }
 
+  private _getOnlyLabels(issue: Issue): string {
+    if (issue.isPullRequest) {
+      if (this.options.onlyPrLabels !== '') {
+        return this.options.onlyPrLabels;
+      }
+    } else {
+      if (this.options.onlyIssueLabels !== '') {
+        return this.options.onlyIssueLabels;
+      }
+    }
+
+    return this.options.onlyLabels;
+  }
+
   private async _removeStaleLabel(
     issue: Issue,
     staleLabel: Readonly<string>
   ): Promise<void> {
     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);
   }
+
+  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);
+    }
+  }
 }
diff --git a/src/interfaces/issues-processor-options.ts b/src/interfaces/issues-processor-options.ts
index bb95eba..28027e9 100644
--- a/src/interfaces/issues-processor-options.ts
+++ b/src/interfaces/issues-processor-options.ts
@@ -19,6 +19,8 @@ export interface IIssuesProcessorOptions {
   closePrLabel: string;
   exemptPrLabels: string;
   onlyLabels: string;
+  onlyIssueLabels: string;
+  onlyPrLabels: string;
   operationsPerRun: number;
   removeStaleWhenUpdated: boolean;
   debugOnly: boolean;
diff --git a/src/main.ts b/src/main.ts
index 14e949c..f5dd905 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -39,6 +39,8 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
     closePrLabel: core.getInput('close-pr-label'),
     exemptPrLabels: core.getInput('exempt-pr-labels'),
     onlyLabels: core.getInput('only-labels'),
+    onlyIssueLabels: core.getInput('only-issue-labels'),
+    onlyPrLabels: core.getInput('only-pr-labels'),
     operationsPerRun: parseInt(
       core.getInput('operations-per-run', {required: true})
     ),
@@ -106,4 +108,4 @@ function _toOptionalBoolean(
   return undefined;
 }
 
-_run();
+void _run();


Test patch:
diff --git a/__tests__/constants/default-processor-options.ts b/__tests__/constants/default-processor-options.ts
index 23fe99b..da7b7ca 100644
--- a/__tests__/constants/default-processor-options.ts
+++ b/__tests__/constants/default-processor-options.ts
@@ -19,6 +19,8 @@ export const DefaultProcessorOptions: IIssuesProcessorOptions = Object.freeze({
   closePrLabel: '',
   exemptPrLabels: '',
   onlyLabels: '',
+  onlyIssueLabels: '',
+  onlyPrLabels: '',
   operationsPerRun: 100,
   debugOnly: true,
   removeStaleWhenUpdated: false,
diff --git a/__tests__/main.spec.ts b/__tests__/main.spec.ts
index 46ea62f..fcb5544 100644
--- a/__tests__/main.spec.ts
+++ b/__tests__/main.spec.ts
@@ -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.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);
+});
diff --git a/__tests__/only-labels.spec.ts b/__tests__/only-labels.spec.ts
new file mode 100644
index 0000000..2ca5167
--- /dev/null
+++ b/__tests__/only-labels.spec.ts
@@ -0,0 +1,1152 @@
+import {Issue} from '../src/classes/issue';
+import {IssuesProcessor} from '../src/classes/issues-processor';
+import {IIssue} from '../src/interfaces/issue';
+import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
+import {DefaultProcessorOptions} from './constants/default-processor-options';
+import {generateIssue} from './functions/generate-issue';
+
+let issuesProcessorBuilder: IssuesProcessorBuilder;
+let issuesProcessor: IssuesProcessor;
+
+describe('only-labels option', (): void => {
+  beforeEach((): void => {
+    issuesProcessorBuilder = new IssuesProcessorBuilder();
+  });
+
+  test('should stale when not set even if the issue has no label', async (): Promise<void> => {
+    expect.assertions(1);
+    issuesProcessor = issuesProcessorBuilder
+      .emptyOnlyLabels()
+      .issuesOrPrs([{labels: []}])
+      .build();
+
+    await issuesProcessor.processIssues();
+
+    expect(issuesProcessor.staleIssues).toHaveLength(1);
+  });
+
+  test('should stale when not set even if the issue has a label', async (): Promise<void> => {
+    expect.assertions(1);
+    issuesProcessor = issuesProcessorBuilder
+      .emptyOnlyLabels()
+      .issuesOrPrs([{labels: [{name: 'label'}]}])
+      .build();
+
+    await issuesProcessor.processIssues();
+
+    expect(issuesProcessor.staleIssues).toHaveLength(1);
+  });
+
+  test('should not stale when set and the issue has no label', async (): Promise<void> => {
+    expect.assertions(1);
+    issuesProcessor = issuesProcessorBuilder
+      .onlyLabels('dummy-label')
+      .issuesOrPrs([{labels: []}])
+      .build();
+
+    await issuesProcessor.processIssues();
+
+    expect(issuesProcessor.staleIssues).toHaveLength(0);
+  });
+
+  test('should not stale when set and the issue has a different label', async (): Promise<void> => {
+    expect.assertions(1);
+    issuesProcessor = issuesProcessorBuilder
+      .onlyLabels('dummy-label')
+      .issuesOrPrs([
+        {
+          labels: [
+            {
+              name: 'label'
+            }
+          ]
+        }
+      ])
+      .build();
+
+    await issuesProcessor.processIssues();
+
+    expect(issuesProcessor.staleIssues).toHaveLength(0);
+  });
+
+  test('should not stale when set and the issue has different labels', async (): Promise<void> => {
+    expect.assertions(1);
+    issuesProcessor = issuesProcessorBuilder
+      .onlyLabels('dummy-label')
+      .issuesOrPrs([
+        {
+          labels: [
+            {
+              name: 'label-1'
+            },
+            {
+              name: 'label-2'
+            }
+          ]
+        }
+      ])
+      .build();
+
+    await issuesProcessor.processIssues();
+
+    expect(issuesProcessor.staleIssues).toHaveLength(0);
+  });
+
+  test('should stale when set and the issue has the same label', async (): Promise<void> => {
+    expect.assertions(1);
+    issuesProcessor = issuesProcessorBuilder
+      .onlyLabels('dummy-label')
+      .issuesOrPrs([
+        {
+          labels: [
+            {
+              name: 'dummy-label'
+            }
+          ]
+        }
+      ])
+      .build();
+
+    await issuesProcessor.processIssues();
+
+    expect(issuesProcessor.staleIssues).toHaveLength(1);
+  });
+
+  test('should not stale when set and the issue has only one of the same label', async (): Promise<void> => {
+    expect.assertions(1);
+    issuesProcessor = issuesProcessorBuilder
+      .onlyLabels('dummy-label-1,dummy-label-2')
+      .issuesOrPrs([
+        {
+          labels: [
+            {
+              name: 'dummy-label-1'
+            }
+          ]
+        }
+      ])
+      .build();
+
+    await issuesProcessor.processIssues();
+
+    expect(issuesProcessor.staleIssues).toHaveLength(0);
+  });
+
+  test('should stale when set and the issue has all the same labels', async (): Promise<void> => {
+    expect.assertions(1);
+    issuesProcessor = issuesProcessorBuilder
+      .onlyLabels('dummy-label-1,dummy-label-2')
+      .issuesOrPrs([
+        {
+          labels: [
+            {
+              name: 'dummy-label-1'
+            },
+            {
+              name: 'dummy-label-2'
+            }
+          ]
+        }
+      ])
+      .build();
+
+    await issuesProcessor.processIssues();
+
+    expect(issuesProcessor.staleIssues).toHaveLength(1);
+  });
+});
+
+describe('only-issue-labels option', (): void => {
+  beforeEach((): void => {
+    issuesProcessorBuilder = new IssuesProcessorBuilder();
+  });
+
+  describe('when the only-labels options is not set', (): void => {
+    beforeEach((): void => {
+      issuesProcessorBuilder.emptyOnlyLabels();
+    });
+
+    test('should stale when not set even if the issue has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyIssueLabels()
+        .issues([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should stale when not set even if the issue has a label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyIssueLabels()
+        .issues([{labels: [{name: 'dummy-label'}]}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should not stale when set and the issue has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the issue has a different label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the issue has different labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'label-1'
+              },
+              {
+                name: 'label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the issue has the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should not stale when set and the issue has only one of the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label-1,dummy-label-2')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the issue has all the same labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label-1,dummy-label-2')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              },
+              {
+                name: 'dummy-label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+  });
+
+  describe('when the only-labels options is set (same as only-issue-labels)', (): void => {
+    beforeEach((): void => {
+      issuesProcessorBuilder.onlyLabels('dummy-label');
+    });
+
+    test('should not stale when not set even if the issue has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyIssueLabels()
+        .issues([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when not set even if the issue has a label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyIssueLabels()
+        .issues([{labels: [{name: 'label'}]}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the issue has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the issue has a different label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the issue has different labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'label-1'
+              },
+              {
+                name: 'label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the issue has the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should not stale when set and the issue has only one of the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label-1,dummy-label-2')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the issue has all the same labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label-1,dummy-label-2')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              },
+              {
+                name: 'dummy-label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+  });
+
+  describe('when the only-labels options is set (different than only-issue-labels)', (): void => {
+    beforeEach((): void => {
+      issuesProcessorBuilder.onlyLabels('dummy-only-label');
+    });
+
+    test('should not stale when not set even if the issue has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyIssueLabels()
+        .issues([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when not set even if the issue has a label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyIssueLabels()
+        .issues([{labels: [{name: 'label'}]}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the issue has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the issue has a different label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the issue has different labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'label-1'
+              },
+              {
+                name: 'label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the issue has the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should not stale when set and the issue has only one of the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label-1,dummy-label-2')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the issue has all the same labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyIssueLabels('dummy-label-1,dummy-label-2')
+        .issues([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              },
+              {
+                name: 'dummy-label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+  });
+});
+
+describe('only-pr-labels option', (): void => {
+  beforeEach((): void => {
+    issuesProcessorBuilder = new IssuesProcessorBuilder();
+  });
+
+  describe('when the only-labels options is not set', (): void => {
+    beforeEach((): void => {
+      issuesProcessorBuilder.emptyOnlyLabels();
+    });
+
+    test('should stale when not set even if the pr has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyPrLabels()
+        .prs([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should stale when not set even if the pr has a label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyPrLabels()
+        .prs([{labels: [{name: 'dummy-label'}]}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should not stale when set and the pr has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the pr has a different label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the pr has different labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'label-1'
+              },
+              {
+                name: 'label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the pr has the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should not stale when set and the pr has only one of the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label-1,dummy-label-2')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the pr has all the same labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label-1,dummy-label-2')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              },
+              {
+                name: 'dummy-label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+  });
+
+  describe('when the only-labels options is set (same as only-pr-labels)', (): void => {
+    beforeEach((): void => {
+      issuesProcessorBuilder.onlyLabels('dummy-label');
+    });
+
+    test('should not stale when not set even if the pr has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyPrLabels()
+        .prs([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when not set even if the pr has a label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyPrLabels()
+        .prs([{labels: [{name: 'label'}]}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the pr has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the pr has a different label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the pr has different labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'label-1'
+              },
+              {
+                name: 'label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the pr has the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should not stale when set and the pr has only one of the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label-1,dummy-label-2')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the pr has all the same labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label-1,dummy-label-2')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              },
+              {
+                name: 'dummy-label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+  });
+
+  describe('when the only-labels options is set (different than only-pr-labels)', (): void => {
+    beforeEach((): void => {
+      issuesProcessorBuilder.onlyLabels('dummy-only-label');
+    });
+
+    test('should not stale when not set even if the pr has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyPrLabels()
+        .prs([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when not set even if the pr has a label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .emptyOnlyPrLabels()
+        .prs([{labels: [{name: 'label'}]}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the pr has no label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([{labels: []}])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the pr has a different label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should not stale when set and the pr has different labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'label-1'
+              },
+              {
+                name: 'label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the pr has the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+
+    test('should not stale when set and the pr has only one of the same label', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label-1,dummy-label-2')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(0);
+    });
+
+    test('should stale when set and the pr has all the same labels', async (): Promise<void> => {
+      expect.assertions(1);
+      issuesProcessor = issuesProcessorBuilder
+        .onlyPrLabels('dummy-label-1,dummy-label-2')
+        .prs([
+          {
+            labels: [
+              {
+                name: 'dummy-label-1'
+              },
+              {
+                name: 'dummy-label-2'
+              }
+            ]
+          }
+        ])
+        .build();
+
+      await issuesProcessor.processIssues();
+
+      expect(issuesProcessor.staleIssues).toHaveLength(1);
+    });
+  });
+});
+
+class IssuesProcessorBuilder {
+  private _options: IIssuesProcessorOptions = {
+    ...DefaultProcessorOptions,
+    daysBeforeStale: 0
+  };
+  private _issues: Issue[] = [];
+
+  onlyLabels(labels: string): IssuesProcessorBuilder {
+    this._options.onlyLabels = labels;
+
+    return this;
+  }
+
+  onlyIssueLabels(labels: string): IssuesProcessorBuilder {
+    this._options.onlyIssueLabels = labels;
+
+    return this;
+  }
+
+  onlyPrLabels(labels: string): IssuesProcessorBuilder {
+    this._options.onlyPrLabels = labels;
+
+    return this;
+  }
+
+  emptyOnlyLabels(): IssuesProcessorBuilder {
+    return this.onlyLabels('');
+  }
+
+  emptyOnlyIssueLabels(): IssuesProcessorBuilder {
+    return this.onlyIssueLabels('');
+  }
+
+  emptyOnlyPrLabels(): IssuesProcessorBuilder {
+    return this.onlyPrLabels('');
+  }
+
+  issuesOrPrs(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
+    this._issues = issues.map(
+      (issue: Readonly<Partial<IIssue>>, index: Readonly<number>): Issue =>
+        generateIssue(
+          this._options,
+          index,
+          issue.title ?? 'dummy-title',
+          issue.updated_at ?? new Date().toDateString(),
+          issue.created_at ?? new Date().toDateString(),
+          !!issue.pull_request,
+          issue.labels ? issue.labels.map(label => label.name) : []
+        )
+    );
+
+    return this;
+  }
+
+  issues(issues: Partial<IIssue>[]): IssuesProcessorBuilder {
+    this.issuesOrPrs(
+      issues.map(
+        (issue: Readonly<Partial<IIssue>>): Partial<IIssue> => {
+          return {
+            ...issue,
+            pull_request: null
+          };
+        }
+      )
+    );
+
+    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;
+  }
+
+  build(): IssuesProcessor {
+    return new IssuesProcessor(
+      this._options,
+      async () => 'abot',
+      async p => (p === 1 ? this._issues : []),
+      async () => [],
+      async () => new Date().toDateString()
+    );
+  }
+}