@dazhicheng/common 1.0.43 → 1.0.46

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/.env.test CHANGED
@@ -13,4 +13,4 @@ VITE_DROP_CONSOLE = true
13
13
  VITE_FEISHU_REDIRECT_URI = http://fulfillment-qd.tiantai-jp.com:23006/feishuOAuth
14
14
 
15
15
  # webSocket地址
16
- VITE_WEBSOCKET_URL = ws://116.6.49.180:11001
16
+ VITE_WEBSOCKET_URL = ws://fulfillment-test.tiantai-jp.com:11000
package/.gitlab-ci.yml CHANGED
@@ -38,6 +38,7 @@ variables:
38
38
  - dist/
39
39
  - nginx_*.conf
40
40
  - docker-entrypoint.sh
41
+ - deploy-docker.sh
41
42
  - Dockerfile
42
43
  expire_in: 1 day
43
44
 
@@ -57,29 +58,20 @@ variables:
57
58
  - dist-test/
58
59
  - nginx_*.conf
59
60
  - docker-entrypoint.sh
61
+ - deploy-docker.sh
60
62
  - Dockerfile
61
63
  expire_in: 1 day
62
64
 
63
65
  # 部署锚点(通用脚本直接写在这里,不需要项目覆盖) -------------------------
64
66
  # 使用 named volume 持久化静态资源:发版时合并新构建并保留旧 hashed 文件,
65
67
  # 使已打开的旧页面仍能懒加载到旧入口资源。
66
- # 若存在 dist-${NODE_ENV}develop 双构建产物),先还原为 dist/ 再 docker build。
68
+ # 热更新逻辑见 deploy-docker.shsync-common 同步到各 *-web)。
67
69
  .deploy_template: &deploy_template
68
70
  stage: deploy
69
71
  image: docker:26.1.4
70
72
  script:
71
- - |
72
- if [ -d "dist-${NODE_ENV}" ]; then
73
- rm -rf dist
74
- mv "dist-${NODE_ENV}" dist
75
- fi
76
- - docker volume create ${IMAGE_NAME}-assets || true
77
- - docker build --build-arg NODE_ENV=$NODE_ENV -t $IMAGE_NAME:latest .
78
- - docker stop $IMAGE_NAME || true
79
- - docker rm $IMAGE_NAME || true
80
- - >
81
- docker run -d --name $IMAGE_NAME --restart always -p $HOST_PORT:$CONTAINER_PORT -v ${IMAGE_NAME}-assets:/usr/share/nginx/html -e ASSET_RETENTION_DAYS=14 $IMAGE_NAME:latest
82
-
73
+ - chmod +x deploy-docker.sh
74
+ - ./deploy-docker.sh
83
75
  - docker image prune -f || true
84
76
 
85
77
  # MR 验证 -----------------------------------------------------------------
@@ -136,27 +128,41 @@ MR 构建验证:
136
128
  # <<<开发环境发版_107_NO>>>
137
129
  # only: [develop]
138
130
 
139
- 测试环境发版:
131
+ # develop:双构建产物 dist-test;构建失败则不发版(needs 非 optional)
132
+ 测试环境发版_develop:
133
+ <<: *deploy_template
134
+ needs: [构建_develop]
135
+ tags: [test]
136
+ resource_group: deploy-test
137
+ variables:
138
+ NODE_ENV: test
139
+ <<<测试环境发版>>>
140
+ only: [develop]
141
+
142
+ # test:单构建产物 dist/;构建失败则不发版
143
+ 测试环境发版_test:
140
144
  <<: *deploy_template
141
- needs:
142
- - job: 构建_develop
143
- optional: true
144
- - job: 构建_test
145
- optional: true
145
+ needs: [构建_test]
146
146
  tags: [test]
147
147
  resource_group: deploy-test
148
148
  variables:
149
149
  NODE_ENV: test
150
150
  <<<测试环境发版>>>
151
- only: [test, develop]
151
+ only: [test]
152
+
153
+ # 测试环境发版_107_develop:
154
+ # <<: *deploy_template
155
+ # needs: [构建_develop]
156
+ # tags: [test_107]
157
+ # resource_group: deploy-test_107
158
+ # variables:
159
+ # NODE_ENV: test
160
+ # <<<测试环境发版_107_NO>>>
161
+ # only: [develop]
152
162
 
153
- # 测试环境发版_107:
163
+ # 测试环境发版_107_test:
154
164
  # <<: *deploy_template
155
- # needs:
156
- # - job: 构建_develop
157
- # optional: true
158
- # - job: 构建_test
159
- # optional: true
165
+ # needs: [构建_test]
160
166
  # tags: [test_107]
161
167
  # resource_group: deploy-test_107
162
168
  # variables:
@@ -0,0 +1,213 @@
1
+ #!/bin/sh
2
+ # 静态 SPA Docker 热更新部署:合并 volume、按需 reload nginx / 重建容器。
3
+ # 由 GitLab CI deploy job 调用;依赖环境变量见下方「必需变量」。
4
+ set -e
5
+
6
+ # --- 必需变量(由 CI job variables 注入)---
7
+ : "${IMAGE_NAME:?IMAGE_NAME is required}"
8
+ : "${NODE_ENV:?NODE_ENV is required}"
9
+ : "${HOST_PORT:?HOST_PORT is required}"
10
+ : "${CONTAINER_PORT:?CONTAINER_PORT is required}"
11
+
12
+ ASSET_RETENTION_DAYS="${ASSET_RETENTION_DAYS:-14}"
13
+
14
+ # 构建产物校验(须在 dist-${NODE_ENV} 还原为 dist/ 之前)
15
+ if [ -d "dist-${NODE_ENV}" ]; then
16
+ test -f "dist-${NODE_ENV}/index.html" || {
17
+ echo "missing dist-${NODE_ENV}/index.html, abort deploy" >&2
18
+ exit 1
19
+ }
20
+ else
21
+ test -f dist/index.html || {
22
+ echo "missing dist/index.html, abort deploy" >&2
23
+ exit 1
24
+ }
25
+ fi
26
+
27
+ if [ -d "dist-${NODE_ENV}" ]; then
28
+ rm -rf dist
29
+ mv "dist-${NODE_ENV}" dist
30
+ fi
31
+
32
+ ASSETS_VOL="${IMAGE_NAME}-assets"
33
+ docker volume create "$ASSETS_VOL" || true
34
+ docker build --build-arg NODE_ENV="$NODE_ENV" -t "${IMAGE_NAME}:latest" .
35
+
36
+ # 合并静态资源到持久卷(entrypoint 执行 cp/find 后 CMD=true 退出,不停正在跑的 nginx)
37
+ docker run --rm \
38
+ -v "${ASSETS_VOL}:/usr/share/nginx/html" \
39
+ -e "ASSET_RETENTION_DAYS=${ASSET_RETENTION_DAYS}" \
40
+ "${IMAGE_NAME}:latest" \
41
+ true
42
+
43
+ file_hash_from_image() {
44
+ _path="$1"
45
+ _cid=$(docker create "${IMAGE_NAME}:latest")
46
+ docker cp "$_cid:$_path" /tmp/img-file-hash.bin
47
+ docker rm -f "$_cid" >/dev/null
48
+ sha256sum /tmp/img-file-hash.bin | awk '{print $1}'
49
+ }
50
+
51
+ file_hash_from_container() {
52
+ _path="$1"
53
+ docker cp "$IMAGE_NAME:$_path" /tmp/ctr-file-hash.bin 2>/dev/null || return 1
54
+ sha256sum /tmp/ctr-file-hash.bin | awk '{print $1}'
55
+ }
56
+
57
+ # 热更新 default.conf:先备份,nginx -t 失败则还原,避免坏配置落盘导致重启起不来
58
+ sync_nginx_conf() {
59
+ _new=$(file_hash_from_image /etc/nginx/conf.d/default.conf)
60
+ _old=$(file_hash_from_container /etc/nginx/conf.d/default.conf || true)
61
+ if [ -z "$_old" ] || [ "$_new" = "$_old" ]; then
62
+ return 0
63
+ fi
64
+
65
+ _cid=$(docker create "${IMAGE_NAME}:latest")
66
+ docker cp "$_cid:/etc/nginx/conf.d/default.conf" /tmp/nginx-default-new.conf
67
+ docker rm -f "$_cid" >/dev/null
68
+
69
+ docker exec "$IMAGE_NAME" sh -c \
70
+ 'cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak'
71
+ docker cp /tmp/nginx-default-new.conf "$IMAGE_NAME:/etc/nginx/conf.d/default.conf"
72
+ if ! docker exec "$IMAGE_NAME" nginx -t; then
73
+ docker exec "$IMAGE_NAME" sh -c \
74
+ 'mv /etc/nginx/conf.d/default.conf.bak /etc/nginx/conf.d/default.conf'
75
+ echo "nginx -t failed, restored previous default.conf" >&2
76
+ return 1
77
+ fi
78
+ docker exec "$IMAGE_NAME" sh -c 'rm -f /etc/nginx/conf.d/default.conf.bak'
79
+ docker exec "$IMAGE_NAME" nginx -s reload
80
+ }
81
+
82
+ need_recreate() {
83
+ if [ "${FORCE_RECREATE:-0}" = "1" ]; then
84
+ echo "FORCE_RECREATE=1, will recreate container"
85
+ return 0
86
+ fi
87
+ # 端口映射变化(HostConfig 在 stopped 时仍可读)
88
+ _bound=$(docker inspect -f \
89
+ "{{with index .HostConfig.PortBindings \"${CONTAINER_PORT}/tcp\"}}{{(index . 0).HostPort}}{{end}}" \
90
+ "$IMAGE_NAME" 2>/dev/null || true)
91
+ if [ "$_bound" != "$HOST_PORT" ]; then
92
+ echo "port mapping changed (${_bound:-none} -> ${HOST_PORT}), will recreate"
93
+ return 0
94
+ fi
95
+ _new_ep=$(file_hash_from_image /docker-entrypoint.sh)
96
+ _old_ep=$(file_hash_from_container /docker-entrypoint.sh || true)
97
+ if [ -n "$_old_ep" ] && [ "$_new_ep" != "$_old_ep" ]; then
98
+ echo "docker-entrypoint.sh changed, will recreate"
99
+ return 0
100
+ fi
101
+ _new_ngx=$(file_hash_from_image /usr/sbin/nginx)
102
+ _old_ngx=$(file_hash_from_container /usr/sbin/nginx || true)
103
+ if [ -n "$_old_ngx" ] && [ "$_new_ngx" != "$_old_ngx" ]; then
104
+ echo "nginx binary changed, will recreate"
105
+ return 0
106
+ fi
107
+ return 1
108
+ }
109
+
110
+ start_container() {
111
+ docker run -d --name "$IMAGE_NAME" --restart always \
112
+ -p "${HOST_PORT}:${CONTAINER_PORT}" \
113
+ -v "${ASSETS_VOL}:/usr/share/nginx/html" \
114
+ -e "ASSET_RETENTION_DAYS=${ASSET_RETENTION_DAYS}" \
115
+ "${IMAGE_NAME}:latest"
116
+ }
117
+
118
+ # 探活:优先本机 curl;CI 容器内无 curl 时用 host 网络 curl 容器访问宿主机映射端口
119
+ http_probe() {
120
+ _port="$1"
121
+ if command -v curl >/dev/null 2>&1; then
122
+ curl -sf --connect-timeout 1 "http://127.0.0.1:${_port}/" >/dev/null
123
+ return $?
124
+ fi
125
+ docker run --rm --network host curlimages/curl:8.5.0 \
126
+ -sf --connect-timeout 1 "http://127.0.0.1:${_port}/" >/dev/null
127
+ }
128
+
129
+ wait_http() {
130
+ _port="$1"
131
+ _attempts="${2:-30}"
132
+ _i=1
133
+ while [ "$_i" -le "$_attempts" ]; do
134
+ if http_probe "$_port"; then
135
+ return 0
136
+ fi
137
+ sleep 0.5
138
+ _i=$((_i + 1))
139
+ done
140
+ return 1
141
+ }
142
+
143
+ # 重建前旁路探活:127.0.0.1 上由 Docker 分配空闲宿主机端口(无固定偏移)
144
+ start_sidecar_container() {
145
+ NEXT_NAME="${IMAGE_NAME}-next"
146
+ _publish="127.0.0.1::${CONTAINER_PORT}"
147
+
148
+ docker rm -f "$NEXT_NAME" 2>/dev/null || true
149
+
150
+ if [ -n "${SIDECAR_HOST_PORT:-}" ]; then
151
+ _publish="127.0.0.1:${SIDECAR_HOST_PORT}:${CONTAINER_PORT}"
152
+ echo "sidecar: using fixed port ${SIDECAR_HOST_PORT}" >&2
153
+ else
154
+ echo "sidecar: binding ephemeral free port on 127.0.0.1" >&2
155
+ fi
156
+
157
+ docker run -d --name "$NEXT_NAME" \
158
+ -p "$_publish" \
159
+ -v "${ASSETS_VOL}:/usr/share/nginx/html" \
160
+ -e "ASSET_RETENTION_DAYS=${ASSET_RETENTION_DAYS}" \
161
+ "${IMAGE_NAME}:latest"
162
+
163
+ if [ -n "${SIDECAR_HOST_PORT:-}" ]; then
164
+ echo "$SIDECAR_HOST_PORT"
165
+ else
166
+ docker port "$NEXT_NAME" "${CONTAINER_PORT}/tcp" | sed 's/.*://'
167
+ fi
168
+ }
169
+
170
+ probe_sidecar_before_recreate() {
171
+ NEXT_NAME="${IMAGE_NAME}-next"
172
+ TEMP_PORT=$(start_sidecar_container) || {
173
+ echo "sidecar container failed to start" >&2
174
+ exit 1
175
+ }
176
+
177
+ if ! wait_http "$TEMP_PORT"; then
178
+ echo "sidecar probe failed on port ${TEMP_PORT}" >&2
179
+ docker logs "$NEXT_NAME" 2>&1 || true
180
+ docker rm -f "$NEXT_NAME" || true
181
+ exit 1
182
+ fi
183
+
184
+ docker stop "$NEXT_NAME" && docker rm "$NEXT_NAME"
185
+ }
186
+
187
+ wait_main_port() {
188
+ if ! wait_http "$HOST_PORT"; then
189
+ echo "main port probe failed on ${HOST_PORT}" >&2
190
+ if docker inspect "$IMAGE_NAME" >/dev/null 2>&1; then
191
+ docker logs "$IMAGE_NAME" 2>&1 || true
192
+ fi
193
+ exit 1
194
+ fi
195
+ }
196
+
197
+ if ! docker inspect "$IMAGE_NAME" >/dev/null 2>&1; then
198
+ start_container
199
+ wait_main_port
200
+ elif need_recreate; then
201
+ probe_sidecar_before_recreate
202
+ docker stop "$IMAGE_NAME" || true
203
+ docker rm "$IMAGE_NAME" || true
204
+ start_container
205
+ wait_main_port
206
+ else
207
+ # 已停止则先 start,再走同一套 conf 备份 / -t / reload(docker exec 需要运行中)
208
+ if [ "$(docker inspect -f '{{.State.Running}}' "$IMAGE_NAME")" != "true" ]; then
209
+ docker start "$IMAGE_NAME"
210
+ fi
211
+ sync_nginx_conf
212
+ wait_main_port
213
+ fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dazhicheng/common",
3
- "version": "1.0.43",
3
+ "version": "1.0.46",
4
4
  "description": "共享配置文件",
5
5
  "type": "module",
6
6
  "types": "./src/types/index.d.ts",
@@ -25,6 +25,7 @@
25
25
  "tsconfig.json",
26
26
  "Dockerfile",
27
27
  "docker-entrypoint.sh",
28
+ "deploy-docker.sh",
28
29
  "nginx_dev.conf",
29
30
  "nginx_test.conf",
30
31
  "sync-common.mjs",
package/sync-common.mjs CHANGED
@@ -27,6 +27,7 @@ const defaultManagedFiles = [
27
27
  "pnpm-workspace.yaml",
28
28
  "Dockerfile",
29
29
  "docker-entrypoint.sh",
30
+ "deploy-docker.sh",
30
31
  { name: "eslint.config.mjs.txt" },
31
32
  "tsconfig.json",
32
33
  { name: "nginx_dev.conf" },