Skip to content

Merge pull request #126 from TrueNine/dev #112

Merge pull request #126 from TrueNine/dev

Merge pull request #126 from TrueNine/dev #112

Workflow file for this run

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