@wjwjq/release-helper 0.2.97 → 0.2.99
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/CLAUDE.md +68 -0
- package/README.md +50 -37
- package/REFACTOR_PLAN.md +861 -0
- package/dist/.release/doc//351/203/250/347/275/262/346/211/213/345/206/214.md +4 -4
- package/dist/.release/nginx/nginx.conf +3 -2
- package/dist/cli.js +2 -2
- package/dist/deploy/pkg/nginx/nginx.service.tpl +0 -1
- package/dist/deploy/pkg/nginx_binary/binary/compile.sh +15 -9
- package/dist/deploy/pkg/nginx_binary/binary/nginx-arm-ssl1.1.1.tar.gz +0 -0
- package/dist/deploy/pkg/nginx_binary/binary/nginx-x86_64-ssl1.0.2.tar.gz +0 -0
- package/dist/deploy/pkg/nginx_binary/binary/nginx-x86_64-ssl1.1.1.tar.gz +0 -0
- package/dist/deploy/pkg/nginx_binary/binary/nginx-x86_64-ssl3.2.1.tar.gz +0 -0
- package/dist/deploy/pkg/nginx_binary/source/nginx-1.31.0.tar.gz +0 -0
- package/dist/deploy/script/common.sh +2 -2
- package/dist/deploy/script/nginx.sh +22 -19
- package/dist/deploy/script/readme.md +1 -1
- package/dist/publish.js +5 -8
- package/dist/release.js +4 -7
- package/example/README.md +71 -0
- package/example/example-frontend/package.json +14 -0
- package/example/example-frontend/pnpm-lock.yaml +1135 -0
- package/package.json +1 -1
- package/dist/deploy/pkg/nginx_binary/binary/nginx-oe2203sp4.x86_64-ssl1.1.1wa.tar.gz +0 -0
- package/dist/deploy/pkg/nginx_binary/binary/nginx-x86_64-ssl3.0.7.tar.gz +0 -0
- package/dist/deploy/pkg/nginx_binary/source/nginx-1.22.1.tar.gz +0 -0
package/REFACTOR_PLAN.md
ADDED
|
@@ -0,0 +1,861 @@
|
|
|
1
|
+
# Release-Helper 重构方案
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
当前 release-helper 是一个前端部署打包工具,核心流程:`init` → `pack` → `release`(GitLab发布)→ 服务端 `install.sh` 安装。
|
|
6
|
+
|
|
7
|
+
**主要问题**:
|
|
8
|
+
1. **二进制膨胀**:Nginx 二进制直接提交到 Git 仓库(`src/deploy/pkg/nginx_binary/binary/`),仓库体积大,管理混乱
|
|
9
|
+
2. **硬编码依赖**:`publish.ts` 硬编码 GitLab,无法支持其他平台
|
|
10
|
+
3. **占位符替换脆弱**:`String.replace()` 简单替换,命名不统一(`__user__` vs `_user_` vs `__APP_NAME__`)
|
|
11
|
+
4. **无自动化测试**
|
|
12
|
+
5. **配置校验手写**:易遗漏字段
|
|
13
|
+
6. **Changelog 解析简陋**:仅支持 `feat:/fix:`
|
|
14
|
+
7. **Bash 脚本问题**:install.sh = upgrade.sh(`callBy` 参数未使用)、`log_warn()` 拼写 bug、`__root__` 占位符未实际替换、大量硬编码路径
|
|
15
|
+
8. **安全合规**:在严格安全环境下,部署包需要能被审计(deps.txt 依赖声明)
|
|
16
|
+
|
|
17
|
+
**重构目标**:保持核心能力不变的前提下,解耦架构、规范化流程、增强可维护性和安全合规性。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 第一阶段:架构解耦(Plugin 化)
|
|
22
|
+
|
|
23
|
+
### 1.1 定义平台抽象层
|
|
24
|
+
|
|
25
|
+
新建 `src/platform/types.ts`:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
interface Platform {
|
|
29
|
+
name: string;
|
|
30
|
+
createTag(version: string, branch: string): Promise<void>;
|
|
31
|
+
createRelease(version: string, description: string): Promise<Release>;
|
|
32
|
+
uploadAsset(releaseId: string, file: Buffer, filename: string): Promise<AssetRef>;
|
|
33
|
+
getTags(): Promise<string[]>;
|
|
34
|
+
getChangelog(range: string): Promise<string>;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 1.2 GitLab 实现
|
|
39
|
+
|
|
40
|
+
将 `src/publish.ts` → `src/platforms/gitlab.ts`:
|
|
41
|
+
- 现有 `@gitbeaker/rest` 调用封装为 `GitlabPlatform implements Platform`
|
|
42
|
+
- 接口不变,只是从自由函数改为类方法
|
|
43
|
+
|
|
44
|
+
### 1.3 平台选择
|
|
45
|
+
|
|
46
|
+
`release.conf.yaml` 新增字段:
|
|
47
|
+
```yaml
|
|
48
|
+
platform: gitlab # 可选: gitlab | github | gitee
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
CLI 根据 `platform` 字段动态加载对应实现。
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 第二阶段:配置系统升级
|
|
56
|
+
|
|
57
|
+
### 2.1 Zod Schema 校验
|
|
58
|
+
|
|
59
|
+
依赖新增:`zod`
|
|
60
|
+
|
|
61
|
+
`src/config/schema.ts`:
|
|
62
|
+
```typescript
|
|
63
|
+
const ReleaseConfSchema = z.object({
|
|
64
|
+
platform: z.enum(['gitlab', 'github', 'gitee']).default('gitlab'),
|
|
65
|
+
host: z.string().url(),
|
|
66
|
+
token: z.string().optional(), // 可从环境变量读取
|
|
67
|
+
buildCmd: z.string().default('npm run build'),
|
|
68
|
+
assetsDir: z.string().default('dist'),
|
|
69
|
+
user: z.string().default('root'),
|
|
70
|
+
userGroup: z.string().default('root'),
|
|
71
|
+
installMode: z.enum(['standalone', 'cluster', 'static', 'all', 'selectable']).default('all'),
|
|
72
|
+
installDir: z.string().default('/opt').refine(isValidLinuxPath),
|
|
73
|
+
nginxInstallMode: z.enum(['source', 'binary']).default('binary'),
|
|
74
|
+
dockerInstallDir: z.string().optional(),
|
|
75
|
+
dockerBackupDir: z.string().optional(),
|
|
76
|
+
binaryRegistry: z.object({
|
|
77
|
+
// 新字段:二进制制品库地址
|
|
78
|
+
url: z.string().url().optional(),
|
|
79
|
+
type: z.enum(['http', 'nexus', 'minio']).default('http'),
|
|
80
|
+
}).optional(),
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 2.2 敏感信息环境变量支持
|
|
85
|
+
|
|
86
|
+
`release.conf.yaml` 支持 `${ENV_VAR}` 语法:
|
|
87
|
+
```yaml
|
|
88
|
+
host: https://gitlab.example.com
|
|
89
|
+
token: ${GITLAB_TOKEN} # 从环境变量读取,不写死在文件里
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
解析时替换:`parseConf()` 增加 `resolveEnvVars()` 步骤。
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 第三阶段:打包逻辑重构
|
|
97
|
+
|
|
98
|
+
### 3.1 模板引擎替换 String.replace
|
|
99
|
+
|
|
100
|
+
依赖新增:`handlebars`
|
|
101
|
+
|
|
102
|
+
统一占位符语法:`{{APP_NAME}}`、`{{INSTALL_PATH}}`、`{{USER}}` 等。
|
|
103
|
+
|
|
104
|
+
| 旧语法 | 新语法 |
|
|
105
|
+
|--------|--------|
|
|
106
|
+
| `__APP_NAME__` | `{{APP_NAME}}` |
|
|
107
|
+
| `_user_` | `{{USER}}` |
|
|
108
|
+
| `__user__` | `{{USER}}` |
|
|
109
|
+
| `__usergroup__` | `{{USER_GROUP}}` |
|
|
110
|
+
| `__root__` | `{{ASSETS_ROOT}}` |
|
|
111
|
+
| `#ERROR_LOG_PLACEHOLDER` | `{{ERROR_LOG_PATH}}` |
|
|
112
|
+
|
|
113
|
+
改造 `src/pack.ts`:
|
|
114
|
+
```typescript
|
|
115
|
+
function renderTemplate(template: string, data: TemplateData): string {
|
|
116
|
+
return Handlebars.compile(template, { noEscape: true })(data);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 3.2 二进制从制品库下载
|
|
121
|
+
|
|
122
|
+
`src/pack.ts` 修改 pack 流程:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
旧流程:
|
|
126
|
+
fs.cpSync(src/deploy, projectDir) ← 直接复制 Git 仓库中的二进制
|
|
127
|
+
|
|
128
|
+
新流程:
|
|
129
|
+
1. 读取 binaryRegistry.url
|
|
130
|
+
2. 下载 manifest.json 获取当前版本的所有平台二进制列表
|
|
131
|
+
3. 并发下载所有平台二进制到临时目录
|
|
132
|
+
4. 复制到 projectDir/pkg/nginx_binary/
|
|
133
|
+
5. 写入 deps.txt(每个二进制的 ldd 输出)供审计
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
制品库 manifest.json 格式:
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"version": "1.31.0",
|
|
140
|
+
"binaries": [
|
|
141
|
+
{
|
|
142
|
+
"id": "x86_64-el7",
|
|
143
|
+
"arch": "x86_64",
|
|
144
|
+
"os": "centos7",
|
|
145
|
+
"openssl": "1.0.2k",
|
|
146
|
+
"url": "https://registry.internal/nginx/1.31.0/x86_64-el7/nginx.tar.gz",
|
|
147
|
+
"sha256": "abc123...",
|
|
148
|
+
"deps": "libssl.so.10 => /lib64/libssl.so.10\n..."
|
|
149
|
+
},
|
|
150
|
+
{ "id": "x86_64-el9", "arch": "x86_64", "os": "centos9", "openssl": "3.0.7", ... },
|
|
151
|
+
{ "id": "aarch64-el8", "arch": "aarch64", "os": "centos8", "openssl": "1.1.1k", ... }
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 3.3 打包产物结构
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
.release/{projectName}/
|
|
160
|
+
├── pkg/
|
|
161
|
+
│ ├── nginx_binary/
|
|
162
|
+
│ │ ├── x86_64-el7/
|
|
163
|
+
│ │ │ ├── nginx.tar.gz
|
|
164
|
+
│ │ │ └── deps.txt ← 安全审计用
|
|
165
|
+
│ │ ├── x86_64-el9/
|
|
166
|
+
│ │ └── aarch64-el8/
|
|
167
|
+
│ ├── nginx/
|
|
168
|
+
│ │ ├── nginx.service.tpl ← 统一占位符为 {{...}}
|
|
169
|
+
│ │ └── nginx.logrotate.tpl
|
|
170
|
+
│ └── assets/ ← 前端构建产物
|
|
171
|
+
├── script/
|
|
172
|
+
│ ├── install.sh
|
|
173
|
+
│ ├── upgrade.sh
|
|
174
|
+
│ ├── common.sh
|
|
175
|
+
│ ├── nginx.sh
|
|
176
|
+
│ ├── prompt.sh
|
|
177
|
+
│ └── after.sh ← 用户自定义(可选)
|
|
178
|
+
└── version
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 第四阶段:Bash 部署脚本修复
|
|
184
|
+
|
|
185
|
+
### 4.1 Bug 修复
|
|
186
|
+
|
|
187
|
+
| 文件 | 问题 | 修复 |
|
|
188
|
+
|------|------|------|
|
|
189
|
+
| `common.sh:33` | `log_warn()` 输出 `[success]` | 改为 `[warn]` |
|
|
190
|
+
| `common.sh:164` | `start()` 不区分 install/upgrade | 增加差异化逻辑(upgrade 时跳过用户创建等) |
|
|
191
|
+
| `nginx.sh:70-73` | `__root__` 占位符替换代码被注释 | 启用并正确使用 Handlebars 风格的 `{{ASSETS_ROOT}}` |
|
|
192
|
+
|
|
193
|
+
### 4.2 二进制选择逻辑重构
|
|
194
|
+
|
|
195
|
+
`nginx.sh` 中的二进制选择改为基于 manifest:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# 旧:按文件名模式匹配
|
|
199
|
+
tar_file="nginx-${arch}-ssl${version}.tar.gz"
|
|
200
|
+
|
|
201
|
+
# 新:读取 deps.txt 和 manifest
|
|
202
|
+
select_binary() {
|
|
203
|
+
local os_id=$(. /etc/os-release && echo "${ID}-${VERSION_ID%%.*}")
|
|
204
|
+
local arch=$(uname -m)
|
|
205
|
+
|
|
206
|
+
local match_dir="${PROJECT_PATH}/pkg/nginx_binary/${arch}-${os_id}"
|
|
207
|
+
if [ -d "$match_dir" ] && [ -f "$match_dir/deps.txt" ]; then
|
|
208
|
+
echo "$match_dir/nginx.tar.gz"
|
|
209
|
+
return 0
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
# 回退:尝试模糊匹配
|
|
213
|
+
# ...
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### 4.3 install.sh 与 upgrade.sh 差异化
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# install.sh 新增行为:
|
|
221
|
+
# - 创建系统用户
|
|
222
|
+
# - 首次安装 nginx
|
|
223
|
+
# - 注册 systemd 服务
|
|
224
|
+
|
|
225
|
+
# upgrade.sh 新增行为:
|
|
226
|
+
# - 跳过用户创建
|
|
227
|
+
# - 备份旧版本
|
|
228
|
+
# - 平滑重启(nginx -s reload 而非 systemctl restart)
|
|
229
|
+
# - 保留原有 nginx.conf 中的自定义配置(diff 合并)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### 4.4 统一占位符
|
|
233
|
+
|
|
234
|
+
所有模板文件统一使用 `{{VAR}}` 语法:
|
|
235
|
+
- `nginx.service.tpl`:`{{USER}}`、`{{USER_GROUP}}`、`{{EXEC_CMD}}`、`{{LOG_PATH}}`、`{{PID_FILE}}`、`{{APP_NAME}}`
|
|
236
|
+
- `nginx.logrotate.tpl`:`{{APP_NAME}}`、`{{USER}}`、`{{USER_GROUP}}`、`{{PID_FILE}}`
|
|
237
|
+
- 安装脚本中在解压后用 `sed` 或内嵌的 shell 函数做替换(避免在部署端引入 handlebars 依赖)
|
|
238
|
+
|
|
239
|
+
### 4.5 移除硬编码路径
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# 旧
|
|
243
|
+
NGINX_BINARY_INSTALL_PATH="/etc/nginx/"
|
|
244
|
+
NGINX_CONF_INSTALL_PATH="$prefix/opt/${APP_NAME}/nginx/"
|
|
245
|
+
|
|
246
|
+
# 新 — 在 release.conf.yaml 中可配置,打包时替换
|
|
247
|
+
NGINX_BINARY_INSTALL_PATH="{{NGINX_BINARY_DIR}}"
|
|
248
|
+
NGINX_CONF_INSTALL_PATH="{{NGINX_CONF_DIR}}"
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 第五阶段:Changelog 与发布
|
|
254
|
+
|
|
255
|
+
### 5.1 Conventional Commits 支持
|
|
256
|
+
|
|
257
|
+
`src/platforms/gitlab.ts` 中的 `getChangeLog()` 改为标准 Conventional Commits 解析:
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
支持的 type:
|
|
261
|
+
feat → Features
|
|
262
|
+
fix → Bug Fixes
|
|
263
|
+
perf → Performance Improvements
|
|
264
|
+
refactor → Code Refactoring (折叠显示)
|
|
265
|
+
docs → Documentation (折叠显示)
|
|
266
|
+
chore/ci → 不显示
|
|
267
|
+
BREAKING CHANGE → 置顶高亮显示
|
|
268
|
+
|
|
269
|
+
scope 支持: feat(auth): xxx → * **auth**: xxx
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
使用正则 `^(feat|fix|perf|refactor|docs|chore|ci)(\(.+\))?: (.+)$` 解析。
|
|
273
|
+
|
|
274
|
+
### 5.2 dry-run 模式
|
|
275
|
+
|
|
276
|
+
CLI 新增 `--dry-run` 标志:
|
|
277
|
+
```bash
|
|
278
|
+
release-helper release --dry-run
|
|
279
|
+
# 输出:将要执行的操作列表,不实际执行
|
|
280
|
+
# - 将切换到 master 分支
|
|
281
|
+
# - 将创建 tag v1.2.3
|
|
282
|
+
# - 将打包: posidon-frontend_v1.2.3.tar.gz
|
|
283
|
+
# - 将发布到 GitLab: ...
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 制品库设计与实现方案
|
|
289
|
+
|
|
290
|
+
> 制品库解决的核心问题:Nginx 二进制不在 Git 仓库中,但 pack 时必须能获取到所有平台的二进制。
|
|
291
|
+
|
|
292
|
+
### 一、存储方案选择
|
|
293
|
+
|
|
294
|
+
提供 3 种后端,按复杂度递增:
|
|
295
|
+
|
|
296
|
+
| 方案 | 适用场景 | 复杂度 | 依赖 |
|
|
297
|
+
|------|---------|--------|------|
|
|
298
|
+
| **A. 静态文件 + HTTP** | 小型团队,无额外基础设施 | 最低 | nginx/caddy/httpd |
|
|
299
|
+
| **B. MinIO (S3 兼容)** | 已有或愿意部署对象存储 | 中 | docker run minio |
|
|
300
|
+
| **C. Nexus Repository** | 企业已有 Nexus 统一管控 | 中 | docker run nexus |
|
|
301
|
+
|
|
302
|
+
推荐 **方案 A** 起步,后续无缝切换到 B 或 C(通过 `binaryRegistry.type` 配置切换)。
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
### 二、制品库目录结构
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
/storage/nginx-binaries/ # 存储根目录
|
|
310
|
+
├── manifest.json # 全局入口,描述所有可用版本
|
|
311
|
+
├── versions.json # 版本列表(用于版本查询/回滚)
|
|
312
|
+
├── 1.31.0/ # 按 nginx 版本分组
|
|
313
|
+
│ ├── manifest.json # 该版本的所有平台二进制描述
|
|
314
|
+
│ ├── x86_64-el7/
|
|
315
|
+
│ │ ├── nginx.tar.gz
|
|
316
|
+
│ │ ├── nginx.tar.gz.sha256 # 校验文件
|
|
317
|
+
│ │ └── deps.txt # ldd 输出(安全审计用)
|
|
318
|
+
│ ├── x86_64-el9/
|
|
319
|
+
│ │ ├── nginx.tar.gz
|
|
320
|
+
│ │ ├── nginx.tar.gz.sha256
|
|
321
|
+
│ │ └── deps.txt
|
|
322
|
+
│ ├── aarch64-el8/
|
|
323
|
+
│ │ ├── nginx.tar.gz
|
|
324
|
+
│ │ ├── nginx.tar.gz.sha256
|
|
325
|
+
│ │ └── deps.txt
|
|
326
|
+
│ └── aarch64-el9/
|
|
327
|
+
│ └── ...
|
|
328
|
+
├── 1.22.1/ # 历史版本保留
|
|
329
|
+
│ └── ...
|
|
330
|
+
└── source/
|
|
331
|
+
└── nginx-1.31.0.tar.gz # 源码归档(可选,用于 source 模式安装)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 三、manifest.json 完整格式
|
|
335
|
+
|
|
336
|
+
**全局 manifest.json** (`/storage/nginx-binaries/manifest.json`):
|
|
337
|
+
```json
|
|
338
|
+
{
|
|
339
|
+
"latest": "1.31.0",
|
|
340
|
+
"versions": ["1.31.0", "1.22.1"],
|
|
341
|
+
"updated_at": "2025-01-15T10:30:00Z"
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**版本级 manifest.json** (`/storage/nginx-binaries/1.31.0/manifest.json`):
|
|
346
|
+
```json
|
|
347
|
+
{
|
|
348
|
+
"nginx_version": "1.31.0",
|
|
349
|
+
"built_at": "2025-01-15T10:30:00Z",
|
|
350
|
+
"binaries": [
|
|
351
|
+
{
|
|
352
|
+
"id": "x86_64-el7",
|
|
353
|
+
"arch": "x86_64",
|
|
354
|
+
"os": "centos",
|
|
355
|
+
"os_version": "7",
|
|
356
|
+
"openssl": "1.0.2k",
|
|
357
|
+
"glibc": "2.17",
|
|
358
|
+
"file_url": "1.31.0/x86_64-el7/nginx.tar.gz",
|
|
359
|
+
"sha256": "a1b2c3d4e5f6...",
|
|
360
|
+
"file_size": 5242880,
|
|
361
|
+
"deps": "libssl.so.10 => /lib64/libssl.so.10\nlibcrypto.so.10 => /lib64/libcrypto.so.10\nlibpcre.so.1 => /lib64/libpcre.so.1\nlibz.so.1 => /lib64/libz.so.1\nlibc.so.6 => /lib64/libc.so.6\n/lib64/ld-linux-x86-64.so.2",
|
|
362
|
+
"compile_info": {
|
|
363
|
+
"docker_image": "centos:7",
|
|
364
|
+
"configure_args": "--prefix=/etc/nginx --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-http_stub_status_module --with-stream",
|
|
365
|
+
"compiler": "gcc 4.8.5"
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"id": "x86_64-el9",
|
|
370
|
+
"arch": "x86_64",
|
|
371
|
+
"os": "rocky",
|
|
372
|
+
"os_version": "9",
|
|
373
|
+
"openssl": "3.0.7",
|
|
374
|
+
"glibc": "2.34",
|
|
375
|
+
"file_url": "1.31.0/x86_64-el9/nginx.tar.gz",
|
|
376
|
+
"sha256": "...",
|
|
377
|
+
"deps": "libssl.so.3 => /lib64/libssl.so.3\nlibcrypto.so.3 => /lib64/libcrypto.so.3\n...",
|
|
378
|
+
"compile_info": { "docker_image": "rockylinux:9", "compiler": "gcc 11.4.1" }
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"id": "aarch64-el8",
|
|
382
|
+
"arch": "aarch64",
|
|
383
|
+
"os": "rocky",
|
|
384
|
+
"os_version": "8",
|
|
385
|
+
"openssl": "1.1.1k",
|
|
386
|
+
"glibc": "2.28",
|
|
387
|
+
"file_url": "1.31.0/aarch64-el8/nginx.tar.gz",
|
|
388
|
+
"sha256": "...",
|
|
389
|
+
"deps": "...",
|
|
390
|
+
"compile_info": { "docker_image": "rockylinux:8", "qemu": true, "compiler": "gcc 8.5.0" }
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### 四、HTTP API 设计
|
|
397
|
+
|
|
398
|
+
方案 A(静态文件)直接通过 URL 访问,无额外 API:
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
GET /nginx-binaries/manifest.json → 全局版本列表
|
|
402
|
+
GET /nginx-binaries/1.31.0/manifest.json → 某版本的所有平台二进制
|
|
403
|
+
GET /nginx-binaries/1.31.0/x86_64-el7/nginx.tar.gz → 下载
|
|
404
|
+
GET /nginx-binaries/1.31.0/x86_64-el7/nginx.tar.gz.sha256 → 校验
|
|
405
|
+
GET /nginx-binaries/1.31.0/x86_64-el7/deps.txt → 审计信息
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
方案 B (MinIO) / C (Nexus) 通过 S3 API / Nexus API 访问,`binary-registry.ts` 封装差异。
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
### 五、Nginx 服务端实现(方案 A 示例)
|
|
413
|
+
|
|
414
|
+
```nginx
|
|
415
|
+
# 在内部服务器上提供制品库 HTTP 访问
|
|
416
|
+
server {
|
|
417
|
+
listen 8080;
|
|
418
|
+
server_name registry.internal;
|
|
419
|
+
|
|
420
|
+
root /storage/nginx-binaries;
|
|
421
|
+
|
|
422
|
+
# 禁止目录浏览
|
|
423
|
+
autoindex off;
|
|
424
|
+
|
|
425
|
+
# 允许访问的文件类型
|
|
426
|
+
location ~ \.(json|gz|sha256|txt)$ {
|
|
427
|
+
autoindex on; # manifest.json 需要目录索引(或显式列出文件)
|
|
428
|
+
add_header Cache-Control "public, max-age=3600";
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
# 认证(可选,内网可关闭)
|
|
432
|
+
# auth_basic "Binary Registry";
|
|
433
|
+
# auth_basic_user_file /etc/nginx/.htpasswd;
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 六、二进制构建与上传流程(CI 侧)
|
|
438
|
+
|
|
439
|
+
这部分**不在 release-helper 项目中**,而是单独的 CI 仓库或 GitLab CI 配置。
|
|
440
|
+
|
|
441
|
+
#### 6.1 编译矩阵配置
|
|
442
|
+
|
|
443
|
+
```yaml
|
|
444
|
+
# nginx-binary-ci/build-config.yml
|
|
445
|
+
nginx_version: "1.31.0"
|
|
446
|
+
targets:
|
|
447
|
+
- id: x86_64-el7
|
|
448
|
+
arch: x86_64
|
|
449
|
+
os: centos
|
|
450
|
+
os_version: "7"
|
|
451
|
+
docker_image: centos:7
|
|
452
|
+
openssl_version: "1.0.2k"
|
|
453
|
+
- id: x86_64-el8
|
|
454
|
+
arch: x86_64
|
|
455
|
+
os: rocky
|
|
456
|
+
os_version: "8"
|
|
457
|
+
docker_image: rockylinux:8
|
|
458
|
+
openssl_version: "1.1.1k"
|
|
459
|
+
- id: x86_64-el9
|
|
460
|
+
arch: x86_64
|
|
461
|
+
os: rocky
|
|
462
|
+
os_version: "9"
|
|
463
|
+
docker_image: rockylinux:9
|
|
464
|
+
openssl_version: "3.0.7"
|
|
465
|
+
- id: aarch64-el8
|
|
466
|
+
arch: aarch64
|
|
467
|
+
os: rocky
|
|
468
|
+
os_version: "8"
|
|
469
|
+
docker_image: rockylinux:8
|
|
470
|
+
openssl_version: "1.1.1k"
|
|
471
|
+
qemu: true
|
|
472
|
+
- id: aarch64-el9
|
|
473
|
+
arch: aarch64
|
|
474
|
+
os: rocky
|
|
475
|
+
os_version: "9"
|
|
476
|
+
docker_image: rockylinux:9
|
|
477
|
+
openssl_version: "3.0.7"
|
|
478
|
+
qemu: true
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
#### 6.2 构建脚本(每个目标执行一次)
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
#!/bin/bash
|
|
485
|
+
# nginx-binary-ci/build.sh
|
|
486
|
+
set -euo pipefail
|
|
487
|
+
|
|
488
|
+
NGINX_VER="$1" # 1.31.0
|
|
489
|
+
TARGET_ID="$2" # x86_64-el7
|
|
490
|
+
DOCKER_IMG="$3" # centos:7
|
|
491
|
+
ARCH="$4" # x86_64
|
|
492
|
+
|
|
493
|
+
OUTPUT_DIR="output/${NGINX_VER}/${TARGET_ID}"
|
|
494
|
+
mkdir -p "$OUTPUT_DIR"
|
|
495
|
+
|
|
496
|
+
# 下载 nginx 源码(若未缓存)
|
|
497
|
+
[ -f "cache/nginx-${NGINX_VER}.tar.gz" ] || \
|
|
498
|
+
curl -fSL "https://nginx.org/download/nginx-${NGINX_VER}.tar.gz" -o "cache/nginx-${NGINX_VER}.tar.gz"
|
|
499
|
+
|
|
500
|
+
# 在 Docker 中编译
|
|
501
|
+
docker run --rm \
|
|
502
|
+
${ARCH:+--platform "linux/${ARCH}"} \
|
|
503
|
+
-v "$(pwd)/cache:/src:ro" \
|
|
504
|
+
-v "$(pwd)/${OUTPUT_DIR}:/output" \
|
|
505
|
+
"$DOCKER_IMG" \
|
|
506
|
+
bash -c '
|
|
507
|
+
set -euo pipefail
|
|
508
|
+
yum install -y gcc gcc-c++ make pcre-devel zlib-devel openssl-devel
|
|
509
|
+
cd /tmp
|
|
510
|
+
tar xzf /src/nginx-'"${NGINX_VER}"'.tar.gz
|
|
511
|
+
cd nginx-'"${NGINX_VER}"'
|
|
512
|
+
./configure \
|
|
513
|
+
--prefix=/etc/nginx \
|
|
514
|
+
--with-http_ssl_module \
|
|
515
|
+
--with-http_v2_module \
|
|
516
|
+
--with-http_gzip_static_module \
|
|
517
|
+
--with-http_stub_status_module \
|
|
518
|
+
--with-stream
|
|
519
|
+
make -j$(nproc)
|
|
520
|
+
make install
|
|
521
|
+
|
|
522
|
+
# 打包
|
|
523
|
+
cd /etc/nginx
|
|
524
|
+
tar czf /output/nginx.tar.gz .
|
|
525
|
+
|
|
526
|
+
# 生成依赖声明
|
|
527
|
+
ldd sbin/nginx > /output/deps.txt 2>&1 || true
|
|
528
|
+
|
|
529
|
+
# 生成 sha256
|
|
530
|
+
sha256sum /output/nginx.tar.gz | awk "{print \$1}" > /output/nginx.tar.gz.sha256
|
|
531
|
+
'
|
|
532
|
+
|
|
533
|
+
echo "Build complete: ${OUTPUT_DIR}/"
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
#### 6.3 上传脚本
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
#!/bin/bash
|
|
540
|
+
# nginx-binary-ci/upload.sh
|
|
541
|
+
# 上传到制品库(方案 A:scp/rsync 到内部服务器)
|
|
542
|
+
|
|
543
|
+
REGISTRY_HOST="registry.internal"
|
|
544
|
+
REGISTRY_PATH="/storage/nginx-binaries"
|
|
545
|
+
|
|
546
|
+
NGINX_VER="$1"
|
|
547
|
+
|
|
548
|
+
# 上传所有文件
|
|
549
|
+
rsync -avz "output/${NGINX_VER}/" \
|
|
550
|
+
"${REGISTRY_HOST}:${REGISTRY_PATH}/${NGINX_VER}/"
|
|
551
|
+
|
|
552
|
+
# 重新生成版本级 manifest.json
|
|
553
|
+
node scripts/generate-manifest.js "$NGINX_VER"
|
|
554
|
+
|
|
555
|
+
# 上传 manifest
|
|
556
|
+
scp "output/${NGINX_VER}/manifest.json" \
|
|
557
|
+
"${REGISTRY_HOST}:${REGISTRY_PATH}/${NGINX_VER}/manifest.json"
|
|
558
|
+
|
|
559
|
+
# 更新全局 manifest
|
|
560
|
+
scp "output/manifest.json" \
|
|
561
|
+
"${REGISTRY_HOST}:${REGISTRY_PATH}/manifest.json"
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
#### 6.4 manifest 自动生成
|
|
565
|
+
|
|
566
|
+
```javascript
|
|
567
|
+
// nginx-binary-ci/scripts/generate-manifest.js
|
|
568
|
+
import { readdirSync, readFileSync, statSync, writeFileSync } from 'fs';
|
|
569
|
+
|
|
570
|
+
const nginxBinaryVersion = process.argv[2]; // "1.31.0"
|
|
571
|
+
const outputDir = `output/${nginxBinaryVersion}`;
|
|
572
|
+
|
|
573
|
+
const targets = readdirSync(outputDir).filter(d =>
|
|
574
|
+
statSync(`${outputDir}/${d}`).isDirectory()
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
const binaries = targets.map(dir => {
|
|
578
|
+
const deps = readFileSync(`${outputDir}/${dir}/deps.txt`, 'utf-8');
|
|
579
|
+
const sha256 = readFileSync(`${outputDir}/${dir}/nginx.tar.gz.sha256`, 'utf-8').trim();
|
|
580
|
+
const size = statSync(`${outputDir}/${dir}/nginx.tar.gz`).size;
|
|
581
|
+
|
|
582
|
+
return {
|
|
583
|
+
id: dir,
|
|
584
|
+
arch: dir.split('-')[0],
|
|
585
|
+
os: extractOS(dir),
|
|
586
|
+
file_url: `${nginxBinaryVersion}/${dir}/nginx.tar.gz`,
|
|
587
|
+
sha256,
|
|
588
|
+
file_size: size,
|
|
589
|
+
deps
|
|
590
|
+
};
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
writeFileSync(`${outputDir}/manifest.json`, JSON.stringify({
|
|
594
|
+
nginx_version: nginxBinaryVersion,
|
|
595
|
+
built_at: new Date().toISOString(),
|
|
596
|
+
binaries
|
|
597
|
+
}, null, 2));
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
### 七、release-helper 侧:下载与集成
|
|
603
|
+
|
|
604
|
+
#### 7.1 `src/binary-registry.ts` 实现
|
|
605
|
+
|
|
606
|
+
```typescript
|
|
607
|
+
interface BinaryRegistryConfig {
|
|
608
|
+
url: string; // e.g. "http://registry.internal:8080"
|
|
609
|
+
type: 'http' | 'minio' | 'nexus';
|
|
610
|
+
auth?: { username: string; password: string };
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
interface BinaryInfo {
|
|
614
|
+
id: string;
|
|
615
|
+
arch: string;
|
|
616
|
+
os: string;
|
|
617
|
+
osVersion: string;
|
|
618
|
+
openssl: string;
|
|
619
|
+
fileUrl: string;
|
|
620
|
+
sha256: string;
|
|
621
|
+
deps: string;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
class BinaryRegistry {
|
|
625
|
+
constructor(private config: BinaryRegistryConfig) {}
|
|
626
|
+
|
|
627
|
+
// 获取指定 nginx 版本的所有平台二进制信息
|
|
628
|
+
async listBinaries(nginxVersion: string): Promise<BinaryInfo[]> {
|
|
629
|
+
const manifestUrl = `${this.config.url}/${nginxVersion}/manifest.json`;
|
|
630
|
+
const resp = await fetch(manifestUrl);
|
|
631
|
+
const manifest = await resp.json();
|
|
632
|
+
return manifest.binaries;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// 下载单个二进制并校验
|
|
636
|
+
async downloadBinary(info: BinaryInfo, destDir: string): Promise<string> {
|
|
637
|
+
const url = `${this.config.url}/${info.fileUrl}`;
|
|
638
|
+
const destPath = `${destDir}/${info.id}/nginx.tar.gz`;
|
|
639
|
+
|
|
640
|
+
// 下载
|
|
641
|
+
await downloadFile(url, destPath);
|
|
642
|
+
|
|
643
|
+
// 校验 sha256
|
|
644
|
+
const actualSha256 = await sha256File(destPath);
|
|
645
|
+
if (actualSha256 !== info.sha256) {
|
|
646
|
+
throw new Error(`SHA256 mismatch for ${info.id}: expected ${info.sha256}, got ${actualSha256}`);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// 写入 deps.txt 供审计
|
|
650
|
+
await writeFile(`${destDir}/${info.id}/deps.txt`, info.deps);
|
|
651
|
+
|
|
652
|
+
return destPath;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// pack 流程调用:下载所有平台二进制
|
|
656
|
+
async downloadAll(nginxVersion: string, destDir: string): Promise<void> {
|
|
657
|
+
const binaries = await this.listBinaries(nginxVersion);
|
|
658
|
+
logger.info(`Found ${binaries.length} platform binaries for nginx ${nginxVersion}`);
|
|
659
|
+
|
|
660
|
+
// 并发下载
|
|
661
|
+
await Promise.all(
|
|
662
|
+
binaries.map(b => this.downloadBinary(b, destDir))
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
#### 7.2 pack 流程改造
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
// src/pack.ts 核心逻辑
|
|
672
|
+
export async function pack(version?: string) {
|
|
673
|
+
// ... 前面的逻辑(版本输入、校验配置等)不变 ...
|
|
674
|
+
|
|
675
|
+
// 复制安装脚本和 nginx 模板(不再复制二进制)
|
|
676
|
+
fs.cpSync(resolve(__dirname, 'deploy/script'), resolve(projectDir, 'script'), { recursive: true });
|
|
677
|
+
fs.cpSync(resolve(__dirname, 'deploy/pkg/nginx'), resolve(projectDir, 'pkg/nginx'), { recursive: true });
|
|
678
|
+
|
|
679
|
+
// 从制品库下载所有平台 nginx 二进制
|
|
680
|
+
if (releaseConf.binaryRegistry?.url) {
|
|
681
|
+
const registry = new BinaryRegistry(releaseConf.binaryRegistry);
|
|
682
|
+
const nginxVersion = '1.31.0'; // 可从配置指定
|
|
683
|
+
await registry.downloadAll(nginxVersion, resolve(projectDir, 'pkg/nginx_binary'));
|
|
684
|
+
} else {
|
|
685
|
+
// 回退:如果没有配置制品库,从本地 src/deploy/pkg/nginx_binary 复制
|
|
686
|
+
// (兼容开发环境或离线场景)
|
|
687
|
+
fs.cpSync(resolve(__dirname, 'deploy/pkg/nginx_binary'), resolve(projectDir, 'pkg/nginx_binary'), { recursive: true });
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// ... 后续打包逻辑不变 ...
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
#### 7.3 离线场景支持
|
|
695
|
+
|
|
696
|
+
```yaml
|
|
697
|
+
# release.conf.yaml
|
|
698
|
+
binaryRegistry:
|
|
699
|
+
url: "" # 为空时使用本地二进制回退
|
|
700
|
+
type: "http"
|
|
701
|
+
|
|
702
|
+
# 离线场景:制品库不可达时使用本地备份
|
|
703
|
+
# 本地备份放在 src/deploy/pkg/nginx_binary/(不提交到 git,但可手动放置)
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### 八、版本管理策略
|
|
707
|
+
|
|
708
|
+
```
|
|
709
|
+
1. 每次编译新的 nginx 二进制时,创建新的版本目录(如 1.31.0/)
|
|
710
|
+
2. 旧版本目录保留不删除(支持回滚)
|
|
711
|
+
3. manifest.json 中的 versions 数组维护可用版本列表
|
|
712
|
+
4. release.conf.yaml 中可指定使用哪个 nginx 版本:
|
|
713
|
+
|
|
714
|
+
nginxBinaryVersion: "1.31.0" # 默认使用 latest
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
### 九、安全与权限
|
|
718
|
+
|
|
719
|
+
| 层面 | 措施 |
|
|
720
|
+
|------|------|
|
|
721
|
+
| 传输 | HTTPS(内网可用自签名证书,`rejectUnauthorized: false`) |
|
|
722
|
+
| 完整性 | SHA256 校验,下载后验证 |
|
|
723
|
+
| 访问控制 | 方案 A:nginx basic auth 或 IP 白名单;方案 B/C:各自认证机制 |
|
|
724
|
+
| 审计 | 每个二进制附带 deps.txt,记录完整动态链接依赖 |
|
|
725
|
+
| 上传 | CI 专用 token/SSH key,非人工上传 |
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
## 第六阶段:构建与测试
|
|
730
|
+
|
|
731
|
+
### 6.1 二进制矩阵构建(CI 侧,非项目代码)
|
|
732
|
+
|
|
733
|
+
在制品库/CI 中维护编译矩阵:
|
|
734
|
+
|
|
735
|
+
```yaml
|
|
736
|
+
# nginx-binary-build.yml (放在单独的 CI 仓库)
|
|
737
|
+
targets:
|
|
738
|
+
- id: x86_64-el7
|
|
739
|
+
docker_image: centos:7
|
|
740
|
+
arch: x86_64
|
|
741
|
+
- id: x86_64-el8
|
|
742
|
+
docker_image: rockylinux:8
|
|
743
|
+
arch: x86_64
|
|
744
|
+
- id: x86_64-el9
|
|
745
|
+
docker_image: rockylinux:9
|
|
746
|
+
arch: x86_64
|
|
747
|
+
- id: aarch64-el8
|
|
748
|
+
docker_image: rockylinux:8
|
|
749
|
+
arch: aarch64
|
|
750
|
+
qemu: true
|
|
751
|
+
- id: aarch64-el9
|
|
752
|
+
docker_image: rockylinux:9
|
|
753
|
+
arch: aarch64
|
|
754
|
+
qemu: true
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
每个 Docker 容器中:动态链接编译 nginx,输出 `nginx.tar.gz` + `deps.txt`。
|
|
758
|
+
|
|
759
|
+
### 6.2 测试(本项目新增)
|
|
760
|
+
|
|
761
|
+
依赖新增:`vitest`、`memfs`
|
|
762
|
+
|
|
763
|
+
```
|
|
764
|
+
src/
|
|
765
|
+
├── pack.test.ts # 测试 pack 流程(memfs mock 文件系统)
|
|
766
|
+
├── config/schema.test.ts # 测试 Zod schema 校验
|
|
767
|
+
├── platforms/gitlab.test.ts # 测试 GitLab API 调用(nock mock)
|
|
768
|
+
└── templates.test.ts # 测试 Handlebars 模板渲染
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
## 文件变更清单
|
|
774
|
+
|
|
775
|
+
### 新建文件
|
|
776
|
+
```
|
|
777
|
+
# 核心功能
|
|
778
|
+
src/config/schema.ts # Zod 配置 schema
|
|
779
|
+
src/config/env.ts # 环境变量解析
|
|
780
|
+
src/platform/types.ts # Platform 接口定义
|
|
781
|
+
src/platforms/gitlab.ts # GitLab 实现(从 publish.ts 迁移)
|
|
782
|
+
src/templates.ts # Handlebars 模板渲染
|
|
783
|
+
src/binary-registry.ts # 制品库下载逻辑
|
|
784
|
+
|
|
785
|
+
# 制品库 CI 侧(单独仓库)
|
|
786
|
+
nginx-binary-ci/build-config.yml # 编译矩阵配置
|
|
787
|
+
nginx-binary-ci/build.sh # Docker 中编译 nginx
|
|
788
|
+
nginx-binary-ci/upload.sh # 上传到制品库
|
|
789
|
+
nginx-binary-ci/scripts/generate-manifest.js # manifest 自动生成
|
|
790
|
+
|
|
791
|
+
# 制品库服务端(方案 A)
|
|
792
|
+
nginx-binary-registry/nginx.conf # nginx 配置,提供 HTTP 访问
|
|
793
|
+
|
|
794
|
+
# 测试
|
|
795
|
+
src/pack.test.ts
|
|
796
|
+
src/config/schema.test.ts
|
|
797
|
+
src/templates.test.ts
|
|
798
|
+
src/binary-registry.test.ts
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### 修改文件
|
|
802
|
+
```
|
|
803
|
+
src/cli.ts # 新增 --dry-run, --platform 选项
|
|
804
|
+
src/prepare.ts # 改用 Zod 校验, 支持 ${ENV} 语法
|
|
805
|
+
src/pack.ts # 重构:模板引擎 + 制品库下载
|
|
806
|
+
src/publish.ts # 简化,委托给 Platform 实现
|
|
807
|
+
src/release.ts # 适配新接口
|
|
808
|
+
src/deploy/script/common.sh # 修复 bug, install/upgrade 差异化
|
|
809
|
+
src/deploy/script/nginx.sh # 重构二进制选择, 启用 __root__ 替换
|
|
810
|
+
src/deploy/script/install.sh # 差异化逻辑
|
|
811
|
+
src/deploy/script/upgrade.sh # 差异化逻辑
|
|
812
|
+
src/deploy/script/prompt.sh # 统一占位符
|
|
813
|
+
src/deploy/pkg/nginx/*.tpl # 统一 {{VAR}} 占位符
|
|
814
|
+
src/.release/release.conf.yaml # 新增 platform, binaryRegistry 字段
|
|
815
|
+
src/.release/nginx/nginx.conf # 统一 {{VAR}} 占位符
|
|
816
|
+
package.json # 新增 zod, handlebars, vitest 依赖
|
|
817
|
+
rollup.config.js # 可能需要调整 handlebars 打包
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### 删除/清理
|
|
821
|
+
```
|
|
822
|
+
src/deploy/pkg/nginx_binary/ # 从 Git 仓库移除(移到制品库)
|
|
823
|
+
src/deploy/pkg/nginx_binary/binary/bak_1.22.1/ # 清理旧版本
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
## 实施顺序与阶段
|
|
829
|
+
|
|
830
|
+
| 阶段 | 内容 | 风险 | 可回滚 |
|
|
831
|
+
|------|------|------|--------|
|
|
832
|
+
| 0. 制品库搭建 | 存储部署 + CI 构建流水线 + manifest | 中 | 是(保留旧二进制在 Git 中) |
|
|
833
|
+
| 1. Plugin 化 | Platform 接口 + GitLab 迁移 | 低 | 是 |
|
|
834
|
+
| 2. 配置系统 | Zod + 环境变量 | 低 | 是 |
|
|
835
|
+
| 3. Bash 脚本修复 | Bug fix + 差异化 | 中 | 是(需测试) |
|
|
836
|
+
| 4. 模板统一 | Handlebars + 占位符统一 | 中 | 是(兼容旧语法过渡) |
|
|
837
|
+
| 5. 制品库集成 | binary-registry.ts + pack 改造 | 高 | 是(无制品库配置时回退本地) |
|
|
838
|
+
| 6. Changelog + dry-run | Conventional Commits | 低 | 是 |
|
|
839
|
+
| 7. 测试 | vitest + memfs | 低 | 是 |
|
|
840
|
+
|
|
841
|
+
**阶段 0(制品库)必须先于阶段 5 完成**,否则 pack 无法获取二进制。过渡期保留 `src/deploy/pkg/nginx_binary/` 作为本地回退。
|
|
842
|
+
|
|
843
|
+
---
|
|
844
|
+
|
|
845
|
+
## 验证方案
|
|
846
|
+
|
|
847
|
+
### 制品库侧(阶段 0)
|
|
848
|
+
1. **构建验证**:CI 流水线跑完,每个目标目录包含 `nginx.tar.gz` + `nginx.tar.gz.sha256` + `deps.txt`
|
|
849
|
+
2. **HTTP 验证**:`curl http://registry.internal:8080/1.31.0/manifest.json` 返回正确 JSON
|
|
850
|
+
3. **下载校验**:手动下载 `nginx.tar.gz`,比对 sha256 与 `.sha256` 文件一致
|
|
851
|
+
4. **部署验证**:下载的 nginx 二进制在对应 Docker 容器中 `ldd` 检查依赖匹配 `deps.txt`
|
|
852
|
+
|
|
853
|
+
### release-helper 侧
|
|
854
|
+
5. **本地测试**:在示例项目中执行 `release-helper init → pack`,验证 tar.gz 结构正确
|
|
855
|
+
6. **脚本测试**:在 CentOS 7/8/9 Docker 容器中运行 `install.sh`,验证 nginx 安装和功能正常
|
|
856
|
+
7. **模板测试**:运行 `vitest src/templates.test.ts`,验证所有占位符正确替换
|
|
857
|
+
8. **配置测试**:运行 `vitest src/config/schema.test.ts`,验证必填字段和类型校验
|
|
858
|
+
9. **制品库下载测试**:运行 `vitest src/binary-registry.test.ts`,mock HTTP 下载并校验
|
|
859
|
+
10. **dry-run 测试**:`release-helper release --dry-run` 输出操作列表但不执行
|
|
860
|
+
11. **离线回退测试**:制品库不可达时,pack 自动回退到本地 `src/deploy/pkg/nginx_binary/`
|
|
861
|
+
12. **平台测试**:切换 `platform: gitlab` 后,完整 release 流程正常
|