-
Notifications
You must be signed in to change notification settings - Fork 0
354 lines (305 loc) · 13.5 KB
/
Copy pathrelease.yml
File metadata and controls
354 lines (305 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
name: Release
on:
workflow_dispatch:
inputs:
version:
description: "发布版本,例如 v1.5.0"
required: true
type: string
prerelease:
description: "是否标记为预发布"
required: false
default: false
type: boolean
push:
tags:
- "v*"
permissions:
contents: write
packages: write
id-token: write
env:
IMAGE_NAME: ghcr.io/sevoniva/oceanbase-mcp
jobs:
metadata:
name: 准备发布信息
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.meta.outputs.tag }}
version: ${{ steps.meta.outputs.version }}
steps:
- name: 解析版本
id: meta
shell: bash
env:
INPUT_VERSION: ${{ github.event.inputs.version || '' }}
run: |
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
TAG="${INPUT_VERSION}"
else
TAG="${GITHUB_REF_NAME}"
fi
if [[ ! "${TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
echo "版本必须是 vX.Y.Z,例如 v1.5.0" >&2
exit 1
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
tag:
name: 创建 Tag
needs: metadata
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- name: 创建并推送 tag
shell: bash
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if git rev-parse "${{ needs.metadata.outputs.tag }}" >/dev/null 2>&1; then
echo "tag 已存在:${{ needs.metadata.outputs.tag }}"
else
git tag -a "${{ needs.metadata.outputs.tag }}" -m "Release ${{ needs.metadata.outputs.tag }}"
git push origin "${{ needs.metadata.outputs.tag }}"
fi
package:
name: 构建 npm 包
needs:
- metadata
- tag
if: always() && needs.metadata.result == 'success' && (needs.tag.result == 'success' || needs.tag.result == 'skipped')
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: 安装 Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "22"
cache: npm
- name: 安装依赖
run: npm ci
- name: 构建
run: npm run build
- name: 测试
run: npm test
- name: 生成 SBOM
run: npm sbom --sbom-format cyclonedx --omit=dev > sbom.cdx.json
- name: 打包源码
run: git archive --format=tar.gz --prefix=oceanbase-mcp-${{ needs.metadata.outputs.version }}/ HEAD -o oceanbase-mcp-${{ needs.metadata.outputs.version }}-source.tar.gz
- name: 打包
run: npm pack
- name: 上传 npm 包产物
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: npm-package
path: |
*.tgz
sbom.cdx.json
oceanbase-mcp-*-source.tar.gz
if-no-files-found: error
publish-npm:
name: 发布 npmjs 包
needs:
- metadata
- package
if: always() && needs.metadata.result == 'success' && needs.package.result == 'success'
runs-on: ubuntu-latest
steps:
- name: 下载 npm 包
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: npm-package
path: dist-release
- name: 安装 Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- name: 安装 npm 11
run: npm install -g npm@11
- name: 发布到 npmjs
shell: bash
run: |
set -euo pipefail
tarball="$(find dist-release -maxdepth 1 -name "*.tgz" -print -quit)"
if [[ -z "${tarball}" ]]; then
echo "No npm package tarball was found." >&2
exit 1
fi
if npm view "@sevoniva/oceanbase-mcp@${{ needs.metadata.outputs.version }}" version --registry=https://registry.npmjs.org/ >/dev/null 2>&1; then
echo "@sevoniva/oceanbase-mcp@${{ needs.metadata.outputs.version }} is already published; skipping npmjs publish."
exit 0
fi
tarball_path="$(realpath "${tarball}")"
npm publish "${tarball_path}" --access public --provenance
image:
name: 推送多架构镜像
needs:
- metadata
- tag
- package
if: always() && needs.metadata.result == 'success' && (needs.tag.result == 'success' || needs.tag.result == 'skipped') && needs.package.result == 'success'
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: 设置 QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: 登录 GHCR
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 构建并推送镜像
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.IMAGE_NAME }}:${{ needs.metadata.outputs.tag }}
${{ env.IMAGE_NAME }}:${{ needs.metadata.outputs.version }}
${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.title=OceanBase MCP
org.opencontainers.image.description=Read-only MCP gateway for OceanBase
org.opencontainers.image.source=https://github.com/sevoniva/oceanbase-mcp
org.opencontainers.image.version=${{ needs.metadata.outputs.version }}
offline-images:
name: 构建离线镜像
needs:
- metadata
- tag
- package
if: always() && needs.metadata.result == 'success' && (needs.tag.result == 'success' || needs.tag.result == 'skipped') && needs.package.result == 'success'
runs-on: ubuntu-latest
strategy:
matrix:
include:
- platform: linux/amd64
arch: amd64
- platform: linux/arm64
arch: arm64
steps:
- name: 检出代码
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: 设置 QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
- name: 设置 Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: 构建离线镜像 tar
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
context: .
platforms: ${{ matrix.platform }}
outputs: type=docker,dest=/tmp/oceanbase-mcp-${{ needs.metadata.outputs.version }}-${{ matrix.arch }}.tar
tags: oceanbase-mcp:${{ needs.metadata.outputs.version }}-${{ matrix.arch }}
- name: 压缩离线镜像
run: gzip -9 /tmp/oceanbase-mcp-${{ needs.metadata.outputs.version }}-${{ matrix.arch }}.tar
- name: 上传离线镜像产物
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: offline-image-${{ matrix.arch }}
path: /tmp/oceanbase-mcp-${{ needs.metadata.outputs.version }}-${{ matrix.arch }}.tar.gz
if-no-files-found: error
github-release:
name: 创建 GitHub Release
needs:
- metadata
- package
- publish-npm
- image
- offline-images
if: always() && needs.metadata.result == 'success' && needs.package.result == 'success' && needs.publish-npm.result == 'success' && needs.image.result == 'success' && needs.offline-images.result == 'success'
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: 下载 npm 包
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: npm-package
path: dist-release
- name: 下载 amd64 离线镜像
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: offline-image-amd64
path: dist-release
- name: 下载 arm64 离线镜像
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: offline-image-arm64
path: dist-release
- name: 写入发布说明
shell: bash
run: |
release_doc="docs/release-v${{ needs.metadata.outputs.version }}.md"
if [[ -f "${release_doc}" ]]; then
cp "${release_doc}" release-notes.md
exit 0
fi
cat > release-notes.md <<'EOF'
## OceanBase MCP ${{ needs.metadata.outputs.tag }}
This release adds npmjs distribution for OceanBase MCP as a Node.js CLI/runtime package. The public package name is `@sevoniva/oceanbase-mcp`; the CLI commands remain `ob-mcp`, `ob-mcp-http`, and `ob-mcp-doctor`. The server remains strictly read-only.
## 新增与修复
- npm 包名改为 `@sevoniva/oceanbase-mcp`,方便通过 npmjs 和 npm registry 镜像分发。
- CLI 命令继续保持 `ob-mcp`、`ob-mcp-http` 和 `ob-mcp-doctor`。
- 支持全局 npm 安装,也支持在开发终端通过 `npx --package` 调用 `ob-mcp`。
- Release workflow 使用 npm Trusted Publishing;npmjs 需要绑定 GitHub repository 和 `release.yml` workflow。
- npmjs 已存在相同版本时,Release workflow 会跳过 npm 发布并继续创建 Release 资产。
- Release 附件包含 scoped npm tarball 和 `oceanbase-mcp-${{ needs.metadata.outputs.version }}-source.tar.gz`。
- README、部署文档、OpenCode 文档和 release 文档补充 npmjs 安装与 registry mirror 说明。
- 只读策略、profile 权限、行数限制、输出大小限制、超时、审计日志和错误脱敏保持不变。
## Delivery Assets
- `oceanbase-mcp-${{ needs.metadata.outputs.version }}-source.tar.gz`:源码归档,用于离线构建。
- `sevoniva-oceanbase-mcp-${{ needs.metadata.outputs.version }}.tgz`:npm package。
- `sbom.cdx.json`:CycloneDX SBOM。
- `oceanbase-mcp-${{ needs.metadata.outputs.version }}-amd64.tar.gz`:amd64 离线镜像。
- `oceanbase-mcp-${{ needs.metadata.outputs.version }}-arm64.tar.gz`:arm64 离线镜像。
- `npm install -g @sevoniva/oceanbase-mcp@${{ needs.metadata.outputs.version }}`
- `npx --registry=https://registry.npmjs.org/ --yes --package=@sevoniva/oceanbase-mcp@${{ needs.metadata.outputs.version }} ob-mcp --help`
- `ghcr.io/sevoniva/oceanbase-mcp:${{ needs.metadata.outputs.tag }}`
- `ghcr.io/sevoniva/oceanbase-mcp:${{ needs.metadata.outputs.version }}`
## 离线源码构建
```bash
tar -xzf oceanbase-mcp-${{ needs.metadata.outputs.version }}-source.tar.gz
cd oceanbase-mcp-${{ needs.metadata.outputs.version }}
npm ci
npm run build
npm run start
```
源码归档不包含 `node_modules` 和 `dist`。npm registry 镜像需要能提供 `package-lock.json` 中锁定的依赖。
## 验证结果
- `npm test`:通过。
- `npm run docs:check`、`npm audit --omit=dev --audit-level=moderate`、`npm pack --dry-run`、Docker build 通过。
- 本地 OceanBase MySQL stdio 和 HTTP smoke test 通过。
- 本地 OceanBase Oracle stdio 和 HTTP smoke test 通过。
- `npm pack --dry-run` 产物名为 `sevoniva-oceanbase-mcp-${{ needs.metadata.outputs.version }}.tgz`。
- npmjs 发布步骤使用 Trusted Publishing;首次启用前需要在 npmjs 包设置中绑定 GitHub Actions。
## 生产注意事项
- 建议数据库账号仍使用只读权限;MCP 会继续拦截写入、DDL、DCL、过程调用和序列 `NEXTVAL`。
- 大 schema 先运行 `ob_plan_er_export`,再使用 CLI 分片导出。
- 全库 ER 不建议通过 MCP inline HTML 返回,应使用本地目录导出。
- `relationshipMode=inferred` 只用于明确的小范围分析,并必须查看 confidence 和 evidence。
- 表多或字段多时调小 `--chunk-size` 和 `--max-columns-per-table`。
- OpenCode 使用前建议先加载随包提供的 Agent/Skill 配置。
EOF
- name: 创建 Release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
with:
tag_name: ${{ needs.metadata.outputs.tag }}
name: OceanBase MCP ${{ needs.metadata.outputs.tag }}
body_path: release-notes.md
prerelease: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.prerelease == 'true' }}
files: |
dist-release/*.tgz
dist-release/*.json
dist-release/*.tar.gz