feat: gitlab通知支持指定阶段
All checks were successful
CI Monitor MIflow / build-image (push) Successful in 1m2s
All checks were successful
CI Monitor MIflow / build-image (push) Successful in 1m2s
This commit is contained in:
parent
cc84b59f78
commit
d8ca8b531d
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -14,6 +14,7 @@
|
|||||||
"Gruntfuggly",
|
"Gruntfuggly",
|
||||||
"micr",
|
"micr",
|
||||||
"mioffice",
|
"mioffice",
|
||||||
|
"oxlint",
|
||||||
"tseslint",
|
"tseslint",
|
||||||
"wlpbbgiky",
|
"wlpbbgiky",
|
||||||
"wujiali",
|
"wujiali",
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
import db from "../../db"
|
||||||
import service from "../../service"
|
import service from "../../service"
|
||||||
import netTool from "../../service/netTool"
|
import netTool from "../../service/netTool"
|
||||||
|
import { EggMessage } from "../../types/eggMessage"
|
||||||
import { Gitlab } from "../../types/gitlab"
|
import { Gitlab } from "../../types/gitlab"
|
||||||
import { Notify } from "../../types/notify"
|
|
||||||
import { sec2minStr } from "../../utils/timeTools"
|
import { sec2minStr } from "../../utils/timeTools"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,8 +20,44 @@ const checkIsMergeCommit = (pipeline: Gitlab.PipelineEvent) => {
|
|||||||
* @param pipeline
|
* @param pipeline
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const checkIsSuccess = (pipeline: Gitlab.PipelineEvent) => {
|
const checkIsSuccess = async (
|
||||||
return pipeline.object_attributes.status === "success"
|
pipeline: Gitlab.PipelineEvent,
|
||||||
|
stage?: string | null
|
||||||
|
) => {
|
||||||
|
/**
|
||||||
|
* 创建结果对象
|
||||||
|
* @param buildId 构建ID
|
||||||
|
* @param continueFlag 是否继续
|
||||||
|
* @returns 结果对象
|
||||||
|
*/
|
||||||
|
const makeResult = (buildId: string, continueFlag: boolean) => ({
|
||||||
|
buildId: continueFlag ? buildId : "",
|
||||||
|
continueFlag,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 没有指定Stage则整个流水线成功即为成功
|
||||||
|
if (!stage)
|
||||||
|
return makeResult(
|
||||||
|
pipeline.builds
|
||||||
|
.sort((a, b) => a.finished_at.localeCompare(b.finished_at))[0]
|
||||||
|
.id.toString(),
|
||||||
|
pipeline.object_attributes.status === "success"
|
||||||
|
)
|
||||||
|
// 指定了Stage,该Stage是否全部成功
|
||||||
|
const builds = pipeline.builds.filter((build) => build.stage === stage)
|
||||||
|
// 没有该Stage的构建
|
||||||
|
if (builds.length === 0) return makeResult("", false)
|
||||||
|
// 有该Stage的构建,但不全成功
|
||||||
|
if (!builds.every((build) => build.status === "success"))
|
||||||
|
return makeResult("", false)
|
||||||
|
// 按finished_at排序,获取最后一个运行的id
|
||||||
|
const lastId = builds.sort((a, b) =>
|
||||||
|
a.finished_at.localeCompare(b.finished_at)
|
||||||
|
)[0].id
|
||||||
|
// 该ID的通知是否已经发送过
|
||||||
|
const notify = await db.notify.getOne(lastId.toString())
|
||||||
|
if (notify) return makeResult("", false)
|
||||||
|
return makeResult(lastId.toString(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,7 +100,7 @@ const getUserInfo = (
|
|||||||
* @param variable 模板变量
|
* @param variable 模板变量
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const getRobotMsg = async (variable: Notify.CardVariable) =>
|
const getRobotMsg = async (variable: EggMessage.CardVariable) =>
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: "template",
|
type: "template",
|
||||||
data: {
|
data: {
|
||||||
@ -83,16 +120,26 @@ const getRobotMsg = async (variable: Notify.CardVariable) =>
|
|||||||
*/
|
*/
|
||||||
const sendNotifyMsg = async (
|
const sendNotifyMsg = async (
|
||||||
pipeline: Gitlab.PipelineEvent,
|
pipeline: Gitlab.PipelineEvent,
|
||||||
apiKey: string
|
apiKey: string,
|
||||||
|
params: URLSearchParams
|
||||||
) => {
|
) => {
|
||||||
|
const { continueFlag, buildId } = await checkIsSuccess(
|
||||||
|
pipeline,
|
||||||
|
params.get("stage")
|
||||||
|
)
|
||||||
// 只处理成功的CICD
|
// 只处理成功的CICD
|
||||||
if (!checkIsSuccess(pipeline)) return netTool.ok()
|
if (!continueFlag) return netTool.ok()
|
||||||
// 获取对应的合并请求
|
// 获取对应的合并请求
|
||||||
const mergeRequest = await getMergeRequest(pipeline)
|
const mergeRequest = await getMergeRequest(pipeline)
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
const { participant, receiver } = getUserInfo(pipeline, mergeRequest)
|
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: Notify.CardVariable = {
|
const variable: EggMessage.CardVariable = {
|
||||||
project: pipeline.project.path_with_namespace,
|
project: pipeline.project.path_with_namespace,
|
||||||
project_link: pipeline.project.web_url,
|
project_link: pipeline.project.web_url,
|
||||||
pipeline: pipeline.object_attributes.id.toString(),
|
pipeline: pipeline.object_attributes.id.toString(),
|
||||||
@ -105,11 +152,14 @@ const sendNotifyMsg = async (
|
|||||||
commit_message: pipeline.commit.title,
|
commit_message: pipeline.commit.title,
|
||||||
mr: mergeRequest ? mergeRequest.references.full : "无关联的MR",
|
mr: mergeRequest ? mergeRequest.references.full : "无关联的MR",
|
||||||
mr_link: mergeRequest ? mergeRequest.web_url : "",
|
mr_link: mergeRequest ? mergeRequest.web_url : "",
|
||||||
|
sonar_link: `https://sonarqube.mioffice.cn/dashboard?${sonarParams}`,
|
||||||
}
|
}
|
||||||
// 获取机器人消息
|
// 获取机器人消息
|
||||||
const robotMsg = await getRobotMsg(variable)
|
const robotMsg = await getRobotMsg(variable)
|
||||||
// 发送消息
|
// 发送消息
|
||||||
await service.message.byUserIdList(receiver, robotMsg, apiKey)
|
service.message.byUserIdList(receiver, robotMsg, apiKey)
|
||||||
|
// 记录日志
|
||||||
|
await db.notify.create({ ...variable, build_id: buildId })
|
||||||
// 返回成功
|
// 返回成功
|
||||||
return netTool.ok()
|
return netTool.ok()
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import notify from "./notify"
|
||||||
import pipeline from "./pipeline"
|
import pipeline from "./pipeline"
|
||||||
import project from "./project"
|
import project from "./project"
|
||||||
import user from "./user"
|
import user from "./user"
|
||||||
@ -8,6 +9,7 @@ const db = {
|
|||||||
pipeline,
|
pipeline,
|
||||||
user,
|
user,
|
||||||
view,
|
view,
|
||||||
|
notify,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default db
|
export default db
|
||||||
|
29
db/notify/index.ts
Normal file
29
db/notify/index.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { DB } from "../../types/db"
|
||||||
|
import { managePb404 } from "../../utils/pbTools"
|
||||||
|
import pbClient from "../pbClient"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从数据库检索一个通知。
|
||||||
|
* @param id 要检索的通知的ID。
|
||||||
|
* @returns 如果找到,返回解析为通知对象的promise;如果未找到,抛出404错误。
|
||||||
|
*/
|
||||||
|
const getOne = (id: string) =>
|
||||||
|
managePb404<DB.Notify>(
|
||||||
|
async () =>
|
||||||
|
await pbClient.collection("notify").getFirstListItem(`build_id="${id}"`)
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个新的通知。
|
||||||
|
* @param data 新通知的数据。
|
||||||
|
* @returns 返回解析为已创建通知对象的promise。
|
||||||
|
*/
|
||||||
|
const create = async (data: Partial<DB.Notify>) =>
|
||||||
|
await pbClient.collection("notify").create<DB.Notify>(data)
|
||||||
|
|
||||||
|
const notify = {
|
||||||
|
create,
|
||||||
|
getOne,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default notify
|
@ -10,9 +10,9 @@
|
|||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,jsx,ts,tsx}": [
|
"*.{js,jsx,ts,tsx}": [
|
||||||
|
"oxlint --fix",
|
||||||
"eslint --fix",
|
"eslint --fix",
|
||||||
"prettier --write",
|
"prettier --write"
|
||||||
"git add"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -27,6 +27,7 @@
|
|||||||
"globals": "^15.8.0",
|
"globals": "^15.8.0",
|
||||||
"husky": "^9.1.1",
|
"husky": "^9.1.1",
|
||||||
"lint-staged": "^15.2.7",
|
"lint-staged": "^15.2.7",
|
||||||
|
"oxlint": "^0.6.1",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"typescript-eslint": "^7.17.0"
|
"typescript-eslint": "^7.17.0"
|
||||||
},
|
},
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
|
|
||||||
组织卡片信息,给 Commit的用户以及 可能的 MR发起者发送通知
|
组织卡片信息,给 Commit的用户以及 可能的 MR发起者发送通知
|
||||||
|
|
||||||
|
## 处理在中间Stage需要提醒的情况
|
||||||
|
在stage全部成功的情况下,按finish_at时间排序,找到最后一个stage,如果stage的状态是成功,且该build的id没有发送过通知,则发送通知
|
||||||
|
|
||||||
# 数据库表信息
|
# 数据库表信息
|
||||||
|
|
||||||
[数据库地址](https://gitlab-pb.xiaomiwh.cn/_/)
|
[数据库地址](https://gitlab-pb.xiaomiwh.cn/_/)
|
||||||
|
@ -14,7 +14,8 @@ export const manageGitlabEventReq = async (req: Request) => {
|
|||||||
// 只处理流水线钩子
|
// 只处理流水线钩子
|
||||||
if (eventType === "Pipeline Hook") {
|
if (eventType === "Pipeline Hook") {
|
||||||
const body = (await req.json()) as Gitlab.PipelineEvent
|
const body = (await req.json()) as Gitlab.PipelineEvent
|
||||||
return managePipelineEvent.sendNotifyMsg(body, apiKey)
|
const params = new URLSearchParams(req.url)
|
||||||
|
return managePipelineEvent.sendNotifyMsg(body, apiKey, params)
|
||||||
}
|
}
|
||||||
return netTool.ok()
|
return netTool.ok()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { RecordModel } from "pocketbase"
|
import { RecordModel } from "pocketbase"
|
||||||
|
|
||||||
|
import { EggMessage } from "./eggMessage"
|
||||||
|
|
||||||
export namespace DB {
|
export namespace DB {
|
||||||
export interface Pipeline extends RecordModel {
|
export interface Pipeline extends RecordModel {
|
||||||
project_id: string
|
project_id: string
|
||||||
@ -49,4 +51,8 @@ export namespace DB {
|
|||||||
duration: number
|
duration: number
|
||||||
ref: string
|
ref: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Notify extends RecordModel, EggMessage.CardVariable {
|
||||||
|
build_id: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export namespace Notify {
|
export namespace EggMessage {
|
||||||
export interface CardVariable {
|
export interface CardVariable {
|
||||||
project: string
|
project: string
|
||||||
project_link: string
|
project_link: string
|
||||||
@ -12,5 +12,6 @@ export namespace Notify {
|
|||||||
commit_message: string
|
commit_message: string
|
||||||
mr: string
|
mr: string
|
||||||
mr_link: string
|
mr_link: string
|
||||||
|
sonar_link: string
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -268,6 +268,28 @@ export namespace Gitlab {
|
|||||||
*/
|
*/
|
||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 构建信息
|
||||||
|
*/
|
||||||
|
builds: {
|
||||||
|
/**
|
||||||
|
* 构建的ID
|
||||||
|
*/
|
||||||
|
id: number
|
||||||
|
/**
|
||||||
|
* 构建的步骤
|
||||||
|
*/
|
||||||
|
stage: string
|
||||||
|
/**
|
||||||
|
* 构建的状态
|
||||||
|
*/
|
||||||
|
status: string
|
||||||
|
/**
|
||||||
|
* 构建的结束时间
|
||||||
|
* @example 2024-07-29 15:49:21 +0800
|
||||||
|
*/
|
||||||
|
finished_at: string
|
||||||
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 合并请求 */
|
/* 合并请求 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user