From a4b997e4c9b76513e7e29ee072c7b6e4501a82f8 Mon Sep 17 00:00:00 2001 From: TrueNine Date: Tue, 24 Feb 2026 20:08:54 +0800 Subject: [PATCH 1/6] ci: restructure GitHub Actions workflows for improved organization - Delete monolithic ci.yml workflow and split into focused workflows - Rename test.yml to pull-request.yml with updated job naming for clarity - Add build step to pull-request workflow before Rust tests - Create deploy-docs.yml workflow for future Vercel documentation deployment - Consolidate CLI release workflows into single release-cli.yml - Add platform-specific GUI release workflows (macos, windows, linux) - Add release-gui-collect.yml for aggregating GUI build artifacts - Update cargo cache keys from test/ci to pr for consistency - Streamline workflow organization by separating concerns into dedicated files --- .github/workflows/ci.yml | 74 ----- .github/workflows/deploy-docs.yml | 12 + .../workflows/{test.yml => pull-request.yml} | 13 +- .github/workflows/release-cli-binary.yml | 140 -------- .github/workflows/release-cli-napi.yml | 165 ---------- .github/workflows/release-cli.yml | 306 +++++++++++++++++- .github/workflows/release-gui-collect.yml | 48 +++ ...{release-gui.yml => release-gui-linux.yml} | 95 +----- .github/workflows/release-gui-macos.yml | 108 +++++++ .github/workflows/release-gui-win.yml | 106 ++++++ 10 files changed, 589 insertions(+), 478 deletions(-) delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/deploy-docs.yml rename .github/workflows/{test.yml => pull-request.yml} (90%) delete mode 100644 .github/workflows/release-cli-binary.yml delete mode 100644 .github/workflows/release-cli-napi.yml create mode 100644 .github/workflows/release-gui-collect.yml rename .github/workflows/{release-gui.yml => release-gui-linux.yml} (55%) create mode 100644 .github/workflows/release-gui-macos.yml create mode 100644 .github/workflows/release-gui-win.yml 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/test.yml b/.github/workflows/pull-request.yml similarity index 90% rename from .github/workflows/test.yml rename to .github/workflows/pull-request.yml index d07fa872..e3541ed9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/pull-request.yml @@ -1,4 +1,4 @@ -name: Test +name: Pull Request on: pull_request: @@ -11,7 +11,7 @@ concurrency: cancel-in-progress: true jobs: - test: + check: runs-on: ubuntu-24.04 if: github.event.pull_request.merged == false steps: @@ -21,7 +21,7 @@ jobs: uses: actions/cache@v4 with: path: /var/cache/apt/archives - key: apt-gtk-${{ runner.os }}-${{ hashFiles('.github/workflows/test.yml') }} + key: apt-gtk-${{ runner.os }}-${{ hashFiles('.github/workflows/pull-request.yml') }} restore-keys: apt-gtk-${{ runner.os }}- - name: Install GTK development dependencies @@ -53,6 +53,9 @@ jobs: - 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 @@ -63,9 +66,9 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-cargo-test-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-cargo-pr-${{ hashFiles('Cargo.lock') }} restore-keys: | - ${{ runner.os }}-cargo-test- + ${{ runner.os }}-cargo-pr- - name: Generate Tauri icons run: pnpm -F @truenine/memory-sync-gui run generate:icons 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..835b6877 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -39,18 +39,157 @@ 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 + + - 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 + + # 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 + + - 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 - # 3. 架构包就绪后,发布主包到 npm + # 4. 架构包就绪后,发布主包到 npm publish-cli: - needs: [check-version, release-cli-napi] + needs: [check-version, publish-napi] if: needs.check-version.outputs.publish == 'true' runs-on: ubuntu-24.04 steps: @@ -91,20 +230,157 @@ jobs: 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 + + # 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 + + # 6. 构建 GUI — 三平台并行(等 CLI 主包发布完成后再构建) + 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.yml b/.github/workflows/release-gui-linux.yml similarity index 55% rename from .github/workflows/release-gui.yml rename to .github/workflows/release-gui-linux.yml index e18f5616..8bb4e62d 100644 --- a/.github/workflows/release-gui.yml +++ b/.github/workflows/release-gui-linux.yml @@ -1,8 +1,4 @@ -name: Release GUI - -concurrency: - group: ${{ github.workflow }}-${{ inputs.version || github.run_id }} - cancel-in-progress: true +name: Release GUI Linux on: workflow_call: @@ -18,38 +14,11 @@ on: type: string permissions: - contents: write + contents: read 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 }} + build-gui-linux: + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -76,16 +45,11 @@ jobs: - 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 @@ -101,15 +65,13 @@ jobs: ${{ 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') }} + key: apt-gtk-${{ runner.os }}-${{ hashFiles('.github/workflows/release-gui-linux.yml') }} restore-keys: apt-gtk-${{ runner.os }}- - - name: Install dependencies (ubuntu x86_64) - if: matrix.platform == 'ubuntu-24.04' + - 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 @@ -138,48 +100,23 @@ jobs: 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 }} + 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 '*.dmg' -o -name '*.exe' -o -name '*.AppImage' -o -name '*.deb' -o -name '*.rpm' \) 2>/dev/null || echo 'No bundle files found' + 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-${{ matrix.platform }} - path: ${{ matrix.artifact_globs }} + 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 - - 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/release-gui-macos.yml b/.github/workflows/release-gui-macos.yml new file mode 100644 index 00000000..9ef8d40f --- /dev/null +++ b/.github/workflows/release-gui-macos.yml @@ -0,0 +1,108 @@ +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 + + - 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: + targets: aarch64-apple-darwin,x86_64-apple-darwin + + - 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: 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 --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..ec2b7b3c --- /dev/null +++ b/.github/workflows/release-gui-win.yml @@ -0,0 +1,106 @@ +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 + + - 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 + + - 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: 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 + + - 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 From 88b0ad9b651800f5372bd80a3ac8b6ac69157e52 Mon Sep 17 00:00:00 2001 From: TrueNine Date: Tue, 24 Feb 2026 20:27:05 +0800 Subject: [PATCH 2/6] chore(gui): simplify Cargo.toml crate types configuration - Remove staticlib and cdylib from crate-type array in gui/src-tauri/Cargo.toml - Keep only rlib crate type for library compilation - Streamline build configuration to reduce unnecessary artifact generation --- gui/src-tauri/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 } From 9fa0321b75898e12e864fd5044bfb1de2b3e18f2 Mon Sep 17 00:00:00 2001 From: TrueNine Date: Tue, 24 Feb 2026 20:48:26 +0800 Subject: [PATCH 3/6] fix(gui): exclude doctests to resolve CI E0460 error Skip doctests in GUI cargo test to avoid incremental compilation cache conflicts with tnmsc_logger in CI environment. The app_lib crate has no doc examples, so this has no functional impact. Fixes: error[E0460]: found possibly newer version of crate tnmsc_logger --- gui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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": { From 5b482fe4506ff9484eca4d9958a66e610006085e Mon Sep 17 00:00:00 2001 From: TrueNine Date: Tue, 24 Feb 2026 21:10:27 +0800 Subject: [PATCH 4/6] ci: exclude doctests from workspace Rust tests Add --lib --bins --tests flags to cargo test in CI to skip doctests across all workspace crates. This prevents E0460 incremental compilation cache conflicts in CI environment while preserving all unit and integration tests. --- .github/workflows/pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index e3541ed9..fd44d877 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -80,4 +80,4 @@ jobs: run: pnpm exec turbo run test - name: Rust tests (excluding GUI) - run: cargo test --workspace --exclude memory-sync-gui + run: cargo test --workspace --exclude memory-sync-gui --lib --bins --tests From c0e2dc6197a5e5bfa6a52eacf374f78a5e86c77d Mon Sep 17 00:00:00 2001 From: TrueNine Date: Tue, 24 Feb 2026 21:11:57 +0800 Subject: [PATCH 5/6] fix(turbo): add Next.js output paths to build cache config Include .next/** in turbo outputs to properly cache Next.js builds for the docs site. Excludes .next/cache/** to avoid caching the internal Next.js cache directory. Fixes: WARNING no output files found for task @truenine/memory-sync-docs#build --- turbo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"], From 5511f1b9b9e41bfd05011c988ee392d468d57f4b Mon Sep 17 00:00:00 2001 From: TrueNine Date: Tue, 24 Feb 2026 21:21:27 +0800 Subject: [PATCH 6/6] ci: extract reusable GitHub Actions setup workflows - Create setup-node-pnpm action for Node.js and pnpm installation with caching - Create setup-rust action for Rust toolchain installation with cargo caching - Create setup-tauri action for Tauri GUI builds with version sync and route generation - Refactor pull-request workflow to use setup-node-pnpm and setup-rust actions - Refactor release-cli workflow to use setup-node-pnpm and setup-rust actions - Refactor release-gui-linux workflow to use setup-node-pnpm and setup-tauri actions - Refactor release-gui-macos workflow to use setup-node-pnpm and setup-tauri actions - Refactor release-gui-win workflow to use setup-node-pnpm and setup-tauri actions - Reduce workflow duplication and improve maintainability across CI/CD pipelines --- .github/actions/setup-node-pnpm/action.yml | 41 ++++++ .github/actions/setup-rust/action.yml | 36 ++++++ .github/actions/setup-tauri/action.yml | 54 ++++++++ .github/workflows/pull-request.yml | 38 +----- .github/workflows/release-cli.yml | 141 +++------------------ .github/workflows/release-gui-linux.yml | 64 +--------- .github/workflows/release-gui-macos.yml | 66 +--------- .github/workflows/release-gui-win.yml | 63 +-------- 8 files changed, 161 insertions(+), 342 deletions(-) create mode 100644 .github/actions/setup-node-pnpm/action.yml create mode 100644 .github/actions/setup-rust/action.yml create mode 100644 .github/actions/setup-tauri/action.yml 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/pull-request.yml b/.github/workflows/pull-request.yml index fd44d877..ff111b3d 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -29,46 +29,14 @@ jobs: 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 + - uses: ./.github/actions/setup-node-pnpm - name: Build run: pnpm exec turbo run build - - name: Install Rust stable - uses: dtolnay/rust-toolchain@stable - - - name: Cache cargo - uses: actions/cache@v4 + - uses: ./.github/actions/setup-rust with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-pr-${{ hashFiles('Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo-pr- + cache-key: pr - name: Generate Tauri icons run: pnpm -F @truenine/memory-sync-gui run generate:icons diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 835b6877..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" @@ -63,58 +62,20 @@ jobs: - 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 + - 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: 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: | @@ -122,7 +83,6 @@ jobs: 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: | @@ -133,7 +93,6 @@ jobs: done echo "Contents of $target_dir:" ls -la "$target_dir/" - - name: Upload CLI platform package uses: actions/upload-artifact@v4 with: @@ -148,22 +107,19 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node + - uses: ./.github/actions/setup-node-pnpm + with: + install: 'false' + - name: Setup npm registry 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: | @@ -174,7 +130,6 @@ jobs: 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: @@ -196,34 +151,14 @@ jobs: - uses: actions/checkout@v4 with: token: ${{ secrets.GH_PAT }} - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node + - 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/ - 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 & Build - run: | - pnpm install --frozen-lockfile --ignore-scripts - pnpm exec turbo run build --filter=@truenine/memory-sync-cli... - + - 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 @@ -259,72 +194,30 @@ jobs: 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 + - uses: ./.github/actions/setup-node-pnpm + - name: Build 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 + - 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: 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 @@ -334,7 +227,6 @@ jobs: cp cli/dist/plugin-runtime.mjs staging/ cd staging tar czf ../${{ matrix.archive }} * - - name: Package (windows) if: runner.os == 'Windows' shell: pwsh @@ -343,7 +235,6 @@ jobs: 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: @@ -351,7 +242,7 @@ jobs: path: ${{ matrix.archive }} if-no-files-found: error - # 6. 构建 GUI — 三平台并行(等 CLI 主包发布完成后再构建) + # 6. 构建 GUI — 三平台并行 release-gui-win: needs: [check-version, publish-cli] if: needs.check-version.outputs.publish == 'true' diff --git a/.github/workflows/release-gui-linux.yml b/.github/workflows/release-gui-linux.yml index 8bb4e62d..b87da61c 100644 --- a/.github/workflows/release-gui-linux.yml +++ b/.github/workflows/release-gui-linux.yml @@ -21,48 +21,7 @@ jobs: 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 - - - 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 - - - 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- + - uses: ./.github/actions/setup-node-pnpm - name: Cache apt packages uses: actions/cache@v4 @@ -76,24 +35,9 @@ jobs: 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 + - uses: ./.github/actions/setup-tauri + with: + version: ${{ inputs.version }} - name: Build GUI working-directory: ./gui diff --git a/.github/workflows/release-gui-macos.yml b/.github/workflows/release-gui-macos.yml index 9ef8d40f..e91c304e 100644 --- a/.github/workflows/release-gui-macos.yml +++ b/.github/workflows/release-gui-macos.yml @@ -21,69 +21,11 @@ jobs: runs-on: macos-14 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 + - uses: ./.github/actions/setup-node-pnpm + - uses: ./.github/actions/setup-tauri 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: - targets: aarch64-apple-darwin,x86_64-apple-darwin - - - 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: 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 + rust-targets: aarch64-apple-darwin,x86_64-apple-darwin + version: ${{ inputs.version }} - name: Build GUI working-directory: ./gui diff --git a/.github/workflows/release-gui-win.yml b/.github/workflows/release-gui-win.yml index ec2b7b3c..c854168f 100644 --- a/.github/workflows/release-gui-win.yml +++ b/.github/workflows/release-gui-win.yml @@ -21,67 +21,10 @@ jobs: runs-on: windows-latest 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 - - - 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 + - uses: ./.github/actions/setup-node-pnpm + - uses: ./.github/actions/setup-tauri 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: 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 + version: ${{ inputs.version }} - name: Build GUI working-directory: ./gui