diff --git a/lib/schema.js b/lib/schema.js index d0586a7..3c718c3 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -26,6 +26,10 @@ const fields = { staleLabel: Joi.string() .description('Label to use when marking as stale'), + closedLabel: Joi.alternatives().try(Joi.string(), Joi.boolean().only(false)) + .error(() => '"closedLabel" must be a string or false') + .description('Label to use when issue is closed. Set to `false` to disable'), + markComment: Joi.alternatives().try(Joi.string(), Joi.any().only(false)) .error(() => '"markComment" must be a string or false') .description('Comment to post when marking as stale. Set to `false` to disable'), @@ -52,6 +56,7 @@ const schema = Joi.object().keys({ exemptMilestones: fields.exemptMilestones.default(false), exemptAssignees: fields.exemptMilestones.default(false), staleLabel: fields.staleLabel.default('wontfix'), + closedLabel: fields.closeComment.default(false), markComment: fields.markComment.default( 'Is this still relevant? If so, what is blocking it? ' + 'Is there anything you can do to help move it forward?' + diff --git a/lib/stale.js b/lib/stale.js index 0c13b8d..9d0e154 100644 --- a/lib/stale.js +++ b/lib/stale.js @@ -155,6 +155,11 @@ module.exports = class Stale { if (closeComment) { await this.github.issues.createComment({ owner, repo, number, body: closeComment }) } + const closedLabel = this.getConfigValue(type, 'closedLabel') + if (closedLabel && closedLabel.trim().length !== 0) { + await this.ensureClosedLabelExists(type) + this.github.issues.addLabels({ owner, repo, number, labels: [closedLabel] }) + } return this.github.issues.edit({ owner, repo, number, state: 'closed' }) } else { this.logger.info('%s/%s#%d would have been closed (dry-run)', owner, repo, number) @@ -214,6 +219,15 @@ module.exports = class Stale { }) } + async ensureClosedLabelExists (type) { + const { owner, repo } = this.config + const closedLabel = this.getConfigValue(type, 'closedLabel') + + return this.github.issues.getLabel({ owner, repo, name: closedLabel }).catch(() => { + return this.github.issues.createLabel({ owner, repo, name: closedLabel, color: 'E99695' }) + }) + } + since (days) { const ttl = days * 24 * 60 * 60 * 1000 let date = new Date(new Date() - ttl) diff --git a/test/schema.test.js b/test/schema.test.js index 10ea3d3..0d97c2f 100644 --- a/test/schema.test.js +++ b/test/schema.test.js @@ -68,6 +68,7 @@ describe('schema', () => { exemptMilestones: false, exemptAssignees: false, staleLabel: 'wontfix', + closedLabel: false, perform: true, markComment: 'Is this still relevant? If so, what is blocking it? ' + 'Is there anything you can do to help move it forward?' + diff --git a/test/stale.test.js b/test/stale.test.js index aedf1e6..8d0b591 100644 --- a/test/stale.test.js +++ b/test/stale.test.js @@ -44,7 +44,7 @@ describe('stale', () => { test( 'removes the stale label and ignores if it has already been removed', async () => { - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) for (const type of ['pulls', 'issues']) { try { @@ -138,7 +138,7 @@ describe('stale', () => { test( 'should not close issues if daysUntilClose is configured as false', async () => { - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.config.daysUntilClose = false stale.getStale = jest.fn().mockImplementation(() => Promise.resolve({ data: { items: [] } })) stale.getClosable = jest.fn() @@ -154,7 +154,7 @@ describe('stale', () => { test( 'should not close issues if the keyword pulls or keyword issues is used, and daysUntilClose is configured as false', async () => { - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.config.pulls = { daysUntilClose: false } stale.config.issues = { daysUntilClose: false } stale.getStale = jest.fn().mockImplementation(() => Promise.resolve({ data: { items: [] } })) @@ -171,7 +171,7 @@ describe('stale', () => { test( 'should not close issues if only keyword is configured with the pulls value', async () => { - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.config.only = 'pulls' stale.config.daysUntilClose = 1 stale.getStale = jest.fn().mockImplementation(() => Promise.resolve({ data: { items: [] } })) @@ -185,7 +185,7 @@ describe('stale', () => { test( 'should not close pull requests if only keyword is configured with the issues value', async () => { - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.config.only = 'issues' stale.config.daysUntilClose = 1 stale.getStale = jest.fn().mockImplementation(() => Promise.resolve({ data: { items: [] } })) @@ -200,7 +200,7 @@ describe('stale', () => { test( 'should not mark issue if it is already closed', async () => { - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.getStale = jest.fn().mockImplementation(() => { return Promise.resolve({ data: { @@ -220,7 +220,7 @@ describe('stale', () => { test( 'should not mark issue if it is locked', async () => { - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.getStale = jest.fn().mockImplementation(() => { return Promise.resolve({ data: { @@ -240,7 +240,7 @@ describe('stale', () => { test( 'should mark issue if it is open', async () => { - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.getStale = jest.fn().mockImplementation(() => { return Promise.resolve({ data: { @@ -263,7 +263,7 @@ describe('stale', () => { 'should not close issue if it is already closed', async () => { const staleLabel = 'stale' - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.config.daysUntilClose = 1 stale.getClosable = jest.fn().mockImplementation(() => { return Promise.resolve({ @@ -285,7 +285,7 @@ describe('stale', () => { 'should not close issue if it is locked', async () => { const staleLabel = 'stale' - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.config.daysUntilClose = 1 stale.getClosable = jest.fn().mockImplementation(() => { return Promise.resolve({ @@ -307,7 +307,7 @@ describe('stale', () => { 'should close issue if it is open', async () => { const staleLabel = 'stale' - let stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) + const stale = new Stale(github, { perform: true, owner: 'probot', repo: 'stale', logger: app.log }) stale.config.daysUntilClose = 1 stale.getClosable = jest.fn().mockImplementation(() => { return Promise.resolve({