diff --git a/.github/actions/setup-node-pnpm/action.yml b/.github/actions/setup-node-pnpm/action.yml new file mode 100644 index 00000000..b22a603a --- /dev/null +++ b/.github/actions/setup-node-pnpm/action.yml @@ -0,0 +1,41 @@ +name: Setup Node + pnpm +description: Setup pnpm, Node.js, pnpm store cache, and install workspace dependencies + +inputs: + node-version: + description: Node.js version + required: false + default: '25' + install: + description: Whether to run pnpm install + required: false + default: 'true' + +runs: + using: composite + steps: + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Cache pnpm store + uses: actions/cache@v4 + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install workspace dependencies + if: inputs.install == 'true' + shell: bash + run: pnpm install --frozen-lockfile --ignore-scripts diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml new file mode 100644 index 00000000..5c08148f --- /dev/null +++ b/.github/actions/setup-rust/action.yml @@ -0,0 +1,36 @@ +name: Setup Rust +description: Install Rust stable toolchain with cargo cache + +inputs: + targets: + description: Additional Rust targets to install (comma-separated) + required: false + default: '' + cache-key: + description: Extra cache key suffix for cargo + required: false + default: 'default' + cache-paths: + description: Additional paths to cache (newline-separated), appended to ~/.cargo/registry and ~/.cargo/git + required: false + default: 'target' + +runs: + using: composite + steps: + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ inputs.targets }} + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ${{ inputs.cache-paths }} + key: ${{ runner.os }}-cargo-${{ inputs.cache-key }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-${{ inputs.cache-key }}- + ${{ runner.os }}-cargo- diff --git a/.github/actions/setup-tauri/action.yml b/.github/actions/setup-tauri/action.yml new file mode 100644 index 00000000..d7541eee --- /dev/null +++ b/.github/actions/setup-tauri/action.yml @@ -0,0 +1,54 @@ +name: Setup Tauri Build +description: Common Tauri GUI build setup - cargo cache with deps-only hash, version sync, route generation + +inputs: + rust-targets: + description: Rust targets to install (comma-separated) + required: false + default: '' + version: + description: Version string to sync into Cargo.toml and tauri.conf.json + required: true + +runs: + using: composite + steps: + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ inputs.rust-targets }} + + - name: Get Cargo deps hash (exclude root package version for stable cache key) + id: cargo-deps-hash + shell: bash + run: | + HASH=$(awk '/^name = "memory-sync-gui"$/{print; getline; sub(/version = ".*"/, "version = \"0.0.0\""); print; next} 1' gui/src-tauri/Cargo.lock | openssl dgst -sha256 2>&1 | awk '{print $NF}') + echo "hash=$HASH" >> $GITHUB_OUTPUT + + - name: Cache cargo + tauri target + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + gui/src-tauri/target + key: ${{ runner.os }}-cargo-${{ steps.cargo-deps-hash.outputs.hash }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Sync Tauri version from CLI + shell: bash + run: | + version="${{ inputs.version }}" + sed -i.bak "s/^version = \".*\"/version = \"${version}\"/" gui/src-tauri/Cargo.toml + rm -f gui/src-tauri/Cargo.toml.bak + jq --arg v "$version" '.version = $v' gui/src-tauri/tauri.conf.json > gui/src-tauri/tauri.conf.tmp && mv gui/src-tauri/tauri.conf.tmp gui/src-tauri/tauri.conf.json + echo "Synced version to ${version}" + + - name: Generate route tree + shell: bash + run: pnpm -F @truenine/memory-sync-gui run generate:routes + + - name: Clean old bundle artifacts + shell: bash + run: rm -rf gui/src-tauri/target/**/bundle diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index eddba9ff..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: CI - -on: - pull_request: - branches: - - main - types: [opened, synchronize, reopened, closed] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-24.04 - if: github.event.pull_request.merged == false - steps: - - uses: actions/checkout@v4 - - - name: Cache apt packages - uses: actions/cache@v4 - with: - path: /var/cache/apt/archives - key: apt-gtk-${{ runner.os }}-${{ hashFiles('.github/workflows/ci.yml') }} - restore-keys: apt-gtk-${{ runner.os }}- - - - name: Install GTK development dependencies - run: | - sudo apt-get update - sudo apt-get install -y libgtk-3-dev libglib2.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf pkg-config - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 25 - cache: 'pnpm' - - - name: Get pnpm store directory - id: pnpm-cache - run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Setup pnpm store cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: pnpm install --frozen-lockfile --ignore-scripts - - - name: Build - run: pnpm exec turbo run build - - - name: Install Rust stable - uses: dtolnay/rust-toolchain@stable - - - name: Cache cargo - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-ci-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-ci- - - - name: Rust tests (excluding GUI) - run: cargo test --workspace --exclude memory-sync-gui diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 00000000..23a4c6de --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,12 @@ +name: Deploy Docs + +# TODO: Vercel deployment for doc/ Next.js site + +on: + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-24.04 + steps: + - run: echo "TODO" diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 00000000..ff111b3d --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,51 @@ +name: Pull Request + +on: + pull_request: + branches: + - main + types: [opened, synchronize, reopened, closed] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-24.04 + if: github.event.pull_request.merged == false + steps: + - uses: actions/checkout@v4 + + - name: Cache apt packages + uses: actions/cache@v4 + with: + path: /var/cache/apt/archives + key: apt-gtk-${{ runner.os }}-${{ hashFiles('.github/workflows/pull-request.yml') }} + restore-keys: apt-gtk-${{ runner.os }}- + + - name: Install GTK development dependencies + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libglib2.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf pkg-config + + - uses: ./.github/actions/setup-node-pnpm + + - name: Build + run: pnpm exec turbo run build + + - uses: ./.github/actions/setup-rust + with: + cache-key: pr + + - name: Generate Tauri icons + run: pnpm -F @truenine/memory-sync-gui run generate:icons + + - name: Generate route tree + run: pnpm -F @truenine/memory-sync-gui run generate:routes + + - name: Run tests + run: pnpm exec turbo run test + + - name: Rust tests (excluding GUI) + run: cargo test --workspace --exclude memory-sync-gui --lib --bins --tests diff --git a/.github/workflows/release-cli-binary.yml b/.github/workflows/release-cli-binary.yml deleted file mode 100644 index 8aa52bd7..00000000 --- a/.github/workflows/release-cli-binary.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: Release CLI Binary - -concurrency: - group: ${{ github.workflow }}-${{ inputs.version || github.run_id }} - cancel-in-progress: true - -on: - workflow_call: - inputs: - version: - required: true - type: string - workflow_dispatch: - inputs: - version: - description: 'Version to release (without v prefix, e.g. 2026.10222.0)' - required: true - type: string - -permissions: - contents: read - -jobs: - build-cli-binary: - 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@v4 - - # 1. Build plugin-runtime.mjs first (needed for embedded-runtime feature) - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 25 - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install & bundle plugin-runtime - shell: bash - run: | - pnpm install --frozen-lockfile --ignore-scripts - pnpm exec turbo run build --filter=@truenine/memory-sync-cli... - ls -la cli/dist/plugin-runtime.mjs - - # 2. Build Rust binary with embedded plugin-runtime.mjs - - name: Install Rust stable - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.target }} - - - name: Install cross-compilation tools (aarch64-linux) - if: matrix.cross - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu - echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV - - - name: Cache cargo registry & target - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-${{ matrix.target }}-cargo-cli-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.target }}-cargo-cli- - - - name: Build tnmsc binary (release, with embedded runtime) - run: cargo build --release --target ${{ matrix.target }} -p tnmsc --features embedded-runtime - - - name: Run tests (native only) - if: ${{ !matrix.cross }} - run: cargo test --release --target ${{ matrix.target }} -p tnmsc --features embedded-runtime - - # 3. Package binary + plugin-runtime.mjs - - 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@v4 - with: - name: cli-${{ matrix.target }} - path: ${{ matrix.archive }} - if-no-files-found: error diff --git a/.github/workflows/release-cli-napi.yml b/.github/workflows/release-cli-napi.yml deleted file mode 100644 index a85050c2..00000000 --- a/.github/workflows/release-cli-napi.yml +++ /dev/null @@ -1,165 +0,0 @@ -name: Release CLI Napi Packages - -concurrency: - group: ${{ github.workflow }}-${{ inputs.version || github.run_id }} - cancel-in-progress: false - -on: - workflow_call: - inputs: - version: - required: true - type: string - workflow_dispatch: - inputs: - version: - description: 'Version to release (without v prefix, e.g. 2026.10222.0)' - required: true - type: string - -permissions: - contents: read - -jobs: - build-napi: - 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@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 25 - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: pnpm install --frozen-lockfile --ignore-scripts - - - name: Install Rust stable - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.target.rust }} - - - name: Install cross-compilation tools (aarch64-linux) - if: matrix.target.cross - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu - echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV - - - name: Cache cargo registry & target - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-${{ matrix.target.rust }}-cargo-napi-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.target.rust }}-cargo-napi- - - - name: Build all napi libraries - shell: bash - run: | - for lib in logger md-compiler config init-bundle; do - echo "Building napi for $lib..." - (cd "libraries/$lib" && pnpm exec napi build --platform --release --target ${{ matrix.target.rust }} --output-dir dist -- --features napi) - done - - - name: Collect .node files into CLI platform package - shell: bash - run: | - target_dir="cli/npm/${{ matrix.target.suffix }}" - mkdir -p "$target_dir" - for lib in logger md-compiler config init-bundle; do - cp libraries/$lib/dist/*.node "$target_dir/" - done - echo "Contents of $target_dir:" - ls -la "$target_dir/" - - - name: Upload CLI platform package - uses: actions/upload-artifact@v4 - with: - name: cli-napi-${{ matrix.target.suffix }} - path: cli/npm/${{ matrix.target.suffix }}/ - if-no-files-found: error - - publish-cli-napi: - needs: build-napi - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 25 - registry-url: https://registry.npmjs.org/ - - - name: Download all platform artifacts - uses: actions/download-artifact@v4 - with: - path: artifacts - pattern: cli-napi-* - - - name: Distribute artifacts to cli/npm/ directories - shell: bash - run: | - 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: Publish CLI platform sub-packages - shell: bash - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: | - for dir in cli/npm/*/; do - if [ -f "${dir}package.json" ]; then - echo "Publishing ${dir}..." - (cd "$dir" && pnpm publish --access public --no-git-checks) || echo "⚠️ Failed to publish ${dir}, may already exist" - fi - done diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 107a3108..0c9f7f5c 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -29,7 +29,6 @@ jobs: version=$(jq -r '.version' cli/package.json) name=$(jq -r '.name' cli/package.json) npm_version=$(npm view "$name" version --registry https://registry.npmjs.org/ 2>/dev/null || echo "") - if [[ "$version" != "$npm_version" ]]; then echo "Version $version not published to npm, will publish" echo "publish=true" >> "$GITHUB_OUTPUT" @@ -39,72 +38,240 @@ jobs: echo "publish=false" >> "$GITHUB_OUTPUT" fi - # 2. 先发布架构包(用户安装主包时 optionalDependencies 已就绪) - release-cli-napi: + # 2. 构建 NAPI 二进制(5 平台矩阵) + build-napi: needs: check-version if: needs.check-version.outputs.publish == 'true' - uses: ./.github/workflows/release-cli-napi.yml - with: - version: ${{ needs.check-version.outputs.version }} - secrets: inherit + 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@v4 + - uses: ./.github/actions/setup-node-pnpm + - uses: ./.github/actions/setup-rust + with: + targets: ${{ matrix.target.rust }} + cache-key: napi-${{ matrix.target.rust }} + - name: Install cross-compilation tools (aarch64-linux) + if: matrix.target.cross + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV + - name: Build all napi libraries + shell: bash + run: | + for lib in logger md-compiler config init-bundle; do + echo "Building napi for $lib..." + (cd "libraries/$lib" && pnpm exec napi build --platform --release --target ${{ matrix.target.rust }} --output-dir dist -- --features napi) + done + - name: Collect .node files into CLI platform package + shell: bash + run: | + target_dir="cli/npm/${{ matrix.target.suffix }}" + mkdir -p "$target_dir" + for lib in logger md-compiler config init-bundle; do + cp libraries/$lib/dist/*.node "$target_dir/" + done + echo "Contents of $target_dir:" + ls -la "$target_dir/" + - name: Upload CLI platform package + uses: actions/upload-artifact@v4 + with: + name: cli-napi-${{ matrix.target.suffix }} + path: cli/npm/${{ matrix.target.suffix }}/ + if-no-files-found: error - # 3. 架构包就绪后,发布主包到 npm - publish-cli: - needs: [check-version, release-cli-napi] + # 3. 收集并发布 NAPI 平台子包到 npm + publish-napi: + needs: [check-version, build-napi] if: needs.check-version.outputs.publish == 'true' runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-node-pnpm with: - token: ${{ secrets.GH_PAT }} - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node + install: 'false' + - name: Setup npm registry uses: actions/setup-node@v4 with: node-version: 25 registry-url: https://registry.npmjs.org/ - cache: 'pnpm' - - - name: Get pnpm store directory - id: pnpm-cache - run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Setup pnpm store cache + - name: Download all platform artifacts + uses: actions/download-artifact@v4 with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install & Build + path: artifacts + pattern: cli-napi-* + - name: Distribute artifacts to cli/npm/ directories + shell: bash run: | - pnpm install --frozen-lockfile --ignore-scripts - pnpm exec turbo run build --filter=@truenine/memory-sync-cli... + 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: Publish CLI platform sub-packages + shell: bash + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + for dir in cli/npm/*/; do + if [ -f "${dir}package.json" ]; then + echo "Publishing ${dir}..." + (cd "$dir" && pnpm publish --access public --no-git-checks) || echo "⚠️ Failed to publish ${dir}, may already exist" + fi + done + # 4. 架构包就绪后,发布主包到 npm + publish-cli: + needs: [check-version, publish-napi] + if: needs.check-version.outputs.publish == 'true' + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_PAT }} + - uses: ./.github/actions/setup-node-pnpm + - name: Setup npm registry + uses: actions/setup-node@v4 + with: + node-version: 25 + registry-url: https://registry.npmjs.org/ + - name: Build + run: pnpm exec turbo run build --filter=@truenine/memory-sync-cli... - name: Publish to npm working-directory: ./cli run: pnpm publish --access public --no-git-checks env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - # 4. 构建 CLI 独立二进制(仅 artifact,不发 Release) - release-cli-binary: - needs: [check-version, release-cli-napi] + # 5. 构建 CLI 独立二进制(仅 artifact,不发 Release) + build-binary: + needs: [check-version, publish-napi] + if: needs.check-version.outputs.publish == 'true' + 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@v4 + - uses: ./.github/actions/setup-node-pnpm + - name: Build plugin-runtime + shell: bash + run: | + pnpm exec turbo run build --filter=@truenine/memory-sync-cli... + ls -la cli/dist/plugin-runtime.mjs + - uses: ./.github/actions/setup-rust + with: + targets: ${{ matrix.target }} + cache-key: cli-${{ matrix.target }} + - name: Install cross-compilation tools (aarch64-linux) + if: matrix.cross + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV + - name: Build tnmsc binary (release, with embedded runtime) + run: cargo build --release --target ${{ matrix.target }} -p tnmsc --features embedded-runtime + - name: Run tests (native only) + if: ${{ !matrix.cross }} + run: cargo test --release --target ${{ matrix.target }} -p tnmsc --features 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@v4 + with: + name: cli-${{ matrix.target }} + path: ${{ matrix.archive }} + if-no-files-found: error + + # 6. 构建 GUI — 三平台并行 + release-gui-win: + needs: [check-version, publish-cli] + if: needs.check-version.outputs.publish == 'true' + uses: ./.github/workflows/release-gui-win.yml + with: + version: ${{ needs.check-version.outputs.version }} + secrets: inherit + + release-gui-linux: + needs: [check-version, publish-cli] if: needs.check-version.outputs.publish == 'true' - uses: ./.github/workflows/release-cli-binary.yml + uses: ./.github/workflows/release-gui-linux.yml with: version: ${{ needs.check-version.outputs.version }} secrets: inherit - # 5. 构建 GUI 并创建 GitHub Release(等 CLI 主包发布完成后再构建) - release-gui: + release-gui-macos: needs: [check-version, publish-cli] if: needs.check-version.outputs.publish == 'true' - uses: ./.github/workflows/release-gui.yml + uses: ./.github/workflows/release-gui-macos.yml + with: + version: ${{ needs.check-version.outputs.version }} + secrets: inherit + + # 7. 收集三平台产物,创建 GitHub Release + tag + release-gui-collect: + needs: [check-version, release-gui-win, release-gui-linux, release-gui-macos] + if: needs.check-version.outputs.publish == 'true' + uses: ./.github/workflows/release-gui-collect.yml with: version: ${{ needs.check-version.outputs.version }} secrets: inherit diff --git a/.github/workflows/release-gui-collect.yml b/.github/workflows/release-gui-collect.yml new file mode 100644 index 00000000..fe81446a --- /dev/null +++ b/.github/workflows/release-gui-collect.yml @@ -0,0 +1,48 @@ +name: Release GUI Collect + +on: + workflow_call: + inputs: + version: + required: true + type: string + workflow_dispatch: + inputs: + version: + description: 'Version to release (without v prefix, e.g. 2026.10213.0)' + required: true + type: string + +permissions: + contents: write + +jobs: + publish-release: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - name: Download all GUI artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + pattern: gui-* + + - name: Clean up unnecessary macOS artifacts + run: | + find artifacts -name '*.icns' -delete + find artifacts -name 'Info.plist' -delete + + - name: Publish Release + uses: softprops/action-gh-release@v2.5.0 + with: + tag_name: v${{ inputs.version }} + name: v${{ inputs.version }} + files: | + artifacts/**/*.dmg + artifacts/**/*.exe + artifacts/**/*.AppImage + artifacts/**/*.deb + artifacts/**/*.rpm + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-gui-linux.yml b/.github/workflows/release-gui-linux.yml new file mode 100644 index 00000000..b87da61c --- /dev/null +++ b/.github/workflows/release-gui-linux.yml @@ -0,0 +1,66 @@ +name: Release GUI Linux + +on: + workflow_call: + inputs: + version: + required: true + type: string + workflow_dispatch: + inputs: + version: + description: 'Version to release (without v prefix, e.g. 2026.10213.0)' + required: true + type: string + +permissions: + contents: read + +jobs: + build-gui-linux: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-node-pnpm + + - name: Cache apt packages + uses: actions/cache@v4 + with: + path: /var/cache/apt/archives + key: apt-gtk-${{ runner.os }}-${{ hashFiles('.github/workflows/release-gui-linux.yml') }} + restore-keys: apt-gtk-${{ runner.os }}- + + - name: Install GTK development dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends libgtk-3-dev libglib2.0-dev pkg-config libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + + - uses: ./.github/actions/setup-tauri + with: + version: ${{ inputs.version }} + + - name: Build GUI + working-directory: ./gui + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + run: pnpm tauri build + + - name: List bundle output (debug) + shell: bash + run: | + echo "=== Finding all bundle artifacts ===" + find gui/src-tauri/target -path '*/bundle/*' -type f \( -name '*.AppImage' -o -name '*.deb' -o -name '*.rpm' \) 2>/dev/null || echo 'No bundle files found' + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: gui-ubuntu-24.04 + path: | + gui/src-tauri/target/*/release/bundle/**/*.AppImage + gui/src-tauri/target/release/bundle/**/*.AppImage + gui/src-tauri/target/*/release/bundle/**/*.deb + gui/src-tauri/target/release/bundle/**/*.deb + gui/src-tauri/target/*/release/bundle/**/*.rpm + gui/src-tauri/target/release/bundle/**/*.rpm + if-no-files-found: error diff --git a/.github/workflows/release-gui-macos.yml b/.github/workflows/release-gui-macos.yml new file mode 100644 index 00000000..e91c304e --- /dev/null +++ b/.github/workflows/release-gui-macos.yml @@ -0,0 +1,50 @@ +name: Release GUI macOS + +on: + workflow_call: + inputs: + version: + required: true + type: string + workflow_dispatch: + inputs: + version: + description: 'Version to release (without v prefix, e.g. 2026.10213.0)' + required: true + type: string + +permissions: + contents: read + +jobs: + build-gui-macos: + runs-on: macos-14 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-node-pnpm + - uses: ./.github/actions/setup-tauri + with: + rust-targets: aarch64-apple-darwin,x86_64-apple-darwin + version: ${{ inputs.version }} + + - name: Build GUI + working-directory: ./gui + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + run: pnpm tauri build --target universal-apple-darwin + + - name: List bundle output (debug) + shell: bash + run: | + echo "=== Finding all bundle artifacts ===" + find gui/src-tauri/target -path '*/bundle/*' -type f -name '*.dmg' 2>/dev/null || echo 'No bundle files found' + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: gui-macos-14 + path: | + gui/src-tauri/target/*/release/bundle/**/*.dmg + gui/src-tauri/target/release/bundle/**/*.dmg + if-no-files-found: error diff --git a/.github/workflows/release-gui-win.yml b/.github/workflows/release-gui-win.yml new file mode 100644 index 00000000..c854168f --- /dev/null +++ b/.github/workflows/release-gui-win.yml @@ -0,0 +1,49 @@ +name: Release GUI Windows + +on: + workflow_call: + inputs: + version: + required: true + type: string + workflow_dispatch: + inputs: + version: + description: 'Version to release (without v prefix, e.g. 2026.10213.0)' + required: true + type: string + +permissions: + contents: read + +jobs: + build-gui-win: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-node-pnpm + - uses: ./.github/actions/setup-tauri + with: + version: ${{ inputs.version }} + + - name: Build GUI + working-directory: ./gui + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + run: pnpm tauri build + + - name: List bundle output (debug) + shell: bash + run: | + echo "=== Finding all bundle artifacts ===" + find gui/src-tauri/target -path '*/bundle/*' -type f -name '*.exe' 2>/dev/null || echo 'No bundle files found' + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: gui-windows-latest + path: | + gui/src-tauri/target/*/release/bundle/**/*.exe + gui/src-tauri/target/release/bundle/**/*.exe + if-no-files-found: error diff --git a/.github/workflows/release-gui.yml b/.github/workflows/release-gui.yml deleted file mode 100644 index e18f5616..00000000 --- a/.github/workflows/release-gui.yml +++ /dev/null @@ -1,185 +0,0 @@ -name: Release GUI - -concurrency: - group: ${{ github.workflow }}-${{ inputs.version || github.run_id }} - cancel-in-progress: true - -on: - workflow_call: - inputs: - version: - required: true - type: string - workflow_dispatch: - inputs: - version: - description: 'Version to release (without v prefix, e.g. 2026.10213.0)' - required: true - type: string - -permissions: - contents: write - -jobs: - build-gui: - strategy: - fail-fast: true - matrix: - include: - - platform: 'macos-14' - args: '--target universal-apple-darwin' - rust_targets: 'aarch64-apple-darwin,x86_64-apple-darwin' - artifact_globs: | - gui/src-tauri/target/*/release/bundle/**/*.dmg - gui/src-tauri/target/release/bundle/**/*.dmg - - platform: 'ubuntu-24.04' - args: '' - rust_targets: '' - artifact_globs: | - gui/src-tauri/target/*/release/bundle/**/*.AppImage - gui/src-tauri/target/release/bundle/**/*.AppImage - gui/src-tauri/target/*/release/bundle/**/*.deb - gui/src-tauri/target/release/bundle/**/*.deb - gui/src-tauri/target/*/release/bundle/**/*.rpm - gui/src-tauri/target/release/bundle/**/*.rpm - - platform: 'windows-latest' - args: '' - rust_targets: '' - artifact_globs: | - gui/src-tauri/target/*/release/bundle/**/*.exe - gui/src-tauri/target/release/bundle/**/*.exe - - runs-on: ${{ matrix.platform }} - steps: - - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 25 - - - name: Get pnpm store directory - id: pnpm-cache - shell: bash - run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install Rust stable - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - targets: ${{ matrix.rust_targets }} - - - name: Get Cargo deps hash (exclude root package version for stable cache key) - id: cargo-deps-hash - shell: bash - run: | - # Normalise root package version in Cargo.lock so version bumps don't invalidate cache - # Use openssl (macOS/Linux/Windows) instead of sha256sum (Linux-only) - HASH=$(awk '/^name = "memory-sync-gui"$/{print; getline; sub(/version = ".*"/, "version = \"0.0.0\""); print; next} 1' gui/src-tauri/Cargo.lock | openssl dgst -sha256 2>&1 | awk '{print $NF}') - echo "hash=$HASH" >> $GITHUB_OUTPUT - - - name: Cache cargo + tauri target - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - gui/src-tauri/target - key: ${{ runner.os }}-cargo-${{ steps.cargo-deps-hash.outputs.hash }} - restore-keys: | - ${{ runner.os }}-cargo- - - - name: Cache apt packages - if: matrix.platform == 'ubuntu-24.04' - uses: actions/cache@v4 - with: - path: /var/cache/apt/archives - key: apt-gtk-${{ runner.os }}-${{ hashFiles('.github/workflows/release-gui.yml') }} - restore-keys: apt-gtk-${{ runner.os }}- - - - name: Install dependencies (ubuntu x86_64) - if: matrix.platform == 'ubuntu-24.04' - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends libgtk-3-dev libglib2.0-dev pkg-config libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf - - - name: Install workspace dependencies - run: pnpm install --frozen-lockfile --ignore-scripts - - - name: Sync Tauri version from CLI - shell: bash - run: | - version="${{ inputs.version }}" - sed -i.bak "s/^version = \".*\"/version = \"${version}\"/" gui/src-tauri/Cargo.toml - rm -f gui/src-tauri/Cargo.toml.bak - jq --arg v "$version" '.version = $v' gui/src-tauri/tauri.conf.json > gui/src-tauri/tauri.conf.tmp && mv gui/src-tauri/tauri.conf.tmp gui/src-tauri/tauri.conf.json - echo "Synced version to ${version}" - - - name: Generate route tree - run: pnpm -F @truenine/memory-sync-gui run generate:routes - - - name: Clean old bundle artifacts - shell: bash - run: rm -rf gui/src-tauri/target/**/bundle - - - name: Build GUI - working-directory: ./gui - env: - TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} - run: pnpm tauri build ${{ matrix.args }} - - - name: List bundle output (debug) - shell: bash - run: | - echo "=== Finding all bundle artifacts ===" - find gui/src-tauri/target -path '*/bundle/*' -type f \( -name '*.dmg' -o -name '*.exe' -o -name '*.AppImage' -o -name '*.deb' -o -name '*.rpm' \) 2>/dev/null || echo 'No bundle files found' - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: gui-${{ matrix.platform }} - path: ${{ matrix.artifact_globs }} - if-no-files-found: error - - publish-release: - needs: build-gui - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - path: artifacts - pattern: gui-* - - - name: Clean up unnecessary macOS artifacts - run: | - find artifacts -name '*.icns' -delete - find artifacts -name 'Info.plist' -delete - - - name: Publish Release - uses: softprops/action-gh-release@v2.5.0 - with: - tag_name: v${{ inputs.version }} - name: v${{ inputs.version }} - files: | - artifacts/**/*.dmg - artifacts/**/*.exe - artifacts/**/*.AppImage - artifacts/**/*.deb - artifacts/**/*.rpm - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index d07fa872..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Test - -on: - pull_request: - branches: - - main - types: [opened, synchronize, reopened, closed] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - test: - runs-on: ubuntu-24.04 - if: github.event.pull_request.merged == false - steps: - - uses: actions/checkout@v4 - - - name: Cache apt packages - uses: actions/cache@v4 - with: - path: /var/cache/apt/archives - key: apt-gtk-${{ runner.os }}-${{ hashFiles('.github/workflows/test.yml') }} - restore-keys: apt-gtk-${{ runner.os }}- - - - name: Install GTK development dependencies - run: | - sudo apt-get update - sudo apt-get install -y libgtk-3-dev libglib2.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf pkg-config - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 25 - cache: 'pnpm' - - - name: Get pnpm store directory - id: pnpm-cache - run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v4 - name: Setup pnpm store cache - with: - path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: Install dependencies - run: pnpm install --frozen-lockfile --ignore-scripts - - - name: Install Rust stable - uses: dtolnay/rust-toolchain@stable - - - name: Cache cargo - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-test-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-test- - - - name: Generate Tauri icons - run: pnpm -F @truenine/memory-sync-gui run generate:icons - - - name: Generate route tree - run: pnpm -F @truenine/memory-sync-gui run generate:routes - - - name: Run tests - run: pnpm exec turbo run test - - - name: Rust tests (excluding GUI) - run: cargo test --workspace --exclude memory-sync-gui diff --git a/gui/package.json b/gui/package.json index 3aecf7a9..64347e30 100644 --- a/gui/package.json +++ b/gui/package.json @@ -19,7 +19,7 @@ "generate:routes": "tsx scripts/generate-routes.ts", "typecheck": "tsc --noEmit", "test:ui": "vitest --run", - "test:tauri": "cargo test --manifest-path src-tauri/Cargo.toml", + "test:tauri": "cargo test --manifest-path src-tauri/Cargo.toml --lib --bins --tests", "test": "pnpm run test:ui && pnpm tsx ./scripts/run-tauri-tests.ts" }, "dependencies": { diff --git a/gui/src-tauri/Cargo.toml b/gui/src-tauri/Cargo.toml index 03147348..41902044 100644 --- a/gui/src-tauri/Cargo.toml +++ b/gui/src-tauri/Cargo.toml @@ -9,7 +9,7 @@ repository.workspace = true [lib] name = "app_lib" -crate-type = ["staticlib", "cdylib", "rlib"] +crate-type = ["rlib"] [build-dependencies] tauri-build = { workspace = true } diff --git a/turbo.json b/turbo.json index 4ca4c1ed..f84f8440 100644 --- a/turbo.json +++ b/turbo.json @@ -3,7 +3,7 @@ "tasks": { "build": { "dependsOn": ["^build"], - "outputs": ["dist/**", "*.node"] + "outputs": ["dist/**", "*.node", ".next/**", "!.next/cache/**"] }, "test": { "dependsOn": ["build", "lint", "typecheck"],