All checks were successful
CI Monitor MIflow / build-image (push) Successful in 45s
312 lines
9.1 KiB
TypeScript
312 lines
9.1 KiB
TypeScript
import db from "../../db"
|
||
import service from "../../service"
|
||
import netTool from "../../service/netTool"
|
||
import { EggMessage } from "../../types/eggMessage"
|
||
import { Gitlab } from "../../types/gitlab"
|
||
import { sec2minStr } from "../../utils/timeTools"
|
||
|
||
/**
|
||
* 判断是否是合并请求
|
||
* @param {Gitlab.PipelineEvent} pipeline - GitLab 流水线事件对象
|
||
* @returns {boolean} - 如果提交信息符合合并请求的格式,返回 true;否则返回 false
|
||
*/
|
||
const checkIsMergeCommit = (pipeline: Gitlab.PipelineEvent): boolean => {
|
||
const regex = /^Merge branch '.*' into '.*'$/
|
||
return regex.test(pipeline.commit.title)
|
||
}
|
||
|
||
enum NEXT_ACTION {
|
||
SKIP,
|
||
NOTIFY,
|
||
ADD_MONITOR,
|
||
REMOVE_MONITOR,
|
||
NOTIFY_AND_REMOVE_MONITOR,
|
||
}
|
||
|
||
/**
|
||
* 获取下一步操作
|
||
* @param {Gitlab.PipelineEvent} pipeline - GitLab 流水线事件对象
|
||
* @param {string | null} [targetStage] - 目标阶段,可选
|
||
* @returns {Promise<NEXT_ACTION>} - 返回下一步操作的枚举值
|
||
*/
|
||
const getNextAction = async (
|
||
pipeline: Gitlab.PipelineEvent,
|
||
targetStage?: string | null
|
||
): Promise<NEXT_ACTION> => {
|
||
// 没有指定Stage则整个流水线成功即为成功
|
||
if (!targetStage) {
|
||
if (pipeline.object_attributes.status === "success") {
|
||
return NEXT_ACTION.NOTIFY
|
||
}
|
||
return NEXT_ACTION.SKIP
|
||
}
|
||
// 指定了stage,但是流水线非成功状态,删除监控
|
||
if (
|
||
["failed", "canceled", "skipped"].includes(
|
||
pipeline.object_attributes.status
|
||
)
|
||
) {
|
||
return NEXT_ACTION.REMOVE_MONITOR
|
||
}
|
||
// 指定了Stage,且流水线成功了,删除监控并发送通知
|
||
if (pipeline.object_attributes.status === "success") {
|
||
return NEXT_ACTION.NOTIFY_AND_REMOVE_MONITOR
|
||
}
|
||
// 在流水线为`running`时,且该stage全部的job有非结束状态时即`created`、`pending`、`running`、`manual`、`scheduled`时,添加监控
|
||
if (pipeline.object_attributes.status === "running") {
|
||
const jobs = await service.gitlab.pipeline.getJobs(
|
||
pipeline.project.id,
|
||
pipeline.object_attributes.id
|
||
)
|
||
if (
|
||
jobs.some(
|
||
(job) =>
|
||
job.stage === targetStage &&
|
||
!["success", "failed", "canceled", "skipped"].includes(job.status)
|
||
)
|
||
) {
|
||
return NEXT_ACTION.ADD_MONITOR
|
||
}
|
||
}
|
||
// 其他情况都跳过
|
||
return NEXT_ACTION.SKIP
|
||
}
|
||
|
||
/**
|
||
* 获取合并请求
|
||
* @param {Gitlab.PipelineEvent} pipeline - GitLab 流水线事件对象
|
||
* @returns {Promise<Gitlab.MergeRequest | null>} - 返回合并请求对象或 null
|
||
*/
|
||
const getMergeRequest = async (
|
||
pipeline: Gitlab.PipelineEvent
|
||
): Promise<Gitlab.MergeRequest | null> => {
|
||
if (!checkIsMergeCommit(pipeline)) return null
|
||
const res = await service.gitlab.commit.getMr(
|
||
pipeline.project.id,
|
||
pipeline.object_attributes.sha
|
||
)
|
||
if (res.length === 0) return null
|
||
return res.find((mr) => mr.merge_commit_sha === pipeline.commit.id) || null
|
||
}
|
||
|
||
/**
|
||
* 获取用户信息
|
||
* @param {Gitlab.PipelineEvent} pipeline - GitLab 流水线事件对象
|
||
* @param {Gitlab.MergeRequest | null} mergeRequest - 合并请求对象或 null
|
||
* @returns {{ participant: string, receiver: string[] }} - 返回包含参与者和接收者信息的对象
|
||
*/
|
||
const getUserInfo = (
|
||
pipeline: Gitlab.PipelineEvent,
|
||
mergeRequest: Gitlab.MergeRequest | null
|
||
): { participant: string; receiver: string[] } => {
|
||
let participant = pipeline.user.name
|
||
const receiver = [pipeline.user.username]
|
||
// 有MR且用户不同
|
||
if (mergeRequest && mergeRequest.author.username !== pipeline.user.username) {
|
||
participant += `、${mergeRequest.author.name}`
|
||
receiver.push(mergeRequest.author.username)
|
||
}
|
||
return { participant, receiver }
|
||
}
|
||
|
||
/**
|
||
* 生成消息模板变量
|
||
* @param {Gitlab.PipelineEvent} pipeline - GitLab 流水线事件对象
|
||
* @returns {Promise<{ receiver: string[], variable: EggMessage.CardVariable }>} - 返回包含接收者和模板变量的对象
|
||
*/
|
||
const genCardVariable = async (
|
||
pipeline: Gitlab.PipelineEvent
|
||
): Promise<{
|
||
receiver: string[]
|
||
variable: EggMessage.CardVariable
|
||
}> => {
|
||
// 获取对应的合并请求
|
||
const mergeRequest = await getMergeRequest(pipeline)
|
||
// 获取用户信息
|
||
const { participant, receiver } = getUserInfo(pipeline, mergeRequest)
|
||
// sonar的ID
|
||
const sonarParams = new URLSearchParams({
|
||
id: pipeline.project.path_with_namespace.replaceAll("/", ":"),
|
||
branch: pipeline.object_attributes.ref,
|
||
})
|
||
// 获取模板变量
|
||
const variable: EggMessage.CardVariable = {
|
||
project: pipeline.project.path_with_namespace,
|
||
project_link: pipeline.project.web_url,
|
||
pipeline: pipeline.object_attributes.id.toString(),
|
||
pipeline_link: `${pipeline.project.web_url}/-/pipelines`,
|
||
ref: pipeline.object_attributes.ref,
|
||
ref_link: `${pipeline.project.web_url}/-/commits/${pipeline.object_attributes.ref}`,
|
||
commit_user: pipeline.user.name,
|
||
duration: sec2minStr(pipeline.object_attributes.duration),
|
||
participant,
|
||
commit_message: pipeline.commit.title,
|
||
mr: mergeRequest ? mergeRequest.references.full : "无关联的MR",
|
||
mr_link: mergeRequest ? mergeRequest.web_url : "",
|
||
sonar_link: `https://sonarqube.mioffice.cn/dashboard?${sonarParams}`,
|
||
}
|
||
return {
|
||
receiver,
|
||
variable,
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成机器人消息
|
||
* @param {EggMessage.CardVariable} variable - 模板变量
|
||
* @returns {string} - 返回生成的机器人消息内容
|
||
*/
|
||
const genLarkRobotMsgContent = (variable: EggMessage.CardVariable): string =>
|
||
JSON.stringify({
|
||
type: "template",
|
||
data: {
|
||
config: {
|
||
update_multi: true,
|
||
},
|
||
template_id: "ctp_AA36QafWyob2",
|
||
template_variable: variable,
|
||
},
|
||
})
|
||
|
||
/**
|
||
* 发送提醒消息
|
||
* @param {Gitlab.PipelineEvent} pipeline - 流水线信息
|
||
* @param {string} apiKey - API 密钥
|
||
* @returns {Promise<void>} - 无返回值
|
||
*/
|
||
const sendNotify = async (
|
||
pipeline: Gitlab.PipelineEvent,
|
||
apiKey: string
|
||
): Promise<void> => {
|
||
// 获取消息信息
|
||
const { receiver, variable } = await genCardVariable(pipeline)
|
||
// 获取机器人消息
|
||
const robotMsg = genLarkRobotMsgContent(variable)
|
||
// 发送消息
|
||
service.message.byUserIdList(receiver, robotMsg, apiKey)
|
||
// 记录日志
|
||
await db.notify.create({ ...variable })
|
||
}
|
||
|
||
/**
|
||
* 添加监控
|
||
* @param {Gitlab.PipelineEvent} pipeline - 流水线信息
|
||
* @param {string} apiKey - API 密钥
|
||
* @param {string} stage - 阶段名称
|
||
* @returns {Promise<void>} - 无返回值
|
||
*/
|
||
const addMonitor = async (
|
||
pipeline: Gitlab.PipelineEvent,
|
||
apiKey: string,
|
||
stage: string
|
||
): Promise<void> => {
|
||
const monitor = await db.monitor.getOne(
|
||
pipeline.project.id.toString(),
|
||
pipeline.object_attributes.id.toString(),
|
||
stage,
|
||
apiKey
|
||
)
|
||
if (monitor) return
|
||
// 获取消息信息
|
||
const { receiver, variable } = await genCardVariable(pipeline)
|
||
await db.monitor.create({
|
||
project_id: pipeline.project.id.toString(),
|
||
pipeline_id: pipeline.object_attributes.id.toString(),
|
||
stage,
|
||
api_key: apiKey,
|
||
receiver,
|
||
variable,
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 移除监控
|
||
* @param {Gitlab.PipelineEvent} pipeline - 流水线信息
|
||
* @param {string} apiKey - API 密钥
|
||
* @param {string} stage - 阶段名称
|
||
* @returns {Promise<void>} - 无返回值
|
||
*/
|
||
const removeMonitor = async (
|
||
pipeline: Gitlab.PipelineEvent,
|
||
apiKey: string,
|
||
stage: string
|
||
): Promise<void> => {
|
||
const monitor = await db.monitor.getOne(
|
||
pipeline.project.id.toString(),
|
||
pipeline.object_attributes.id.toString(),
|
||
stage,
|
||
apiKey
|
||
)
|
||
if (!monitor) return
|
||
await db.monitor.del(monitor.id)
|
||
}
|
||
|
||
/**
|
||
* 移除监控并发送提醒消息
|
||
* @param {Gitlab.PipelineEvent} pipeline - 流水线信息
|
||
* @param {string} apiKey - API 密钥
|
||
* @param {string} stage - 阶段名称
|
||
* @returns {Promise<void>} - 无返回值
|
||
*/
|
||
const removeMonitorAndNotify = async (
|
||
pipeline: Gitlab.PipelineEvent,
|
||
apiKey: string,
|
||
stage: string
|
||
): Promise<void> => {
|
||
const monitor = await db.monitor.getOne(
|
||
pipeline.project.id.toString(),
|
||
pipeline.object_attributes.id.toString(),
|
||
stage,
|
||
apiKey
|
||
)
|
||
if (!monitor) return
|
||
db.monitor.del(monitor.id)
|
||
sendNotify(pipeline, apiKey)
|
||
}
|
||
|
||
/**
|
||
* 发送通知消息
|
||
* @param {Gitlab.PipelineEvent} pipeline - GitLab 流水线事件对象
|
||
* @param {string} apiKey - API 密钥
|
||
* @param {URLSearchParams} params - URL 查询参数
|
||
* @returns {Promise<Response>} - 返回操作结果
|
||
*/
|
||
const manageRawEvent = async (
|
||
pipeline: Gitlab.PipelineEvent,
|
||
apiKey: string,
|
||
params: URLSearchParams
|
||
): Promise<Response> => {
|
||
// 获取Stage参数
|
||
const stage = params.get("stage")
|
||
// 获取下一步操作
|
||
const action = await getNextAction(pipeline, stage)
|
||
// 发送通知
|
||
if (action === NEXT_ACTION.NOTIFY) {
|
||
sendNotify(pipeline, apiKey)
|
||
}
|
||
// 添加监控
|
||
if (action === NEXT_ACTION.ADD_MONITOR) {
|
||
addMonitor(pipeline, apiKey, stage!)
|
||
}
|
||
// 删除监控
|
||
if (action === NEXT_ACTION.REMOVE_MONITOR) {
|
||
removeMonitor(pipeline, apiKey, stage!)
|
||
}
|
||
// 删除监控并发送通知
|
||
if (action === NEXT_ACTION.NOTIFY_AND_REMOVE_MONITOR) {
|
||
removeMonitorAndNotify(pipeline, apiKey, stage!)
|
||
}
|
||
// 返回成功
|
||
return netTool.ok()
|
||
}
|
||
|
||
/**
|
||
* 管理流水线事件
|
||
*/
|
||
const managePipelineEvent = {
|
||
manageRawEvent,
|
||
genLarkRobotMsgContent,
|
||
}
|
||
|
||
export default managePipelineEvent
|