Skip to content

Commit eb633ed

Browse files
authored
Merge pull request #189 from matt1398/fix/macos-arch-routing-and-yml-merge
fix(release): correct arch routing for macOS dmg + in-app updates
2 parents 6535fbb + d0c4af2 commit eb633ed

2 files changed

Lines changed: 73 additions & 16 deletions

File tree

.github/workflows/release.yml

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,13 @@ jobs:
4949

5050
release-mac:
5151
needs: build
52-
strategy:
53-
fail-fast: false
54-
matrix:
55-
include:
56-
- arch: arm64
57-
runner: macos-14
58-
dist_command: pnpm dist:mac:arm64
59-
- arch: x64
60-
runner: macos-15-intel
61-
dist_command: pnpm dist:mac:x64
62-
runs-on: ${{ matrix.runner }}
52+
# Single job builds BOTH arm64 and x64 in one electron-builder invocation.
53+
# Reason: when arches are split into separate jobs each running `--publish always`,
54+
# each upload OVERWRITES latest-mac.yml — so the final yml only lists one arch
55+
# and electron-updater silently routes the wrong-arch dmg to users (arm64 users
56+
# get x64 → Rosetta penalty). Building both in one invocation produces a single
57+
# latest-mac.yml that lists both arches, so auto-update routes correctly.
58+
runs-on: macos-14 # arm64 runner; macOS supports x64 cross-build natively
6359

6460
steps:
6561
- name: Checkout
@@ -93,24 +89,82 @@ jobs:
9389
VERSION="${GITHUB_REF#refs/tags/v}"
9490
pnpm pkg set version="$VERSION"
9591
96-
- name: Build app (macOS ${{ matrix.arch }})
92+
- name: Build app
9793
run: pnpm build
9894

99-
- name: Verify packaged inputs (macOS ${{ matrix.arch }})
95+
- name: Verify packaged inputs
10096
run: |
10197
test -f dist-electron/main/index.cjs
10298
test -f dist-electron/preload/index.js
10399
test -f out/renderer/index.html
104100
105-
- name: Package & Release (macOS ${{ matrix.arch }})
101+
- name: Package & Release (macOS arm64 + x64)
106102
env:
107103
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
108104
CSC_LINK: ${{ secrets.CSC_LINK }}
109105
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
110106
APPLE_ID: ${{ secrets.APPLE_ID }}
111107
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
112108
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
113-
run: ${{ matrix.dist_command }}
109+
run: pnpm dist:mac
110+
111+
- name: Assert arch suffix in artifact names
112+
run: |
113+
set -euo pipefail
114+
shopt -s nullglob
115+
BAD=()
116+
for f in release/*.dmg release/*.zip; do
117+
base=$(basename "$f")
118+
case "$base" in
119+
*blockmap*|*-arm64.dmg|*-arm64.zip|*-arm64-mac.zip|*-x64.dmg|*-x64.zip|*-x64-mac.zip)
120+
;;
121+
*)
122+
BAD+=("$base")
123+
;;
124+
esac
125+
done
126+
if [ ${#BAD[@]} -gt 0 ]; then
127+
echo "::error::Found macOS artifacts without '-arm64' or '-x64' suffix. Apple Silicon users would download x64 by default and run under Rosetta. Fix package.json build.mac.artifactName."
128+
printf ' bad: %s\n' "${BAD[@]}"
129+
exit 1
130+
fi
131+
echo "OK: all macOS artifacts have arch suffix"
132+
ls -la release/ | grep -E '\.(dmg|zip)$' || true
133+
134+
- name: Verify latest-mac.yml lists BOTH arm64 and x64
135+
run: |
136+
set -euo pipefail
137+
YML=release/latest-mac.yml
138+
test -f "$YML" || { echo "::error::missing $YML — electron-updater cannot route auto-updates"; exit 1; }
139+
echo "--- latest-mac.yml ---"
140+
cat "$YML"
141+
echo "----------------------"
142+
# Both arches must appear in the yml, otherwise electron-updater will route
143+
# the wrong dmg to one arch's users (the regression that caused #186).
144+
grep -q -- '-arm64\.dmg' "$YML" || { echo "::error::latest-mac.yml missing arm64 dmg entry — arm64 users will receive x64 build"; exit 1; }
145+
grep -q -- '-x64\.dmg' "$YML" || { echo "::error::latest-mac.yml missing x64 dmg entry — x64 users will receive arm64 build"; exit 1; }
146+
echo "OK: latest-mac.yml lists both arm64 and x64"
147+
148+
- name: Verify each dmg's binary matches its arch suffix
149+
run: |
150+
set -euo pipefail
151+
fail=0
152+
for dmg in release/*-arm64.dmg release/*-x64.dmg; do
153+
[ -e "$dmg" ] || continue
154+
expected=$([[ "$dmg" == *"-arm64.dmg" ]] && echo arm64 || echo x86_64)
155+
mount=$(hdiutil attach "$dmg" -nobrowse -readonly | tail -1 | awk '{print $NF}')
156+
bin="$mount/claude-devtools.app/Contents/MacOS/claude-devtools"
157+
if [ -f "$bin" ]; then
158+
actual=$(file "$bin")
159+
echo "$dmg -> $actual"
160+
echo "$actual" | grep -q "$expected" || { echo "::error::$dmg has wrong binary arch (expected $expected)"; fail=1; }
161+
else
162+
echo "::warning::no binary at $bin"
163+
fi
164+
hdiutil detach "$mount" -quiet || true
165+
done
166+
[ "$fail" = 0 ] || exit 1
167+
echo "OK: each dmg's binary matches its arch suffix"
114168
115169
release-win:
116170
needs: build

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"dev": "electron-vite dev",
2222
"build": "electron-vite build",
2323
"dist": "electron-builder --mac --win --linux",
24-
"dist:mac": "electron-builder --mac --publish always",
24+
"dist:mac": "electron-builder --mac --arm64 --x64 --publish always",
2525
"dist:mac:arm64": "electron-builder --mac --arm64 --publish always",
2626
"dist:mac:x64": "electron-builder --mac --x64 --publish always",
2727
"dist:win": "electron-builder --win --publish always",
@@ -138,6 +138,7 @@
138138
"dmg",
139139
"zip"
140140
],
141+
"artifactName": "${productName}-${version}-${arch}.${ext}",
141142
"hardenedRuntime": true,
142143
"gatekeeperAssess": false,
143144
"notarize": true,
@@ -152,6 +153,7 @@
152153
"target": [
153154
"nsis"
154155
],
156+
"artifactName": "${productName}-Setup-${version}-${arch}.${ext}",
155157
"icon": "resources/icons/win/icon.ico"
156158
},
157159
"linux": {
@@ -161,6 +163,7 @@
161163
"rpm",
162164
"pacman"
163165
],
166+
"artifactName": "${productName}-${version}-${arch}.${ext}",
164167
"icon": "resources/icons/png",
165168
"category": "Development"
166169
},

0 commit comments

Comments
 (0)