Merge pull request #126 from TrueNine/dev #112
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 Packages | |
| env: | |
| NPM_REGISTRY_URL: https://registry.npmjs.org/ | |
| CLI_NATIVE_MODULE_DIRS: | | |
| libraries/logger | |
| libraries/md-compiler | |
| libraries/script-runtime | |
| sdk | |
| CLI_NATIVE_BINDING_PREFIXES: | | |
| napi-logger. | |
| napi-md-compiler. | |
| napi-script-runtime. | |
| napi-memory-sync-cli. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - .github/actions/** | |
| - .github/workflows/build-gui-all.yml | |
| - .github/workflows/release-*.yml | |
| - assets/** | |
| - cli/** | |
| - mcp/** | |
| - gui/** | |
| - libraries/** | |
| - scripts/** | |
| - Cargo.toml | |
| - Cargo.lock | |
| - package.json | |
| - pnpm-lock.yaml | |
| - pnpm-workspace.yaml | |
| - turbo.json | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| # 1. 版本检查(快速,决定是否继续) | |
| check-version: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| outputs: | |
| publish_cli: ${{ steps.check.outputs.publish_cli }} | |
| publish_mcp: ${{ steps.check.outputs.publish_mcp }} | |
| publish_npm: ${{ steps.check.outputs.publish_npm }} | |
| version: ${{ steps.check.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-node-pnpm | |
| with: | |
| cache: "false" | |
| install: "false" | |
| - name: Check if should publish | |
| id: check | |
| uses: ./.github/actions/check-release-state | |
| with: | |
| registry-url: ${{ env.NPM_REGISTRY_URL }} | |
| # 1.5. GUI 版本检查(独立于 npm,检查 GitHub Release) | |
| check-gui-version: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| outputs: | |
| should_release: ${{ steps.check.outputs.should_release }} | |
| version: ${{ steps.check.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Check if GUI should be released | |
| id: check | |
| uses: ./.github/actions/check-gui-release-state | |
| with: | |
| github-token: ${{ github.token }} | |
| # 2. 构建 NAPI 二进制(5 平台矩阵) | |
| build-napi: | |
| needs: check-version | |
| if: needs.check-version.outputs.publish_cli == 'true' | |
| timeout-minutes: 45 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - os: ubuntu-24.04 | |
| rust: x86_64-unknown-linux-gnu | |
| suffix: linux-x64-gnu | |
| - os: ubuntu-24.04 | |
| rust: aarch64-unknown-linux-gnu | |
| suffix: linux-arm64-gnu | |
| cross: true | |
| - os: macos-14 | |
| rust: aarch64-apple-darwin | |
| suffix: darwin-arm64 | |
| - os: macos-14 | |
| rust: x86_64-apple-darwin | |
| suffix: darwin-x64 | |
| - os: windows-latest | |
| rust: x86_64-pc-windows-msvc | |
| suffix: win32-x64-msvc | |
| runs-on: ${{ matrix.target.os }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-node-pnpm | |
| - uses: ./.github/actions/setup-rust | |
| with: | |
| targets: ${{ matrix.target.rust }} | |
| cache-key: napi-${{ matrix.target.rust }} | |
| - name: Setup cross-compile | |
| if: matrix.target.cross | |
| uses: ./.github/actions/setup-cross-compile | |
| - name: Build all napi native modules | |
| shell: bash | |
| run: | | |
| while IFS= read -r module_dir; do | |
| if [ -z "$module_dir" ]; then | |
| continue | |
| fi | |
| echo "Building napi in ${module_dir}..." | |
| ( | |
| cd "${module_dir}" && \ | |
| pnpm exec napi build --platform --release --target ${{ matrix.target.rust }} --output-dir dist -- --features napi | |
| ) | |
| done <<< "$CLI_NATIVE_MODULE_DIRS" | |
| - name: Collect .node files into CLI platform package | |
| shell: bash | |
| run: | | |
| target_dir="cli/npm/${{ matrix.target.suffix }}" | |
| mkdir -p "$target_dir" | |
| shopt -s nullglob | |
| while IFS= read -r module_dir; do | |
| if [ -z "$module_dir" ]; then | |
| continue | |
| fi | |
| node_files=("${module_dir}"/dist/*.node) | |
| if [ "${#node_files[@]}" -eq 0 ]; then | |
| echo "ERROR: no .node files found in ${module_dir}/dist" | |
| exit 1 | |
| fi | |
| cp "${node_files[@]}" "$target_dir/" | |
| done <<< "$CLI_NATIVE_MODULE_DIRS" | |
| expected_count=0 | |
| while IFS= read -r binding_prefix; do | |
| if [ -z "$binding_prefix" ]; then | |
| continue | |
| fi | |
| expected_count=$((expected_count + 1)) | |
| matches=("$target_dir"/${binding_prefix}*.node) | |
| if [ "${#matches[@]}" -eq 0 ]; then | |
| echo "ERROR: missing binding with prefix ${binding_prefix} in ${target_dir}" | |
| exit 1 | |
| fi | |
| done <<< "$CLI_NATIVE_BINDING_PREFIXES" | |
| actual_count=$(find "$target_dir" -maxdepth 1 -type f -name '*.node' | wc -l | tr -d ' ') | |
| if [ "$actual_count" -ne "$expected_count" ]; then | |
| echo "ERROR: expected ${expected_count} .node files in ${target_dir}, found ${actual_count}" | |
| exit 1 | |
| fi | |
| echo "Contents of $target_dir:" | |
| ls -la "$target_dir/" | |
| - name: Upload CLI platform package | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: cli-napi-${{ matrix.target.suffix }} | |
| path: cli/npm/${{ matrix.target.suffix }}/ | |
| if-no-files-found: error | |
| # 3. 收集并发布 NAPI 平台子包到 npm | |
| publish-napi: | |
| needs: [check-version, build-napi] | |
| if: needs.check-version.outputs.publish_cli == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-node-pnpm | |
| with: | |
| install: "true" | |
| registry-url: https://registry.npmjs.org/ | |
| - name: Download all platform artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: artifacts | |
| pattern: cli-napi-* | |
| - name: Distribute artifacts to cli/npm/ directories | |
| shell: bash | |
| run: | | |
| shopt -s nullglob | |
| for artifact_dir in artifacts/cli-napi-*/; do | |
| suffix=$(basename "$artifact_dir" | sed 's/cli-napi-//') | |
| target_dir="cli/npm/${suffix}" | |
| mkdir -p "$target_dir" | |
| echo "Copying from ${artifact_dir} to ${target_dir}" | |
| cp "${artifact_dir}"*.node "$target_dir/" || { echo "ERROR: no .node files found in ${artifact_dir}"; exit 1; } | |
| done | |
| - name: Generate CLI platform package shims | |
| shell: bash | |
| run: | | |
| shopt -s nullglob | |
| dirs=(cli/npm/*/) | |
| if [ "${#dirs[@]}" -eq 0 ]; then | |
| echo "No CLI platform package directories found" | |
| exit 0 | |
| fi | |
| pnpm exec tsx scripts/write-platform-package-shims.ts "${dirs[@]}" | |
| - name: Validate CLI platform packages | |
| shell: bash | |
| run: | | |
| shopt -s nullglob | |
| expected_count=0 | |
| while IFS= read -r binding_prefix; do | |
| if [ -z "$binding_prefix" ]; then | |
| continue | |
| fi | |
| expected_count=$((expected_count + 1)) | |
| done <<< "$CLI_NATIVE_BINDING_PREFIXES" | |
| for target_dir in cli/npm/*/; do | |
| if [ ! -f "${target_dir}package.json" ]; then | |
| continue | |
| fi | |
| if [ ! -f "${target_dir}noop.mjs" ]; then | |
| echo "ERROR: missing ${target_dir}noop.mjs" | |
| exit 1 | |
| fi | |
| if [ ! -f "${target_dir}noop.d.mts" ]; then | |
| echo "ERROR: missing ${target_dir}noop.d.mts" | |
| exit 1 | |
| fi | |
| actual_count=$(find "${target_dir}" -maxdepth 1 -type f -name '*.node' | wc -l | tr -d ' ') | |
| if [ "$actual_count" -ne "$expected_count" ]; then | |
| echo "ERROR: expected ${expected_count} .node files in ${target_dir}, found ${actual_count}" | |
| exit 1 | |
| fi | |
| while IFS= read -r binding_prefix; do | |
| if [ -z "$binding_prefix" ]; then | |
| continue | |
| fi | |
| matches=("${target_dir}"${binding_prefix}*.node) | |
| if [ "${#matches[@]}" -eq 0 ]; then | |
| echo "ERROR: missing binding with prefix ${binding_prefix} in ${target_dir}" | |
| exit 1 | |
| fi | |
| done <<< "$CLI_NATIVE_BINDING_PREFIXES" | |
| done | |
| - name: Publish CLI platform sub-packages | |
| uses: ./.github/actions/npm-publish-package | |
| with: | |
| npm-token: ${{ secrets.NPM_TOKEN }} | |
| registry-url: ${{ env.NPM_REGISTRY_URL }} | |
| package-dir: cli/npm | |
| # 4. 架构包就绪后,发布主包到 npm | |
| publish-cli: | |
| needs: [check-version, publish-napi] | |
| if: needs.check-version.outputs.publish_cli == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-node-pnpm | |
| with: | |
| registry-url: https://registry.npmjs.org/ | |
| - uses: ./.github/actions/npm-auth-preflight | |
| with: | |
| npm-token: ${{ secrets.NPM_TOKEN }} | |
| registry-url: ${{ env.NPM_REGISTRY_URL }} | |
| package-dir: cli | |
| package-name: "@truenine/memory-sync-cli" | |
| - name: Build | |
| run: pnpm -F @truenine/memory-sync-cli run build | |
| - uses: ./.github/actions/npm-publish-package | |
| with: | |
| npm-token: ${{ secrets.NPM_TOKEN }} | |
| registry-url: ${{ env.NPM_REGISTRY_URL }} | |
| package-dir: cli | |
| # 4.5. CLI 可用后,发布 MCP 包到 npm | |
| publish-mcp: | |
| needs: [check-version, publish-cli] | |
| if: | | |
| needs.check-version.outputs.publish_mcp == 'true' && | |
| (needs.publish-cli.result == 'success' || needs.publish-cli.result == 'skipped') | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-node-pnpm | |
| with: | |
| registry-url: https://registry.npmjs.org/ | |
| - uses: ./.github/actions/npm-auth-preflight | |
| with: | |
| npm-token: ${{ secrets.NPM_TOKEN }} | |
| registry-url: ${{ env.NPM_REGISTRY_URL }} | |
| package-dir: mcp | |
| package-name: "@truenine/memory-sync-mcp" | |
| - name: Build | |
| run: pnpm exec turbo run build --filter=@truenine/memory-sync-mcp | |
| - uses: ./.github/actions/npm-publish-package | |
| with: | |
| npm-token: ${{ secrets.NPM_TOKEN }} | |
| registry-url: ${{ env.NPM_REGISTRY_URL }} | |
| package-dir: mcp | |
| # 5. 构建 CLI 独立二进制(仅 artifact,不发 Release) | |
| build-binary: | |
| needs: [check-version, check-gui-version, publish-napi] | |
| if: | | |
| (needs.check-version.outputs.publish_cli == 'true' || needs.check-gui-version.outputs.should_release == 'true') && | |
| (needs.publish-napi.result == 'success' || needs.publish-napi.result == 'skipped') | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: ubuntu-24.04 | |
| target: x86_64-unknown-linux-gnu | |
| binary: tnmsc | |
| archive: tnmsc-linux-x86_64.tar.gz | |
| - platform: ubuntu-24.04 | |
| target: aarch64-unknown-linux-gnu | |
| binary: tnmsc | |
| archive: tnmsc-linux-aarch64.tar.gz | |
| cross: true | |
| - platform: macos-14 | |
| target: aarch64-apple-darwin | |
| binary: tnmsc | |
| archive: tnmsc-darwin-aarch64.tar.gz | |
| - platform: macos-14 | |
| target: x86_64-apple-darwin | |
| binary: tnmsc | |
| archive: tnmsc-darwin-x86_64.tar.gz | |
| - platform: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| binary: tnmsc.exe | |
| archive: tnmsc-windows-x86_64.zip | |
| runs-on: ${{ matrix.platform }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: ./.github/actions/setup-node-pnpm | |
| - name: Build plugin-runtime | |
| shell: bash | |
| run: | | |
| pnpm -F @truenine/memory-sync-cli run build | |
| ls -la cli/dist/plugin-runtime.mjs | |
| - uses: ./.github/actions/setup-rust | |
| with: | |
| targets: ${{ matrix.target }} | |
| cache-key: cli-${{ matrix.target }} | |
| - name: Setup cross-compile | |
| if: matrix.cross | |
| uses: ./.github/actions/setup-cross-compile | |
| - name: Build tnmsc binary (release, with embedded runtime) | |
| run: cargo build --release --target ${{ matrix.target }} -p tnmsc-cli-shell --features tnmsc/embedded-runtime | |
| - name: Run tests (native only) | |
| if: ${{ !matrix.cross }} | |
| run: cargo test --release --target ${{ matrix.target }} -p tnmsc-cli-shell --features tnmsc/embedded-runtime | |
| - name: Package (unix) | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| mkdir -p staging | |
| cp target/${{ matrix.target }}/release/${{ matrix.binary }} staging/ | |
| cp cli/dist/plugin-runtime.mjs staging/ | |
| cd staging | |
| tar czf ../${{ matrix.archive }} * | |
| - name: Package (windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force -Path staging | |
| Copy-Item "target/${{ matrix.target }}/release/${{ matrix.binary }}" staging/ | |
| Copy-Item "cli/dist/plugin-runtime.mjs" staging/ | |
| Compress-Archive -Path staging/* -DestinationPath ${{ matrix.archive }} | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: cli-${{ matrix.target }} | |
| path: ${{ matrix.archive }} | |
| if-no-files-found: error | |
| # 6. 构建 GUI — 三平台并行(fail-fast: 任一失败则全部取消) | |
| build-gui-all: | |
| needs: [check-gui-version] | |
| if: needs.check-gui-version.outputs.should_release == 'true' | |
| uses: ./.github/workflows/build-gui-all.yml | |
| with: | |
| version: ${{ needs.check-gui-version.outputs.version }} | |
| secrets: inherit | |
| # 7. 收集三平台产物,创建 GitHub Release + tag | |
| release-gui-collect: | |
| needs: [check-gui-version, build-gui-all, build-binary] | |
| if: needs.check-gui-version.outputs.should_release == 'true' | |
| permissions: | |
| contents: write | |
| uses: ./.github/workflows/release-gui-collect.yml | |
| with: | |
| version: ${{ needs.check-gui-version.outputs.version }} | |
| secrets: inherit |