mirror of
https://github.com/dessant/lock-threads.git
synced 2026-03-13 01:27:03 -04:00
feat: validate config and set custom options for issues and pulls
This commit is contained in:
28
README.md
28
README.md
@@ -30,18 +30,30 @@ The file can be empty, or it can override any of these default settings:
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 365
|
||||
|
||||
# Issues and pull requests with these labels will not be locked. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: >
|
||||
This thread has been automatically locked because it has not had recent
|
||||
activity. Please open a new issue for related bugs and link to relevant
|
||||
comments in this thread.
|
||||
# Issues or pull requests with these labels will not be locked
|
||||
# exemptLabels:
|
||||
# - no-locking
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs.
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
# Add a label when locking. Set to `false` to disable
|
||||
# lockLabel: outdated
|
||||
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - help-wanted
|
||||
# lockLabel: outdated
|
||||
|
||||
# pulls:
|
||||
# daysUntilLock: 30
|
||||
```
|
||||
|
||||
## How are issues and pull requests determined to be inactive?
|
||||
|
||||
@@ -17,18 +17,30 @@ Create `.github/lock.yml` in the default branch to enable the app. The file can
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 365
|
||||
|
||||
# Issues and pull requests with these labels will not be locked. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: >
|
||||
This thread has been automatically locked because it has not had recent
|
||||
activity. Please open a new issue for related bugs and link to relevant
|
||||
comments in this thread.
|
||||
# Issues or pull requests with these labels will not be locked
|
||||
# exemptLabels:
|
||||
# - no-locking
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs.
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
# Add a label when locking. Set to `false` to disable
|
||||
# lockLabel: outdated
|
||||
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - help-wanted
|
||||
# lockLabel: outdated
|
||||
|
||||
# pulls:
|
||||
# daysUntilLock: 30
|
||||
```
|
||||
|
||||
## Supporting the Project
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"release": "standard-version && yarn run push"
|
||||
},
|
||||
"dependencies": {
|
||||
"joi": "^13.2.0",
|
||||
"probot": "^6.1.0",
|
||||
"probot-scheduler": "^1.1.0"
|
||||
},
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
daysUntilLock: 365,
|
||||
exemptLabels: [],
|
||||
lockLabel: false,
|
||||
lockComment:
|
||||
'This thread has been automatically locked because ' +
|
||||
'it has not had recent activity. Please open a new issue ' +
|
||||
'for related bugs and link to relevant comments in this thread.'
|
||||
};
|
||||
30
src/index.js
30
src/index.js
@@ -1,6 +1,7 @@
|
||||
const createScheduler = require('probot-scheduler');
|
||||
|
||||
const App = require('./lock');
|
||||
const schema = require('./schema');
|
||||
|
||||
module.exports = robot => {
|
||||
scheduler = createScheduler(robot);
|
||||
@@ -8,14 +9,39 @@ module.exports = robot => {
|
||||
robot.on('schedule.repository', async context => {
|
||||
const app = await getApp(context);
|
||||
if (app) {
|
||||
await app.lock();
|
||||
const {only: type} = app.config;
|
||||
if (type) {
|
||||
await app.lock(type);
|
||||
} else {
|
||||
await app.lock('issues');
|
||||
await app.lock('pulls');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function getApp(context) {
|
||||
let config = await context.config('lock.yml');
|
||||
const config = await getConfig(context);
|
||||
if (config) {
|
||||
return new App(context, config, robot.log);
|
||||
}
|
||||
}
|
||||
|
||||
async function getConfig(context) {
|
||||
const {owner, repo} = context.issue();
|
||||
let config;
|
||||
try {
|
||||
const repoConfig = await context.config('lock.yml');
|
||||
if (repoConfig) {
|
||||
const {error, value} = schema.validate(repoConfig);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
config = value;
|
||||
}
|
||||
} catch (err) {
|
||||
robot.log.warn({err: new Error(err), owner, repo}, 'Invalid config');
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
49
src/lock.js
49
src/lock.js
@@ -1,17 +1,16 @@
|
||||
const defaults = require('./defaults');
|
||||
|
||||
module.exports = class Lock {
|
||||
constructor(context, config, logger) {
|
||||
this.context = context;
|
||||
this.config = Object.assign({}, defaults, config);
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
async lock() {
|
||||
async lock(type) {
|
||||
const {owner, repo} = this.context.repo();
|
||||
const {lockComment, lockLabel} = this.config;
|
||||
const lockLabel = this.getConfigValue(type, 'lockLabel');
|
||||
const lockComment = this.getConfigValue(type, 'lockComment');
|
||||
|
||||
const issues = await this.getLockableIssues();
|
||||
const issues = await this.getLockableIssues(type);
|
||||
for (const issue of issues) {
|
||||
const issueUrl = `${owner}/${repo}/issues/${issue.number}`;
|
||||
if (lockComment) {
|
||||
@@ -47,17 +46,12 @@ module.exports = class Lock {
|
||||
}
|
||||
}
|
||||
|
||||
async getLockableIssues() {
|
||||
const results = await this.search();
|
||||
return results.data.items.filter(issue => !issue.locked);
|
||||
}
|
||||
|
||||
search() {
|
||||
search(type) {
|
||||
const {owner, repo} = this.context.repo();
|
||||
const {exemptLabels, daysUntilLock, only} = this.config;
|
||||
const timestamp = this.since(daysUntilLock)
|
||||
.toISOString()
|
||||
.replace(/\.\d{3}\w$/, '');
|
||||
const daysUntilLock = this.getConfigValue(type, 'daysUntilLock');
|
||||
const exemptLabels = this.getConfigValue(type, 'exemptLabels');
|
||||
|
||||
const timestamp = this.getUpdatedTimestamp(daysUntilLock);
|
||||
|
||||
let query = `repo:${owner}/${repo} is:closed updated:<${timestamp}`;
|
||||
if (exemptLabels.length) {
|
||||
@@ -66,13 +60,13 @@ module.exports = class Lock {
|
||||
.join(' ');
|
||||
query += ` ${queryPart}`;
|
||||
}
|
||||
if (only === 'issues') {
|
||||
if (type === 'issues') {
|
||||
query += ' is:issue';
|
||||
} else if (only === 'pulls') {
|
||||
} else {
|
||||
query += ' is:pr';
|
||||
}
|
||||
|
||||
this.logger.info(`[${owner}/${repo}] Searching`);
|
||||
this.logger.info(`[${owner}/${repo}] Searching ${type}`);
|
||||
return this.context.github.search.issues({
|
||||
q: query,
|
||||
sort: 'updated',
|
||||
@@ -81,8 +75,21 @@ module.exports = class Lock {
|
||||
});
|
||||
}
|
||||
|
||||
since(days) {
|
||||
async getLockableIssues(type) {
|
||||
const results = await this.search(type);
|
||||
return results.data.items.filter(issue => !issue.locked);
|
||||
}
|
||||
|
||||
getUpdatedTimestamp(days) {
|
||||
const ttl = days * 24 * 60 * 60 * 1000;
|
||||
return new Date(new Date() - ttl);
|
||||
const date = new Date(new Date() - ttl);
|
||||
return date.toISOString().replace(/\.\d{3}\w$/, '');
|
||||
}
|
||||
|
||||
getConfigValue(type, key) {
|
||||
if (this.config[type] && typeof this.config[type][key] !== 'undefined') {
|
||||
return this.config[type][key];
|
||||
}
|
||||
return this.config[key];
|
||||
}
|
||||
};
|
||||
|
||||
44
src/schema.js
Normal file
44
src/schema.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const Joi = require('joi');
|
||||
|
||||
const fields = {
|
||||
daysUntilLock: Joi.number()
|
||||
.min(1)
|
||||
.description(
|
||||
'Number of days of inactivity before a closed issue or pull request is locked'
|
||||
),
|
||||
|
||||
exemptLabels: Joi.array()
|
||||
.single()
|
||||
.items(Joi.string())
|
||||
.description(
|
||||
'Issues and pull requests with these labels will not be locked. Set to `[]` to disable'
|
||||
),
|
||||
|
||||
lockLabel: Joi.alternatives()
|
||||
.try(Joi.string(), Joi.boolean().only(false))
|
||||
.description(
|
||||
'Label to add before locking, such as `outdated`. Set to `false` to disable'
|
||||
),
|
||||
|
||||
lockComment: Joi.alternatives()
|
||||
.try(Joi.string(), Joi.boolean().only(false))
|
||||
.description('Comment to post before locking. Set to `false` to disable')
|
||||
};
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
daysUntilLock: fields.daysUntilLock.default(365),
|
||||
exemptLabels: fields.exemptLabels.default([]),
|
||||
lockLabel: fields.lockLabel.default(false),
|
||||
lockComment: fields.lockComment.default(
|
||||
'This thread has been automatically locked since there has not been ' +
|
||||
'any recent activity after it was closed. Please open a new issue for ' +
|
||||
'related bugs.'
|
||||
),
|
||||
only: Joi.string()
|
||||
.valid('issues', 'pulls')
|
||||
.description('Limit to only `issues` or `pulls`'),
|
||||
pulls: Joi.object().keys(fields),
|
||||
issues: Joi.object().keys(fields)
|
||||
});
|
||||
|
||||
module.exports = schema;
|
||||
28
yarn.lock
28
yarn.lock
@@ -1664,6 +1664,10 @@ hoek@4.x.x:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
|
||||
|
||||
hoek@5.x.x:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-5.0.3.tgz#b71d40d943d0a95da01956b547f83c4a5b4a34ac"
|
||||
|
||||
hosted-git-info@^2.1.4, hosted-git-info@^2.1.5, hosted-git-info@^2.4.2:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c"
|
||||
@@ -1975,6 +1979,12 @@ isarray@1.0.0, isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
|
||||
isemail@3.x.x:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.1.2.tgz#937cf919002077999a73ea8b1951d590e84e01dd"
|
||||
dependencies:
|
||||
punycode "2.x.x"
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
@@ -1997,6 +2007,14 @@ jju@^1.1.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/jju/-/jju-1.3.0.tgz#dadd9ef01924bc728b03f2f7979bdbd62f7a2aaa"
|
||||
|
||||
joi@^13.2.0:
|
||||
version "13.2.0"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-13.2.0.tgz#72307f1765bb40b068361f9368a4ba1092b8478e"
|
||||
dependencies:
|
||||
hoek "5.x.x"
|
||||
isemail "3.x.x"
|
||||
topo "3.x.x"
|
||||
|
||||
js-yaml@^3.6.1:
|
||||
version "3.11.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
|
||||
@@ -3128,6 +3146,10 @@ pstree.remy@^1.1.0:
|
||||
dependencies:
|
||||
ps-tree "^1.1.0"
|
||||
|
||||
punycode@2.x.x:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d"
|
||||
|
||||
punycode@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
||||
@@ -3977,6 +3999,12 @@ to-regex@^3.0.1, to-regex@^3.0.2:
|
||||
regex-not "^1.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
topo@3.x.x:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/topo/-/topo-3.0.0.tgz#37e48c330efeac784538e0acd3e62ca5e231fe7a"
|
||||
dependencies:
|
||||
hoek "5.x.x"
|
||||
|
||||
touch@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
|
||||
|
||||
Reference in New Issue
Block a user