Skip to content

Release

Release #18

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
version:
description: "发布版本,例如 v1.4.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
run: |
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
TAG="${{ inputs.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.4.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@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
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 分发和私有制品库同步。
- 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 安装与私有制品库同步说明。
- 只读策略、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 制品库需要能提供 `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' && inputs.prerelease == true }}
files: |
dist-release/*.tgz
dist-release/*.json
dist-release/*.tar.gz