style: 优化项目结构
This commit is contained in:
parent
eca7e7cb41
commit
3f220f7943
@ -1,33 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "ci_monitor",
|
"name": "ci_monitor",
|
||||||
"image": "git.yingbo.im:333/zhaoyingbo/dev:bun",
|
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
|
||||||
"remoteUser": "bun",
|
|
||||||
"containerUser": "bun",
|
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"settings": {
|
"settings": {
|
||||||
"files.autoSave": "afterDelay",
|
"files.autoSave": "afterDelay",
|
||||||
"editor.guides.bracketPairs": true,
|
"editor.guides.bracketPairs": true,
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.stylelint": true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"extensions": [
|
"extensions": [
|
||||||
|
"eamodio.gitlens",
|
||||||
|
"Gruntfuggly.todo-tree",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
"eamodio.gitlens",
|
|
||||||
"unifiedjs.vscode-mdx",
|
|
||||||
"oderwat.indent-rainbow",
|
|
||||||
"jock.svg",
|
|
||||||
"ChakrounAnas.turbo-console-log",
|
"ChakrounAnas.turbo-console-log",
|
||||||
"Gruntfuggly.todo-tree",
|
"streetsidesoftware.code-spell-checker",
|
||||||
"MS-CEINTL.vscode-language-pack-zh-hans",
|
"MS-CEINTL.vscode-language-pack-zh-hans"
|
||||||
"stylelint.vscode-stylelint",
|
|
||||||
"GitHub.copilot",
|
|
||||||
"streetsidesoftware.code-spell-checker"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"onCreateCommand": "curl -fsSL https://bun.sh/install | bash"
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import db from "../../db";
|
import db from "../../db";
|
||||||
import { PipelineRecordModel } from "../../db/pipeline";
|
|
||||||
import { ProjectRecordModel } from "../../db/project";
|
|
||||||
import service from "../../service";
|
import service from "../../service";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import { Gitlab } from "../../types/gitlab";
|
||||||
|
import { DB } from "../../types/db";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取全部的pipeline列表
|
* 获取全部的pipeline列表
|
||||||
*/
|
*/
|
||||||
const getFullPipelineList = async (project: ProjectRecordModel) => {
|
const getFullPipelineList = async (project: DB.Project) => {
|
||||||
// 先获取最新的pipelineID
|
// 先获取最新的pipelineID
|
||||||
const latestOne = await db.pipeline.getLatestOne(project.id);
|
const latestOne = await db.pipeline.getLatestOne(project.id);
|
||||||
// 获取本次数据获取的截止时间,如果没有,则获取从20240101到现在所有pipeline信息
|
// 获取本次数据获取的截止时间,如果没有,则获取从20240101到现在所有pipeline信息
|
||||||
@ -15,11 +15,11 @@ const getFullPipelineList = async (project: ProjectRecordModel) => {
|
|||||||
latestOne?.created_at || "2024-01-01T00:00:00.000+08:00"
|
latestOne?.created_at || "2024-01-01T00:00:00.000+08:00"
|
||||||
);
|
);
|
||||||
// 获取pipeline列表并保存
|
// 获取pipeline列表并保存
|
||||||
const fullPipelineList: GitlabPipeline[] = [];
|
const fullPipelineList: Gitlab.Pipeline[] = [];
|
||||||
let page = 1;
|
let page = 1;
|
||||||
let hasBeforeLatestTime = false;
|
let hasBeforeLatestTime = false;
|
||||||
while (!hasBeforeLatestTime) {
|
while (!hasBeforeLatestTime) {
|
||||||
const pipelines = await service.gitlab.fetchPipelines(
|
const pipelines = await service.gitlab.pipeline.getList(
|
||||||
project.project_id,
|
project.project_id,
|
||||||
page++
|
page++
|
||||||
);
|
);
|
||||||
@ -37,20 +37,20 @@ const getFullPipelineList = async (project: ProjectRecordModel) => {
|
|||||||
}
|
}
|
||||||
const fullPipelineDetailList = await Promise.all(
|
const fullPipelineDetailList = await Promise.all(
|
||||||
fullPipelineList.map(({ project_id, id, created_at }) =>
|
fullPipelineList.map(({ project_id, id, created_at }) =>
|
||||||
service.gitlab.fetchPipelineDetails(project_id, id, created_at)
|
service.gitlab.pipeline.getDetail(project_id, id, created_at)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return fullPipelineDetailList.filter(
|
return fullPipelineDetailList.filter((v) => v) as (Gitlab.PipelineDetail & {
|
||||||
(v) => v
|
created_at: string;
|
||||||
) as GitlabPipelineDetailWithCreateAt[];
|
})[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertFullPipelineList = async (
|
const insertFullPipelineList = async (
|
||||||
fullPipelineList: GitlabPipelineDetailWithCreateAt[][],
|
fullPipelineList: (Gitlab.PipelineDetail & { created_at: string })[][],
|
||||||
fullUserMap: Record<string, string>,
|
fullUserMap: Record<string, string>,
|
||||||
fullProjectMap: Record<string, string>
|
fullProjectMap: Record<string, string>
|
||||||
) => {
|
) => {
|
||||||
const dbPipelineList: Partial<PipelineRecordModel> = [];
|
const dbPipelineList: Partial<DB.Pipeline> = [];
|
||||||
|
|
||||||
fullPipelineList.forEach((pipelineList) => {
|
fullPipelineList.forEach((pipelineList) => {
|
||||||
pipelineList.forEach((pipeline) => {
|
pipelineList.forEach((pipeline) => {
|
||||||
@ -73,9 +73,7 @@ const insertFullPipelineList = async (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
dbPipelineList.map((v: Partial<PipelineRecordModel>) =>
|
dbPipelineList.map((v: Partial<DB.Pipeline>) => db.pipeline.create(v))
|
||||||
db.pipeline.create(v)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
import db from "../../db";
|
import db from "../../db";
|
||||||
import { ProjectRecordModel } from "../../db/project";
|
|
||||||
import service from "../../service";
|
import service from "../../service";
|
||||||
|
import { DB } from "../../types/db";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 填充项目信息
|
* 填充项目信息
|
||||||
*/
|
*/
|
||||||
const fillProj = async (project: ProjectRecordModel) => {
|
const fillProj = async (project: DB.Project) => {
|
||||||
const projDetail = await service.gitlab.fetchProjectDetails(
|
const projDetail = await service.gitlab.project.getDetail(project.project_id);
|
||||||
project.project_id
|
|
||||||
);
|
|
||||||
if (!projDetail) {
|
if (!projDetail) {
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
const useFulParams: Partial<ProjectRecordModel> = {
|
const useFulParams: Partial<DB.Project> = {
|
||||||
...project,
|
...project,
|
||||||
avatar_url: projDetail.avatar_url,
|
avatar_url: projDetail.avatar_url,
|
||||||
description: projDetail.description,
|
description: projDetail.description,
|
||||||
@ -40,7 +38,7 @@ const getFullProjList = async () => {
|
|||||||
return filledFullProjList;
|
return filledFullProjList;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFullProjectMap = (fullProjList: ProjectRecordModel[]) => {
|
const getFullProjectMap = (fullProjList: DB.Project[]) => {
|
||||||
const fullProjectMap: Record<string, string> = {};
|
const fullProjectMap: Record<string, string> = {};
|
||||||
fullProjList.forEach((item) => {
|
fullProjList.forEach((item) => {
|
||||||
fullProjectMap[item.project_id] = item.id;
|
fullProjectMap[item.project_id] = item.id;
|
||||||
|
@ -121,7 +121,7 @@ const getRobotMsg = async () =>
|
|||||||
* @param chat_id
|
* @param chat_id
|
||||||
*/
|
*/
|
||||||
const sendCIReportByChatId = async (chat_id: string) => {
|
const sendCIReportByChatId = async (chat_id: string) => {
|
||||||
await service.sendMessage.byChatId(chat_id, await getRobotMsg());
|
await service.message.byChatId(chat_id, await getRobotMsg());
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,7 +129,7 @@ const sendCIReportByChatId = async (chat_id: string) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const sendCIReportByCron = async () =>
|
const sendCIReportByCron = async () =>
|
||||||
await service.sendMessage.byGroupId("52usf3w8l6z4vs1", await getRobotMsg());
|
await service.message.byGroupId("52usf3w8l6z4vs1", await getRobotMsg());
|
||||||
|
|
||||||
const manageRobot = {
|
const manageRobot = {
|
||||||
sendCIReportByChatId,
|
sendCIReportByChatId,
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import db from "../../db";
|
import db from "../../db";
|
||||||
|
import { Gitlab } from "../../types/gitlab";
|
||||||
|
|
||||||
const getFullUserMap = async (fullPipelineList: GitlabPipelineDetail[][]) => {
|
const getFullUserMap = async (fullPipelineList: Gitlab.PipelineDetail[][]) => {
|
||||||
const userList: GitlabUser[] = [];
|
const userList: Gitlab.User[] = [];
|
||||||
fullPipelineList.forEach((fullPipeline) => {
|
fullPipelineList.forEach((fullPipeline) => {
|
||||||
fullPipeline.forEach((item) => {
|
fullPipeline.forEach((item) => {
|
||||||
if (item.user && !userList.find((v) => v.id === item.user?.id)) {
|
if (item.user && !userList.find((v) => v.id === item.user?.id)) {
|
||||||
|
@ -1,33 +1,24 @@
|
|||||||
import { RecordModel } from "pocketbase";
|
|
||||||
import pbClient from "../pbClient";
|
import pbClient from "../pbClient";
|
||||||
import { managePb404 } from "../../utils/pbTools";
|
import { managePb404 } from "../../utils/pbTools";
|
||||||
|
import { DB } from "../../types/db";
|
||||||
|
|
||||||
export interface PipelineRecordModel extends RecordModel {
|
/**
|
||||||
project_id: string;
|
* 通过其 ID 检索一个管道。
|
||||||
user_id: string;
|
* @param id 管道的 ID。
|
||||||
pipeline_id: number;
|
* @returns 一个解析为管道对象的 promise。
|
||||||
ref: string;
|
*/
|
||||||
status: string;
|
|
||||||
web_url: string;
|
|
||||||
// 2024-03-06 02:53:59.509Z
|
|
||||||
created_at: string;
|
|
||||||
started_at: string;
|
|
||||||
finished_at: string;
|
|
||||||
duration: number;
|
|
||||||
queued_duration: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getOne = (id: string) =>
|
const getOne = (id: string) =>
|
||||||
managePb404<PipelineRecordModel>(
|
managePb404<DB.Pipeline>(
|
||||||
async () => await pbClient.collection("pipeline").getOne(id)
|
async () => await pbClient.collection("pipeline").getOne(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取项目最新一次构建
|
* 检索项目的最新管道。
|
||||||
* @param project_id 项目id
|
* @param project_id 项目的 ID。
|
||||||
|
* @returns 一个解析为最新管道对象的 promise。
|
||||||
*/
|
*/
|
||||||
const getLatestOne = (project_id: string) => {
|
const getLatestOne = (project_id: string) => {
|
||||||
return managePb404<PipelineRecordModel>(
|
return managePb404<DB.Pipeline>(
|
||||||
async () =>
|
async () =>
|
||||||
await pbClient
|
await pbClient
|
||||||
.collection("pipeline")
|
.collection("pipeline")
|
||||||
@ -37,12 +28,18 @@ const getLatestOne = (project_id: string) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: Partial<PipelineRecordModel>) =>
|
/**
|
||||||
await pbClient.collection("pipeline").create<PipelineRecordModel>(data);
|
* 创建一个新的管道。
|
||||||
|
* @param data 新管道的数据。
|
||||||
|
* @returns 一个解析为创建的管道对象的 promise。
|
||||||
|
*/
|
||||||
|
const create = async (data: Partial<DB.Pipeline>) =>
|
||||||
|
await pbClient.collection("pipeline").create<DB.Pipeline>(data);
|
||||||
|
|
||||||
const pipeline = {
|
const pipeline = {
|
||||||
create,
|
create,
|
||||||
getOne,
|
getOne,
|
||||||
getLatestOne,
|
getLatestOne,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default pipeline;
|
export default pipeline;
|
||||||
|
@ -1,28 +1,36 @@
|
|||||||
import { RecordModel } from "pocketbase";
|
|
||||||
import { managePb404 } from "../../utils/pbTools";
|
import { managePb404 } from "../../utils/pbTools";
|
||||||
import pbClient from "../pbClient";
|
import pbClient from "../pbClient";
|
||||||
|
import { DB } from "../../types/db";
|
||||||
|
|
||||||
export interface ProjectRecordModel extends RecordModel {
|
/**
|
||||||
project_id: number;
|
* 通过其 ID 检索单个项目。
|
||||||
description: string;
|
* @param id - 项目的 ID。
|
||||||
name: string;
|
* @returns 一个解析为项目对象的 promise。
|
||||||
path_with_namespace: string;
|
*/
|
||||||
web_url: string;
|
|
||||||
avatar_url: string;
|
|
||||||
has_new_cicd: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getOne = (id: string) =>
|
const getOne = (id: string) =>
|
||||||
managePb404<ProjectRecordModel>(
|
managePb404<DB.Project>(
|
||||||
async () => await pbClient.collection("project").getOne(id)
|
async () => await pbClient.collection("project").getOne(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检索项目的完整列表。
|
||||||
|
* @returns 一个解析为项目对象数组的 promise。
|
||||||
|
*/
|
||||||
const getFullList = async () =>
|
const getFullList = async () =>
|
||||||
await pbClient.collection("project").getFullList<ProjectRecordModel>();
|
await pbClient.collection("project").getFullList<DB.Project>();
|
||||||
|
|
||||||
const update = async (id: string, data: Partial<ProjectRecordModel>) =>
|
/**
|
||||||
await pbClient.collection("project").update<ProjectRecordModel>(id, data);
|
* 使用新数据更新项目。
|
||||||
|
* @param id - 要更新的项目的 ID。
|
||||||
|
* @param data - 用于更新项目的部分数据。
|
||||||
|
* @returns 一个解析为更新后的项目对象的 promise。
|
||||||
|
*/
|
||||||
|
const update = async (id: string, data: Partial<DB.Project>) =>
|
||||||
|
await pbClient.collection("project").update<DB.Project>(id, data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于管理项目的函数集合。
|
||||||
|
*/
|
||||||
const project = {
|
const project = {
|
||||||
getFullList,
|
getFullList,
|
||||||
getOne,
|
getOne,
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
import { RecordModel } from "pocketbase";
|
|
||||||
import { managePb404 } from "../../utils/pbTools";
|
import { managePb404 } from "../../utils/pbTools";
|
||||||
import pbClient from "../pbClient";
|
import pbClient from "../pbClient";
|
||||||
|
import { DB } from "../../types/db";
|
||||||
|
|
||||||
export interface UserRecordModel extends RecordModel {
|
/**
|
||||||
user_id: number;
|
* 根据提供的ID从数据库检索单个用户。
|
||||||
username: string;
|
* @param id 要检索的用户的ID。
|
||||||
name: string;
|
* @returns 如果找到,返回解析为用户对象的promise;如果未找到,抛出404错误。
|
||||||
avatar_url: string;
|
*/
|
||||||
web_url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getOne = (id: string) =>
|
const getOne = (id: string) =>
|
||||||
managePb404<UserRecordModel>(
|
managePb404<DB.User>(
|
||||||
async () => await pbClient.collection("user").getOne(id)
|
async () => await pbClient.collection("user").getOne(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据提供的用户ID从数据库检索单个用户。
|
||||||
|
* @param user_id 要检索的用户的用户ID。
|
||||||
|
* @returns 如果找到,返回解析为用户对象的promise;如果未找到,抛出404错误。
|
||||||
|
*/
|
||||||
const getOneByUserId = (user_id: number) => {
|
const getOneByUserId = (user_id: number) => {
|
||||||
return managePb404<UserRecordModel>(
|
return managePb404<DB.User>(
|
||||||
async () =>
|
async () =>
|
||||||
await pbClient
|
await pbClient
|
||||||
.collection("user")
|
.collection("user")
|
||||||
@ -26,16 +28,32 @@ const getOneByUserId = (user_id: number) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const create = async (data: Partial<UserRecordModel>) =>
|
/**
|
||||||
await pbClient.collection("user").create<UserRecordModel>(data);
|
* 在数据库中创建一个新用户。
|
||||||
|
* @param data 新用户的数据。
|
||||||
|
* @returns 返回解析为已创建用户对象的promise。
|
||||||
|
*/
|
||||||
|
const create = async (data: Partial<DB.User>) =>
|
||||||
|
await pbClient.collection("user").create<DB.User>(data);
|
||||||
|
|
||||||
const upsert = async (data: Partial<UserRecordModel>) => {
|
/**
|
||||||
|
* 在数据库中插入或更新一个用户。
|
||||||
|
* 如果具有相同用户ID的用户已存在,则更新现有用户。
|
||||||
|
* 如果具有相同用户ID的用户不存在,则创建一个新用户。
|
||||||
|
* @param data 要插入或更新的用户数据。
|
||||||
|
* @returns 返回解析为插入或更新的用户对象的promise。
|
||||||
|
* 如果数据不包含用户ID,则返回null。
|
||||||
|
*/
|
||||||
|
const upsert = async (data: Partial<DB.User>) => {
|
||||||
if (!data.user_id) return null;
|
if (!data.user_id) return null;
|
||||||
const userInfo = await getOneByUserId(data.user_id);
|
const userInfo = await getOneByUserId(data.user_id);
|
||||||
if (userInfo) return userInfo;
|
if (userInfo) return userInfo;
|
||||||
return await create(data);
|
return await create(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户模块提供了与数据库中的用户集合交互的函数。
|
||||||
|
*/
|
||||||
const user = {
|
const user = {
|
||||||
create,
|
create,
|
||||||
upsert,
|
upsert,
|
||||||
|
@ -1,25 +1,14 @@
|
|||||||
import { RecordModel } from "pocketbase";
|
|
||||||
import { managePb404 } from "../../utils/pbTools";
|
import { managePb404 } from "../../utils/pbTools";
|
||||||
import pbClient from "../pbClient";
|
import pbClient from "../pbClient";
|
||||||
|
import { DB } from "../../types/db";
|
||||||
|
|
||||||
export interface StatisticsPerWeekRecordModel extends RecordModel {
|
/**
|
||||||
week: string;
|
* 根据给定的周来检索完整的统计信息。
|
||||||
total_count: number;
|
* @param week - 需要检索统计信息的周。
|
||||||
failed_count: number;
|
* @returns 一个解析为指定周的完整统计信息的promise。
|
||||||
success_count: number;
|
*/
|
||||||
success_rate: number;
|
|
||||||
duration: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StatisticsPerProjRecordModel extends RecordModel {
|
|
||||||
week: string;
|
|
||||||
name: string;
|
|
||||||
duration: number;
|
|
||||||
ref: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFullStatisticsByWeek = (week: string) => {
|
const getFullStatisticsByWeek = (week: string) => {
|
||||||
return managePb404<StatisticsPerWeekRecordModel>(
|
return managePb404<DB.StatisticsPerWeek>(
|
||||||
async () =>
|
async () =>
|
||||||
await pbClient
|
await pbClient
|
||||||
.collection("statisticsPerWeek")
|
.collection("statisticsPerWeek")
|
||||||
@ -27,8 +16,13 @@ const getFullStatisticsByWeek = (week: string) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据给定的周来检索项目统计信息。
|
||||||
|
* @param week - 需要检索统计信息的周。
|
||||||
|
* @returns 一个解析为指定周的项目统计信息数组的promise。
|
||||||
|
*/
|
||||||
const getProjStatisticsByWeek = (week: string) => {
|
const getProjStatisticsByWeek = (week: string) => {
|
||||||
return managePb404<StatisticsPerProjRecordModel[]>(
|
return managePb404<DB.StatisticsPerProj[]>(
|
||||||
async () =>
|
async () =>
|
||||||
await pbClient
|
await pbClient
|
||||||
.collection("statisticsPerProj")
|
.collection("statisticsPerProj")
|
||||||
|
25
index.ts
25
index.ts
@ -1,4 +1,4 @@
|
|||||||
import manageRobot from "./controllers/manageRobot";
|
import { manageCIMonitorReq } from "./routes/ci";
|
||||||
import initSchedule from "./schedule";
|
import initSchedule from "./schedule";
|
||||||
import netTool from "./service/netTool";
|
import netTool from "./service/netTool";
|
||||||
|
|
||||||
@ -7,18 +7,19 @@ initSchedule();
|
|||||||
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
async fetch(req) {
|
async fetch(req) {
|
||||||
const url = new URL(req.url);
|
try {
|
||||||
if (url.pathname === "/ci") {
|
const url = new URL(req.url);
|
||||||
const chat_id = url.searchParams.get("chat_id");
|
// 根路由
|
||||||
if (!chat_id) return netTool.badRequest("chat_id is required!");
|
if (url.pathname === "/") return netTool.ok("hello, glade to see you!");
|
||||||
manageRobot.sendCIReportByChatId(chat_id);
|
// CI 监控
|
||||||
return Response.json({
|
if (url.pathname === "/ci") return manageCIMonitorReq(req);
|
||||||
code: 0,
|
// 其他
|
||||||
msg: "success",
|
return netTool.ok("hello, glade to see you!");
|
||||||
data: "reporting...",
|
} catch (error: any) {
|
||||||
});
|
// 错误处理
|
||||||
|
console.error("🚀 ~ serve ~ error", error);
|
||||||
|
return netTool.serverError(error.message || "server error");
|
||||||
}
|
}
|
||||||
return netTool.ok();
|
|
||||||
},
|
},
|
||||||
port: 3000,
|
port: 3000,
|
||||||
});
|
});
|
||||||
|
22
routes/ci/index.ts
Normal file
22
routes/ci/index.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import manageRobot from "../../controllers/manageRobot";
|
||||||
|
import netTool from "../../service/netTool";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理管理CI监视器的请求。
|
||||||
|
* @param req - 请求对象。
|
||||||
|
* @returns 响应对象。
|
||||||
|
*/
|
||||||
|
export const manageCIMonitorReq = (req: Request) => {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
if (url.pathname === "/ci") {
|
||||||
|
const chat_id = url.searchParams.get("chat_id");
|
||||||
|
if (!chat_id) return netTool.badRequest("chat_id is required!");
|
||||||
|
manageRobot.sendCIReportByChatId(chat_id);
|
||||||
|
return Response.json({
|
||||||
|
code: 0,
|
||||||
|
msg: "success",
|
||||||
|
data: "reporting...",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return netTool.ok();
|
||||||
|
};
|
@ -1,4 +1,6 @@
|
|||||||
import service from "../service";
|
import service from "../../service";
|
||||||
|
import { BadgeSetParams } from "../../service/gitlab/badge";
|
||||||
|
import { Gitlab } from "../../types/gitlab";
|
||||||
|
|
||||||
const projectList = [
|
const projectList = [
|
||||||
// "cloud-ml/cloudml-maas",
|
// "cloud-ml/cloudml-maas",
|
||||||
@ -23,14 +25,14 @@ const projectList = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const getNewProjectBadge = async (
|
const getNewProjectBadge = async (
|
||||||
projectDetail: GitlabProjDetail
|
projectDetail: Gitlab.ProjDetail
|
||||||
): Promise<GitlabBadgeSetParams[]> => {
|
): Promise<BadgeSetParams[]> => {
|
||||||
// 项目路径 cloud-ml/cloudml-dev
|
// 项目路径 cloud-ml/cloudml-dev
|
||||||
const projectPath = projectDetail.path_with_namespace;
|
const projectPath = projectDetail.path_with_namespace;
|
||||||
// 根据项目路径获取sonarqubeId 类似于 cloud-ml/cloudml-dev -> cloud-ml:cloudml-dev
|
// 根据项目路径获取sonarqubeId 类似于 cloud-ml/cloudml-dev -> cloud-ml:cloudml-dev
|
||||||
const sonarqubeId = projectPath.replace("/", ":");
|
const sonarqubeId = projectPath.replace("/", ":");
|
||||||
// 获取项目的badges
|
// 获取项目的badges
|
||||||
const badges: GitlabBadge[] = await service.gitlab.fetchProjectBadges(
|
const badges: Gitlab.Badge[] = await service.gitlab.badge.get(
|
||||||
projectDetail.id
|
projectDetail.id
|
||||||
);
|
);
|
||||||
// 对badges进行补全,可能只有 name: "sonarqube_coverage" 的情况,把剩下的补全
|
// 对badges进行补全,可能只有 name: "sonarqube_coverage" 的情况,把剩下的补全
|
||||||
@ -44,7 +46,7 @@ const getNewProjectBadge = async (
|
|||||||
"sonarqube_quality_gate",
|
"sonarqube_quality_gate",
|
||||||
];
|
];
|
||||||
const diff = [...badgeNameList].filter((x) => !badgeNameSet.has(x));
|
const diff = [...badgeNameList].filter((x) => !badgeNameSet.has(x));
|
||||||
const newBadges: GitlabBadgeSetParams[] = diff.map((name) => {
|
const newBadges: BadgeSetParams[] = diff.map((name) => {
|
||||||
const link_url = encodeURI(
|
const link_url = encodeURI(
|
||||||
`https://sonarqube.mioffice.cn/dashboard?id=${sonarqubeId}`
|
`https://sonarqube.mioffice.cn/dashboard?id=${sonarqubeId}`
|
||||||
);
|
);
|
||||||
@ -65,16 +67,12 @@ const getNewProjectBadge = async (
|
|||||||
return newBadges;
|
return newBadges;
|
||||||
};
|
};
|
||||||
|
|
||||||
const addNewProjectBadge = async (
|
const addNewProjectBadge = async (badgeSetParamsList: BadgeSetParams[]) => {
|
||||||
badgeSetParamsList: GitlabBadgeSetParams[]
|
|
||||||
) => {
|
|
||||||
const chunkSize = 5; // 每次并发请求的数量
|
const chunkSize = 5; // 每次并发请求的数量
|
||||||
for (let i = 0; i < badgeSetParamsList.length; i += chunkSize) {
|
for (let i = 0; i < badgeSetParamsList.length; i += chunkSize) {
|
||||||
const chunk = badgeSetParamsList.slice(i, i + chunkSize);
|
const chunk = badgeSetParamsList.slice(i, i + chunkSize);
|
||||||
const res = await Promise.all(
|
const res = await Promise.all(
|
||||||
chunk.map((badgeSetParams) =>
|
chunk.map((badgeSetParams) => service.gitlab.badge.add(badgeSetParams))
|
||||||
service.gitlab.addProjectBadge(badgeSetParams)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
console.log(res);
|
console.log(res);
|
||||||
}
|
}
|
||||||
@ -84,9 +82,7 @@ const main = async () => {
|
|||||||
const errorList: string[] = [];
|
const errorList: string[] = [];
|
||||||
const badgeAddParamsList = await Promise.all(
|
const badgeAddParamsList = await Promise.all(
|
||||||
projectList.map(async (projectName) => {
|
projectList.map(async (projectName) => {
|
||||||
const projectDetail = await service.gitlab.fetchProjectDetails(
|
const projectDetail = await service.gitlab.project.getDetail(projectName);
|
||||||
projectName
|
|
||||||
);
|
|
||||||
if (!projectDetail) {
|
if (!projectDetail) {
|
||||||
errorList.push(projectName);
|
errorList.push(projectName);
|
||||||
return [];
|
return [];
|
@ -1,4 +1,6 @@
|
|||||||
import service from "../service";
|
import service from "../../service";
|
||||||
|
import { BadgeSetParams } from "../../service/gitlab/badge";
|
||||||
|
import { Gitlab } from "../../types/gitlab";
|
||||||
|
|
||||||
const projectList = [
|
const projectList = [
|
||||||
"miai-fe/fe/ai-admin-fe",
|
"miai-fe/fe/ai-admin-fe",
|
||||||
@ -54,16 +56,14 @@ const projectList = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const getProjectId = async (projectName: string) => {
|
const getProjectId = async (projectName: string) => {
|
||||||
const res = await service.gitlab.fetchProjectDetails(projectName);
|
const res = await service.gitlab.project.getDetail(projectName);
|
||||||
return res?.id;
|
return res?.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNewProjectBadge = async (
|
const getNewProjectBadge = async (
|
||||||
projectId: number
|
projectId: number
|
||||||
): Promise<GitlabBadgeSetParams[]> => {
|
): Promise<BadgeSetParams[]> => {
|
||||||
const badges: GitlabBadge[] = await service.gitlab.fetchProjectBadges(
|
const badges: Gitlab.Badge[] = await service.gitlab.badge.get(projectId);
|
||||||
projectId
|
|
||||||
);
|
|
||||||
|
|
||||||
const replacePath = (value: string) =>
|
const replacePath = (value: string) =>
|
||||||
value.replace(
|
value.replace(
|
||||||
@ -71,7 +71,7 @@ const getNewProjectBadge = async (
|
|||||||
"http://scan.sonarqube.xiaomi.srv"
|
"http://scan.sonarqube.xiaomi.srv"
|
||||||
);
|
);
|
||||||
|
|
||||||
return badges.map((badge: GitlabBadge) => ({
|
return badges.map((badge: Gitlab.Badge) => ({
|
||||||
id: projectId,
|
id: projectId,
|
||||||
badge_id: badge.id,
|
badge_id: badge.id,
|
||||||
link_url: replacePath(badge.link_url),
|
link_url: replacePath(badge.link_url),
|
||||||
@ -81,16 +81,12 @@ const getNewProjectBadge = async (
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const setNewProjectBadge = async (
|
const setNewProjectBadge = async (badgeSetParamsList: BadgeSetParams[]) => {
|
||||||
badgeSetParamsList: GitlabBadgeSetParams[]
|
|
||||||
) => {
|
|
||||||
const chunkSize = 5; // 每次并发请求的数量
|
const chunkSize = 5; // 每次并发请求的数量
|
||||||
for (let i = 0; i < badgeSetParamsList.length; i += chunkSize) {
|
for (let i = 0; i < badgeSetParamsList.length; i += chunkSize) {
|
||||||
const chunk = badgeSetParamsList.slice(i, i + chunkSize);
|
const chunk = badgeSetParamsList.slice(i, i + chunkSize);
|
||||||
const res = await Promise.all(
|
const res = await Promise.all(
|
||||||
chunk.map((badgeSetParams) =>
|
chunk.map((badgeSetParams) => service.gitlab.badge.set(badgeSetParams))
|
||||||
service.gitlab.setProjectBadge(badgeSetParams)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
console.log(res);
|
console.log(res);
|
||||||
}
|
}
|
83
script/pipline/pending.json
Normal file
83
script/pipline/pending.json
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"object_kind": "pipeline",
|
||||||
|
"object_attributes": {
|
||||||
|
"id": 8779191,
|
||||||
|
"ref": "main",
|
||||||
|
"tag": false,
|
||||||
|
"sha": "66ac20f0f6a839f29753b17dcdf12deb91148fed",
|
||||||
|
"before_sha": "39a21d2cb3a0d39d2fb0df81a244ebeae8989ce4",
|
||||||
|
"source": "push",
|
||||||
|
"status": "pending",
|
||||||
|
"detailed_status": "pending",
|
||||||
|
"stages": [
|
||||||
|
"print"
|
||||||
|
],
|
||||||
|
"created_at": "2024-07-23 09:28:44 +0800",
|
||||||
|
"finished_at": null,
|
||||||
|
"duration": null,
|
||||||
|
"queued_duration": null,
|
||||||
|
"variables": []
|
||||||
|
},
|
||||||
|
"merge_request": null,
|
||||||
|
"user": {
|
||||||
|
"id": 30441,
|
||||||
|
"name": "伍嘉丽",
|
||||||
|
"username": "wujiali5",
|
||||||
|
"avatar_url": "https://git.n.xiaomi.com/uploads/-/system/user/avatar/30441/avatar.png",
|
||||||
|
"email": "[REDACTED]"
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"id": 145623,
|
||||||
|
"name": "test",
|
||||||
|
"description": "",
|
||||||
|
"web_url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test",
|
||||||
|
"avatar_url": null,
|
||||||
|
"git_ssh_url": "git@git.n.xiaomi.com:cloudml-visuals/fe/test.git",
|
||||||
|
"git_http_url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test.git",
|
||||||
|
"namespace": "fe",
|
||||||
|
"visibility_level": 0,
|
||||||
|
"path_with_namespace": "cloudml-visuals/fe/test",
|
||||||
|
"default_branch": "main",
|
||||||
|
"ci_config_path": ""
|
||||||
|
},
|
||||||
|
"commit": {
|
||||||
|
"id": "66ac20f0f6a839f29753b17dcdf12deb91148fed",
|
||||||
|
"message": "Merge branch 'ci' into 'main'\n\nchore: Update .gitlab-ci.yml to print \"Hello, world~\"\n\nSee merge request cloudml-visuals/fe/test!2",
|
||||||
|
"title": "Merge branch 'ci' into 'main'",
|
||||||
|
"timestamp": "2024-07-23T09:28:43+08:00",
|
||||||
|
"url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test/-/commit/66ac20f0f6a839f29753b17dcdf12deb91148fed",
|
||||||
|
"author": {
|
||||||
|
"name": "伍嘉丽",
|
||||||
|
"email": "wujiali5@xiaomi.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"builds": [
|
||||||
|
{
|
||||||
|
"id": 25375438,
|
||||||
|
"stage": "print",
|
||||||
|
"name": "print",
|
||||||
|
"status": "pending",
|
||||||
|
"created_at": "2024-07-23 09:28:44 +0800",
|
||||||
|
"started_at": null,
|
||||||
|
"finished_at": null,
|
||||||
|
"duration": null,
|
||||||
|
"queued_duration": 3.506639987,
|
||||||
|
"when": "on_success",
|
||||||
|
"manual": false,
|
||||||
|
"allow_failure": false,
|
||||||
|
"user": {
|
||||||
|
"id": 30441,
|
||||||
|
"name": "伍嘉丽",
|
||||||
|
"username": "wujiali5",
|
||||||
|
"avatar_url": "https://git.n.xiaomi.com/uploads/-/system/user/avatar/30441/avatar.png",
|
||||||
|
"email": "[REDACTED]"
|
||||||
|
},
|
||||||
|
"runner": null,
|
||||||
|
"artifacts_file": {
|
||||||
|
"filename": null,
|
||||||
|
"size": null
|
||||||
|
},
|
||||||
|
"environment": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
92
script/pipline/running.json
Normal file
92
script/pipline/running.json
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"object_kind": "pipeline",
|
||||||
|
"object_attributes": {
|
||||||
|
"id": 8778929,
|
||||||
|
"ref": "ci",
|
||||||
|
"tag": false,
|
||||||
|
"sha": "fc0fdf57c3b662b296b89dc2a289798d130d1be1",
|
||||||
|
"before_sha": "0000000000000000000000000000000000000000",
|
||||||
|
"source": "push",
|
||||||
|
"status": "running",
|
||||||
|
"detailed_status": "running",
|
||||||
|
"stages": [
|
||||||
|
"print"
|
||||||
|
],
|
||||||
|
"created_at": "2024-07-23 08:57:14 +0800",
|
||||||
|
"finished_at": null,
|
||||||
|
"duration": null,
|
||||||
|
"queued_duration": 3,
|
||||||
|
"variables": []
|
||||||
|
},
|
||||||
|
"merge_request": null,
|
||||||
|
"user": {
|
||||||
|
"id": 10011,
|
||||||
|
"name": "赵英博",
|
||||||
|
"username": "zhaoyingbo",
|
||||||
|
"avatar_url": "https://git.n.xiaomi.com/uploads/-/system/user/avatar/10011/avatar.png",
|
||||||
|
"email": "zhaoyingbo@live.cn"
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"id": 145623,
|
||||||
|
"name": "test",
|
||||||
|
"description": "",
|
||||||
|
"web_url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test",
|
||||||
|
"avatar_url": null,
|
||||||
|
"git_ssh_url": "git@git.n.xiaomi.com:cloudml-visuals/fe/test.git",
|
||||||
|
"git_http_url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test.git",
|
||||||
|
"namespace": "fe",
|
||||||
|
"visibility_level": 0,
|
||||||
|
"path_with_namespace": "cloudml-visuals/fe/test",
|
||||||
|
"default_branch": "main",
|
||||||
|
"ci_config_path": ""
|
||||||
|
},
|
||||||
|
"commit": {
|
||||||
|
"id": "fc0fdf57c3b662b296b89dc2a289798d130d1be1",
|
||||||
|
"message": "chore: Add .gitlab-ci.yml for printing \"Hello, world!\"\n",
|
||||||
|
"title": "chore: Add .gitlab-ci.yml for printing \"Hello, world!\"",
|
||||||
|
"timestamp": "2024-07-23T08:57:13+08:00",
|
||||||
|
"url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test/-/commit/fc0fdf57c3b662b296b89dc2a289798d130d1be1",
|
||||||
|
"author": {
|
||||||
|
"name": "zhaoyingbo",
|
||||||
|
"email": "zhaoyingbo@xiaomi.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"builds": [
|
||||||
|
{
|
||||||
|
"id": 25374785,
|
||||||
|
"stage": "print",
|
||||||
|
"name": "print",
|
||||||
|
"status": "running",
|
||||||
|
"created_at": "2024-07-23 08:57:14 +0800",
|
||||||
|
"started_at": "2024-07-23 08:57:18 +0800",
|
||||||
|
"finished_at": null,
|
||||||
|
"duration": 2.977211601,
|
||||||
|
"queued_duration": 3.550911,
|
||||||
|
"when": "on_success",
|
||||||
|
"manual": false,
|
||||||
|
"allow_failure": false,
|
||||||
|
"user": {
|
||||||
|
"id": 10011,
|
||||||
|
"name": "赵英博",
|
||||||
|
"username": "zhaoyingbo",
|
||||||
|
"avatar_url": "https://git.n.xiaomi.com/uploads/-/system/user/avatar/10011/avatar.png",
|
||||||
|
"email": "zhaoyingbo@live.cn"
|
||||||
|
},
|
||||||
|
"runner": {
|
||||||
|
"id": 9134,
|
||||||
|
"description": "cloudml-fe-bj",
|
||||||
|
"runner_type": "group_type",
|
||||||
|
"active": true,
|
||||||
|
"is_shared": false,
|
||||||
|
"tags": [
|
||||||
|
"cloudml-fe-bj"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"artifacts_file": {
|
||||||
|
"filename": null,
|
||||||
|
"size": null
|
||||||
|
},
|
||||||
|
"environment": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
92
script/pipline/success.json
Normal file
92
script/pipline/success.json
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"object_kind": "pipeline",
|
||||||
|
"object_attributes": {
|
||||||
|
"id": 8778929,
|
||||||
|
"ref": "ci",
|
||||||
|
"tag": false,
|
||||||
|
"sha": "fc0fdf57c3b662b296b89dc2a289798d130d1be1",
|
||||||
|
"before_sha": "0000000000000000000000000000000000000000",
|
||||||
|
"source": "push",
|
||||||
|
"status": "success",
|
||||||
|
"detailed_status": "passed",
|
||||||
|
"stages": [
|
||||||
|
"print"
|
||||||
|
],
|
||||||
|
"created_at": "2024-07-23 08:57:14 +0800",
|
||||||
|
"finished_at": "2024-07-23 08:57:24 +0800",
|
||||||
|
"duration": 5,
|
||||||
|
"queued_duration": 3,
|
||||||
|
"variables": []
|
||||||
|
},
|
||||||
|
"merge_request": null,
|
||||||
|
"user": {
|
||||||
|
"id": 10011,
|
||||||
|
"name": "赵英博",
|
||||||
|
"username": "zhaoyingbo",
|
||||||
|
"avatar_url": "https://git.n.xiaomi.com/uploads/-/system/user/avatar/10011/avatar.png",
|
||||||
|
"email": "zhaoyingbo@live.cn"
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"id": 145623,
|
||||||
|
"name": "test",
|
||||||
|
"description": "",
|
||||||
|
"web_url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test",
|
||||||
|
"avatar_url": null,
|
||||||
|
"git_ssh_url": "git@git.n.xiaomi.com:cloudml-visuals/fe/test.git",
|
||||||
|
"git_http_url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test.git",
|
||||||
|
"namespace": "fe",
|
||||||
|
"visibility_level": 0,
|
||||||
|
"path_with_namespace": "cloudml-visuals/fe/test",
|
||||||
|
"default_branch": "main",
|
||||||
|
"ci_config_path": ""
|
||||||
|
},
|
||||||
|
"commit": {
|
||||||
|
"id": "fc0fdf57c3b662b296b89dc2a289798d130d1be1",
|
||||||
|
"message": "chore: Add .gitlab-ci.yml for printing \"Hello, world!\"\n",
|
||||||
|
"title": "chore: Add .gitlab-ci.yml for printing \"Hello, world!\"",
|
||||||
|
"timestamp": "2024-07-23T08:57:13+08:00",
|
||||||
|
"url": "https://git.n.xiaomi.com/cloudml-visuals/fe/test/-/commit/fc0fdf57c3b662b296b89dc2a289798d130d1be1",
|
||||||
|
"author": {
|
||||||
|
"name": "zhaoyingbo",
|
||||||
|
"email": "zhaoyingbo@xiaomi.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"builds": [
|
||||||
|
{
|
||||||
|
"id": 25374785,
|
||||||
|
"stage": "print",
|
||||||
|
"name": "print",
|
||||||
|
"status": "success",
|
||||||
|
"created_at": "2024-07-23 08:57:14 +0800",
|
||||||
|
"started_at": "2024-07-23 08:57:18 +0800",
|
||||||
|
"finished_at": "2024-07-23 08:57:24 +0800",
|
||||||
|
"duration": 5.914004,
|
||||||
|
"queued_duration": 3.550911,
|
||||||
|
"when": "on_success",
|
||||||
|
"manual": false,
|
||||||
|
"allow_failure": false,
|
||||||
|
"user": {
|
||||||
|
"id": 10011,
|
||||||
|
"name": "赵英博",
|
||||||
|
"username": "zhaoyingbo",
|
||||||
|
"avatar_url": "https://git.n.xiaomi.com/uploads/-/system/user/avatar/10011/avatar.png",
|
||||||
|
"email": "zhaoyingbo@live.cn"
|
||||||
|
},
|
||||||
|
"runner": {
|
||||||
|
"id": 9134,
|
||||||
|
"description": "cloudml-fe-bj",
|
||||||
|
"runner_type": "group_type",
|
||||||
|
"active": true,
|
||||||
|
"is_shared": false,
|
||||||
|
"tags": [
|
||||||
|
"cloudml-fe-bj"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"artifacts_file": {
|
||||||
|
"filename": null,
|
||||||
|
"size": null
|
||||||
|
},
|
||||||
|
"environment": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,116 +0,0 @@
|
|||||||
import netTool from "./netTool";
|
|
||||||
|
|
||||||
const AUTH_HEADER = { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" };
|
|
||||||
|
|
||||||
const BASE_URL = "https://git.n.xiaomi.com/api/v4";
|
|
||||||
|
|
||||||
const gitlabReqWarp = async <T = any>(
|
|
||||||
func: Function,
|
|
||||||
default_value: any
|
|
||||||
): Promise<T> => {
|
|
||||||
try {
|
|
||||||
let response = {} as T & GitlabError;
|
|
||||||
response = (await func()) as T & GitlabError;
|
|
||||||
if (response.message === "404 Project Not Found") return default_value;
|
|
||||||
return response;
|
|
||||||
} catch {
|
|
||||||
return default_value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取项目详情
|
|
||||||
* @param id
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const fetchProjectDetails = async (id: number | string) => {
|
|
||||||
if (typeof id === "string") id = encodeURIComponent(id);
|
|
||||||
const URL = `${BASE_URL}/projects/${id}`;
|
|
||||||
return gitlabReqWarp<GitlabProjDetail>(
|
|
||||||
() => netTool.get(URL, {}, AUTH_HEADER),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取流水线列表
|
|
||||||
* @param project_id
|
|
||||||
* @param page
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const fetchPipelines = async (project_id: number, page = 1) => {
|
|
||||||
const URL = `${BASE_URL}/projects/${project_id}/pipelines`;
|
|
||||||
const params = { scope: "finished", per_page: 100, page };
|
|
||||||
return gitlabReqWarp<GitlabPipeline[]>(
|
|
||||||
() => netTool.get(URL, params, AUTH_HEADER),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取流水线详情
|
|
||||||
* @param project_id
|
|
||||||
* @param pipeline_id
|
|
||||||
* @param created_at
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const fetchPipelineDetails = async (
|
|
||||||
project_id: number,
|
|
||||||
pipeline_id: number,
|
|
||||||
created_at: string
|
|
||||||
) => {
|
|
||||||
const URL = `${BASE_URL}/projects/${project_id}/pipelines/${pipeline_id}`;
|
|
||||||
const res = await gitlabReqWarp<GitlabPipelineDetail>(
|
|
||||||
() => netTool.get(URL, {}, AUTH_HEADER),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
if (res === null) return null;
|
|
||||||
return { ...res, created_at };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取项目的所有徽章
|
|
||||||
* @param project_id
|
|
||||||
*/
|
|
||||||
const fetchProjectBadges = async (project_id: number) => {
|
|
||||||
const URL = `${BASE_URL}/projects/${project_id}/badges`;
|
|
||||||
return gitlabReqWarp<GitlabBadge[]>(
|
|
||||||
() => netTool.get(URL, {}, AUTH_HEADER),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置徽章
|
|
||||||
* @param badge
|
|
||||||
*/
|
|
||||||
const setProjectBadge = async (badge: GitlabBadgeSetParams) => {
|
|
||||||
const URL = `${BASE_URL}/projects/${badge.id}/badges/${badge.badge_id}`;
|
|
||||||
return gitlabReqWarp<GitlabBadge>(
|
|
||||||
() => netTool.put(URL, badge, AUTH_HEADER),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加徽章
|
|
||||||
* @param badge
|
|
||||||
*/
|
|
||||||
const addProjectBadge = async (badge: GitlabBadgeSetParams) => {
|
|
||||||
const URL = `${BASE_URL}/projects/${badge.id}/badges`;
|
|
||||||
return gitlabReqWarp<GitlabBadge>(
|
|
||||||
() => netTool.post(URL, badge, {}, AUTH_HEADER),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const gitlab = {
|
|
||||||
fetchPipelines,
|
|
||||||
setProjectBadge,
|
|
||||||
addProjectBadge,
|
|
||||||
fetchProjectBadges,
|
|
||||||
fetchProjectDetails,
|
|
||||||
fetchPipelineDetails,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default gitlab;
|
|
60
service/gitlab/badge.ts
Normal file
60
service/gitlab/badge.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { Gitlab } from "../../types/gitlab";
|
||||||
|
import netTool from "../netTool";
|
||||||
|
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代表设置 GitLab 徽章的参数。
|
||||||
|
*/
|
||||||
|
export type BadgeSetParams = Omit<Gitlab.Badge, "kind" | "name"> & {
|
||||||
|
badge_id?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为特定项目检索 GitLab 徽章。
|
||||||
|
* @param project_id 项目的 ID。
|
||||||
|
* @returns 一个承诺,解析为 GitLab 徽章的数组。
|
||||||
|
*/
|
||||||
|
const get = async (project_id: number) => {
|
||||||
|
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/badges`;
|
||||||
|
return gitlabReqWarp<Gitlab.Badge[]>(
|
||||||
|
() => netTool.get(URL, {}, GITLAB_AUTH_HEADER),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 GitLab 徽章。
|
||||||
|
* @param badge 徽章参数。
|
||||||
|
* @returns 一个承诺,解析为更新后的徽章。
|
||||||
|
*/
|
||||||
|
const set = async (badge: BadgeSetParams) => {
|
||||||
|
const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges/${badge.badge_id}`;
|
||||||
|
return gitlabReqWarp<Gitlab.Badge>(
|
||||||
|
() => netTool.put(URL, badge, {}, GITLAB_AUTH_HEADER),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加 GitLab 徽章。
|
||||||
|
* @param badge 徽章参数。
|
||||||
|
* @returns 一个承诺,解析为添加的徽章。
|
||||||
|
*/
|
||||||
|
const add = async (badge: BadgeSetParams) => {
|
||||||
|
const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges`;
|
||||||
|
return gitlabReqWarp<Gitlab.Badge>(
|
||||||
|
() => netTool.post(URL, badge, {}, GITLAB_AUTH_HEADER),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代表一系列与徽章相关的函数。
|
||||||
|
*/
|
||||||
|
const badge = {
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
add,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default badge;
|
22
service/gitlab/commit.ts
Normal file
22
service/gitlab/commit.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import netTool from "../netTool";
|
||||||
|
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检索与特定提交关联的合并请求。
|
||||||
|
* @param project_id - 项目的ID。
|
||||||
|
* @param sha - 提交的SHA。
|
||||||
|
* @returns 一个解析为合并请求数组的promise。
|
||||||
|
*/
|
||||||
|
const getMr = async (project_id: number, sha: string) => {
|
||||||
|
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/repository/commits/${sha}/merge_requests`;
|
||||||
|
return gitlabReqWarp<any[]>(
|
||||||
|
() => netTool.get(URL, {}, GITLAB_AUTH_HEADER),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const commit = {
|
||||||
|
getMr,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default commit;
|
14
service/gitlab/index.ts
Normal file
14
service/gitlab/index.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
import badge from "./badge";
|
||||||
|
import commit from "./commit";
|
||||||
|
import pipeline from "./pipeline";
|
||||||
|
import project from "./project";
|
||||||
|
|
||||||
|
const gitlab = {
|
||||||
|
project,
|
||||||
|
badge,
|
||||||
|
commit,
|
||||||
|
pipeline,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default gitlab;
|
46
service/gitlab/pipeline.ts
Normal file
46
service/gitlab/pipeline.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Gitlab } from "../../types/gitlab";
|
||||||
|
import netTool from "../netTool";
|
||||||
|
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取特定GitLab流水线的详细信息。
|
||||||
|
* @param project_id - 项目的ID。
|
||||||
|
* @param pipeline_id - 流水线的ID。
|
||||||
|
* @param created_at - 流水线的创建日期。
|
||||||
|
* @returns 一个解析为流水线详细信息的promise。
|
||||||
|
*/
|
||||||
|
const getDetail = async (
|
||||||
|
project_id: number,
|
||||||
|
pipeline_id: number,
|
||||||
|
created_at: string
|
||||||
|
) => {
|
||||||
|
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines/${pipeline_id}`;
|
||||||
|
const res = await gitlabReqWarp<Gitlab.PipelineDetail>(
|
||||||
|
() => netTool.get(URL, {}, GITLAB_AUTH_HEADER),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
if (res === null) return null;
|
||||||
|
return { ...res, created_at };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取特定项目的GitLab流水线列表。
|
||||||
|
* @param project_id - 项目的ID。
|
||||||
|
* @param page - 结果的页码(默认值:1)。
|
||||||
|
* @returns 一个解析为流水线数组的promise。
|
||||||
|
*/
|
||||||
|
const getList = async (project_id: number, page = 1) => {
|
||||||
|
const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines`;
|
||||||
|
const params = { scope: "finished", per_page: 100, page };
|
||||||
|
return gitlabReqWarp<Gitlab.Pipeline[]>(
|
||||||
|
() => netTool.get(URL, params, GITLAB_AUTH_HEADER),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pipeline = {
|
||||||
|
getDetail,
|
||||||
|
getList,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default pipeline;
|
24
service/gitlab/project.ts
Normal file
24
service/gitlab/project.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Gitlab } from "../../types/gitlab";
|
||||||
|
import netTool from "../netTool";
|
||||||
|
import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 GitLab 项目的详细信息。
|
||||||
|
* @param project_id - 项目的 ID 或 URL-encoded 路径。
|
||||||
|
* @returns 一个解析为项目详细信息的 promise。
|
||||||
|
*/
|
||||||
|
const getDetail = async (project_id: number | string) => {
|
||||||
|
if (typeof project_id === "string")
|
||||||
|
project_id = encodeURIComponent(project_id);
|
||||||
|
const URL = `${GITLAB_BASE_URL}/projects/${project_id}`;
|
||||||
|
return gitlabReqWarp<Gitlab.ProjDetail>(
|
||||||
|
() => netTool.get(URL, {}, GITLAB_AUTH_HEADER),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const project = {
|
||||||
|
getDetail,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default project;
|
19
service/gitlab/tools.ts
Normal file
19
service/gitlab/tools.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Gitlab } from "../../types/gitlab";
|
||||||
|
|
||||||
|
export const gitlabReqWarp = async <T = any>(
|
||||||
|
func: Function,
|
||||||
|
default_value: any
|
||||||
|
): Promise<T> => {
|
||||||
|
try {
|
||||||
|
let response = {} as T & Gitlab.Error;
|
||||||
|
response = (await func()) as T & Gitlab.Error;
|
||||||
|
if (response.message === "404 Project Not Found") return default_value;
|
||||||
|
return response;
|
||||||
|
} catch {
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GITLAB_BASE_URL = "https://git.n.xiaomi.com/api/v4";
|
||||||
|
|
||||||
|
export const GITLAB_AUTH_HEADER = { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" };
|
@ -1,9 +1,9 @@
|
|||||||
import sendMessage from "./sendMessage";
|
|
||||||
import gitlab from "./gitlab";
|
import gitlab from "./gitlab";
|
||||||
|
import message from "./message";
|
||||||
|
|
||||||
const service = {
|
const service = {
|
||||||
gitlab,
|
gitlab,
|
||||||
sendMessage,
|
message,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default service;
|
export default service;
|
||||||
|
44
service/message/index.ts
Normal file
44
service/message/index.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import netTool from "../netTool";
|
||||||
|
|
||||||
|
const API_KEY = "1dfz4wlpbbgiky0";
|
||||||
|
const URL = "https://egg.imoaix.cn/message";
|
||||||
|
|
||||||
|
const message = async (body: any) => {
|
||||||
|
try {
|
||||||
|
const res = await netTool.post(URL, {
|
||||||
|
api_key: API_KEY,
|
||||||
|
...body,
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
message.byGroupId = async (group_id: string, content: string) => {
|
||||||
|
return message({
|
||||||
|
group_id,
|
||||||
|
msg_type: "interactive",
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
message.byChatId = async (chat_id: string, content: string) => {
|
||||||
|
return message({
|
||||||
|
receive_id: chat_id,
|
||||||
|
receive_id_type: "chat_id",
|
||||||
|
msg_type: "interactive",
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
message.byUserId = async (user_id: string, content: string) => {
|
||||||
|
return message({
|
||||||
|
receive_id: user_id,
|
||||||
|
receive_id_type: "user_id",
|
||||||
|
msg_type: "interactive",
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default message;
|
@ -1,35 +0,0 @@
|
|||||||
import netTool from "./netTool";
|
|
||||||
|
|
||||||
const API_KEY = "1dfz4wlpbbgiky0";
|
|
||||||
|
|
||||||
const sendMessage = async (body: any) => {
|
|
||||||
const URL = "https://egg.imoaix.cn/message";
|
|
||||||
try {
|
|
||||||
const res = await netTool.post(URL, {
|
|
||||||
api_key: API_KEY,
|
|
||||||
...body,
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
sendMessage.byGroupId = async (group_id: string, content: string) => {
|
|
||||||
return sendMessage({
|
|
||||||
group_id,
|
|
||||||
msg_type: "interactive",
|
|
||||||
content,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
sendMessage.byChatId = async (chat_id: string, content: string) => {
|
|
||||||
return sendMessage({
|
|
||||||
receive_id: chat_id,
|
|
||||||
receive_id_type: "chat_id",
|
|
||||||
msg_type: "interactive",
|
|
||||||
content,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default sendMessage;
|
|
71
service/typings.d.ts
vendored
71
service/typings.d.ts
vendored
@ -1,71 +0,0 @@
|
|||||||
interface GitlabProjDetail {
|
|
||||||
id: number;
|
|
||||||
description: string;
|
|
||||||
name: string;
|
|
||||||
path_with_namespace: string;
|
|
||||||
web_url: string;
|
|
||||||
avatar_url?: any;
|
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GitlabPipeline {
|
|
||||||
id: number;
|
|
||||||
project_id: number;
|
|
||||||
sha: string;
|
|
||||||
ref: string;
|
|
||||||
status: string;
|
|
||||||
source: string;
|
|
||||||
created_at: string;
|
|
||||||
updated_at: string;
|
|
||||||
web_url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GitlabError {
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GitlabUser {
|
|
||||||
id: number;
|
|
||||||
username: string;
|
|
||||||
name: string;
|
|
||||||
state: string;
|
|
||||||
avatar_url: string;
|
|
||||||
web_url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GitlabPipelineDetail {
|
|
||||||
id: number;
|
|
||||||
project_id: number;
|
|
||||||
ref: string;
|
|
||||||
status: string;
|
|
||||||
web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe/-/pipelines/7646046";
|
|
||||||
user: GitlabUser;
|
|
||||||
started_at: string;
|
|
||||||
finished_at: string;
|
|
||||||
duration: number;
|
|
||||||
queued_duration: number;
|
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GitlabPipelineDetailWithCreateAt extends GitlabPipelineDetail {
|
|
||||||
created_at: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GitlabBadge {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
link_url: string;
|
|
||||||
image_url: string;
|
|
||||||
rendered_link_url: string;
|
|
||||||
rendered_image_url: string;
|
|
||||||
kind: "project" | "group";
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GitlabBadgeSetParams {
|
|
||||||
id: number;
|
|
||||||
badge_id?: number;
|
|
||||||
link_url: string;
|
|
||||||
image_url: string;
|
|
||||||
rendered_link_url: string;
|
|
||||||
rendered_image_url: string;
|
|
||||||
}
|
|
52
types/db.ts
Normal file
52
types/db.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { RecordModel } from "pocketbase";
|
||||||
|
|
||||||
|
export namespace DB {
|
||||||
|
export interface Pipeline extends RecordModel {
|
||||||
|
project_id: string;
|
||||||
|
user_id: string;
|
||||||
|
pipeline_id: number;
|
||||||
|
ref: string;
|
||||||
|
status: string;
|
||||||
|
web_url: string;
|
||||||
|
// 2024-03-06 02:53:59.509Z
|
||||||
|
created_at: string;
|
||||||
|
started_at: string;
|
||||||
|
finished_at: string;
|
||||||
|
duration: number;
|
||||||
|
queued_duration: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Project extends RecordModel {
|
||||||
|
project_id: number;
|
||||||
|
description: string;
|
||||||
|
name: string;
|
||||||
|
path_with_namespace: string;
|
||||||
|
web_url: string;
|
||||||
|
avatar_url: string;
|
||||||
|
has_new_cicd: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface User extends RecordModel {
|
||||||
|
user_id: number;
|
||||||
|
username: string;
|
||||||
|
name: string;
|
||||||
|
avatar_url: string;
|
||||||
|
web_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatisticsPerWeek extends RecordModel {
|
||||||
|
week: string;
|
||||||
|
total_count: number;
|
||||||
|
failed_count: number;
|
||||||
|
success_count: number;
|
||||||
|
success_rate: number;
|
||||||
|
duration: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatisticsPerProj extends RecordModel {
|
||||||
|
week: string;
|
||||||
|
name: string;
|
||||||
|
duration: number;
|
||||||
|
ref: string;
|
||||||
|
}
|
||||||
|
}
|
59
types/gitlab.ts
Normal file
59
types/gitlab.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
export namespace Gitlab {
|
||||||
|
export interface Error {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
export interface User {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
name: string;
|
||||||
|
state: string;
|
||||||
|
avatar_url: string;
|
||||||
|
web_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProjDetail {
|
||||||
|
id: number;
|
||||||
|
description: string;
|
||||||
|
name: string;
|
||||||
|
path_with_namespace: string;
|
||||||
|
web_url: string;
|
||||||
|
avatar_url?: any;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PipelineDetail {
|
||||||
|
id: number;
|
||||||
|
project_id: number;
|
||||||
|
ref: string;
|
||||||
|
status: string;
|
||||||
|
web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe/-/pipelines/7646046";
|
||||||
|
user: User;
|
||||||
|
started_at: string;
|
||||||
|
finished_at: string;
|
||||||
|
duration: number;
|
||||||
|
queued_duration: number;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Pipeline {
|
||||||
|
id: number;
|
||||||
|
project_id: number;
|
||||||
|
sha: string;
|
||||||
|
ref: string;
|
||||||
|
status: string;
|
||||||
|
source: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
web_url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Badge {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
link_url: string;
|
||||||
|
image_url: string;
|
||||||
|
rendered_link_url: string;
|
||||||
|
rendered_image_url: string;
|
||||||
|
kind: "project" | "group";
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user