Skip to content

Commit

Permalink
feat: validate config and set custom options for issues and pulls
Browse files Browse the repository at this point in the history
  • Loading branch information
dessant committed May 6, 2018
1 parent 7a737c7 commit ecb0e91
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 48 deletions.
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
28 changes: 20 additions & 8 deletions assets/app-description.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"release": "standard-version && yarn run push"
},
"dependencies": {
"joi": "^13.2.0",
"probot": "^6.1.0",
"probot-scheduler": "^1.1.0"
},
Expand Down
9 changes: 0 additions & 9 deletions src/defaults.js

This file was deleted.

30 changes: 28 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,47 @@
const createScheduler = require('probot-scheduler');

const App = require('./lock');
const schema = require('./schema');

module.exports = robot => {
scheduler = createScheduler(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 changes: 28 additions & 21 deletions src/lock.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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',
Expand All @@ -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 changes: 44 additions & 0 deletions src/schema.js
Original file line number Diff line number Diff line change
@@ -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 changes: 28 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit ecb0e91

Please sign in to comment.