Release #18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |