mirror of
https://github.com/xuexb/github-bot.git
synced 2026-01-18 13:56:38 +00:00
feat: Add modules
This commit is contained in:
parent
d86e3a81f8
commit
f6cc9f164a
48
src/app.js
Executable file
48
src/app.js
Executable file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file github-bot 入口文件
|
||||
* @author xuexb <fe.xiaowu@gmail.com>
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const Koa = require('koa');
|
||||
const bodyParser = require('koa-bodyparser');
|
||||
const requireDir = require('require-dir');
|
||||
const {verifySignature, getRepo} = require('./utils');
|
||||
const issueActions = requireDir('./modules/issues');
|
||||
const app = new Koa();
|
||||
const githubEvent = new EventEmitter();
|
||||
|
||||
app.use(bodyParser());
|
||||
|
||||
app.use(ctx => {
|
||||
let eventName = ctx.request.headers['x-github-event'];
|
||||
if (eventName && verifySignature(ctx.request)) {
|
||||
const payload = ctx.request.body;
|
||||
const action = payload.action || payload.ref_type;
|
||||
|
||||
eventName += `_${action}`;
|
||||
console.log(`receive event: ${eventName}`);
|
||||
|
||||
if (payload.sender.login !== process.env.GITHUB_BOT_NAME) {
|
||||
githubEvent.emit(eventName, {
|
||||
repo: getRepo(payload.repository.full_name),
|
||||
payload
|
||||
});
|
||||
}
|
||||
|
||||
ctx.body = 'Ok.';
|
||||
}
|
||||
else {
|
||||
ctx.body = 'Go away.';
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(issueActions).forEach((key) => {
|
||||
issueActions[key](githubEvent.on.bind(githubEvent));
|
||||
});
|
||||
|
||||
const port = 8000;
|
||||
app.listen(port);
|
||||
console.log(`Listening on http://0.0.0.0:${port}`);
|
||||
69
src/github.js
Executable file
69
src/github.js
Executable file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @file github 操作库
|
||||
* @author xuexb <fe.xiaowu@gmail.com>
|
||||
*/
|
||||
|
||||
const GitHub = require('github');
|
||||
|
||||
const github = new GitHub({
|
||||
debug: process.env.NODE_ENV === 'development'
|
||||
});
|
||||
|
||||
github.authenticate({
|
||||
type: 'token',
|
||||
token: process.env.GITHUB_TOKEN
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
commentIssue(payload, body) {
|
||||
const owner = payload.repository.owner.login;
|
||||
const repo = payload.repository.name;
|
||||
const number = payload.issue.number;
|
||||
|
||||
github.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
number,
|
||||
body
|
||||
});
|
||||
},
|
||||
|
||||
closeIssue(payload) {
|
||||
const owner = payload.repository.owner.login;
|
||||
const repo = payload.repository.name;
|
||||
const number = payload.issue.number;
|
||||
|
||||
github.issues.edit({
|
||||
owner,
|
||||
repo,
|
||||
number,
|
||||
state: 'closed'
|
||||
});
|
||||
},
|
||||
|
||||
addAssigneesToIssue(payload, assign) {
|
||||
const owner = payload.repository.owner.login;
|
||||
const repo = payload.repository.name;
|
||||
const number = payload.issue.number;
|
||||
|
||||
github.issues.edit({
|
||||
owner,
|
||||
repo,
|
||||
number,
|
||||
assignees: Array.isArray(assign) ? assign : [assign]
|
||||
});
|
||||
},
|
||||
|
||||
addLabelsToIssue(payload, labels) {
|
||||
const owner = payload.repository.owner.login;
|
||||
const repo = payload.repository.name;
|
||||
const number = payload.issue.number;
|
||||
|
||||
github.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
number,
|
||||
labels: Array.isArray(labels) ? labels : [labels]
|
||||
});
|
||||
}
|
||||
};
|
||||
25
src/modules/issues/autoAssign.js
Executable file
25
src/modules/issues/autoAssign.js
Executable file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @file issue 自动 `assign` 给指定人员
|
||||
* @author xuexb <fe.xiaowu@gmail.com>
|
||||
*/
|
||||
|
||||
const {addAssigneesToIssue} = require('../../github');
|
||||
|
||||
const assignMap = {
|
||||
bug: 'xuexb',
|
||||
enhancement: 'xuexb',
|
||||
question: 'xuexb'
|
||||
};
|
||||
|
||||
function autoAssign(on) {
|
||||
on('issues_labeled', ({payload, repo}) => {
|
||||
if (assignMap[payload.label.name]) {
|
||||
addAssigneesToIssue(
|
||||
payload,
|
||||
assignMap[payload.label.name]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = autoAssign;
|
||||
18
src/modules/issues/autoReleaseNote.js
Normal file
18
src/modules/issues/autoReleaseNote.js
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @file 根据 tag 自动 release
|
||||
* @author xuexb <fe.xiaowu@gmail.com>
|
||||
*/
|
||||
|
||||
const {getReleaseByTag} = require('../../github');
|
||||
const {cloneRepo, getTags} = require('../../utils');
|
||||
|
||||
function autoReleaseNote(on) {
|
||||
on('create_tag', ({payload, repo}) => {
|
||||
const repoDir = cloneRepo(payload.repository.clone_url, repo);
|
||||
const tags = getTags(repoDir);
|
||||
|
||||
console.log(repoDir, tags);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = autoReleaseNote;
|
||||
41
src/modules/issues/replyInvalid.js
Executable file
41
src/modules/issues/replyInvalid.js
Executable file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @file 不规范issue则自动关闭
|
||||
* @author xuexb <fe.xiaowu@gmail.com>
|
||||
*/
|
||||
|
||||
const format = require('string-template');
|
||||
const {
|
||||
commentIssue,
|
||||
closeIssue,
|
||||
addLabelsToIssue
|
||||
} = require('../../github');
|
||||
|
||||
const comment = [
|
||||
'hi @{user},非常感谢您的反馈,',
|
||||
'但是由于您没有使用 [规范的issue](https://github.com/xuexb/github-bot) 格式, 将直接被关闭, 谢谢!'
|
||||
].join('');
|
||||
|
||||
const match = str => {
|
||||
return /node version:\s*(\d\.?)+/.test(str) && /url:\s*(https?:)?\/\/(\w{3,})/.test(str);
|
||||
};
|
||||
|
||||
function replyInvalid(on) {
|
||||
on('issues_opened', ({payload}) => {
|
||||
const issue = payload.issue;
|
||||
const opener = issue.user.login;
|
||||
|
||||
if (!match(issue.body)) {
|
||||
commentIssue(
|
||||
payload,
|
||||
format(comment, {
|
||||
user: opener
|
||||
})
|
||||
);
|
||||
|
||||
closeIssue(payload);
|
||||
addLabelsToIssue(payload, 'invalid');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = replyInvalid;
|
||||
25
src/modules/issues/replyNeedDemo.js
Executable file
25
src/modules/issues/replyNeedDemo.js
Executable file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @file 不规范issue则自动关闭
|
||||
* @author xuexb <fe.xiaowu@gmail.com>
|
||||
*/
|
||||
|
||||
const format = require('string-template');
|
||||
const {commentIssue} = require('../../github');
|
||||
|
||||
const comment = 'hi @{user},请提供一个可预览的链接,如: <https://codepen.io/pen?template=KgPZrE&editors=0010>';
|
||||
|
||||
function replyNeedDemo(on) {
|
||||
on('issues_labeled', ({payload, repo}) => {
|
||||
if (payload.label.name === 'need demo') {
|
||||
commentIssue(
|
||||
payload,
|
||||
format(comment, {
|
||||
user: payload.issue.user.login
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = replyNeedDemo;
|
||||
54
src/utils.js
Executable file
54
src/utils.js
Executable file
@ -0,0 +1,54 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const crypto = require('crypto');
|
||||
const {fixedTimeComparison} = require('cryptiles');
|
||||
const {execSync, exec} = require('child_process');
|
||||
|
||||
const utils = {
|
||||
mentioned(body) {
|
||||
return body.includes(`@${process.env.GITHUB_BOT_NAME}`);
|
||||
},
|
||||
|
||||
verifySignature(request) {
|
||||
let signature = crypto.createHmac('sha1', process.env.GITHUB_SECRET_TOKEN)
|
||||
.update(request.rawBody)
|
||||
.digest('hex');
|
||||
signature = `sha1=${signature}`;
|
||||
return fixedTimeComparison(signature, request.headers['x-hub-signature']);
|
||||
},
|
||||
|
||||
getRepo(url) {
|
||||
return url.split('/')[1];
|
||||
},
|
||||
|
||||
isDirectory(file) {
|
||||
try {
|
||||
return fs.statSync(file).isDirectory();
|
||||
}
|
||||
catch (e) {
|
||||
if (e.code !== 'ENOENT') {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
cloneRepo(url, repo) {
|
||||
const repoDir = path.resolve(__dirname, '../github/', repo);
|
||||
|
||||
if (!utils.isDirectory(repoDir)) {
|
||||
throw new Error(`${repoDir} 不是github目录!`);
|
||||
}
|
||||
|
||||
execSync(`cd ${repoDir} && git pull`);
|
||||
|
||||
return repoDir;
|
||||
},
|
||||
|
||||
getTags(dir) {
|
||||
return execSync(`cd ${dir} && git tag -l`).toString().split(/\n+/).filter(tag => !!tag).reverse();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = utils;
|
||||
Loading…
x
Reference in New Issue
Block a user