mirror of
https://gitea.com/Lydanne/issues-helper.git
synced 2025-10-13 23:33:51 +08:00
wip: code review
This commit is contained in:
6967
dist/index.js
vendored
6967
dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -14,7 +14,7 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "xrkffgg",
|
"author": "xrkffgg",
|
||||||
"main": "src/main.js",
|
"main": "src/main.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "APP_ROOT=web dumi dev",
|
"start": "APP_ROOT=web dumi dev",
|
||||||
"docs:build": "APP_ROOT=web dumi build",
|
"docs:build": "APP_ROOT=web dumi build",
|
||||||
@@ -23,13 +23,14 @@
|
|||||||
"deploy": "npm run docs:build && npm run docs:deploy",
|
"deploy": "npm run docs:build && npm run docs:deploy",
|
||||||
"format": "prettier --write **/*.ts",
|
"format": "prettier --write **/*.ts",
|
||||||
"format-check": "prettier --check **/*.ts",
|
"format-check": "prettier --check **/*.ts",
|
||||||
|
"lint": "eslint src/*.ts",
|
||||||
|
"lint-fix": "eslint src/*.ts --fix",
|
||||||
"test": "father test",
|
"test": "father test",
|
||||||
"package": "ncc build src/main.ts -o dist",
|
"package": "ncc build src/main.ts -o dist",
|
||||||
"users": "node ./script/update-users.js",
|
"users": "node ./script/update-users.js",
|
||||||
"main": "node ./src/main.js",
|
"version": "node ./script/update-version.js",
|
||||||
"ver": "node ./script/update-version.js",
|
|
||||||
"pub": "npm run package",
|
"pub": "npm run package",
|
||||||
"all": "npm run format-check && npm run lint && npm run package"
|
"all": "npm run format-check && npm run lint && npm run test && npm run package"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.6.0",
|
"@actions/core": "^1.6.0",
|
||||||
|
@@ -24,16 +24,14 @@ export const getInput = (key: string, options?: core.InputOptions): string | voi
|
|||||||
core.getInput(key, options);
|
core.getInput(key, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setOutput = (key: string, value: string | number | object | void ) => {
|
export const setOutput = (key: string, value: string | boolean | number | object ) => {
|
||||||
let formatValue: string | number | object | void;
|
let formatValue: string | number | boolean | object;
|
||||||
if (value || typeof(value) === 'boolean') {
|
|
||||||
if (typeof(value) === 'object') {
|
if (typeof(value) === 'object') {
|
||||||
formatValue = JSON.stringify(value);
|
formatValue = JSON.stringify(value);
|
||||||
} else {
|
} else {
|
||||||
formatValue = value;
|
formatValue = value;
|
||||||
}
|
}
|
||||||
core.setOutput(key, formatValue);
|
core.setOutput(key, formatValue);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setFailed = (mess: string) => {
|
export const setFailed = (mess: string) => {
|
||||||
|
@@ -1,14 +1,20 @@
|
|||||||
import { dealStringToArr } from 'actions-util';
|
import { dealStringToArr, checkPermission, TPermissionType } from 'actions-util';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import utc from 'dayjs/plugin/utc';
|
import utc from 'dayjs/plugin/utc';
|
||||||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||||
import * as core from '../core';
|
import * as core from '../core';
|
||||||
import { TIssueState, TUpdateMode, TEmoji, TLockReasons } from '../types';
|
import { matchKeyword, checkDuplicate, replaceStr2Arr } from '../util';
|
||||||
import { ELockReasons } from '../shared';
|
import { TIssueState, TOutList, TEmoji } from '../types';
|
||||||
import { IIssueCoreEngine, IListIssuesParams, TListIssuesResults } from '../issue';
|
import { IIssueCoreEngine, IListIssuesParams, TIssueList, TCommentInfo } from '../issue';
|
||||||
import {
|
import {
|
||||||
|
doAddAssignees,
|
||||||
doAddLabels,
|
doAddLabels,
|
||||||
doCreateComment,
|
doCreateComment,
|
||||||
|
doCreateCommentEmoji,
|
||||||
|
doCloseIssue,
|
||||||
|
doLockIssue,
|
||||||
|
doUpdateComment,
|
||||||
|
doSetLabels,
|
||||||
} from './base';
|
} from './base';
|
||||||
|
|
||||||
let ICE: IIssueCoreEngine;
|
let ICE: IIssueCoreEngine;
|
||||||
@@ -16,7 +22,7 @@ export function initAdvancedICE(_ICE: IIssueCoreEngine) {
|
|||||||
ICE = _ICE;
|
ICE = _ICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doQueryIssues(state: TIssueState | 'all', creator?: string): Promise<TListIssuesResults> {
|
export async function doQueryIssues(state: TIssueState | 'all', creator?: string, ignoreLabels?: boolean): Promise<TIssueList> {
|
||||||
const params = {
|
const params = {
|
||||||
state,
|
state,
|
||||||
} as IListIssuesParams;
|
} as IListIssuesParams;
|
||||||
@@ -29,12 +35,12 @@ export async function doQueryIssues(state: TIssueState | 'all', creator?: string
|
|||||||
issueMentioned ? (params.mentioned = issueMentioned) : null;
|
issueMentioned ? (params.mentioned = issueMentioned) : null;
|
||||||
|
|
||||||
const labels = core.getInput('labels');
|
const labels = core.getInput('labels');
|
||||||
labels ? params.labels = labels : null;
|
labels && !ignoreLabels ? params.labels = labels : null;
|
||||||
|
|
||||||
creator? params.creator = creator : null;
|
creator ? params.creator = creator : null;
|
||||||
|
|
||||||
const issuesList = await ICE.listIssues(params);
|
const issuesList = await ICE.listIssues(params);
|
||||||
const issues: TListIssuesResults = [];
|
const issues: TIssueList = [];
|
||||||
const issueNumbers: number[] = [];
|
const issueNumbers: number[] = [];
|
||||||
|
|
||||||
if (issuesList.length) {
|
if (issuesList.length) {
|
||||||
@@ -91,8 +97,9 @@ export async function doCheckInactive(body: string, emoji?: string) {
|
|||||||
const inactiveLabel = core.getInput('inactive-label') || 'inactive';
|
const inactiveLabel = core.getInput('inactive-label') || 'inactive';
|
||||||
for (const issue of issues) {
|
for (const issue of issues) {
|
||||||
const { labels, number } = issue;
|
const { labels, number } = issue;
|
||||||
const labelNames = labels.map(({name}) => name);
|
const labelNames = labels.map(({ name }) => name);
|
||||||
if (!labelNames.includes(inactiveLabel)) {
|
if (!labelNames.includes(inactiveLabel)) {
|
||||||
|
core.info(`[doCheckInactive] Doing ---> ${number}`);
|
||||||
await doAddLabels([inactiveLabel], number);
|
await doAddLabels([inactiveLabel], number);
|
||||||
if (body) await doCreateComment(body, emoji, number);
|
if (body) await doCreateComment(body, emoji, number);
|
||||||
} else {
|
} else {
|
||||||
@@ -103,3 +110,259 @@ export async function doCheckInactive(body: string, emoji?: string) {
|
|||||||
core.info(`[doCheckInactive] Query issues empty!`);
|
core.info(`[doCheckInactive] Query issues empty!`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查 issue 是否满足条件,满足返回 true
|
||||||
|
* 当前 issue 的指定人是否有一个满足 assigneeIncludes 里的某个
|
||||||
|
* 关键字匹配,是否包含前一个某个+后一个某个 '官网,网站/挂了,无法访问'
|
||||||
|
*/
|
||||||
|
export async function doCheckIssue() {
|
||||||
|
let checkResult = true;
|
||||||
|
|
||||||
|
const issue = await ICE.getIssue();
|
||||||
|
const assigneeIncludes = core.getInput('assignee-includes');
|
||||||
|
|
||||||
|
if (assigneeIncludes) {
|
||||||
|
const assigneesCheck = dealStringToArr(assigneeIncludes);
|
||||||
|
let checkAssignee = false;
|
||||||
|
issue.assignees.forEach(it => {
|
||||||
|
if (checkResult && !checkAssignee && assigneesCheck.includes(it.login)) {
|
||||||
|
checkResult = true;
|
||||||
|
checkAssignee = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
!checkAssignee ? (checkResult = false) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleRemove = core.getInput('title-excludes');
|
||||||
|
if (!!checkResult && titleRemove) {
|
||||||
|
const removes = dealStringToArr(titleRemove);
|
||||||
|
let t = issue.title;
|
||||||
|
removes.forEach(re => {
|
||||||
|
t = t.replace(re, '');
|
||||||
|
});
|
||||||
|
if (t.trim().length == 0) {
|
||||||
|
checkResult = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleIncludes = core.getInput('title-includes');
|
||||||
|
if (!!checkResult && titleIncludes) {
|
||||||
|
const titleArr = titleIncludes.split('/');
|
||||||
|
const keyword1 = dealStringToArr(titleArr[0]);
|
||||||
|
const keyword2 = dealStringToArr(titleArr[1]);
|
||||||
|
checkResult = keyword2.length
|
||||||
|
? matchKeyword(issue.title, keyword1) && matchKeyword(issue.title, keyword2)
|
||||||
|
: matchKeyword(issue.title, keyword1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bodyIncludes = core.getInput('body-includes');
|
||||||
|
if (!!checkResult && bodyIncludes) {
|
||||||
|
const bodyArr = bodyIncludes.split('/');
|
||||||
|
const keyword1 = dealStringToArr(bodyArr[0]);
|
||||||
|
const keyword2 = dealStringToArr(bodyArr[1]);
|
||||||
|
checkResult = keyword2.length
|
||||||
|
? matchKeyword(issue.body, keyword1) && matchKeyword(issue.body, keyword2)
|
||||||
|
: matchKeyword(issue.body, keyword1);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`[doCheckIssue] result is [${checkResult}]`);
|
||||||
|
core.setOutput('check-result', checkResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function doCloseIssues(body: string, emoji?: string) {
|
||||||
|
const issues = await doQueryIssues('open');
|
||||||
|
if (issues.length) {
|
||||||
|
for (const { number } of issues) {
|
||||||
|
core.info(`[doCloseIssues] Doing ---> ${number}`);
|
||||||
|
if (body) await doCreateComment(body, emoji, number);
|
||||||
|
await doCloseIssue(number);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(`[doCloseIssues] Query issues empty!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function doFindComments() {
|
||||||
|
const commentList = await ICE.listComments();
|
||||||
|
core.info(`[doFindComments] success!`);
|
||||||
|
|
||||||
|
const comments: TOutList = [];
|
||||||
|
|
||||||
|
if (commentList.length) {
|
||||||
|
const commentAuth = core.getInput('comment-auth');
|
||||||
|
const bodyIncludes = core.getInput('body-includes');
|
||||||
|
const direction = core.getInput('direction') === 'desc' ? 'desc' : 'asc';
|
||||||
|
for (const comment of commentList) {
|
||||||
|
const checkUser = commentAuth ? comment.user.login === commentAuth : true;
|
||||||
|
const checkBody = bodyIncludes ? comment.body.includes(bodyIncludes) : true;
|
||||||
|
if (checkUser && checkBody) {
|
||||||
|
comments.push({
|
||||||
|
id: comment.id,
|
||||||
|
auth: comment.user.login,
|
||||||
|
body: comment.body,
|
||||||
|
created: comment.created_at,
|
||||||
|
updated: comment.updated_at,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direction === 'desc') {
|
||||||
|
comments.reverse();
|
||||||
|
}
|
||||||
|
core.setOutput('comments', comments);
|
||||||
|
core.info(`[doFindComments] comments --> ${JSON.stringify(comments)}`);
|
||||||
|
} else {
|
||||||
|
core.info(`[doFindComments] Query comments empty!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function doFindIssues() {
|
||||||
|
let issueState = core.getInput('issue-state');
|
||||||
|
if (issueState !== 'all' && issueState !== 'closed') {
|
||||||
|
issueState = 'open';
|
||||||
|
}
|
||||||
|
const issueList = await doQueryIssues(issueState as TIssueState | 'all');
|
||||||
|
let issues: TOutList = [];
|
||||||
|
if (issueList.length) {
|
||||||
|
const direction = core.getInput('direction') === 'desc' ? 'desc' : 'asc';
|
||||||
|
issues = issueList.map(issue => {
|
||||||
|
return {
|
||||||
|
auth: issue.user.login,
|
||||||
|
number: issue.number,
|
||||||
|
title: issue.title,
|
||||||
|
body: issue.body,
|
||||||
|
state: issue.state,
|
||||||
|
created: issue.created_at,
|
||||||
|
updated: issue.updated_at,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (direction === 'desc') {
|
||||||
|
issues.reverse();
|
||||||
|
}
|
||||||
|
core.info(`[doFindIssues] issues --> ${JSON.stringify(issues)}`);
|
||||||
|
} else {
|
||||||
|
core.info(`[doFindIssues] Query issues empty!`);
|
||||||
|
}
|
||||||
|
core.setOutput('issues', issues);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function doLockIssues(body: string, emoji?: string) {
|
||||||
|
let issueState = core.getInput('issue-state');
|
||||||
|
if (issueState !== 'all' && issueState !== 'closed') {
|
||||||
|
issueState = 'open';
|
||||||
|
}
|
||||||
|
const issues = await doQueryIssues(issueState as TIssueState | 'all');
|
||||||
|
|
||||||
|
if (issues.length) {
|
||||||
|
for (const { number } of issues) {
|
||||||
|
core.info(`[doLockIssues] Doing ---> ${number}`);
|
||||||
|
if (body) await doCreateComment(body, emoji, number);
|
||||||
|
await doLockIssue(number);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(`[doLockIssues] Query issues empty!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function doMarkAssignees(comment: TCommentInfo) {
|
||||||
|
const assignCommand = core.getInput('assign-command') || '/assign';
|
||||||
|
if (comment.body.startsWith(assignCommand)) {
|
||||||
|
const { body, user } = comment;
|
||||||
|
const assigns = replaceStr2Arr(body, assignCommand, '@');
|
||||||
|
const requirePermission = core.getInput('require-permission') || 'write';
|
||||||
|
const permission = await ICE.getUserPermission(user.login);
|
||||||
|
if (!checkPermission(requirePermission as TPermissionType, permission)) {
|
||||||
|
core.info(`[doMarkAssignees] The user ${user.login} is not allow!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await doAddAssignees(assigns);
|
||||||
|
core.info(`[doMarkAssignees] Done!`);
|
||||||
|
} else {
|
||||||
|
core.info(`[doMarkAssignees] The issues ignore!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function doMarkDuplicate(comment: TCommentInfo, labels?: string[] | void, emoji?: string) {
|
||||||
|
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 = comment.id;
|
||||||
|
const commentBody = comment.body;
|
||||||
|
const commentUser = comment.user.login;
|
||||||
|
|
||||||
|
const ifCommandInput = !!duplicateCommand;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!commentBody.includes('?') &&
|
||||||
|
((ifCommandInput &&
|
||||||
|
commentBody.startsWith(duplicateCommand) &&
|
||||||
|
commentBody.split(' ')[0] == duplicateCommand) ||
|
||||||
|
checkDuplicate(commentBody))
|
||||||
|
) {
|
||||||
|
const permission = await ICE.getUserPermission(commentUser);
|
||||||
|
if (!checkPermission(requirePermission as TPermissionType, permission)) {
|
||||||
|
core.info(`[doMarkDuplicate] The user ${commentUser} is not allow!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifCommandInput) {
|
||||||
|
const nextBody = commentBody.replace(duplicateCommand, 'Duplicate of');
|
||||||
|
await doUpdateComment(commentId, nextBody, 'replace', emoji);
|
||||||
|
} else if (emoji) {
|
||||||
|
await doCreateCommentEmoji(commentId, emoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
const issue = await ICE.getIssue();
|
||||||
|
let newLabels: string[] = [];
|
||||||
|
if (issue.labels.length > 0) {
|
||||||
|
newLabels = issue.labels
|
||||||
|
.map(({ name }) => name)
|
||||||
|
.filter(name => !dealStringToArr(removeLables).includes(name));
|
||||||
|
}
|
||||||
|
if (duplicateLabels) {
|
||||||
|
newLabels = [...newLabels, ...dealStringToArr(duplicateLabels)];
|
||||||
|
}
|
||||||
|
if (labels) {
|
||||||
|
newLabels = [...labels];
|
||||||
|
}
|
||||||
|
if (newLabels.length > 0) {
|
||||||
|
await doSetLabels(newLabels);
|
||||||
|
}
|
||||||
|
if (closeIssue === 'true') {
|
||||||
|
await doCloseIssue();
|
||||||
|
}
|
||||||
|
core.info(`[doMarkDuplicate] Done!`);
|
||||||
|
} else {
|
||||||
|
core.warning(
|
||||||
|
`This comment body should start whith 'duplicate-command' or 'Duplicate of' and not include '?'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function doWelcome(auth: string, issueNumber: number, body: string, labels?: string[] | void, assignees?: string[] | void, emoji?: string) {
|
||||||
|
core.info(`[doWelcome] [${auth}]`);
|
||||||
|
const issues = await doQueryIssues('all', auth, true);
|
||||||
|
if (issues.length == 0 || (issues.length == 1 && issues[0].number == issueNumber)) {
|
||||||
|
if (body) {
|
||||||
|
await doCreateComment(body, emoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assignees?.length) {
|
||||||
|
await doAddAssignees(assignees);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (labels?.length) {
|
||||||
|
await doAddLabels(labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
const issueEmoji = core.getInput('issue-emoji');
|
||||||
|
if (issueEmoji) {
|
||||||
|
await ICE.createIssueEmoji(dealStringToArr(issueEmoji) as TEmoji[]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(`[doWelcome] ${auth} is not first time!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -20,9 +20,10 @@ export async function doAddLabels(labels: string[], issueNumber?: number) {
|
|||||||
core.info(`[doAddLabels] [${labels}] success!`);
|
core.info(`[doAddLabels] [${labels}] success!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doCloseIssue(issueNumber: number) {
|
export async function doCloseIssue(issueNumber?: number) {
|
||||||
|
if (issueNumber) ICE.setIssueNumber(issueNumber);
|
||||||
await ICE.closeIssue();
|
await ICE.closeIssue();
|
||||||
core.info(`[doCloseIssue] [${issueNumber}] success!`);
|
core.info(`[doCloseIssue] success!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doCreateComment(body: string, emoji?: string, issueNumber?: number) {
|
export async function doCreateComment(body: string, emoji?: string, issueNumber?: number) {
|
||||||
@@ -87,20 +88,20 @@ export async function doDeleteComment(_commentId: number | void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doLockIssue(issueNumber: number) {
|
export async function doLockIssue(issueNumber?: number) {
|
||||||
const lockReason = core.getInput('lock-reason') || '';
|
if (issueNumber) ICE.setIssueNumber(issueNumber);
|
||||||
// @ts-ignore
|
const lockReason = (core.getInput('lock-reason') || '') as TLockReasons;
|
||||||
if (lockReason && !ELockReasons[lockReason]) {
|
if (lockReason && !ELockReasons[lockReason]) {
|
||||||
core.warning(`[doLockIssue] lock-reason is illegal!`);
|
core.warning(`[doLockIssue] lock-reason is illegal!`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await ICE.lockIssue(lockReason as TLockReasons);
|
await ICE.lockIssue(lockReason as TLockReasons);
|
||||||
core.info(`[doLockIssue] ${issueNumber} success!`);
|
core.info(`[doLockIssue] success!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doOpenIssue(issueNumber: number) {
|
export async function doOpenIssue() {
|
||||||
await ICE.openIssue();
|
await ICE.openIssue();
|
||||||
core.info(`[doOpenIssue] [${issueNumber}] success!`);
|
core.info(`[doOpenIssue] success!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doRemoveAssignees(assignees: string[]) {
|
export async function doRemoveAssignees(assignees: string[]) {
|
||||||
@@ -118,9 +119,9 @@ export async function doSetLabels(labels: string[]) {
|
|||||||
core.info(`[doSetLabels] [${labels}] success!`);
|
core.info(`[doSetLabels] [${labels}] success!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doUnlockIssue(issueNumber: number) {
|
export async function doUnlockIssue() {
|
||||||
await ICE.unlockIssue();
|
await ICE.unlockIssue();
|
||||||
core.info(`[doUnlockIssue] [${issueNumber}] success!`);
|
core.info(`[doUnlockIssue] success!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function doUpdateComment(_commentId: number | void, body: string, updateMode: TUpdateMode, emoji: string | void) {
|
export async function doUpdateComment(_commentId: number | void, body: string, updateMode: TUpdateMode, emoji: string | void) {
|
||||||
@@ -137,6 +138,7 @@ export async function doUpdateComment(_commentId: number | void, body: string, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function doUpdateIssue(issueNumber: number, state: TIssueState, title: string | void, body: string | void, updateMode: TUpdateMode, labels?: string[] | void, assignees?: string[] | void) {
|
export async function doUpdateIssue(issueNumber: number, state: TIssueState, title: string | void, body: string | void, updateMode: TUpdateMode, labels?: string[] | void, assignees?: string[] | void) {
|
||||||
|
if (issueNumber) ICE.setIssueNumber(issueNumber);
|
||||||
await ICE.updateIssue(state, title, body, updateMode, labels, assignees);
|
await ICE.updateIssue(state, title, body, updateMode, labels, assignees);
|
||||||
core.info(`[doUpdateIssue] [${issueNumber}] success!`);
|
core.info(`[doUpdateIssue] success!`);
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import { Context, TIssueState, TUpdateMode, TAction, TEmoji } from '../types';
|
|||||||
import {
|
import {
|
||||||
IssueCoreEngine,
|
IssueCoreEngine,
|
||||||
IIssueCoreEngine,
|
IIssueCoreEngine,
|
||||||
|
TCommentInfo,
|
||||||
} from '../issue';
|
} from '../issue';
|
||||||
import { dealRandomAssignees } from '../util';
|
import { dealRandomAssignees } from '../util';
|
||||||
import { IIssueHelperEngine } from './types';
|
import { IIssueHelperEngine } from './types';
|
||||||
@@ -32,6 +33,14 @@ import {
|
|||||||
import {
|
import {
|
||||||
initAdvancedICE,
|
initAdvancedICE,
|
||||||
doCheckInactive,
|
doCheckInactive,
|
||||||
|
doCheckIssue,
|
||||||
|
doCloseIssues,
|
||||||
|
doFindComments,
|
||||||
|
doFindIssues,
|
||||||
|
doLockIssues,
|
||||||
|
doMarkAssignees,
|
||||||
|
doMarkDuplicate,
|
||||||
|
doWelcome,
|
||||||
} from './advanced';
|
} from './advanced';
|
||||||
|
|
||||||
export class IssueHelperEngine implements IIssueHelperEngine {
|
export class IssueHelperEngine implements IIssueHelperEngine {
|
||||||
@@ -106,7 +115,7 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async doExeAction(action: TAction) {
|
public async doExeAction(action: TAction) {
|
||||||
const { owner, repo, issueNumber, emoji, labels, assignees, title, body, updateMode, state } = this;
|
const { owner, repo, issueNumber, emoji, labels, assignees, title, body, updateMode, state, ctx } = this;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
// ---[ Base Begin ]--->>>
|
// ---[ Base Begin ]--->>>
|
||||||
case 'add-assignees': {
|
case 'add-assignees': {
|
||||||
@@ -126,7 +135,7 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'close-issue': {
|
case 'close-issue': {
|
||||||
await doCloseIssue(issueNumber);
|
await doCloseIssue();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'create-comment': {
|
case 'create-comment': {
|
||||||
@@ -146,11 +155,11 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'lock-issue': {
|
case 'lock-issue': {
|
||||||
await doLockIssue(issueNumber);
|
await doLockIssue();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'open-issue': {
|
case 'open-issue': {
|
||||||
await doOpenIssue(issueNumber);
|
await doOpenIssue();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'remove-assignees': {
|
case 'remove-assignees': {
|
||||||
@@ -178,7 +187,7 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'unlock-issue': {
|
case 'unlock-issue': {
|
||||||
await doUnlockIssue(issueNumber);
|
await doUnlockIssue();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'update-comment': {
|
case 'update-comment': {
|
||||||
@@ -186,7 +195,7 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'update-issue': {
|
case 'update-issue': {
|
||||||
await doUpdateIssue(issueNumber, state, title, body, updateMode, labels, assignees);
|
await doUpdateIssue(0, state, title, body, updateMode, labels, assignees);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// ---[ Base End ]--->>>
|
// ---[ Base End ]--->>>
|
||||||
@@ -196,6 +205,59 @@ export class IssueHelperEngine implements IIssueHelperEngine {
|
|||||||
await doCheckInactive(body, emoji);
|
await doCheckInactive(body, emoji);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'check-issue': {
|
||||||
|
await doCheckIssue();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case 'close-issues': {
|
||||||
|
await doCloseIssues(body, emoji);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'find-comments': {
|
||||||
|
await doFindComments();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'find-issues': {
|
||||||
|
await doFindIssues();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'lock-issues': {
|
||||||
|
await doLockIssues(body, emoji);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'mark-assignees': {
|
||||||
|
if (this.checkEvent4Mark()) {
|
||||||
|
core.warning(`[mark-assignees] only support event '[issue_comment: created/edited]'!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await doMarkAssignees(ctx.payload.comment as TCommentInfo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'mark-duplicate': {
|
||||||
|
if (this.checkEvent4Mark()) {
|
||||||
|
core.warning(`[mark-duplicate] only support event '[issue_comment: created/edited]'!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await doMarkDuplicate(ctx.payload.comment as TCommentInfo, labels, emoji);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'welcome': {
|
||||||
|
if (ctx.eventName === 'issues' && ctx.payload.action === 'opened') {
|
||||||
|
await doWelcome(ctx.actor, issueNumber, body, labels, assignees, emoji);
|
||||||
|
} else {
|
||||||
|
core.warning('[welcome] only support issue opened!');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
core.warning(`The ${action} is not allowed.`)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkEvent4Mark() {
|
||||||
|
const { ctx } = this;
|
||||||
|
return ctx.eventName !== 'issue_comment' && (ctx.payload.action === 'created' || ctx.payload.action === 'edited');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Octokit } from '@octokit/rest';
|
import { Octokit } from '@octokit/rest';
|
||||||
|
import { EEmoji } from '../shared';
|
||||||
import { TEmoji, TLockReasons, TUpdateMode, TIssueState } from '../types';
|
import { TEmoji, TLockReasons, TUpdateMode, TIssueState, TUserPermission } from '../types';
|
||||||
import { IIssueBaseInfo, IIssueCoreEngine, IListIssuesParams, TListIssuesResults } from './types';
|
import { IIssueBaseInfo, IIssueCoreEngine, IListIssuesParams, TIssueList, TIssueInfo, TCommentList } from './types';
|
||||||
|
|
||||||
export class IssueCoreEngine implements IIssueCoreEngine {
|
export class IssueCoreEngine implements IIssueCoreEngine {
|
||||||
private owner!: string;
|
private owner!: string;
|
||||||
@@ -70,6 +70,7 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
public async createCommentEmoji(commentId: number, emoji: TEmoji[]) {
|
public async createCommentEmoji(commentId: number, emoji: TEmoji[]) {
|
||||||
const { owner, repo, octokit } = this;
|
const { owner, repo, octokit } = this;
|
||||||
for (const content of emoji) {
|
for (const content of emoji) {
|
||||||
|
if (content && EEmoji[content]) {
|
||||||
await octokit.reactions.createForIssueComment({
|
await octokit.reactions.createForIssueComment({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
@@ -78,6 +79,7 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async createIssue(title: string, body: string, labels?: string[], assignees?: string[]): Promise<number> {
|
public async createIssue(title: string, body: string, labels?: string[], assignees?: string[]): Promise<number> {
|
||||||
const { owner, repo, octokit } = this;
|
const { owner, repo, octokit } = this;
|
||||||
@@ -95,6 +97,7 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
public async createIssueEmoji(emoji: TEmoji[]) {
|
public async createIssueEmoji(emoji: TEmoji[]) {
|
||||||
const { owner, repo, octokit, issueNumber } = this;
|
const { owner, repo, octokit, issueNumber } = this;
|
||||||
for (const content of emoji) {
|
for (const content of emoji) {
|
||||||
|
if (content && EEmoji[content]) {
|
||||||
await octokit.reactions.createForIssue({
|
await octokit.reactions.createForIssue({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
@@ -103,6 +106,7 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async createLabel(labelName: string, labelColor: string, labelDescription: string | undefined) {
|
public async createLabel(labelName: string, labelColor: string, labelDescription: string | undefined) {
|
||||||
const { owner, repo, octokit } = this;
|
const { owner, repo, octokit } = this;
|
||||||
@@ -124,6 +128,42 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getIssue() {
|
||||||
|
const { owner, repo, octokit, issueNumber } = this;
|
||||||
|
const issue = await octokit.issues.get({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: issueNumber,
|
||||||
|
});
|
||||||
|
return issue.data as TIssueInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getUserPermission(username: string) {
|
||||||
|
const { owner, repo, octokit } = this;
|
||||||
|
const { data } = await octokit.repos.getCollaboratorPermissionLevel({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
username,
|
||||||
|
});
|
||||||
|
return data.permission as TUserPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async listComments(page = 1) {
|
||||||
|
const { octokit, owner, repo, issueNumber } = this;
|
||||||
|
const { data } = await octokit.issues.listComments({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: issueNumber,
|
||||||
|
per_page: 100,
|
||||||
|
page,
|
||||||
|
});
|
||||||
|
let comments = [...data] as unknown as TCommentList;
|
||||||
|
if (comments.length >= 100) {
|
||||||
|
comments = comments.concat(await this.listComments(page + 1));
|
||||||
|
}
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
|
||||||
public async listIssues(params: IListIssuesParams, page = 1) {
|
public async listIssues(params: IListIssuesParams, page = 1) {
|
||||||
const { octokit, owner, repo } = this;
|
const { octokit, owner, repo } = this;
|
||||||
const { data } = await octokit.issues.listForRepo({
|
const { data } = await octokit.issues.listForRepo({
|
||||||
@@ -133,7 +173,7 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
per_page: 100,
|
per_page: 100,
|
||||||
page,
|
page,
|
||||||
});
|
});
|
||||||
let issues = [...data] as unknown as TListIssuesResults;
|
let issues = [...data] as unknown as TIssueList;
|
||||||
if (issues.length >= 100) {
|
if (issues.length >= 100) {
|
||||||
issues = issues.concat(await this.listIssues(params, page + 1));
|
issues = issues.concat(await this.listIssues(params, page + 1));
|
||||||
}
|
}
|
||||||
@@ -174,14 +214,10 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async removeLabels(labels: string[]) {
|
public async removeLabels(labels: string[]) {
|
||||||
const { owner, repo, octokit, issueNumber } = this;
|
const { owner, repo, octokit, issueNumber, getIssue } = this;
|
||||||
const issue = await octokit.issues.get({
|
const issue = await getIssue();
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
});
|
|
||||||
|
|
||||||
const baseLabels: string[] = issue.data.labels.map(({ name }: any) => name);
|
const baseLabels: string[] = issue.labels.map(({ name }: any) => name);
|
||||||
const removeLabels = baseLabels.filter(name => labels.includes(name));
|
const removeLabels = baseLabels.filter(name => labels.includes(name));
|
||||||
|
|
||||||
for (const label of removeLabels) {
|
for (const label of removeLabels) {
|
||||||
@@ -198,15 +234,10 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
// https://github.com/octokit/rest.js/issues/34
|
// https://github.com/octokit/rest.js/issues/34
|
||||||
// - Probability to appear
|
// - Probability to appear
|
||||||
// - avoid use setLabels
|
// - avoid use setLabels
|
||||||
const { owner, repo, octokit, issueNumber } = this;
|
const { getIssue } = this;
|
||||||
|
const issue = await getIssue();
|
||||||
|
|
||||||
const issue = await octokit.issues.get({
|
const baseLabels: string[] = issue.labels.map(({ name }: any) => name);
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
});
|
|
||||||
|
|
||||||
const baseLabels: string[] = issue.data.labels.map(({ name }: any) => name);
|
|
||||||
const removeLabels = baseLabels.filter(name => !labels.includes(name));
|
const removeLabels = baseLabels.filter(name => !labels.includes(name));
|
||||||
const addLabels = labels.filter(name => !baseLabels.includes(name));
|
const addLabels = labels.filter(name => !baseLabels.includes(name));
|
||||||
|
|
||||||
@@ -247,15 +278,9 @@ export class IssueCoreEngine implements IIssueCoreEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async updateIssue(state: TIssueState, title: string | void, body: string | void, mode: TUpdateMode, labels?: string[] | void, assignees?: string[] | void) {
|
public async updateIssue(state: TIssueState, title: string | void, body: string | void, mode: TUpdateMode, labels?: string[] | void, assignees?: string[] | void) {
|
||||||
const { owner, repo, octokit, issueNumber } = this;
|
const { owner, repo, octokit, issueNumber, getIssue } = this;
|
||||||
|
const issue = await getIssue();
|
||||||
const issue = await octokit.issues.get({
|
const { body: baseBody, title: baseTitle, labels: baseLabels, assignees: baseAssigness, state: baseState } = issue;
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { body: baseBody, title: baseTitle, labels: baseLabels, assignees: baseAssigness, state: baseState } = issue.data;
|
|
||||||
|
|
||||||
const baseLabelsName = baseLabels.map(({ name }: any) => name);
|
const baseLabelsName = baseLabels.map(({ name }: any) => name);
|
||||||
const baseAssignessName = baseAssigness?.map(({ login }: any) => login);
|
const baseAssignessName = baseAssigness?.map(({ login }: any) => login);
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { TEmoji, TLockReasons, TIssueState, TUpdateMode } from '../types';
|
import { TEmoji, TLockReasons, TIssueState, TUpdateMode, TUserPermission } from '../types';
|
||||||
|
|
||||||
export interface IIssueBaseInfo {
|
export interface IIssueBaseInfo {
|
||||||
owner: string;
|
owner: string;
|
||||||
@@ -15,18 +15,38 @@ export interface IListIssuesParams {
|
|||||||
labels?: string;
|
labels?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TListIssuesResult = {
|
export type TIssueInfo = {
|
||||||
number: number;
|
number: number;
|
||||||
title: string;
|
title: string;
|
||||||
body: string;
|
body: string;
|
||||||
|
user: {
|
||||||
|
login: string;
|
||||||
|
};
|
||||||
|
assignees: {
|
||||||
|
login: string;
|
||||||
|
}[];
|
||||||
labels: {
|
labels: {
|
||||||
name: string;
|
name: string;
|
||||||
}[];
|
}[];
|
||||||
|
state: TIssueState;
|
||||||
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
pull_request?: any;
|
pull_request?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TListIssuesResults = TListIssuesResult[];
|
export type TIssueList = TIssueInfo[];
|
||||||
|
|
||||||
|
export type TCommentInfo = {
|
||||||
|
id: number;
|
||||||
|
body: string;
|
||||||
|
user: {
|
||||||
|
login: string;
|
||||||
|
};
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TCommentList = TCommentInfo[];
|
||||||
|
|
||||||
export interface IIssueCoreEngine {
|
export interface IIssueCoreEngine {
|
||||||
setIssueNumber(newIssueNumber: number): void;
|
setIssueNumber(newIssueNumber: number): void;
|
||||||
@@ -53,7 +73,11 @@ export interface IIssueCoreEngine {
|
|||||||
|
|
||||||
deleteComment(commentId: number): Promise<void>;
|
deleteComment(commentId: number): Promise<void>;
|
||||||
|
|
||||||
listIssues(params: IListIssuesParams): Promise<TListIssuesResults>;
|
getIssue(): Promise<TIssueInfo>;
|
||||||
|
getUserPermission(username: string): Promise<TUserPermission>;
|
||||||
|
|
||||||
|
listComments(): Promise<TCommentList>;
|
||||||
|
listIssues(params: IListIssuesParams): Promise<TIssueList>;
|
||||||
lockIssue(lockReason: TLockReasons): Promise<void>;
|
lockIssue(lockReason: TLockReasons): Promise<void>;
|
||||||
|
|
||||||
openIssue(): Promise<void>;
|
openIssue(): Promise<void>;
|
||||||
|
@@ -2,8 +2,8 @@ import * as github from '@actions/github';
|
|||||||
import { dealStringToArr, THANKS } from 'actions-util';
|
import { dealStringToArr, THANKS } from 'actions-util';
|
||||||
|
|
||||||
import * as core from './core';
|
import * as core from './core';
|
||||||
import { TAction } from './types';
|
|
||||||
import { IssueHelperEngine } from './helper';
|
import { IssueHelperEngine } from './helper';
|
||||||
|
import type { TAction } from './types';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
@@ -16,6 +16,6 @@ async function main() {
|
|||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
core.setFailed(err.message);
|
core.setFailed(err.message);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
export const EEmoji = {
|
export const EEmoji = {
|
||||||
'+1': '+1',
|
'+1': '+1',
|
||||||
'-1': '-1',
|
'-1': '-1',
|
||||||
'laugh': 'laugh',
|
laugh: 'laugh',
|
||||||
'confused': 'confused',
|
confused: 'confused',
|
||||||
'heart': 'heart',
|
heart: 'heart',
|
||||||
'hooray': 'hooray',
|
hooray: 'hooray',
|
||||||
'rocket': 'rocket',
|
rocket: 'rocket',
|
||||||
'eyes': 'eyes',
|
eyes: 'eyes',
|
||||||
}
|
};
|
||||||
|
|
||||||
export const ELockReasons = {
|
export const ELockReasons = {
|
||||||
'off-topic': 'off-topic',
|
'off-topic': 'off-topic',
|
||||||
'too heated': 'too heated',
|
'too heated': 'too heated',
|
||||||
'resolved': 'resolved',
|
resolved: 'resolved',
|
||||||
'spam': 'spam',
|
spam: 'spam',
|
||||||
}
|
};
|
||||||
|
22
src/types.ts
22
src/types.ts
@@ -1,3 +1,5 @@
|
|||||||
|
import type { TPermissionType } from 'actions-util';
|
||||||
|
|
||||||
export { Context } from '@actions/github/lib/context';
|
export { Context } from '@actions/github/lib/context';
|
||||||
|
|
||||||
export type TEmoji = '+1' | '-1' | 'laugh' | 'confused' | 'heart' | 'hooray' | 'rocket' | 'eyes';
|
export type TEmoji = '+1' | '-1' | 'laugh' | 'confused' | 'heart' | 'hooray' | 'rocket' | 'eyes';
|
||||||
@@ -8,9 +10,24 @@ export type TIssueState = 'open' | 'closed';
|
|||||||
|
|
||||||
export type TUpdateMode = 'append' | 'replace';
|
export type TUpdateMode = 'append' | 'replace';
|
||||||
|
|
||||||
|
export type TUserPermission = TPermissionType;
|
||||||
|
|
||||||
|
export type TOutInfo = {
|
||||||
|
auth: string;
|
||||||
|
id?: number;
|
||||||
|
number?: number;
|
||||||
|
title?: string;
|
||||||
|
body?: string;
|
||||||
|
state?: TIssueState;
|
||||||
|
created: string;
|
||||||
|
updated: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TOutList = TOutInfo[];
|
||||||
|
|
||||||
export type TAction =
|
export type TAction =
|
||||||
// [ Base Begin ]
|
// [ Base Begin ]
|
||||||
'add-assignees'
|
| 'add-assignees'
|
||||||
| 'add-labels'
|
| 'add-labels'
|
||||||
| 'close-issue'
|
| 'close-issue'
|
||||||
| 'create-comment'
|
| 'create-comment'
|
||||||
@@ -36,6 +53,5 @@ export type TAction =
|
|||||||
| 'lock-issues'
|
| 'lock-issues'
|
||||||
| 'mark-assignees'
|
| 'mark-assignees'
|
||||||
| 'mark-duplicate'
|
| 'mark-duplicate'
|
||||||
| 'month-statistics'
|
|
||||||
| 'welcome';
|
| 'welcome';
|
||||||
// [ Advanced End ]
|
// [ Advanced End ]
|
||||||
|
@@ -1,37 +0,0 @@
|
|||||||
import sampleSize from 'lodash/sampleSize';
|
|
||||||
import { dealStringToArr } from 'actions-util';
|
|
||||||
export { dealStringToArr };
|
|
||||||
|
|
||||||
export const matchKeyword = (content: string, keywords: string[]): string | undefined => {
|
|
||||||
return keywords.find(item => content.toLowerCase().includes(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
export const testDuplicate = (body: string | void): boolean => {
|
|
||||||
if (!body || !body.startsWith('Duplicate of')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let arr = body.split(' ');
|
|
||||||
if (arr[0] == 'Duplicate' && arr[1] == 'of') {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getPreMonth = (m: number): number => {
|
|
||||||
return m == 1 ? 12 : m - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const checkPermission = (require: string, permission: string): boolean => {
|
|
||||||
/**
|
|
||||||
* 有权限返回 true
|
|
||||||
*/
|
|
||||||
const permissions = ['none', 'read', 'write', 'admin'];
|
|
||||||
const requireNo = permissions.indexOf(require);
|
|
||||||
const permissionNo = permissions.indexOf(permission);
|
|
||||||
|
|
||||||
return requireNo <= permissionNo;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@@ -1,10 +0,0 @@
|
|||||||
import sampleSize from 'lodash/sampleSize';
|
|
||||||
import { dealStringToArr } from 'actions-util';
|
|
||||||
|
|
||||||
export const dealRandomAssignees = (assignees: string, randomTo: string | void): string[] => {
|
|
||||||
let arr = dealStringToArr(assignees);
|
|
||||||
if (randomTo && Number(randomTo) > 0 && Number(randomTo) < arr.length) {
|
|
||||||
arr = sampleSize(arr, Number(randomTo));
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
@@ -1,5 +1,31 @@
|
|||||||
import { dealStringToArr } from 'actions-util';
|
import { dealStringToArr } from 'actions-util';
|
||||||
export { dealStringToArr };
|
import sampleSize from 'lodash/sampleSize';
|
||||||
|
|
||||||
export * from './data';
|
export const dealRandomAssignees = (assignees: string, randomTo: string | void): string[] => {
|
||||||
export * from './deal';
|
let arr = dealStringToArr(assignees);
|
||||||
|
if (randomTo && Number(randomTo) > 0 && Number(randomTo) < arr.length) {
|
||||||
|
arr = sampleSize(arr, Number(randomTo));
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const matchKeyword = (content: string, keywords: string[]): boolean => {
|
||||||
|
return !!keywords.find(item => content.toLowerCase().includes(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const checkDuplicate = (body: string | void): boolean => {
|
||||||
|
if (!body || !body.startsWith('Duplicate of')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const arr = body.split(' ');
|
||||||
|
return arr[0] == 'Duplicate' && arr[1] == 'of'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPreMonth = (m: number): number => {
|
||||||
|
return m == 1 ? 12 : m - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace some & split & cull empty
|
||||||
|
export const replaceStr2Arr = (str: string, replace: string, split: string): string[] => {
|
||||||
|
return str.replace(replace, '').trim().split(split).reduce((result: string[], it) => it ? [...result, it.trim()] : result, []);
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
describe('Test Public', () => {
|
import { replaceStr2Arr } from '../src/util';
|
||||||
it('test query', () => {
|
|
||||||
|
describe('Test', () => {
|
||||||
|
it('test doQueryIssues', () => {
|
||||||
const issues = [
|
const issues = [
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
@@ -42,4 +44,12 @@ describe('Test Public', () => {
|
|||||||
expect(r[2].id).toEqual(5);
|
expect(r[2].id).toEqual(5);
|
||||||
expect(r.length).toEqual(3);
|
expect(r.length).toEqual(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test replaceStr2Arr', () => {
|
||||||
|
const st = '/assign @1 @2 @3@a 3 @s @1_2 2';
|
||||||
|
const re = '/assign';
|
||||||
|
const sp = '@';
|
||||||
|
|
||||||
|
expect(replaceStr2Arr(st, re, sp)).toEqual(['1', '2', '3', 'a 3', 's', '1_2 2']);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// For dumi style
|
// For dumi style
|
||||||
|
import './less/dumi.less';
|
||||||
import './less/main.less';
|
import './less/main.less';
|
||||||
import './less/markdown.less';
|
import './less/markdown.less';
|
||||||
import './less/dumi.less';
|
|
||||||
|
Reference in New Issue
Block a user