From 104f44f01fcdfdd9531ef31540f541a4f221ef74 Mon Sep 17 00:00:00 2001 From: akemmanuel Date: Mon, 25 May 2026 12:22:57 -0300 Subject: [PATCH] ci: speed up macos builds - Split x64 and arm64 into parallel matrix jobs (was sequential) - Switch from macos-15-intel to macos-latest (Apple Silicon) - Override mac compression to 'normal' (was 'maximum') - Add preflight job: lint, check, build, test once; cache dist/ - Add Electron binary caching (~/.cache/electron, ~/.cache/electron-builder) - Add merge-latest-mac.mjs script to combine x64/arm64 update manifests - Set mac artifactName with arch qualifier for electron-updater - Enable asar.smartUnpack for faster packaging - Disable unused DMG writeUpdateInfo --- .github/workflows/build.yml | 123 ++++++++++++++++++++++++++++------- package.json | 9 ++- scripts/merge-latest-mac.mjs | 62 ++++++++++++++++++ 3 files changed, 168 insertions(+), 26 deletions(-) create mode 100644 scripts/merge-latest-mac.mjs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2165b8b..9441545 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,8 +5,10 @@ on: tags: ["v*"] jobs: - build-linux: + preflight: runs-on: ubuntu-latest + outputs: + dist-cache-key: ${{ steps.cache-dist.outputs.cache-key }} steps: - uses: actions/checkout@v6 @@ -32,6 +34,40 @@ jobs: - name: Run unit tests run: pnpm exec vp test + - name: Cache dist + id: cache-dist + uses: actions/cache/save@v6 + with: + path: | + dist + dist-electron + key: dist-${{ github.sha }} + + build-linux: + needs: preflight + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Restore dist + uses: actions/cache/restore@v6 + with: + path: | + dist + dist-electron + key: dist-${{ github.sha }} + fail-on-cache-miss: true + - name: Build Docker image run: docker build -t opengui:web . @@ -46,7 +82,12 @@ jobs: retention-days: 30 build-mac: - runs-on: macos-15-intel + needs: preflight + strategy: + matrix: + arch: [x64, arm64] + fail-fast: false + runs-on: macos-latest steps: - uses: actions/checkout@v6 @@ -57,27 +98,35 @@ jobs: node-version: 24 cache: pnpm + - name: Cache Electron binaries + uses: actions/cache@v6 + with: + path: | + ~/.cache/electron + ~/.cache/electron-builder + key: electron-cache-mac-${{ matrix.arch }}-${{ hashFiles('package.json') }} + - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Lint - run: pnpm exec vp lint - - - name: Check - run: pnpm exec vp check - - - name: Build frontend - run: pnpm exec vp build + - name: Restore dist + uses: actions/cache/restore@v6 + with: + path: | + dist + dist-electron + key: dist-${{ github.sha }} + fail-on-cache-miss: true - - name: Package mac artifacts - run: pnpm exec electron-builder --mac dmg zip --x64 --arm64 --publish never + - name: Package mac ${{ matrix.arch }} + run: pnpm exec electron-builder --mac dmg zip --${{ matrix.arch }} --publish never env: CSC_IDENTITY_AUTO_DISCOVERY: "false" - - name: Upload mac artifacts + - name: Upload mac ${{ matrix.arch }} artifacts uses: actions/upload-artifact@v6 with: - name: mac-release + name: mac-release-${{ matrix.arch }} path: | release/*.dmg release/*.zip @@ -86,6 +135,7 @@ jobs: retention-days: 30 build-windows: + needs: preflight runs-on: windows-latest steps: - uses: actions/checkout@v6 @@ -100,14 +150,14 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Lint - run: pnpm exec vp lint - - - name: Check - run: pnpm exec vp check - - - name: Build frontend - run: pnpm exec vp build + - name: Restore dist + uses: actions/cache/restore@v6 + with: + path: | + dist + dist-electron + key: dist-${{ github.sha }} + fail-on-cache-miss: true - name: Package .exe (NSIS) run: pnpm exec electron-builder --win nsis --publish never @@ -209,24 +259,49 @@ jobs: permissions: contents: write steps: + - uses: actions/checkout@v6 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Download linux artifact uses: actions/download-artifact@v6 with: name: linux-deb path: artifacts/linux-deb - - name: Download mac artifact + - name: Download mac x64 artifacts uses: actions/download-artifact@v6 with: - name: mac-release + name: mac-release-x64 path: artifacts/mac-release + - name: Download mac arm64 artifacts + uses: actions/download-artifact@v6 + with: + name: mac-release-arm64 + path: artifacts/mac-release-arm64 + - name: Download windows artifact uses: actions/download-artifact@v6 with: name: windows-nsis path: artifacts/windows-nsis + - name: Merge mac update manifests + run: | + node scripts/merge-latest-mac.mjs \ + --x64-dir artifacts/mac-release \ + --arm64-dir artifacts/mac-release-arm64 \ + --out-dir artifacts/mac-release + - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: diff --git a/package.json b/package.json index 881e736..b2ba971 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,9 @@ "appId": "com.opengui.app", "productName": "OpenGUI", "compression": "maximum", - "asar": true, + "asar": { + "smartUnpack": true + }, "linux": { "target": "deb", "icon": "assets/icon.png", @@ -78,6 +80,8 @@ "dmg", "zip" ], + "artifactName": "${productName}-${version}-${arch}-mac.${ext}", + "compression": "normal", "icon": "assets/icon.icns", "category": "public.app-category.developer-tools", "hardenedRuntime": true, @@ -85,7 +89,8 @@ "identity": null }, "dmg": { - "icon": "assets/icon.icns" + "icon": "assets/icon.icns", + "writeUpdateInfo": false }, "win": { "target": "nsis", diff --git a/scripts/merge-latest-mac.mjs b/scripts/merge-latest-mac.mjs new file mode 100644 index 0000000..198790f --- /dev/null +++ b/scripts/merge-latest-mac.mjs @@ -0,0 +1,62 @@ +import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { createRequire } from "node:module"; + +const require = createRequire(import.meta.url); +const yaml = require("js-yaml"); + +function parseArgs() { + const args = process.argv.slice(2); + const opts = {}; + for (let i = 0; i < args.length; i++) { + if (args[i] === "--x64-dir") opts.x64Dir = resolve(args[++i]); + else if (args[i] === "--arm64-dir") opts.arm64Dir = resolve(args[++i]); + else if (args[i] === "--out-dir") opts.outDir = resolve(args[++i]); + } + if (!opts.x64Dir || !opts.arm64Dir || !opts.outDir) { + console.error( + "Usage: node merge-latest-mac.mjs --x64-dir --arm64-dir --out-dir ", + ); + process.exit(1); + } + return opts; +} + +function readManifest(dir) { + const p = join(dir, "latest-mac.yml"); + if (!existsSync(p)) { + console.error(`Manifest not found: ${p}`); + process.exit(1); + } + return yaml.load(readFileSync(p, "utf8")); +} + +function merge() { + const { x64Dir, arm64Dir, outDir } = parseArgs(); + + const x64 = readManifest(x64Dir); + const arm64 = readManifest(arm64Dir); + + const releaseDate = new Date( + Math.max(new Date(x64.releaseDate ?? 0).getTime(), new Date(arm64.releaseDate ?? 0).getTime()), + ).toISOString(); + + const merged = { + version: x64.version ?? arm64.version, + releaseDate, + files: [...(x64.files ?? []), ...(arm64.files ?? [])], + }; + + if (!existsSync(outDir)) { + mkdirSync(outDir, { recursive: true }); + } + + const outPath = join(outDir, "latest-mac.yml"); + writeFileSync(outPath, yaml.dump(merged, { lineWidth: -1, noRefs: true, sortKeys: false })); + console.log(`Merged mac update manifest written to ${outPath}`); + console.log(` x64 files: ${(x64.files ?? []).length}`); + console.log(` arm64 files: ${(arm64.files ?? []).length}`); + console.log(` total: ${merged.files.length}`); +} + +merge();