From cf5f78119a036b75225063fdba3046ff0cf058cf Mon Sep 17 00:00:00 2001 From: Sewer56 Date: Tue, 19 May 2026 13:33:36 +0100 Subject: [PATCH 1/3] Cache package Electron native rebuild outputs Split the shared package composite action so packaged electron-rebuild outputs can use a dedicated actions/cache entry. This action is used by both package.yml and nightly.yml. This branch is based on the separate Electron rebuild controls PR. Controls alone do not optimize package.yml; this commit owns the package behavior changes: - install dependencies with VORTEX_ELECTRON_REBUILD=defer - cache native build headers - build the workspace without running a workspace Electron native rebuild - publish src/main/dist with VORTEX_ELECTRON_REBUILD=skip - restore/cache packaged native outputs under src/main/dist/node_modules - explicitly rebuild packaged native modules in src/main/dist - run electron-builder packaging from src/main Only the packaged/dist native output cache is needed. An initial version cached both workspace node_modules/.pnpm outputs and packaged src/main/dist/node_modules outputs, but probe runs on Sewer56/Vortex showed the workspace Electron rebuild/cache is not required for package.yml to build successfully. Removing the workspace rebuild/cache still passed publish and installer packaging. The packaged/dist cache is still required. pnpm deploy creates src/main/dist/node_modules from the content-addressable store and dist virtual store, so it does not preserve Electron-rebuilt workspace virtual-store outputs. Cold/no-hit dist-cache-only probe still passed, but spent about 97s from the packaged cache step to the installer step: - https://github.com/Sewer56/Vortex/actions/runs/26091864064 The original two-cache warmup spent about 67s in the packaged rebuild before the packaged cache existed: - https://github.com/Sewer56/Vortex/actions/runs/26067243939 Warm packaged cache hits reduce that rebuild path to about 1-2s. The explicit dist rebuild calls node ./node_modules/@electron/rebuild/lib/cli.js instead of pnpm --dir src/main/dist exec electron-rebuild because pnpm exec in the deployed dist package can try dependency resolution there and fail on peer-suffixed specs. Calling the installed CLI directly avoids that resolver path. Baseline package.yml runs before this optimization: - https://github.com/Sewer56/Vortex/actions/runs/26065180899 - https://github.com/Sewer56/Vortex/actions/runs/26065183051 - https://github.com/Sewer56/Vortex/actions/runs/26065185231 Package Vortex median was 814s. Original two-cache measured runs: - https://github.com/Sewer56/Vortex/actions/runs/26067806598 - https://github.com/Sewer56/Vortex/actions/runs/26067807434 - https://github.com/Sewer56/Vortex/actions/runs/26067808289 Package Vortex median was 642s (-172s vs baseline). Dist-cache-only warm measured runs: - https://github.com/Sewer56/Vortex/actions/runs/26092460250 - https://github.com/Sewer56/Vortex/actions/runs/26093157889 - https://github.com/Sewer56/Vortex/actions/runs/26093159302 Package Vortex median was 633s (-181s vs baseline). Packaged native cache hit on all warm measured runs. Cleaned-stack validation after rebase: - https://github.com/Sewer56/Vortex/actions/runs/26099063004 - https://github.com/Sewer56/Vortex/actions/runs/26099063777 - https://github.com/Sewer56/Vortex/actions/runs/26100726058 - https://github.com/Sewer56/Vortex/actions/runs/26115728616 - https://github.com/Sewer56/Vortex/actions/runs/26115728667 These runs succeeded with packaged cache hit true where measured. Package Vortex steps were 685s, 649s, and 661s in the first three validation runs. --- .github/actions/package/action.yml | 53 ++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/.github/actions/package/action.yml b/.github/actions/package/action.yml index 2160f5f40..a859bdf82 100644 --- a/.github/actions/package/action.yml +++ b/.github/actions/package/action.yml @@ -115,6 +115,55 @@ runs: - name: Install dependencies shell: pwsh run: pnpm install --frozen-lockfile + env: + VORTEX_ELECTRON_REBUILD: "defer" + + - name: Cache Electron native build headers + uses: actions/cache@v4 + with: + path: | + ~/.electron-gyp + ~/.cache/node-gyp + key: package-native-headers-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'src/main/package.json') }} + + - name: Build workspace + shell: pwsh + env: + NODE_ENV: production + run: pnpm run build + + - name: Publish dist package + shell: pwsh + env: + NODE_ENV: production + VORTEX_ELECTRON_REBUILD: "skip" + run: pnpm nx run @vortex/main:publish + + - name: Cache packaged Electron native rebuild outputs + id: packaged-native-cache + uses: actions/cache@v4 + with: + path: | + src/main/dist/node_modules/@nexusmods/fomod-installer-native/build + src/main/dist/node_modules/@nexusmods/fomod-installer-native/bin + src/main/dist/node_modules/drivelist/build + src/main/dist/node_modules/drivelist/bin + src/main/dist/node_modules/leveldown/build + src/main/dist/node_modules/leveldown/bin + src/main/dist/node_modules/winapi-bindings/build + src/main/dist/node_modules/winapi-bindings/bin + src/main/dist/node_modules/xxhash-addon/build + src/main/dist/node_modules/xxhash-addon/bin + src/main/dist/node_modules/@parcel/watcher/build + src/main/dist/node_modules/@parcel/watcher/bin + key: package-dist-electron-native-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'src/main/package.json', 'src/main/postinstall.mjs') }} + + - name: Rebuild packaged Electron native modules + shell: pwsh + working-directory: src/main/dist + run: | + Write-Output "Packaged Electron native cache hit: ${{ steps.packaged-native-cache.outputs.cache-hit }}" + node ./node_modules/@electron/rebuild/lib/cli.js - name: Build Installer shell: pwsh @@ -122,12 +171,12 @@ runs: $signingAvailable = -not [string]::IsNullOrWhiteSpace($env:ES_USERNAME) $wantSigning = "${{ inputs.use-codesigning }}" -eq "true" -or "${{ steps.nightly-flag.outputs.is_nightly }}" -eq "true" if ($wantSigning -and $signingAvailable) { - pnpm run package + pnpm --dir src/main run package } else { if ($wantSigning -and -not $signingAvailable) { Write-Warning "::warning::Codesigning requested but secrets unavailable - building unsigned" } - pnpm run package:nosign + pnpm --dir src/main run package:nosign } - name: Validate Package Creation From 596c95b38e64310f415d2f4779babd1d9c0e2c98 Mon Sep 17 00:00:00 2001 From: Sewer56 Date: Wed, 20 May 2026 22:41:38 +0100 Subject: [PATCH 2/3] fix(ci): use workspace filter instead of --dir for package commands pnpm --dir src/main run package works but is fragile - breaks silently if the directory moves. pnpm -F @vortex/main is the idiomatic workspace filter that resolves the same script by package name. Semantically equivalent: both run only the 'package' script without dependency resolution, preserving the native rebuild optimization. Using pnpm nx run would be WRONG here - it resolves the dependency chain (package -> publish -> rimraf ./dist), destroying the native rebuilds. --- .github/actions/package/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/package/action.yml b/.github/actions/package/action.yml index a859bdf82..e7f9c3b86 100644 --- a/.github/actions/package/action.yml +++ b/.github/actions/package/action.yml @@ -171,12 +171,12 @@ runs: $signingAvailable = -not [string]::IsNullOrWhiteSpace($env:ES_USERNAME) $wantSigning = "${{ inputs.use-codesigning }}" -eq "true" -or "${{ steps.nightly-flag.outputs.is_nightly }}" -eq "true" if ($wantSigning -and $signingAvailable) { - pnpm --dir src/main run package + pnpm -F @vortex/main run package } else { if ($wantSigning -and -not $signingAvailable) { Write-Warning "::warning::Codesigning requested but secrets unavailable - building unsigned" } - pnpm --dir src/main run package:nosign + pnpm -F @vortex/main run package:nosign } - name: Validate Package Creation From f87cbd554130a90892c5fa7e4da47d5e8a1fe8c4 Mon Sep 17 00:00:00 2001 From: Sewer56 Date: Thu, 21 May 2026 00:06:47 +0100 Subject: [PATCH 3/3] fix(ci): improve package action rebuild and script invocation - Replace pnpm --dir with pnpm -F for package/package:nosign scripts (--dir breaks silently if directory moves; -F uses package name) - Guard native rebuild on cache miss (skip @electron/rebuild entirely when packaged-native-cache hits, instead of running unconditionally for a wasteful tree scan) - Use npx electron-rebuild instead of direct node path (pnpm exec fails in dist/ with LOCKFILE_CONFIG_MISMATCH since deployed dist has no pnpm workspace context; npx resolves from node_modules/.bin without pnpm's lockfile validation) --- .github/actions/package/action.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/actions/package/action.yml b/.github/actions/package/action.yml index e7f9c3b86..90ef1867f 100644 --- a/.github/actions/package/action.yml +++ b/.github/actions/package/action.yml @@ -159,11 +159,10 @@ runs: key: package-dist-electron-native-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', 'src/main/package.json', 'src/main/postinstall.mjs') }} - name: Rebuild packaged Electron native modules + if: steps.packaged-native-cache.outputs.cache-hit != 'true' # skip when cached shell: pwsh working-directory: src/main/dist - run: | - Write-Output "Packaged Electron native cache hit: ${{ steps.packaged-native-cache.outputs.cache-hit }}" - node ./node_modules/@electron/rebuild/lib/cli.js + run: npx electron-rebuild - name: Build Installer shell: pwsh