From 260aa8c350acc7308d644a8941b0f26847dac586 Mon Sep 17 00:00:00 2001 From: zhaoyingbo Date: Mon, 6 May 2024 03:28:58 +0000 Subject: [PATCH] init --- .devcontainer/devcontainer.json | 32 +++++ .gitattributes | 1 + .gitea/workflows/cicd.yaml.template | 57 +++++++++ .gitignore | 58 +++++++++ Dockerfile | 13 ++ bun.lockb | Bin 0 -> 55727 bytes controllers/template/index.ts | 7 ++ db/index.ts | 7 ++ db/pbClient.ts | 7 ++ db/user/index.ts | 46 +++++++ docker-compose.yml | 9 ++ eslint.config.js | 26 ++++ index.ts | 14 +++ package.json | 25 ++++ readme.md | 182 ++++++++++++++++++++++++++++ schedule/index.ts | 10 ++ service/index.ts | 28 +++++ tsconfig.json | 22 ++++ types/index.ts | 0 utils/pbTools.ts | 10 ++ 20 files changed, 554 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitattributes create mode 100644 .gitea/workflows/cicd.yaml.template create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100755 bun.lockb create mode 100644 controllers/template/index.ts create mode 100644 db/index.ts create mode 100644 db/pbClient.ts create mode 100644 db/user/index.ts create mode 100644 docker-compose.yml create mode 100644 eslint.config.js create mode 100644 index.ts create mode 100644 package.json create mode 100644 readme.md create mode 100644 schedule/index.ts create mode 100644 service/index.ts create mode 100644 tsconfig.json create mode 100644 types/index.ts create mode 100644 utils/pbTools.ts diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..f49d6f5 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,32 @@ +{ + "name": "replace_me", + "image": "micr.cloud.mioffice.cn/zhaoyingbo/dev:bun", + "remoteUser": "bun", + "containerUser": "bun", + "customizations": { + "vscode": { + "settings": { + "files.autoSave": "afterDelay", + "editor.guides.bracketPairs": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.stylelint": true + } + }, + "extensions": [ + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "eamodio.gitlens", + "unifiedjs.vscode-mdx", + "oderwat.indent-rainbow", + "jock.svg", + "ChakrounAnas.turbo-console-log", + "Gruntfuggly.todo-tree", + "MS-CEINTL.vscode-language-pack-zh-hans", + "stylelint.vscode-stylelint", + "GitHub.copilot" + ] + } + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..81c05ed --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.lockb binary diff=lockb diff --git a/.gitea/workflows/cicd.yaml.template b/.gitea/workflows/cicd.yaml.template new file mode 100644 index 0000000..216ee74 --- /dev/null +++ b/.gitea/workflows/cicd.yaml.template @@ -0,0 +1,57 @@ +name: CI Monitor CI/CD +on: [push] + +jobs: + build-image: + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + steps: + - name: Check out repository code + uses: actions/checkout@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + registry: git.yingbo.im:333 + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Build and push + uses: docker/build-push-action@v4 + with: + push: true + tags: git.yingbo.im:333/zhaoyingbo/ci_monitor:${{ github.sha }} + + deploy: + needs: build-image + runs-on: ubuntu-latest + container: catthehacker/ubuntu:act-latest + steps: + # 检出代码 + - name: Check out repository code + uses: actions/checkout@v3 + # 使用scp命令将docker-compose.yml文件上传到服务器 + - name: Upload docker-compose.yml to server + uses: appleboy/scp-action@master + with: + host: ${{ secrets.SERVER_HOST }} + username: ${{ secrets.SERVER_USERNAME }} + key: ${{ secrets.SERVER_KEY }} + port: ${{ secrets.SERVER_PORT }} + source: docker-compose.yml + target: /home/${{ secrets.SERVER_USERNAME }}/docker/ci_monitor + # 登录服务器,执行docker-compose命令 + - name: Login to the server and execute docker-compose command + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.SERVER_HOST }} + username: ${{ secrets.SERVER_USERNAME }} + key: ${{ secrets.SERVER_KEY }} + port: ${{ secrets.SERVER_PORT }} + script: | + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} git.yingbo.im:333 + cd /home/${{ secrets.SERVER_USERNAME }}/docker/ci_monitor + sed -i "s/sha/${{ github.sha }}/g" docker-compose.yml + docker compose up -d --force-recreate --no-deps ci_monitor diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52962c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# 0x +profile-* + +# mac files +.DS_Store + +# vim swap files +*.swp + +# webstorm +.idea + +# vscode +.vscode +*code-workspace + +# clinic +profile* +*clinic* +*flamegraph* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..90e78e6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM micr.cloud.mioffice.cn/zhaoyingbo/bun:alpine-cn + +WORKDIR /app + +COPY package*.json ./ + +COPY bun.lockb ./ + +RUN bun install + +COPY . . + +CMD ["bun", "start"] \ No newline at end of file diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..9961da94437656c854a98c1308fe0afd41989b6a GIT binary patch literal 55727 zcmeIb2V4|a*FV044IB0X_JV!s*u@qb#x7!)1r}IbmUVXlv0}$wqOo^lk2M;*#ujVr zQLzP$y(D6dM*V-!of){2#a(v&f8O`~eVFGt+&gp5`JQvnJ#+7!JF~G;1@{=E!97@` zb64wP%Lm8k@_>}u=#c(l8m(Fx7Hx=9nUt{}c^ni9#jkt)^}|X;pUORXUfk%nb(;Qh zWw`DS$A2bIef?rpufKksTJ9=@a3qR_!ZBT$Ae}!LD^*yLN1;gg5j858pv;eQyr57N zM>!N_K9mNOc~NR(21e^3qm2$#86$83_3|jxMy*C?QYeyhh&Y#}a&b=a{IqmFD3?M} zkZ{zo1f$U7ob2|I%5apqaUQBx>y;X#VjuJh;CvfOy51AQbbcKr>HUU3PAJ!aPkxR@ z>54KIC0*ATmEqdxU`0zbisbv_I<-R+u8TIP6^bC~JWQhtRmSKQMF38GN2%0PSEx*I zMyrX^m=ptr`BLKA{+QD}_nfohb=B}Ds9&SJmnRi+4Ke|3CFgi6~V{8}zzoFY_4C43IAfOEoW zUrLnUjgsP~ie$}G#27TnNZ8cJ=;A}7L)BwTi~eX)LN(zT849q<1Oe>yM93l(S(&KVLG8!MTG*;2@7yeeoR71^2SOcUZhbO zuZq$tREBW9%3xH7<2uEwn5$SiqNIGjg_7b&$)G|%7=eS3Bm|%&dvg5a;4{f}t0tCh zQBph{tBZJj!6!YR8VUtmPk4*#B~cc{d1aK#YKn63mHBH_N5!fQ3dOlvB3`^otJN4y ziuCzReg(%X6hMeH{@p5^H=fMV5h#LK{3+EJPYwmvQHDTGdhsn_^>h?O< z&vCEwv9~)qsOBF(a67iq!g}p5?#fr}L&uksOYiKOFnD9Z)o%l?Cf6)nuI->Q6_YC+ z$X#(~Phr8emTwm8%g6d}G8FYcFmUIqIyvgMS?BHO+OEyFy(ZNCUOBVEp~AgJSlxZStYvuFAq`=8}laPw)k*Y!fbB&1~H}+MZj*~tZPti&&p zCJwl8{nwQxT0|D#ljwDSL;Uo>+XLRrnc3v<#U0&CUVSq)bk~wUpUGOHYm7w8ebe}FhU6{LQb@k-m zUal;>z+pz%G|$E>T!)M*6-|-z+IaZLwUe{{ir8>{_mc|7S}wo+xmQ1D%Bx$tvM=^u zx>C8`u~WZ2a_QLD?Qjo|H+d#)pPH-v;5dEtXU!JH?on#a=P(?5=(9d%)6FN-e?D|b zQ_(ZA#>bOlLICTYbp(PomNh`s6!d5Lt7` z`lX=Q75b!yB@pX_%;-l$e;o8NcUv8M!k6u5gW=B5r?$)2h;QkTWgq8ID0)JlH!THnbj+5Q|!pX}1L%-T&yK>HokPo_Wcob%72n23~SpU>o0?>X7yCL0`^4QX~v(4q3km37ij7XFs$4PlG=BPw}TiF2%Eq|H^)*Kwlm|ZPD+D zha8GO;gRo|^?xn&Y5pMFq-$*s%Kqnc631V#EK7&1-yQm7Umkbl>(=W31bvF1 zwfC)zE88iHhje-VlzG;avi=}RU-sV`U6PUYe}{fIJN&OzP;7r@V$a*32z|c)Gwc6- z=!e0745OJk9{vmzJ0U5`VGqU|Wc*v#p%lB<&?T101@{ja0 z(+9HsUC;IZ;`Q~$~Lza_tAzN}xPs6x>N z`j|%8k=gi&{npy1Hp%`Eg}zSGr|Z@pvi@V}cjW9_+YhpS1ME}!L!Zv6?ecX?e#mlT zpilQNmZ9vhHU_eOo|2;f^0{1EYrkhYeM@>WU$(Or`gH%x=Q7_?Da)6_reqNGt!=lA zE9(!2KFvR*Pw~%e{+)xq-2b-pUws#G{Y3pI`y~7LrBaq%0Dbb`+Psr7W&ITB%kzI` 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 ANdN!< literal 0 HcmV?d00001 diff --git a/controllers/template/index.ts b/controllers/template/index.ts new file mode 100644 index 0000000..437b535 --- /dev/null +++ b/controllers/template/index.ts @@ -0,0 +1,7 @@ +const templateFunc = () => {} + +const template = { + templateFunc, +}; + +export default template; diff --git a/db/index.ts b/db/index.ts new file mode 100644 index 0000000..8570c84 --- /dev/null +++ b/db/index.ts @@ -0,0 +1,7 @@ +import user from "./user"; + +const db = { + user, +}; + +export default db; diff --git a/db/pbClient.ts b/db/pbClient.ts new file mode 100644 index 0000000..7999649 --- /dev/null +++ b/db/pbClient.ts @@ -0,0 +1,7 @@ +import PocketBase from "pocketbase"; + +const pbClient = new PocketBase("https://ci-pb.xiaomiwh.cn"); + +pbClient.autoCancellation(false); + +export default pbClient; diff --git a/db/user/index.ts b/db/user/index.ts new file mode 100644 index 0000000..41f30ce --- /dev/null +++ b/db/user/index.ts @@ -0,0 +1,46 @@ +import { RecordModel } from "pocketbase"; +import { managePb404 } from "../../utils/pbTools"; +import pbClient from "../pbClient"; + +export interface UserRecordModel extends RecordModel { + user_id: number; + username: string; + name: string; + avatar_url: string; + web_url: string; +} + +const getOne = (id: string) => + managePb404( + async () => await pbClient.collection("user").getOne(id) + ) as Promise; + +const getOneByUserId = (user_id: number) => { + return managePb404( + async () => + await pbClient + .collection("user") + .getFirstListItem(`user_id="${user_id}"`, { + sort: "-created", + }) + ) as Promise; +}; + +const create = async (data: Partial) => + await pbClient.collection("user").create(data); + +const upsert = async (data: Partial) => { + if (!data.user_id) return null; + const userInfo = await getOneByUserId(data.user_id); + if (userInfo) return userInfo; + return await create(data); +}; + +const user = { + create, + upsert, + getOne, + getOneByUserId, +}; + +export default user; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..876edd6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: "3" + +services: + replace_me: + image: git.yingbo.im:333/zhaoyingbo/replace_me:sha + container_name: replace_me + restart: always + ports: + - 3001:3000 diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..ac8d057 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,26 @@ +export default { + languageOptions: { + ecmaVersion: 2021, + sourceType: 'module', + }, + ignores: ['*.js', '*.cjs', '*.mjs', '/src/Backup/*'], + plugins: ['@typescript-eslint', 'simple-import-sort'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + ], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + }, + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parser: '@typescript-eslint/parser', + }, + }, + ], +}; \ No newline at end of file diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..e67874e --- /dev/null +++ b/index.ts @@ -0,0 +1,14 @@ +import { initSchedule } from "./schedule"; + +initSchedule() + +Bun.serve({ + async fetch(req) { + const url = new URL(req.url); + // 根路由 + if (url.pathname === "/") return new Response("hello, glade to see you!"); + if (url.pathname === '/ci') return new Response("OK") + return new Response("OK"); + }, + port: 3000, +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..7287580 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "replace_me", + "module": "index.ts", + "type": "module", + "scripts": { + "start": "bun run index.ts" + }, + "devDependencies": { + "eslint": "^9.2.0", + "bun-types": "latest", + "@types/lodash": "^4.14.202", + "@types/node-schedule": "^2.1.6", + "eslint-plugin-simple-import-sort": "^12.1.0", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "lodash": "^4.17.21", + "node-schedule": "^2.1.1", + "pocketbase": "^0.21.1" + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..ac2512f --- /dev/null +++ b/readme.md @@ -0,0 +1,182 @@ +# CI 监控 + +监听新 projId,自动补全内容,获取从 20240101 到当前的所有流水线信息 + +监听功能未知原因不好用,先不做了,改手动遍历了 + +拿到 project_id 后,获取数据表中最新的 pipeline 的 Id,然后比对接口中的 ID 进行填充 + +如果没有 pipeline 的 id,直接从接口中获取 20240101 到当前的流水线信息 + +先从数据中获取用户信息填充,随后在根据填充完的用户信息获取全部的 userid 的列表,再写 pipeline 表 + +# 图表库(不用了) + +https://g2plot.antv.antgroup.com/examples + +# 数据信息 + +project 信息 + +```js +{ + id: 'aaa', + project_id: 131366, + description: "场景复现平台-展示设备(移动、音箱、小爱建议、车载、手表等设备)上小爱执行结果及相关处理流程", + name: "ai-scene-review-fe", + path_with_namespace: "miai-fe/fe/ai-scene-review-fe", + web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe", + avatar_url: null, + has_new_cicd: false, +} +``` + +pipeline 信息 + +```js +{ + id: 'bbb', + project_id: 'aaa', + user_id: 'ccc', + pipeline_id: 7646046, + ref: "preview", + status: "success", + web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe/-/pipelines/7646046", + started_at: "2024-03-01T16:47:40.192+08:00", + finished_at: "2024-03-01T16:49:30.624+08:00", + duration: 100, + queued_duration: 6, +} +``` + +user 信息 + +```js +{ + id: 'ccc', + user_id: 10011, + username: "zhaoyingbo", + name: "赵英博", + avatar_url: "https://git.n.xiaomi.com/uploads/-/system/user/avatar/10011/avatar.png", + web_url: "https://git.n.xiaomi.com/zhaoyingbo" +} +``` + +每周每个项目按分支的运行时长统计 SQL `statisticsPerProj` + +```SQL +SELECT + (ROW_NUMBER() OVER()) as id, + strftime('%Y-%W', datetime(pip.started_at)) AS week, + p.name AS name, + ROUND(AVG(pip.duration/60.0), 1) AS duration, + pip.ref +FROM project p +JOIN pipeline pip ON p.id = pip.project_id +GROUP BY name, week, pip.ref; +``` + +每周流水线运行统计 SQL `statisticsPerWeek` + +```SQL +SELECT + (ROW_NUMBER() OVER()) as id, + strftime('%Y-%W', datetime(started_at)) AS week, + COUNT(*) AS total_count, + SUM(CASE WHEN status = 'success' THEN 0 ELSE 1 END) AS failed_count, + SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) AS success_count, + ROUND(SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) AS success_rate, + ROUND(AVG(duration/60.0), 1) AS duration +FROM pipeline +GROUP BY week; +``` + +# GPT + +我有一个 sqlite 数据库,表如下 +project 表 + +```js +{ + id: 'aaa', + project_id: 131366, + description: "场景复现平台-展示设备(移动、音箱、小爱建议、车载、手表等设备)上小爱执行结果及相关处理流程", + name: "ai-scene-review-fe", + path_with_namespace: "miai-fe/fe/ai-scene-review-fe", + web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe", + avatar_url: null, + has_new_cicd: false, +} +``` + +pipeline 表 + +```js +{ + id: 'bbb', + project_id: 'aaa', + user_id: 'ccc', + pipeline_id: 7646046, + ref: "preview", + status: "success", + web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe/-/pipelines/7646046", + started_at: "2024-03-01T16:47:40.192+08:00", + finished_at: "2024-03-01T16:49:30.624+08:00", + duration: 100, + queued_duration: 6, +} +``` + +user 表 + +```js +{ + id: 'ccc', + user_id: 10011, + username: "zhaoyingbo", + name: "赵英博", + avatar_url: "https://git.n.xiaomi.com/uploads/-/system/user/avatar/10011/avatar.png", + web_url: "https://git.n.xiaomi.com/zhaoyingbo" +} +``` + +我想按天展示每个项目的 pipline 按 ref 区分的平均 duration,如何创建视图 + +# 机器人 + +卡片 ID:ctp_AAyVLS6Q37cL + +JSON 示例 + +```json +{ + "total_count": "29", // OK + "group_table": [ + { + "project_name": "ai-ak-fe", + "project_ref": "master", + "project_duration": "1.4", + "project_duration_rate": "↓12%" + }, + { + "project_name": "ai-class-schedule-fe", + "project_ref": "preview", + "project_duration": "3.2", + "project_duration_rate": "↑5%" + }, + { + "project_name": "ai-scene-review-fe", + "project_ref": "staging", + "project_duration": "5.4", + "project_duration_rate": "↓6%" + } + ], + "weekly_count_rate": "较上周 ↑5%", // OK + "weekly_duration_rate": "较上周 ↓12%", // OK + "weekly_success_rate": "较上周 ↑12%", // OK + "duration": "0.9", // OK + "success_rate": "100", // OK + "has_new_cicd_count": "15", // OK + "without_new_cicd_count": "20" // OK +} +``` diff --git a/schedule/index.ts b/schedule/index.ts new file mode 100644 index 0000000..6a3ceec --- /dev/null +++ b/schedule/index.ts @@ -0,0 +1,10 @@ +import schedule from 'node-schedule' + +const func = () => {} + +export const initSchedule = async () => { + // 定时任务,每15分钟刷新一次token + schedule.scheduleJob('*/15 * * * *', func); + // 立即执行一次 + func() +} diff --git a/service/index.ts b/service/index.ts new file mode 100644 index 0000000..6fcaa20 --- /dev/null +++ b/service/index.ts @@ -0,0 +1,28 @@ +const fetchGetParams = { + method: "GET", + headers: { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" }, +}; + +/** + * 获取项目详情 + * @param id 项目id + */ +const fetchTemplate = async (id: number) => { + try { + const response = await fetch( + `https://git.n.xiaomi.com/api/v4/projects/${id}`, + fetchGetParams + ); + const body = (await response.json()) as any; + if (body.message === "404 Project Not Found") return null; + return body; + } catch { + return null; + } +}; + +const service = { + fetchTemplate, +}; + +export default service; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7556e1d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" // add Bun global + ] + } +} diff --git a/types/index.ts b/types/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/utils/pbTools.ts b/utils/pbTools.ts new file mode 100644 index 0000000..01d3186 --- /dev/null +++ b/utils/pbTools.ts @@ -0,0 +1,10 @@ +export const managePb404 = async (dbFunc: Function) => { + try { + return await dbFunc(); + } catch (err: any) { + // 没有这个提醒就返回空 + if (err?.message === "The requested resource wasn't found.") { + return null; + } else throw err; + } +};