mirror of
https://gitea.com/Lydanne/issues-helper.git
synced 2025-08-19 18:25:58 +08:00
docs: up website & sh
This commit is contained in:
318
src/advanced.js
318
src/advanced.js
@@ -1,318 +0,0 @@
|
||||
const core = require('@actions/core');
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
|
||||
const {
|
||||
doAddLabels,
|
||||
doCreateComment,
|
||||
doCloseIssue,
|
||||
doLockIssue,
|
||||
doCreateIssue,
|
||||
} = require('./base.js');
|
||||
|
||||
const { doQueryIssues, getIssuesInMonth, getCreatedMonth } = require('./public.js');
|
||||
|
||||
const { dealStringToArr, matchKeyword, getPreMonth } = require('./util.js');
|
||||
|
||||
// **************************************************************************
|
||||
var dayjs = require('dayjs');
|
||||
var utc = require('dayjs/plugin/utc');
|
||||
dayjs.extend(utc);
|
||||
|
||||
// **************************************************************************
|
||||
const token = core.getInput('token');
|
||||
const octokit = new Octokit({ auth: `token ${token}` });
|
||||
|
||||
let direction = core.getInput('direction');
|
||||
direction = direction === 'desc' ? 'desc' : 'asc';
|
||||
const commentAuth = core.getInput('comment-auth');
|
||||
const bodyIncludes = core.getInput('body-includes');
|
||||
const titleIncludes = core.getInput('title-includes');
|
||||
const titleRemove = core.getInput('title-excludes');
|
||||
const assigneeIncludes = core.getInput('assignee-includes');
|
||||
|
||||
let issueState = core.getInput('issue-state') || 'open';
|
||||
if (issueState != 'all' && issueState != 'closed') {
|
||||
issueState = 'open';
|
||||
}
|
||||
|
||||
const inactiveLabel = core.getInput('inactive-label') || 'inactive';
|
||||
|
||||
// **************************************************************************
|
||||
async function doCheckInactive(owner, repo, labels) {
|
||||
const issues = await doQueryIssues(owner, repo, labels, issueState);
|
||||
|
||||
if (issues.length) {
|
||||
for (let i = 0; i < issues.length; i++) {
|
||||
let arr = [];
|
||||
issues[i].labels.forEach(it => {
|
||||
arr.push(it.name);
|
||||
});
|
||||
if (!arr.includes(inactiveLabel)) {
|
||||
await doAddLabels(owner, repo, issues[i].number, inactiveLabel);
|
||||
if (core.getInput('body')) {
|
||||
await doCreateComment(owner, repo, issues[i].number, core.getInput('body'));
|
||||
}
|
||||
} else {
|
||||
core.info(`Actions: [add-inactive] issue ${issues[i].number} has label!`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
core.info(`Actions: [query-issues] empty!`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 issue 是否满足条件,满足返回 true
|
||||
* 当前 issue 的指定人是否有一个满足 assigneeIncludes 里的某个
|
||||
* 关键字匹配,是否包含前一个某个+后一个某个 '官网,网站/挂了,无法访问'
|
||||
*/
|
||||
async function doCheckIssue(owner, repo, issueNumber) {
|
||||
let checkResult = true;
|
||||
const issue = await octokit.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
|
||||
if (!!checkResult && assigneeIncludes) {
|
||||
let assigneesCheck = dealStringToArr(assigneeIncludes);
|
||||
let checkAssignee = false;
|
||||
issue.data.assignees.forEach(it => {
|
||||
if (checkResult && !checkAssignee && assigneesCheck.includes(it.login)) {
|
||||
checkResult = true;
|
||||
checkAssignee = true;
|
||||
}
|
||||
});
|
||||
!checkAssignee ? (checkResult = false) : null;
|
||||
}
|
||||
|
||||
if (!!checkResult && titleRemove) {
|
||||
const removes = dealStringToArr(titleRemove);
|
||||
let t = issue.data.title;
|
||||
removes.forEach(re => {
|
||||
t = t.replace(re, '');
|
||||
});
|
||||
if (t.trim().length == 0) {
|
||||
checkResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!!checkResult && titleIncludes) {
|
||||
const titleArr = titleIncludes.split('/');
|
||||
const keyword1 = dealStringToArr(titleArr[0]);
|
||||
const keyword2 = dealStringToArr(titleArr[1]);
|
||||
checkResult = keyword2.length
|
||||
? matchKeyword(issue.data.title, keyword1) && matchKeyword(issue.data.title, keyword2)
|
||||
: matchKeyword(issue.data.title, keyword1);
|
||||
}
|
||||
|
||||
if (!!checkResult && bodyIncludes) {
|
||||
const bodyArr = bodyIncludes.split('/');
|
||||
const keyword1 = dealStringToArr(bodyArr[0]);
|
||||
const keyword2 = dealStringToArr(bodyArr[1]);
|
||||
checkResult = keyword2.length
|
||||
? matchKeyword(issue.data.body, keyword1) && matchKeyword(issue.data.body, keyword2)
|
||||
: matchKeyword(issue.data.body, keyword1);
|
||||
}
|
||||
core.info(`Actions: [check-issue][${!!checkResult}] success!`);
|
||||
core.setOutput('check-result', !!checkResult);
|
||||
}
|
||||
|
||||
async function doCloseIssues(owner, repo, labels) {
|
||||
const issues = await doQueryIssues(owner, repo, labels, 'open');
|
||||
|
||||
if (issues.length) {
|
||||
for (let i = 0; i < issues.length; i++) {
|
||||
await doCloseIssue(owner, repo, issues[i].number);
|
||||
if (core.getInput('body')) {
|
||||
await doCreateComment(owner, repo, issues[i].number, core.getInput('body'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
core.info(`Actions: [query-issues] empty!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function doFindComments(owner, repo, issueNumber) {
|
||||
const commentList = await listComments(owner, repo, issueNumber);
|
||||
core.info(`Actions: [find-comments][${issueNumber}] success!`);
|
||||
let comments = [];
|
||||
commentList.forEach(item => {
|
||||
const a = commentAuth ? item.user.login === commentAuth : true;
|
||||
const b = bodyIncludes ? item.body.includes(bodyIncludes) : true;
|
||||
if (a && b) {
|
||||
comments.push({
|
||||
id: item.id,
|
||||
auth: item.user.login,
|
||||
body: item.body,
|
||||
created: item.created_at,
|
||||
updated: item.updated_at,
|
||||
});
|
||||
if (direction === 'desc') {
|
||||
comments.reverse();
|
||||
}
|
||||
}
|
||||
});
|
||||
core.setOutput('comments', comments);
|
||||
core.info(`out-comments: ${JSON.stringify(comments)}`);
|
||||
}
|
||||
|
||||
async function listComments(owner, repo, issueNumber, page = 1) {
|
||||
let { data: comments } = await octokit.issues.listComments({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
per_page: 100,
|
||||
page,
|
||||
});
|
||||
if (comments.length >= 100) {
|
||||
comments = comments.concat(await listComments(page + 1));
|
||||
}
|
||||
return comments;
|
||||
}
|
||||
|
||||
async function doLockIssues(owner, repo, labels) {
|
||||
const issues = await doQueryIssues(owner, repo, labels, issueState);
|
||||
|
||||
if (issues.length) {
|
||||
for (let i = 0; i < issues.length; i++) {
|
||||
await doLockIssue(owner, repo, issues[i].number);
|
||||
if (core.getInput('body')) {
|
||||
await doCreateComment(owner, repo, issues[i].number, core.getInput('body'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
core.info(`Actions: [query-issues] empty!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function doMonthStatistics(owner, repo, labels, assignees) {
|
||||
const countLables = core.getInput('count-lables');
|
||||
const countComments = core.getInput('count-comments');
|
||||
|
||||
const thisMonth = dayjs.utc().month() + 1;
|
||||
const year = thisMonth == 1 ? dayjs.utc().year() - 1 : dayjs.utc().year();
|
||||
|
||||
const month = getPreMonth(thisMonth);
|
||||
const showMonth = month < 10 ? `0${month}` : month;
|
||||
|
||||
let issues = await getIssuesInMonth(owner, repo, thisMonth);
|
||||
if (issues.length == 0) {
|
||||
core.info(`Actions: [query-issues-${month}] empty!`);
|
||||
return false;
|
||||
}
|
||||
issues = issues.filter(i => {
|
||||
return getCreatedMonth(i.created_at) == month;
|
||||
});
|
||||
let total = issues.length;
|
||||
let totalIssues = [...issues];
|
||||
let openTotal = 0;
|
||||
let openIssuesNumber = [];
|
||||
let closeTotal = 0;
|
||||
let closeIssuesNumber = [];
|
||||
let labelsTotals = [];
|
||||
const title = `[${owner}/${repo}] Month Statistics: ${year}-${showMonth}`;
|
||||
for (let i = 0; i < issues.length; i++) {
|
||||
if (issues[i].state == 'closed') {
|
||||
closeTotal += 1;
|
||||
closeIssuesNumber.push(issues[i].number);
|
||||
} else if (issues[i].state == 'open') {
|
||||
openTotal += 1;
|
||||
openIssuesNumber.push(issues[i].number);
|
||||
}
|
||||
if (countLables && issues[i].labels) {
|
||||
issues[i].labels.forEach(l => {
|
||||
if (l.name in labelsTotals) {
|
||||
labelsTotals[l.name] += 1;
|
||||
} else {
|
||||
labelsTotals[l.name] = 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
let now = dayjs().utc().format('YYYY-MM-DD HH:mm:ss');
|
||||
let body = `
|
||||
- Created time: ${now}
|
||||
|
||||
- Time base: UTC +0
|
||||
`;
|
||||
let totalShow = `
|
||||
### Count
|
||||
|
||||
| Total | Open | Closed |
|
||||
| -- | -- | -- |
|
||||
| ${total} | ${openTotal} | ${closeTotal} |
|
||||
|
||||
`;
|
||||
|
||||
body += totalShow;
|
||||
|
||||
if (countLables == 'true') {
|
||||
let labelsArr = [];
|
||||
for (var lab in labelsTotals) {
|
||||
labelsArr.push({
|
||||
labelName: lab,
|
||||
number: labelsTotals[lab],
|
||||
});
|
||||
}
|
||||
labelsArr.sort((a, b) => b.number - a.number);
|
||||
let labelsTitle = `
|
||||
### Labels statistics
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Number</th>
|
||||
</tr>`;
|
||||
let labelsBody = '';
|
||||
labelsArr.forEach(it => {
|
||||
labelsBody += `<tr><td>${it.labelName}</td><td>${it.number}</td></tr>`;
|
||||
});
|
||||
body =
|
||||
body +
|
||||
labelsTitle +
|
||||
labelsBody +
|
||||
`</table>
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
if (countComments == 'true') {
|
||||
totalIssues.sort((a, b) => b.comments - a.comments);
|
||||
const maxComments = totalIssues.slice(0, 3);
|
||||
let commentTitle = `
|
||||
### Most comments
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Issue</th>
|
||||
<th>Title</th>
|
||||
<th>Number</th>
|
||||
<th>State</th>
|
||||
</tr>
|
||||
`;
|
||||
let commentBody = '';
|
||||
maxComments.forEach((it, ind) => {
|
||||
commentBody += `<tr>
|
||||
<td>${ind + 1}</td>
|
||||
<td>${it.number}</td>
|
||||
<td>${it.title}</td>
|
||||
<td>${it.comments}</td>
|
||||
<td>${it.state}</td></tr>`;
|
||||
});
|
||||
body = body + commentTitle + commentBody + '</table>';
|
||||
}
|
||||
|
||||
await doCreateIssue(owner, repo, title, body, labels, assignees);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
module.exports = {
|
||||
doCheckInactive,
|
||||
doCheckIssue,
|
||||
doCloseIssues,
|
||||
doFindComments,
|
||||
doLockIssues,
|
||||
doMonthStatistics,
|
||||
};
|
533
src/base.js
533
src/base.js
@@ -1,533 +0,0 @@
|
||||
const core = require('@actions/core');
|
||||
const github = require('@actions/github');
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
|
||||
// **************************************************************************
|
||||
const ALLREACTIONS = ['+1', '-1', 'laugh', 'confused', 'heart', 'hooray', 'rocket', 'eyes'];
|
||||
|
||||
const { doQueryIssues } = require('./public.js');
|
||||
|
||||
const {
|
||||
dealStringToArr,
|
||||
dealRandomAssignees,
|
||||
testDuplicate,
|
||||
checkPermission,
|
||||
} = require('./util.js');
|
||||
|
||||
// **************************************************************************
|
||||
const token = core.getInput('token');
|
||||
const octokit = new Octokit({ auth: `token ${token}` });
|
||||
const context = github.context;
|
||||
|
||||
const contents = core.getInput('contents');
|
||||
|
||||
const randomTo = core.getInput('random-to');
|
||||
|
||||
// **************************************************************************
|
||||
async function doAddAssignees(owner, repo, issueNumber, assignees) {
|
||||
const arr = dealRandomAssignees(assignees, randomTo);
|
||||
await octokit.issues.addAssignees({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
assignees: arr,
|
||||
});
|
||||
core.info(`Actions: [add-assignees][${arr}] success!`);
|
||||
}
|
||||
|
||||
async function doAddLabels(owner, repo, issueNumber, labels) {
|
||||
await octokit.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
labels: dealStringToArr(labels),
|
||||
});
|
||||
core.info(`Actions: [add-labels][${labels}] success!`);
|
||||
}
|
||||
|
||||
async function doCloseIssue(owner, repo, issueNumber) {
|
||||
await octokit.issues.update({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
state: 'closed',
|
||||
});
|
||||
core.info(`Actions: [close-issue][${issueNumber}] success!`);
|
||||
}
|
||||
|
||||
async function doCreateComment(owner, repo, issueNumber, body) {
|
||||
const { data } = await octokit.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body,
|
||||
});
|
||||
core.info(`Actions: [create-comment][${body}] success!`);
|
||||
core.setOutput('comment-id', data.id);
|
||||
|
||||
if (contents) {
|
||||
await doCreateCommentContent(owner, repo, data.id, dealStringToArr(contents));
|
||||
}
|
||||
}
|
||||
|
||||
async function doCreateCommentContent(owner, repo, commentId, contents) {
|
||||
if (contents.length) {
|
||||
contents.forEach(async item => {
|
||||
if (testContent(item)) {
|
||||
await octokit.reactions.createForIssueComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: commentId,
|
||||
content: item,
|
||||
});
|
||||
core.info(`Actions: [create-reactions][${item}] success!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function doCreateIssue(owner, repo, title, body, labels, assignees) {
|
||||
let params = {
|
||||
owner,
|
||||
repo,
|
||||
title,
|
||||
body,
|
||||
labels: dealStringToArr(labels),
|
||||
assignees: dealRandomAssignees(assignees, randomTo),
|
||||
};
|
||||
|
||||
const { data } = await octokit.issues.create(params);
|
||||
core.info(`Actions: [create-issue][${title}] success!`);
|
||||
core.setOutput('issue-number', data.number);
|
||||
|
||||
if (contents) {
|
||||
await doCreateIssueContent(owner, repo, data.number, dealStringToArr(contents));
|
||||
}
|
||||
}
|
||||
|
||||
async function doCreateIssueContent(owner, repo, issueNumber, contents) {
|
||||
if (contents.length) {
|
||||
contents.forEach(async item => {
|
||||
if (testContent(item)) {
|
||||
await octokit.reactions.createForIssue({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
content: item,
|
||||
});
|
||||
core.info(`Actions: [create-reactions][${item}] success!`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function doCreateLabel(owner, repo) {
|
||||
const name = core.getInput('label-name');
|
||||
const color = core.getInput('label-color') || 'ededed';
|
||||
const description = core.getInput('label-desc') || '';
|
||||
|
||||
if (!name) {
|
||||
core.setFailed(`This actions should input 'label-name'!`);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await octokit.issues.createLabel({
|
||||
owner,
|
||||
repo,
|
||||
name,
|
||||
color,
|
||||
description,
|
||||
});
|
||||
core.info(`Actions: [create-label][${name}] success!`);
|
||||
} catch (err) {
|
||||
console.log(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function doDeleteComment(owner, repo, commentId) {
|
||||
let id = commentId;
|
||||
|
||||
const outComments = core.getInput('out-comments');
|
||||
if (outComments) {
|
||||
const outCommentsArr = JSON.parse(outComments);
|
||||
core.info(`Actions: [out-comments-length][${outCommentsArr.length}] success!`);
|
||||
outCommentsArr.forEach(async item => {
|
||||
await doDelet(item.id);
|
||||
});
|
||||
} else {
|
||||
await doDelet(id);
|
||||
}
|
||||
|
||||
async function doDelet(id) {
|
||||
await octokit.issues.deleteComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: id,
|
||||
});
|
||||
core.info(`Actions: [delete-comment][${id}] success!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function doLockIssue(owner, repo, issueNumber) {
|
||||
const lockReason = core.getInput('lock-reason');
|
||||
let params = {
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
};
|
||||
const reasons = ['off-topic', 'too heated', 'resolved', 'spam'];
|
||||
if (lockReason && reasons.includes(lockReason)) {
|
||||
params.lock_reason = lockReason;
|
||||
}
|
||||
await octokit.issues.lock(params);
|
||||
core.info(`Actions: [lock-issue][${issueNumber}] success!`);
|
||||
}
|
||||
|
||||
async function doMarkDuplicate(owner, repo, labels) {
|
||||
if (context.eventName != 'issue_comment') {
|
||||
core.info(`This actions only support on 'issue_comment'!`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.payload.action == 'created' || context.payload.action == 'edited') {
|
||||
const duplicateCommand = core.getInput('duplicate-command');
|
||||
const duplicateLabels = core.getInput('duplicate-labels');
|
||||
const removeLables = core.getInput('remove-labels');
|
||||
const closeIssue = core.getInput('close-issue');
|
||||
const requirePermission = core.getInput('require-permission') || 'write';
|
||||
|
||||
const commentId = context.payload.comment.id;
|
||||
const commentBody = context.payload.comment.body;
|
||||
const commentUser = context.payload.comment.user.login;
|
||||
const issueNumber = context.payload.issue.number;
|
||||
|
||||
const ifCommandInput = !!duplicateCommand;
|
||||
|
||||
if (
|
||||
!commentBody.includes('?') &&
|
||||
((ifCommandInput &&
|
||||
commentBody.startsWith(duplicateCommand) &&
|
||||
commentBody.split(' ')[0] == duplicateCommand) ||
|
||||
testDuplicate(commentBody))
|
||||
) {
|
||||
try {
|
||||
const res = await octokit.repos.getCollaboratorPermissionLevel({
|
||||
owner,
|
||||
repo,
|
||||
username: commentUser,
|
||||
});
|
||||
const { permission } = res.data;
|
||||
if (!checkPermission(requirePermission, permission)) {
|
||||
core.info(`The user ${commentUser} is not allow!`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ifCommandInput) {
|
||||
const nextBody = commentBody.replace(duplicateCommand, 'Duplicate of');
|
||||
await doUpdateComment(owner, repo, commentId, nextBody, 'replace', true);
|
||||
} else if (contents) {
|
||||
await doCreateCommentContent(owner, repo, commentId, dealStringToArr(contents));
|
||||
}
|
||||
|
||||
const issue = await octokit.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
let newLabels = [];
|
||||
if (issue.data.labels.length > 0) {
|
||||
newLabels = issue.data.labels
|
||||
.map(({ name }) => name)
|
||||
.filter(name => !dealStringToArr(removeLables).includes(name));
|
||||
}
|
||||
if (duplicateLabels) {
|
||||
newLabels = [...newLabels, ...dealStringToArr(duplicateLabels)];
|
||||
}
|
||||
if (labels) {
|
||||
newLabels = dealStringToArr(labels);
|
||||
}
|
||||
if (newLabels.length > 0) {
|
||||
await doSetLabels(owner, repo, issueNumber, newLabels.toString());
|
||||
core.info(`Actions: [mark-duplicate-labels][${newLabels}] success!`);
|
||||
}
|
||||
|
||||
if (closeIssue == 'true') {
|
||||
await doCloseIssue(owner, repo, issueNumber);
|
||||
}
|
||||
} catch (error) {
|
||||
core.info(error.message);
|
||||
}
|
||||
} else {
|
||||
core.info(
|
||||
`This comment body should start whith 'duplicate-command' or 'Duplicate of' and not include '?'`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
core.info(`This actions only support on 'issue_comment' created or edited!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function doOpenIssue(owner, repo, issueNumber) {
|
||||
await octokit.issues.update({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
state: 'open',
|
||||
});
|
||||
core.info(`Actions: [open-issue][${issueNumber}] success!`);
|
||||
}
|
||||
|
||||
async function doRemoveAssignees(owner, repo, issueNumber, assignees) {
|
||||
await octokit.issues.removeAssignees({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
assignees: dealStringToArr(assignees),
|
||||
});
|
||||
core.info(`Actions: [remove-assignees][${assignees}] success!`);
|
||||
}
|
||||
|
||||
async function doRemoveLabels(owner, repo, issueNumber, labels) {
|
||||
const issue = await octokit.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
|
||||
const baseLabels = issue.data.labels.map(({ name }) => name);
|
||||
const removeLabels = baseLabels.filter(name => dealStringToArr(labels).includes(name));
|
||||
|
||||
core.info(`Actions: [filter-labels][${removeLabels.join(',')}] success!`);
|
||||
|
||||
for (const label of removeLabels) {
|
||||
await octokit.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
name: label,
|
||||
});
|
||||
core.info(`Actions: [remove-label][${label}] success!`);
|
||||
}
|
||||
|
||||
core.info(`Actions: [remove-labels][${labels}] success!`);
|
||||
}
|
||||
|
||||
async function doSetLabels(owner, repo, issueNumber, labels) {
|
||||
// 概率性出现问题:https://github.com/octokit/rest.js/issues/1982,规避 setLabels
|
||||
if (labels) {
|
||||
// await octokit.issues.setLabels({
|
||||
// owner,
|
||||
// repo,
|
||||
// issue_number: issueNumber,
|
||||
// labels: dealStringToArr(labels)
|
||||
// });
|
||||
const issue = await octokit.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
const baseLabels = issue.data.labels.map(({ name }) => name);
|
||||
const removeLabels = baseLabels.filter(name => !dealStringToArr(labels).includes(name));
|
||||
const addLabels = dealStringToArr(labels).filter(name => !baseLabels.includes(name));
|
||||
|
||||
if (removeLabels.length > 0) {
|
||||
await doRemoveLabels(owner, repo, issueNumber, removeLabels.toString());
|
||||
core.info(`Actions: [set-labels-remove][${removeLabels}] success!`);
|
||||
}
|
||||
|
||||
if (addLabels.length > 0) {
|
||||
await doAddLabels(owner, repo, issueNumber, addLabels.toString());
|
||||
core.info(`Actions: [set-labels-add][${addLabels}] success!`);
|
||||
}
|
||||
|
||||
core.info(`Actions: [set-labels][${labels}] success!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function doUnlockIssue(owner, repo, issueNumber) {
|
||||
await octokit.issues.unlock({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
core.info(`Actions: [unlock-issue][${issueNumber}] success!`);
|
||||
}
|
||||
|
||||
async function doUpdateComment(owner, repo, commentId, body, updateMode, ifUpdateBody) {
|
||||
let id = commentId;
|
||||
|
||||
const outComments = core.getInput('out-comments');
|
||||
if (outComments) {
|
||||
const outCommentsArr = JSON.parse(outComments);
|
||||
core.info(`Actions: [out-comments-length][${outCommentsArr.length}] success!`);
|
||||
outCommentsArr.forEach(async item => {
|
||||
await doComment(item.id);
|
||||
});
|
||||
} else {
|
||||
await doComment(id);
|
||||
}
|
||||
|
||||
async function doComment(id) {
|
||||
const comment = await octokit.issues.getComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: id,
|
||||
});
|
||||
const comment_body = comment.data.body;
|
||||
|
||||
let params = {
|
||||
owner,
|
||||
repo,
|
||||
comment_id: id,
|
||||
};
|
||||
|
||||
if (core.getInput('body') || ifUpdateBody) {
|
||||
if (updateMode === 'append') {
|
||||
params.body = `${comment_body}\n${body}`;
|
||||
} else {
|
||||
params.body = body;
|
||||
}
|
||||
|
||||
await octokit.issues.updateComment(params);
|
||||
core.info(`Actions: [update-comment][${id}] success!`);
|
||||
}
|
||||
|
||||
if (contents) {
|
||||
await doCreateCommentContent(owner, repo, id, dealStringToArr(contents));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function doUpdateIssue(
|
||||
owner,
|
||||
repo,
|
||||
issueNumber,
|
||||
state,
|
||||
title,
|
||||
body,
|
||||
updateMode,
|
||||
assignees,
|
||||
labels,
|
||||
) {
|
||||
const issue = await octokit.issues.get({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
const issue_body = issue.data.body;
|
||||
const issue_title = issue.data.title;
|
||||
|
||||
let issue_labels = [];
|
||||
if (issue.data.labels.length > 0) {
|
||||
issue.data.labels.forEach(it => {
|
||||
issue_labels.push(it.name);
|
||||
});
|
||||
}
|
||||
|
||||
let issue_assignees = [];
|
||||
if (issue.data.assignees.length > 0) {
|
||||
issue.data.assignees.forEach(it => {
|
||||
issue_assignees.push(it.login);
|
||||
});
|
||||
}
|
||||
|
||||
let params = {
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
state,
|
||||
};
|
||||
|
||||
params.title = core.getInput('title') ? title : issue_title;
|
||||
|
||||
let next_body;
|
||||
if (core.getInput('body')) {
|
||||
if (updateMode === 'append') {
|
||||
next_body = `${issue_body}\n${body}`;
|
||||
} else {
|
||||
next_body = body;
|
||||
}
|
||||
} else {
|
||||
next_body = issue_body;
|
||||
}
|
||||
params.body = next_body;
|
||||
|
||||
params.labels = labels ? dealStringToArr(labels) : issue_labels;
|
||||
params.assignees = assignees ? dealStringToArr(assignees) : issue_assignees;
|
||||
|
||||
await octokit.issues.update(params);
|
||||
core.info(`Actions: [update-issue][${issueNumber}] success!`);
|
||||
|
||||
if (contents) {
|
||||
await doCreateIssueContent(owner, repo, issueNumber, contents);
|
||||
}
|
||||
}
|
||||
|
||||
async function doWelcome(owner, repo, assignees, labels, body) {
|
||||
const context = github.context;
|
||||
const isIssue = !!context.payload.issue;
|
||||
const issueContents = core.getInput('issue-contents');
|
||||
if (!isIssue) {
|
||||
core.setFailed('The event that triggered this action must be a issue. Error!');
|
||||
} else {
|
||||
const auth = context.payload.sender.login;
|
||||
core.info(`Actions: [welcome: auth=][${auth}]`);
|
||||
const issueNumber = context.issue.number;
|
||||
const issues = await doQueryIssues(owner, repo, false, 'all', auth);
|
||||
if (issues.length == 0 || (issues.length == 1 && issues[0].number == issueNumber)) {
|
||||
if (core.getInput('body')) {
|
||||
await doCreateComment(owner, repo, issueNumber, body);
|
||||
} else {
|
||||
core.info(`Actions: [welcome] no body!`);
|
||||
}
|
||||
|
||||
if (assignees) {
|
||||
await doAddAssignees(owner, repo, issueNumber, assignees);
|
||||
}
|
||||
|
||||
if (labels) {
|
||||
await doAddLabels(owner, repo, issueNumber, labels);
|
||||
}
|
||||
|
||||
if (issueContents) {
|
||||
await doCreateIssueContent(owner, repo, issueNumber, dealStringToArr(issueContents));
|
||||
}
|
||||
} else {
|
||||
core.info(`Actions: [welcome][${auth}] is not first time!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
function testContent(con) {
|
||||
if (ALLREACTIONS.includes(con)) {
|
||||
return true;
|
||||
} else {
|
||||
core.setFailed('This actions not supported!');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
module.exports = {
|
||||
doAddAssignees,
|
||||
doAddLabels,
|
||||
doCloseIssue,
|
||||
doCreateComment,
|
||||
doCreateCommentContent,
|
||||
doCreateIssue,
|
||||
doCreateIssueContent,
|
||||
doCreateLabel,
|
||||
doDeleteComment,
|
||||
doMarkDuplicate,
|
||||
doLockIssue,
|
||||
doOpenIssue,
|
||||
doRemoveAssignees,
|
||||
doRemoveLabels,
|
||||
doSetLabels,
|
||||
doUnlockIssue,
|
||||
doUpdateComment,
|
||||
doUpdateIssue,
|
||||
doWelcome,
|
||||
};
|
217
src/main.js
217
src/main.js
@@ -1,217 +0,0 @@
|
||||
const core = require('@actions/core');
|
||||
const github = require('@actions/github');
|
||||
const { THANKS } = require('actions-util/lib/thanks');
|
||||
|
||||
// **************************************************************************
|
||||
const {
|
||||
doAddAssignees,
|
||||
doAddLabels,
|
||||
doCloseIssue,
|
||||
doCreateComment,
|
||||
doCreateIssue,
|
||||
doCreateLabel,
|
||||
doDeleteComment,
|
||||
doMarkDuplicate,
|
||||
doLockIssue,
|
||||
doOpenIssue,
|
||||
doRemoveAssignees,
|
||||
doRemoveLabels,
|
||||
doSetLabels,
|
||||
doUnlockIssue,
|
||||
doUpdateComment,
|
||||
doUpdateIssue,
|
||||
doWelcome,
|
||||
} = require('./base.js');
|
||||
|
||||
const {
|
||||
doCheckInactive,
|
||||
doCheckIssue,
|
||||
doCloseIssues,
|
||||
doFindComments,
|
||||
doLockIssues,
|
||||
doMonthStatistics,
|
||||
} = require('./advanced.js');
|
||||
|
||||
// **************************************************************************
|
||||
const ALLACTIONS = [
|
||||
// base
|
||||
'add-assignees',
|
||||
'add-labels',
|
||||
'close-issue',
|
||||
'create-comment',
|
||||
'create-issue',
|
||||
'create-label',
|
||||
'delete-comment',
|
||||
'lock-issue',
|
||||
'mark-duplicate',
|
||||
'open-issue',
|
||||
'remove-assignees',
|
||||
'remove-labels',
|
||||
'set-labels',
|
||||
'unlock-issue',
|
||||
'update-comment',
|
||||
'update-issue',
|
||||
'welcome',
|
||||
|
||||
// advanced
|
||||
'check-inactive',
|
||||
'check-issue',
|
||||
'close-issues',
|
||||
'find-comments',
|
||||
'lock-issues',
|
||||
'month-statistics',
|
||||
];
|
||||
|
||||
// **************************************************************************
|
||||
async function main() {
|
||||
try {
|
||||
const ctx = github.context;
|
||||
|
||||
// No display to outside
|
||||
let owner, repo;
|
||||
if (core.getInput('repo')) {
|
||||
owner = core.getInput('repo').split('/')[0];
|
||||
repo = core.getInput('repo').split('/')[1];
|
||||
} else {
|
||||
owner = ctx.repo.owner;
|
||||
repo = ctx.repo.repo;
|
||||
}
|
||||
|
||||
let defaultNo;
|
||||
if (ctx.eventName === 'issues' || ctx.eventName === 'issue_comment') {
|
||||
defaultNo = ctx.payload.issue.number;
|
||||
}
|
||||
|
||||
const issueNumber = core.getInput('issue-number') || defaultNo;
|
||||
const commentId = core.getInput('comment-id');
|
||||
|
||||
const defaultBody = `Currently at ${owner}/${repo}. And this is default comment.`;
|
||||
const body = core.getInput('body') || defaultBody;
|
||||
|
||||
const defaultTitle = `Default Title`;
|
||||
const title = core.getInput('title') || defaultTitle;
|
||||
|
||||
const assignees = core.getInput('assignees');
|
||||
|
||||
const labels = core.getInput('labels');
|
||||
const state = core.getInput('state') || 'open';
|
||||
|
||||
let updateMode = core.getInput('update-mode');
|
||||
if (updateMode !== 'append') {
|
||||
updateMode = 'replace';
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = core.getInput('actions', { required: true });
|
||||
|
||||
const actionsArr = actions.split(',');
|
||||
for (const action of actionsArr) {
|
||||
await testActions(action.trim());
|
||||
}
|
||||
|
||||
async function testActions(action) {
|
||||
if (ALLACTIONS.includes(action)) {
|
||||
await choseActions(action);
|
||||
} else {
|
||||
core.setFailed(`Actions: [${action}] is not supported!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function choseActions(action) {
|
||||
switch (action) {
|
||||
// base
|
||||
case 'add-assignees':
|
||||
await doAddAssignees(owner, repo, issueNumber, assignees);
|
||||
break;
|
||||
case 'add-labels':
|
||||
await doAddLabels(owner, repo, issueNumber, labels);
|
||||
break;
|
||||
case 'close-issue':
|
||||
await doCloseIssue(owner, repo, issueNumber);
|
||||
break;
|
||||
case 'create-comment':
|
||||
await doCreateComment(owner, repo, issueNumber, body);
|
||||
break;
|
||||
case 'create-issue':
|
||||
await doCreateIssue(owner, repo, title, body, labels, assignees);
|
||||
break;
|
||||
case 'create-label':
|
||||
await doCreateLabel(owner, repo);
|
||||
break;
|
||||
case 'delete-comment':
|
||||
await doDeleteComment(owner, repo, commentId);
|
||||
break;
|
||||
case 'lock-issue':
|
||||
await doLockIssue(owner, repo, issueNumber);
|
||||
break;
|
||||
case 'mark-duplicate':
|
||||
await doMarkDuplicate(owner, repo, labels);
|
||||
break;
|
||||
case 'open-issue':
|
||||
await doOpenIssue(owner, repo, issueNumber);
|
||||
break;
|
||||
case 'remove-assignees':
|
||||
await doRemoveAssignees(owner, repo, issueNumber, assignees);
|
||||
break;
|
||||
case 'remove-labels':
|
||||
await doRemoveLabels(owner, repo, issueNumber, labels);
|
||||
break;
|
||||
case 'set-labels':
|
||||
await doSetLabels(owner, repo, issueNumber, labels);
|
||||
break;
|
||||
case 'unlock-issue':
|
||||
await doUnlockIssue(owner, repo, issueNumber);
|
||||
break;
|
||||
case 'update-comment':
|
||||
await doUpdateComment(owner, repo, commentId, body, updateMode);
|
||||
break;
|
||||
case 'update-issue':
|
||||
await doUpdateIssue(
|
||||
owner,
|
||||
repo,
|
||||
issueNumber,
|
||||
state,
|
||||
title,
|
||||
body,
|
||||
updateMode,
|
||||
assignees,
|
||||
labels,
|
||||
);
|
||||
break;
|
||||
case 'welcome':
|
||||
await doWelcome(owner, repo, assignees, labels, body);
|
||||
break;
|
||||
|
||||
// advanced
|
||||
case 'check-inactive':
|
||||
await doCheckInactive(owner, repo, labels);
|
||||
break;
|
||||
case 'check-issue':
|
||||
await doCheckIssue(owner, repo, issueNumber);
|
||||
break;
|
||||
case 'close-issues':
|
||||
await doCloseIssues(owner, repo, labels);
|
||||
break;
|
||||
case 'find-comments':
|
||||
await doFindComments(owner, repo, issueNumber);
|
||||
break;
|
||||
case 'lock-issues':
|
||||
await doLockIssues(owner, repo, labels);
|
||||
break;
|
||||
case 'month-statistics':
|
||||
await doMonthStatistics(owner, repo, labels, assignees);
|
||||
break;
|
||||
// default
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
core.info('\n' + THANKS);
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
main();
|
@@ -1,6 +1,7 @@
|
||||
import * as github from '@actions/github';
|
||||
import { dealStringToArr, THANKS } from 'actions-util';
|
||||
|
||||
import * as github from '@actions/github';
|
||||
|
||||
import * as core from './core';
|
||||
import { IssueHelperEngine } from './helper';
|
||||
import type { TAction } from './types';
|
||||
|
139
src/public.js
139
src/public.js
@@ -1,139 +0,0 @@
|
||||
const core = require('@actions/core');
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
|
||||
const { getPreMonth, dealStringToArr } = require('./util.js');
|
||||
|
||||
// **************************************************************************
|
||||
var dayjs = require('dayjs');
|
||||
var utc = require('dayjs/plugin/utc');
|
||||
dayjs.extend(utc);
|
||||
var isSameOrBefore = require('dayjs/plugin/isSameOrBefore');
|
||||
dayjs.extend(isSameOrBefore);
|
||||
|
||||
// **************************************************************************
|
||||
const token = core.getInput('token');
|
||||
const octokit = new Octokit({ auth: `token ${token}` });
|
||||
|
||||
const perPage = 100;
|
||||
|
||||
const issueCreator = core.getInput('issue-creator');
|
||||
const issueAssignee = core.getInput('issue-assignee');
|
||||
const issueMentioned = core.getInput('issue-mentioned');
|
||||
|
||||
const bodyIncludes = core.getInput('body-includes');
|
||||
const titleIncludes = core.getInput('title-includes');
|
||||
|
||||
const excludeLabels = core.getInput('exclude-labels');
|
||||
|
||||
const inactiveDay = core.getInput('inactive-day');
|
||||
|
||||
// **************************************************************************
|
||||
/**
|
||||
* 查询 Issues 列表
|
||||
* @param {*} owner
|
||||
* @param {*} repo
|
||||
* @param {*} labels
|
||||
* @param {*} state
|
||||
* @param {*} creator
|
||||
* @returns
|
||||
*/
|
||||
async function doQueryIssues(owner, repo, labels, state, creator) {
|
||||
let params = {
|
||||
owner,
|
||||
repo,
|
||||
state,
|
||||
};
|
||||
|
||||
issueCreator ? (params.creator = issueCreator) : null;
|
||||
issueAssignee ? (params.assignee = issueAssignee) : null;
|
||||
issueMentioned ? (params.mentioned = issueMentioned) : null;
|
||||
|
||||
if (labels) {
|
||||
params.labels = labels;
|
||||
}
|
||||
|
||||
if (creator) {
|
||||
params.creator = creator;
|
||||
}
|
||||
|
||||
const res = await getIssues(params);
|
||||
let issues = [];
|
||||
let issueNumbers = [];
|
||||
if (res.length) {
|
||||
const excludeLabelsArr = dealStringToArr(excludeLabels);
|
||||
res.forEach(iss => {
|
||||
const a = bodyIncludes ? iss.body.includes(bodyIncludes) : true;
|
||||
const b = titleIncludes ? iss.title.includes(titleIncludes) : true;
|
||||
/**
|
||||
* Note: GitHub's REST API v3 considers every pull request an issue, but not every issue is a pull request.
|
||||
* For this reason, "Issues" endpoints may return both issues and pull requests in the response.
|
||||
* You can identify pull requests by the pull_request key.
|
||||
*/
|
||||
if (a && b && iss.pull_request === undefined) {
|
||||
if (excludeLabelsArr.length) {
|
||||
for (let i = 0; i < iss.labels.length; i += 1) {
|
||||
if (excludeLabelsArr.includes(iss.labels[i].name)) return;
|
||||
}
|
||||
}
|
||||
|
||||
if (inactiveDay) {
|
||||
let lastTime = dayjs.utc().subtract(Number(inactiveDay), 'day');
|
||||
let updateTime = dayjs.utc(iss.updated_at);
|
||||
if (updateTime.isSameOrBefore(lastTime)) {
|
||||
issues.push(iss);
|
||||
issueNumbers.push(iss.number);
|
||||
}
|
||||
} else {
|
||||
issues.push(iss);
|
||||
issueNumbers.push(iss.number);
|
||||
}
|
||||
}
|
||||
});
|
||||
core.info(`Actions: [query-issues]: [${JSON.stringify(issueNumbers)}]!`);
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
async function getIssues(params, page = 1) {
|
||||
let { data: issues } = await octokit.issues.listForRepo({
|
||||
...params,
|
||||
per_page: perPage,
|
||||
page,
|
||||
});
|
||||
if (issues.length >= perPage) {
|
||||
issues = issues.concat(await getIssues(params, page + 1));
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
async function getIssuesInMonth(owner, repo, thisMonth, page = 1) {
|
||||
const month = getPreMonth(thisMonth);
|
||||
let { data: issues } = await octokit.issues.listForRepo({
|
||||
owner,
|
||||
repo,
|
||||
state: 'all',
|
||||
per_page: perPage,
|
||||
page,
|
||||
});
|
||||
issues = issues.filter(i => {
|
||||
return i.pull_request === undefined;
|
||||
});
|
||||
if (issues.length && getCreatedMonth(issues[issues.length - 1].created_at) >= month) {
|
||||
issues = issues.concat(await getIssuesInMonth(owner, repo, thisMonth, page + 1));
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
function getCreatedMonth(d) {
|
||||
return dayjs(d).utc().month() + 1;
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
module.exports = {
|
||||
doQueryIssues,
|
||||
getIssues,
|
||||
getIssuesInMonth,
|
||||
getCreatedMonth,
|
||||
};
|
@@ -54,4 +54,4 @@ export type TAction =
|
||||
| 'mark-assignees'
|
||||
| 'mark-duplicate'
|
||||
| 'welcome';
|
||||
// [ Advanced End ]
|
||||
//// [ Advanced End ]
|
||||
|
Reference in New Issue
Block a user