require('dotenv').config();
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 res = await octokit.issues.listComments({
owner,
repo,
issue_number: issueNumber,
});
core.info(`Actions: [find-comments][${issueNumber}] success!`);
let comments = [];
res.data.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 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
Name |
Number |
`;
let labelsBody = '';
labelsArr.forEach(it => {
labelsBody += `${it.labelName} | ${it.number} |
`;
});
body =
body +
labelsTitle +
labelsBody +
`
`;
}
if (countComments == 'true') {
totalIssues.sort((a, b) => b.comments - a.comments);
const maxComments = totalIssues.slice(0, 3);
let commentTitle = `
### Most comments
# |
Issue |
Title |
Number |
State |
`;
let commentBody = '';
maxComments.forEach((it, ind) => {
commentBody += `
${ind + 1} |
${it.number} |
${it.title} |
${it.comments} |
${it.state} |
`;
});
body = body + commentTitle + commentBody + '
';
}
await doCreateIssue(owner, repo, title, body, labels, assignees);
}
// **************************************************************************
module.exports = {
doCheckInactive,
doCheckIssue,
doCloseIssues,
doFindComments,
doLockIssues,
doMonthStatistics,
};