@gadmin2n/schematics 0.0.87 → 0.0.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/application/files/gadmin2-game-angle-demo/.dockerignore +16 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.codegen +40 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.server +76 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.web +53 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +219 -33
- package/dist/lib/application/files/gadmin2-game-angle-demo/compose-ctl.sh +250 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +4 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/dev/postgres/init.sql +12 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.md +170 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.yml +254 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +8 -7
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/lib/page-helpers.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/prismaModels.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/agenda.seed.ts +39 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/audit.seed.ts +40 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/bootstrap.ts +56 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/canvas.seed.ts +39 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/{scripts/sync-data-mngt-pages.ts → seed/data-mngt.seed.ts} +36 -20
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/game.seed.ts +44 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +30 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permission.seed.ts +130 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-event-trigger.ts +60 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +11 -25
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +108 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.service.spec.ts +41 -57
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.service.spec.ts +309 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.service.spec.ts +315 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.service.spec.ts +312 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.service.spec.ts +317 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.service.spec.ts +309 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.service.spec.ts +299 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.service.spec.ts +307 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.spec.ts +31 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +309 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.spec.ts +205 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.ts +116 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.spec.ts +158 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +110 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/webhook-signature.util.spec.ts +79 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/webhook-signature.util.ts +54 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +34 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +457 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +241 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.spec.ts +34 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.spec.ts +24 -30
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.spec.ts +34 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.spec.ts +36 -36
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.spec.ts +34 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.spec.ts +48 -24
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +312 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/TODO.md +152 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.dockerignore +12 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/Dockerfile +79 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/GRACEFUL-DEPLOYMENT.md +270 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/index.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/reporting.ts +23 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/index.ts +70 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/outbox-poller.ts +246 -90
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/tests/cron-trigger-workflow.test.ts +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/workflows/dsl-workflow.ts +96 -8
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/nginx.conf +74 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/ElementInspector.tsx +18 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/promptGenerator.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/form.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +4 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasEditPage.tsx +9 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +156 -139
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +14 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +62 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/PublishModal.tsx +4 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasApi.ts +18 -27
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasDefaults.ts +32 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/demos.ts +48 -61
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas-page/index.tsx +3 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/DslView.tsx +16 -16
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +28 -35
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +34 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.css +6 -0
- package/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile +0 -63
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/sync-resources.ts +0 -100
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +0 -302
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.controller.spec.ts +0 -20
- package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/sql/create-event-trigger.sql +0 -87
- /package/dist/lib/application/files/gadmin2-game-angle-demo/{GRACEFUL-DEPLOYMENT.md → server/GRACEFUL-DEPLOYMENT.md} +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
.DS_Store
|
|
2
|
-
web/
|
|
3
2
|
**/node_modules
|
|
4
3
|
**/dist
|
|
5
4
|
**/generated
|
|
@@ -11,6 +10,11 @@ npm-debug.log
|
|
|
11
10
|
.coverage
|
|
12
11
|
.coverage.*
|
|
13
12
|
.aws
|
|
13
|
+
|
|
14
|
+
.agent/
|
|
15
|
+
.claude/
|
|
16
|
+
.claude-internal/
|
|
17
|
+
|
|
14
18
|
# local env files
|
|
15
19
|
.env.local
|
|
16
20
|
.env.*.local
|
|
@@ -30,4 +34,14 @@ yarn-error.log*
|
|
|
30
34
|
*.sw?
|
|
31
35
|
|
|
32
36
|
run.cmd
|
|
33
|
-
todo.md
|
|
37
|
+
todo.md
|
|
38
|
+
|
|
39
|
+
sync-to-template.sh
|
|
40
|
+
CLAUDE.md
|
|
41
|
+
|
|
42
|
+
# Build/CI orchestration files — read directly by `docker build -f` / Jenkins / docker compose,
|
|
43
|
+
# 不需要进入任何镜像的 build context
|
|
44
|
+
Dockerfile.*
|
|
45
|
+
Jenkinsfile
|
|
46
|
+
docker-compose.yml
|
|
47
|
+
docker-compose.md
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
#
|
|
3
|
+
# 代码生成专用镜像(方案 C)。
|
|
4
|
+
# Jenkins 流水线在构建 web/server 业务镜像之前,先 docker build 本镜像;
|
|
5
|
+
# 后续 Dockerfile.web / Dockerfile.server 通过 ARG + FROM 引用本镜像,
|
|
6
|
+
# 直接 COPY --from=gen 拿到 gadmin2 g prisma 已生成的源码。
|
|
7
|
+
#
|
|
8
|
+
# 本镜像不会被推送到 registry,仅在 Jenkins agent 本地 docker daemon 中存活,
|
|
9
|
+
# 流水线末尾会清理。
|
|
10
|
+
|
|
11
|
+
FROM node:20-alpine
|
|
12
|
+
|
|
13
|
+
WORKDIR /app
|
|
14
|
+
|
|
15
|
+
# Prisma 在 alpine 上需要 openssl 才能加载原生引擎
|
|
16
|
+
RUN apk add --no-cache openssl
|
|
17
|
+
|
|
18
|
+
# 禁止 husky 在无 .git 环境下报错
|
|
19
|
+
ENV HUSKY=0
|
|
20
|
+
|
|
21
|
+
# 配置腾讯软件源
|
|
22
|
+
RUN yarn config set registry https://mirrors.cloud.tencent.com/npm/
|
|
23
|
+
|
|
24
|
+
# 安装 gadmin2 CLI
|
|
25
|
+
RUN yarn global add @gadmin2n/cli@latest
|
|
26
|
+
|
|
27
|
+
# 先复制 server 依赖描述文件,源码未变时此层 layer cache 可复用
|
|
28
|
+
# (prisma generators 是 server 的 devDependencies,必须先 yarn install)
|
|
29
|
+
COPY server/package.json server/yarn.lock ./server/
|
|
30
|
+
|
|
31
|
+
RUN --mount=type=cache,target=/root/.yarn \
|
|
32
|
+
cd server && yarn install --frozen-lockfile
|
|
33
|
+
|
|
34
|
+
# 复制全部 codegen 输入:上层 config/、server/(含 schema.prisma 与 gadmin-cli.json)、web/
|
|
35
|
+
COPY config/ ./config/
|
|
36
|
+
COPY server/ ./server/
|
|
37
|
+
COPY web/ ./web/
|
|
38
|
+
|
|
39
|
+
# 执行代码生成;产物会写入 server/src 与 web/src
|
|
40
|
+
RUN gadmin2 g prisma
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
#
|
|
3
|
+
# 通过 ARG CODEGEN_IMAGE + FROM ${CODEGEN_IMAGE} 引用 Jenkins 流水线先行构建的 codegen 镜像。
|
|
4
|
+
# 直接 COPY --from=gen /app/server,复用 codegen 已经装好的 server/node_modules
|
|
5
|
+
# (含 prisma client 与 gadmin2 generators),避免重复 yarn install。
|
|
6
|
+
|
|
7
|
+
ARG CODEGEN_IMAGE
|
|
8
|
+
FROM ${CODEGEN_IMAGE} AS gen
|
|
9
|
+
|
|
10
|
+
# ─── 阶段 1:构建 ────────────────────────────────────────────
|
|
11
|
+
FROM node:20-alpine AS builder
|
|
12
|
+
|
|
13
|
+
WORKDIR /app
|
|
14
|
+
|
|
15
|
+
# Prisma 在 alpine 上需要 openssl
|
|
16
|
+
RUN apk add --no-cache openssl
|
|
17
|
+
|
|
18
|
+
# 禁止 husky 在无 .git 环境下报错
|
|
19
|
+
ENV HUSKY=0
|
|
20
|
+
|
|
21
|
+
# 从 codegen 镜像取完整 server/(含已生成代码 + 已安装的 node_modules)
|
|
22
|
+
COPY --from=gen /app/server ./server
|
|
23
|
+
|
|
24
|
+
# 编译
|
|
25
|
+
RUN cd server && yarn build
|
|
26
|
+
|
|
27
|
+
# ─── 阶段 2:运行 ────────────────────────────────────────────
|
|
28
|
+
FROM node:20-alpine AS runner
|
|
29
|
+
|
|
30
|
+
# Prisma 运行时需要 openssl;中国项目默认时区上海
|
|
31
|
+
RUN apk add --no-cache openssl tzdata && \
|
|
32
|
+
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
|
33
|
+
echo "Asia/Shanghai" > /etc/timezone
|
|
34
|
+
ENV TZ=Asia/Shanghai
|
|
35
|
+
|
|
36
|
+
WORKDIR /app/server
|
|
37
|
+
|
|
38
|
+
STOPSIGNAL SIGQUIT
|
|
39
|
+
|
|
40
|
+
# 禁止 husky 在无 .git 环境下报错;显式声明生产环境(NestJS / 第三方库行为优化)
|
|
41
|
+
ENV HUSKY=0 \
|
|
42
|
+
NODE_ENV=production
|
|
43
|
+
|
|
44
|
+
# 从 builder 复制依赖描述文件(其源头是 codegen 镜像)
|
|
45
|
+
COPY --from=builder --chown=node:node /app/server/package.json /app/server/yarn.lock ./
|
|
46
|
+
|
|
47
|
+
# 只安装 production 依赖,大幅减少镜像体积
|
|
48
|
+
RUN --mount=type=cache,target=/root/.yarn \
|
|
49
|
+
yarn install --production --frozen-lockfile
|
|
50
|
+
|
|
51
|
+
# 从构建阶段复制 Prisma 生成的 Client(含平台原生二进制)
|
|
52
|
+
COPY --from=builder --chown=node:node /app/server/node_modules/.prisma ./node_modules/.prisma
|
|
53
|
+
|
|
54
|
+
# 从构建阶段复制编译产物
|
|
55
|
+
COPY --from=builder --chown=node:node /app/server/dist ./dist
|
|
56
|
+
|
|
57
|
+
# 复制 Prisma schema(feature 分支部署时 prisma db push 需要)
|
|
58
|
+
COPY --from=builder --chown=node:node /app/server/prisma ./prisma
|
|
59
|
+
|
|
60
|
+
# 复制启动脚本和数据库迁移脚本(codegen 不会修改这些文件,但统一从 builder 拿,避免 COPY 源混用)
|
|
61
|
+
COPY --from=builder --chown=node:node /app/server/start-prod.sh /app/server/migrate-between-pg-schemas.js ./
|
|
62
|
+
RUN chmod +x start-prod.sh
|
|
63
|
+
|
|
64
|
+
# 复制环境变量配置(作为默认值,K8s 注入的同名变量优先级更高)
|
|
65
|
+
COPY --from=builder --chown=node:node /app/server/.env ./.env
|
|
66
|
+
|
|
67
|
+
# 切换到非 root 用户(node 镜像内置 uid/gid 1000)
|
|
68
|
+
USER node
|
|
69
|
+
|
|
70
|
+
EXPOSE 8000
|
|
71
|
+
|
|
72
|
+
# 容器健康检查:调用 NestJS 的 /health/live 端点
|
|
73
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
74
|
+
CMD wget -qO- http://127.0.0.1:8000/health/live || exit 1
|
|
75
|
+
|
|
76
|
+
CMD ["yarn", "start:prod"]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
#
|
|
3
|
+
# 通过 ARG CODEGEN_IMAGE + FROM ${CODEGEN_IMAGE} 引用 Jenkins 流水线先行构建的 codegen 镜像,
|
|
4
|
+
# 直接 COPY --from=gen 拿到 gadmin2 g prisma 已生成的 web/ 源码。
|
|
5
|
+
# 本镜像不再安装 gadmin2 CLI,构建上下文也不再需要 web/ 源码。
|
|
6
|
+
#
|
|
7
|
+
# 本地手工调试用:先 `docker build -f Dockerfile.codegen -t gadmin2-codegen:dev .`
|
|
8
|
+
# 再 `docker build -f Dockerfile.web --build-arg CODEGEN_IMAGE=gadmin2-codegen:dev -t gadmin2-web:dev .`
|
|
9
|
+
|
|
10
|
+
ARG CODEGEN_IMAGE
|
|
11
|
+
FROM ${CODEGEN_IMAGE} AS gen
|
|
12
|
+
|
|
13
|
+
# ─── 阶段 1:构建 ────────────────────────────────────────────
|
|
14
|
+
FROM node:20-alpine AS builder
|
|
15
|
+
|
|
16
|
+
WORKDIR /app
|
|
17
|
+
|
|
18
|
+
# 禁止 husky 在无 .git 环境下报错
|
|
19
|
+
ENV HUSKY=0
|
|
20
|
+
|
|
21
|
+
# 配置腾讯软件源
|
|
22
|
+
RUN yarn config set registry https://mirrors.cloud.tencent.com/npm/
|
|
23
|
+
|
|
24
|
+
# 从 codegen 镜像取完整 web/(含生成代码、不含 node_modules)
|
|
25
|
+
COPY --from=gen /app/web ./web
|
|
26
|
+
|
|
27
|
+
# 移除 yarn.lock 中的 resolved 地址,强制使用新的 registry
|
|
28
|
+
RUN sed -i 's|https://registry.npmmirror.com/|https://mirrors.cloud.tencent.com/npm/|g' web/yarn.lock
|
|
29
|
+
|
|
30
|
+
# 安装依赖
|
|
31
|
+
RUN --mount=type=cache,target=/root/.yarn \
|
|
32
|
+
cd web && yarn install --frozen-lockfile
|
|
33
|
+
|
|
34
|
+
# 声明构建参数
|
|
35
|
+
ARG NODE_ENV=production
|
|
36
|
+
ARG DEPLOY_NAME
|
|
37
|
+
# 设置环境变量,让构建命令可以读取到(src 中通过 import.meta.env.VITE_DEPLOY_NAME 使用)
|
|
38
|
+
ENV VITE_DEPLOY_NAME=${DEPLOY_NAME}
|
|
39
|
+
|
|
40
|
+
# 编译
|
|
41
|
+
RUN --mount=type=cache,target=/root/.yarn \
|
|
42
|
+
cd web && yarn build --mode ${NODE_ENV}
|
|
43
|
+
|
|
44
|
+
# ─── 阶段 2:运行 ────────────────────────────────────────────
|
|
45
|
+
FROM nginx:alpine
|
|
46
|
+
|
|
47
|
+
# nginx 配置和构建产物均来自 builder 阶段(其源头是 codegen 镜像)
|
|
48
|
+
COPY --from=builder /app/web/nginx.conf /etc/nginx/nginx.conf
|
|
49
|
+
COPY --from=builder /app/web/dist /var/www/html
|
|
50
|
+
|
|
51
|
+
EXPOSE 80
|
|
52
|
+
|
|
53
|
+
CMD ["nginx", "-g", "daemon off;"]
|
|
@@ -1,70 +1,256 @@
|
|
|
1
1
|
// see https://www.jenkins.io/doc/book/pipeline/syntax/
|
|
2
|
+
//
|
|
3
|
+
// Monorepo pipeline for gadmin-test: 3 deployable modules (web / server / worker).
|
|
4
|
+
// 路径触发:哪个模块代码有改动就构建哪个,纯文档改动跳过所有构建。
|
|
5
|
+
// 风格沿用 erp-frontend / erp-backend 的 Jenkinsfile(声明式、Coding CI 环境变量、Spinnaker webhook)。
|
|
6
|
+
|
|
2
7
|
pipeline {
|
|
3
8
|
agent any
|
|
9
|
+
|
|
4
10
|
environment {
|
|
5
11
|
CODING_DOCKER_REG_HOST = "${CCI_CURRENT_TEAM}-${CCI_DOCKER_REGISTRY_DOMAIN}"
|
|
6
12
|
IMAGE_VERSION = "build-${env.CI_BUILD_NUMBER}"
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
DEPLOY_ENV = "${env.BRANCH_NAME == 'test' ? 'test' : 'prod'}"
|
|
14
|
+
IMAGE_NAME_PREFIX = "${PROJECT_NAME.toLowerCase()}/${DEPLOY_ENV}"
|
|
15
|
+
|
|
16
|
+
// 镜像名后缀(Coding CI 通常用 ${CCI_JOB_NAME},但 monorepo 需要 3 个不同名字,这里固定写死)
|
|
17
|
+
// TODO: 确认这三个 image name 与 Spinnaker 应用 / K8s deployment 命名一致
|
|
18
|
+
WEB_IMAGE_SHORT = "gadmin2-web"
|
|
19
|
+
SERVER_IMAGE_SHORT = "gadmin2-server"
|
|
20
|
+
WORKER_IMAGE_SHORT = "gadmin2-worker"
|
|
21
|
+
|
|
22
|
+
// 代码生成中间镜像(不推送到 registry,仅本地 docker daemon 临时存活)
|
|
23
|
+
CODEGEN_IMAGE = "gadmin2-codegen:${IMAGE_VERSION}"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
options {
|
|
27
|
+
timeout(time: 30, unit: 'MINUTES')
|
|
9
28
|
}
|
|
10
29
|
|
|
11
30
|
stages {
|
|
31
|
+
|
|
12
32
|
stage('检出Git') {
|
|
13
|
-
when {
|
|
14
|
-
anyOf {
|
|
15
|
-
branch 'test';
|
|
16
|
-
branch 'master'
|
|
17
|
-
}
|
|
18
|
-
}
|
|
33
|
+
when { anyOf { branch 'test'; branch 'master' } }
|
|
19
34
|
steps {
|
|
20
|
-
checkout([$class: 'GitSCM',
|
|
35
|
+
checkout([$class: 'GitSCM',
|
|
36
|
+
branches: [[name: env.GIT_BUILD_REF]],
|
|
37
|
+
userRemoteConfigs: [[url: env.GIT_REPO_URL, credentialsId: env.CREDENTIALS_ID]]
|
|
38
|
+
])
|
|
21
39
|
}
|
|
22
40
|
}
|
|
23
41
|
|
|
24
|
-
stage('
|
|
25
|
-
when {
|
|
26
|
-
anyOf {
|
|
27
|
-
branch 'test';
|
|
28
|
-
branch 'master'
|
|
29
|
-
}
|
|
30
|
-
}
|
|
42
|
+
stage('检测变更模块') {
|
|
43
|
+
when { anyOf { branch 'test'; branch 'master' } }
|
|
31
44
|
steps {
|
|
32
45
|
script {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
// 对比基准:优先使用 Jenkins git plugin 的 GIT_PREVIOUS_SUCCESSFUL_COMMIT
|
|
47
|
+
// 兜底:基准缺失或 commit 在当前 clone 中找不到 → 全量构建(保险策略)
|
|
48
|
+
def base = env.GIT_PREVIOUS_SUCCESSFUL_COMMIT ?: ''
|
|
49
|
+
def baseValid = false
|
|
50
|
+
if (base) {
|
|
51
|
+
// shallow clone 时旧 commit 可能不在本地,检测一下
|
|
52
|
+
def rc = sh(script: "git cat-file -e ${base}^{commit} 2>/dev/null", returnStatus: true)
|
|
53
|
+
baseValid = (rc == 0)
|
|
54
|
+
if (!baseValid) {
|
|
55
|
+
echo "GIT_PREVIOUS_SUCCESSFUL_COMMIT=${base} 在当前 clone 中不可达(可能是 shallow clone),全量构建"
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
echo "无 GIT_PREVIOUS_SUCCESSFUL_COMMIT(首次构建?),全量构建"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
def changedFiles = []
|
|
62
|
+
if (baseValid) {
|
|
63
|
+
def diffOut = sh(script: "git diff --name-only ${base} HEAD", returnStdout: true).trim()
|
|
64
|
+
changedFiles = diffOut ? diffOut.split('\n') as List : []
|
|
65
|
+
echo "对比基准 ${base}..HEAD,变更文件 ${changedFiles.size()} 个"
|
|
39
66
|
}
|
|
67
|
+
|
|
68
|
+
// 路径规则
|
|
69
|
+
def matches = { List files, String prefix -> files.any { it.startsWith(prefix) } }
|
|
70
|
+
def isDocOnly = { String f ->
|
|
71
|
+
f.startsWith('docs/') || f == 'CLAUDE.md' || f == 'readme.md' ||
|
|
72
|
+
f == '.gitignore' || f == 'Jenkinsfile' || f == 'sync-to-template.sh' ||
|
|
73
|
+
f == 'sync-to-template.md' || f.startsWith('.claude/')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def buildWeb = false
|
|
77
|
+
def buildServer = false
|
|
78
|
+
def buildWorker = false
|
|
79
|
+
|
|
80
|
+
if (!baseValid) {
|
|
81
|
+
buildWeb = true; buildServer = true; buildWorker = true
|
|
82
|
+
} else if (changedFiles.isEmpty()) {
|
|
83
|
+
echo '没有文件变更,全部跳过'
|
|
84
|
+
} else {
|
|
85
|
+
// 纯文档变更 → 全部跳过
|
|
86
|
+
def nonDoc = changedFiles.findAll { !isDocOnly(it) }
|
|
87
|
+
if (nonDoc.isEmpty()) {
|
|
88
|
+
echo '仅文档/配置文件变更,全部跳过'
|
|
89
|
+
} else {
|
|
90
|
+
// 路径触发规则:
|
|
91
|
+
// web/** → web
|
|
92
|
+
// config/ui/** → web(前端 CRUD 代码由 config/ui 生成)
|
|
93
|
+
// config/prisma/** → server + web(后端模型由 prisma 生成;前端 enums 通过 yarn sync:code 与后端同步)
|
|
94
|
+
// server/** → server
|
|
95
|
+
// temporal/worker/**→ worker
|
|
96
|
+
buildWeb = matches(nonDoc, 'web/') || matches(nonDoc, 'config/ui/') || matches(nonDoc, 'config/prisma/')
|
|
97
|
+
buildServer = matches(nonDoc, 'server/') || matches(nonDoc, 'config/prisma/')
|
|
98
|
+
buildWorker = matches(nonDoc, 'temporal/worker/')
|
|
99
|
+
// 注:本项目根目录没有共享的 package.json / tsconfig.json,
|
|
100
|
+
// 各模块依赖独立在 web/ server/ temporal/worker/ 下,无需"全量兜底"逻辑。
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 用 env 暴露给后续 stage(when 表达式只能读 env / params)
|
|
105
|
+
env.BUILD_WEB = buildWeb.toString()
|
|
106
|
+
env.BUILD_SERVER = buildServer.toString()
|
|
107
|
+
env.BUILD_WORKER = buildWorker.toString()
|
|
108
|
+
|
|
109
|
+
echo "buildWeb=${buildWeb}, buildServer=${buildServer}, buildWorker=${buildWorker}"
|
|
40
110
|
}
|
|
41
111
|
}
|
|
42
112
|
}
|
|
43
113
|
|
|
44
|
-
stage('
|
|
114
|
+
stage('代码生成') {
|
|
115
|
+
// 仅当需要构建 web 或 server 时执行;worker 模块与 gadmin2 codegen 无关
|
|
45
116
|
when {
|
|
46
117
|
anyOf {
|
|
47
|
-
|
|
118
|
+
environment name: 'BUILD_WEB', value: 'true'
|
|
119
|
+
environment name: 'BUILD_SERVER', value: 'true'
|
|
48
120
|
}
|
|
49
121
|
}
|
|
50
122
|
steps {
|
|
51
|
-
|
|
52
|
-
|
|
123
|
+
script {
|
|
124
|
+
// 构建一个本地代码生成镜像:内部装好 gadmin2 CLI + server devDeps,
|
|
125
|
+
// 并已执行 `gadmin2 g prisma`,产出包含生成代码的 /app/web 与 /app/server。
|
|
126
|
+
//
|
|
127
|
+
// 关键点:
|
|
128
|
+
// - 镜像不 push 到 registry,仅在本 Jenkins agent 的 docker daemon 中存活
|
|
129
|
+
// - 后续 Dockerfile.web / Dockerfile.server 通过 ARG CODEGEN_IMAGE + FROM ${CODEGEN_IMAGE}
|
|
130
|
+
// 直接引用本地镜像,无需 registry 周转
|
|
131
|
+
// - 流水线末尾会清理本镜像
|
|
132
|
+
docker.build(env.CODEGEN_IMAGE, "-f Dockerfile.codegen .")
|
|
133
|
+
}
|
|
53
134
|
}
|
|
54
135
|
}
|
|
55
136
|
|
|
56
|
-
stage('
|
|
57
|
-
when {
|
|
58
|
-
|
|
59
|
-
|
|
137
|
+
stage('并行构建推送镜像') {
|
|
138
|
+
when { anyOf { branch 'test'; branch 'master' } }
|
|
139
|
+
parallel {
|
|
140
|
+
|
|
141
|
+
stage('构建 Web') {
|
|
142
|
+
when { environment name: 'BUILD_WEB', value: 'true' }
|
|
143
|
+
steps {
|
|
144
|
+
script {
|
|
145
|
+
def imageName = "${env.IMAGE_NAME_PREFIX}/${env.WEB_IMAGE_SHORT}"
|
|
146
|
+
def imageFull = "${env.CODING_DOCKER_REG_HOST}/${imageName}:${env.IMAGE_VERSION}"
|
|
147
|
+
def buildArg = (env.BRANCH_NAME == 'test') ? '--build-arg NODE_ENV=test' : '--build-arg NODE_ENV=production'
|
|
148
|
+
|
|
149
|
+
docker.withRegistry(
|
|
150
|
+
"${CCI_CURRENT_WEB_PROTOCOL}://${env.CODING_DOCKER_REG_HOST}",
|
|
151
|
+
"${env.CODING_ARTIFACTS_CREDENTIALS_ID}"
|
|
152
|
+
) {
|
|
153
|
+
// FROM ${CODEGEN_IMAGE} 在本地 docker daemon 解析,不会触发 registry 拉取
|
|
154
|
+
docker.build(
|
|
155
|
+
"${imageName}:${env.IMAGE_VERSION}",
|
|
156
|
+
"${buildArg} --build-arg CODEGEN_IMAGE=${env.CODEGEN_IMAGE} -f Dockerfile.web ."
|
|
157
|
+
).push()
|
|
158
|
+
sh "docker image rm ${imageFull}"
|
|
159
|
+
}
|
|
160
|
+
env.WEB_IMAGE_FULL = imageFull
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
stage('构建 Server') {
|
|
166
|
+
when { environment name: 'BUILD_SERVER', value: 'true' }
|
|
167
|
+
steps {
|
|
168
|
+
script {
|
|
169
|
+
def imageName = "${env.IMAGE_NAME_PREFIX}/${env.SERVER_IMAGE_SHORT}"
|
|
170
|
+
def imageFull = "${env.CODING_DOCKER_REG_HOST}/${imageName}:${env.IMAGE_VERSION}"
|
|
171
|
+
|
|
172
|
+
docker.withRegistry(
|
|
173
|
+
"${CCI_CURRENT_WEB_PROTOCOL}://${env.CODING_DOCKER_REG_HOST}",
|
|
174
|
+
"${env.CODING_ARTIFACTS_CREDENTIALS_ID}"
|
|
175
|
+
) {
|
|
176
|
+
// FROM ${CODEGEN_IMAGE} 在本地 docker daemon 解析,不会触发 registry 拉取
|
|
177
|
+
docker.build(
|
|
178
|
+
"${imageName}:${env.IMAGE_VERSION}",
|
|
179
|
+
"--build-arg CODEGEN_IMAGE=${env.CODEGEN_IMAGE} -f Dockerfile.server ."
|
|
180
|
+
).push()
|
|
181
|
+
sh "docker image rm ${imageFull}"
|
|
182
|
+
}
|
|
183
|
+
env.SERVER_IMAGE_FULL = imageFull
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
stage('构建 Worker') {
|
|
189
|
+
when { environment name: 'BUILD_WORKER', value: 'true' }
|
|
190
|
+
steps {
|
|
191
|
+
script {
|
|
192
|
+
def imageName = "${env.IMAGE_NAME_PREFIX}/${env.WORKER_IMAGE_SHORT}"
|
|
193
|
+
def imageFull = "${env.CODING_DOCKER_REG_HOST}/${imageName}:${env.IMAGE_VERSION}"
|
|
194
|
+
|
|
195
|
+
docker.withRegistry(
|
|
196
|
+
"${CCI_CURRENT_WEB_PROTOCOL}://${env.CODING_DOCKER_REG_HOST}",
|
|
197
|
+
"${env.CODING_ARTIFACTS_CREDENTIALS_ID}"
|
|
198
|
+
) {
|
|
199
|
+
// worker 的构建上下文是 temporal/worker/
|
|
200
|
+
docker.build("${imageName}:${env.IMAGE_VERSION}", "-f temporal/worker/Dockerfile temporal/worker").push()
|
|
201
|
+
sh "docker image rm ${imageFull}"
|
|
202
|
+
}
|
|
203
|
+
env.WORKER_IMAGE_FULL = imageFull
|
|
204
|
+
}
|
|
205
|
+
}
|
|
60
206
|
}
|
|
61
207
|
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
stage('触发 Spinnaker 部署') {
|
|
211
|
+
when { anyOf { branch 'test'; branch 'master' } }
|
|
62
212
|
steps {
|
|
63
213
|
script {
|
|
64
|
-
|
|
65
|
-
|
|
214
|
+
// 任一模块构建过就触发对应的 Spinnaker webhook
|
|
215
|
+
// TODO: 确认 Spinnaker 上是按"每模块一个 application"还是"一个 application 接收所有模块"
|
|
216
|
+
// 下面按"每模块一个 application"实现,与 erp-frontend / erp-backend 保持一致
|
|
217
|
+
def env_ = env.DEPLOY_ENV // test 或 prod
|
|
218
|
+
def webhookBase = 'https://api-spinnaker.devops.intlgame.com/webhooks/webhook'
|
|
219
|
+
|
|
220
|
+
def triggerOne = { String moduleSlug, String imageFull ->
|
|
221
|
+
// TODO: 确认 webhook 路径名 oit-gadmin2-${moduleSlug}-${env_} 已在 Spinnaker 注册
|
|
222
|
+
def url = "${webhookBase}/oit-gadmin2-${moduleSlug}-${env_}"
|
|
223
|
+
def payload = """{"parameters": {"image_tag": "${env.IMAGE_VERSION}", "repository": "${imageFull}"}}"""
|
|
224
|
+
sh "curl -sf '${url}' -X POST -H 'content-type: application/json' -d '${payload}'"
|
|
225
|
+
echo "已触发 ${moduleSlug} 部署:${url}"
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (env.BUILD_WEB == 'true' && env.WEB_IMAGE_FULL) {
|
|
229
|
+
triggerOne('web', env.WEB_IMAGE_FULL)
|
|
230
|
+
}
|
|
231
|
+
if (env.BUILD_SERVER == 'true' && env.SERVER_IMAGE_FULL) {
|
|
232
|
+
triggerOne('server', env.SERVER_IMAGE_FULL)
|
|
233
|
+
}
|
|
234
|
+
if (env.BUILD_WORKER == 'true' && env.WORKER_IMAGE_FULL) {
|
|
235
|
+
triggerOne('worker', env.WORKER_IMAGE_FULL)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (env.BUILD_WEB != 'true' && env.BUILD_SERVER != 'true' && env.BUILD_WORKER != 'true') {
|
|
239
|
+
echo '本次构建无模块更新,跳过 Spinnaker 触发'
|
|
240
|
+
} else {
|
|
241
|
+
echo '部署已经触发,可以在 Spinnaker 查看状态: https://api-spinnaker.devops.intlgame.com'
|
|
242
|
+
}
|
|
66
243
|
}
|
|
67
244
|
}
|
|
68
245
|
}
|
|
69
246
|
}
|
|
70
|
-
|
|
247
|
+
|
|
248
|
+
post {
|
|
249
|
+
// 清理本地 codegen 中间镜像(不影响 web/server 已 push 的产物镜像)
|
|
250
|
+
always {
|
|
251
|
+
script {
|
|
252
|
+
sh "docker image rm ${env.CODEGEN_IMAGE} || true"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|