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 `; let labelsBody = ''; labelsArr.forEach(it => { labelsBody += ``; }); body = body + labelsTitle + labelsBody + `
Name Number
${it.labelName}${it.number}
`; } if (countComments == 'true') { totalIssues.sort((a, b) => b.comments - a.comments); const maxComments = totalIssues.slice(0, 3); let commentTitle = ` ### Most comments `; let commentBody = ''; maxComments.forEach((it, ind) => { commentBody += ``; }); body = body + commentTitle + commentBody + '
# Issue Title Number State
${ind + 1} ${it.number} ${it.title} ${it.comments} ${it.state}
'; } await doCreateIssue(owner, repo, title, body, labels, assignees); } // ************************************************************************** module.exports = { doCheckInactive, doCheckIssue, doCloseIssues, doFindComments, doLockIssues, doMonthStatistics, };