Skip to content

Commit 8fb9fce

Browse files
authored
Convert to building a universal Mac app (Stirling-Tools#6302)
1 parent ed56490 commit 8fb9fce

6 files changed

Lines changed: 217 additions & 45 deletions

File tree

.github/workflows/multiOSReleases.yml

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,18 @@ jobs:
9393
echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"}]}' >> $GITHUB_OUTPUT
9494
;;
9595
"macos")
96-
echo 'matrix={"include":[{"platform":"macos-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"macos-15-intel","args":"--target x86_64-apple-darwin","name":"macos-x86_64"}]}' >> $GITHUB_OUTPUT
96+
echo 'matrix={"include":[{"platform":"macos-15","args":"--target universal-apple-darwin","name":"macos-universal"}]}' >> $GITHUB_OUTPUT
9797
;;
9898
"linux")
9999
echo 'matrix={"include":[{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT
100100
;;
101101
*)
102-
echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"macos-15-intel","args":"--target x86_64-apple-darwin","name":"macos-x86_64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT
102+
echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-15","args":"--target universal-apple-darwin","name":"macos-universal"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT
103103
;;
104104
esac
105105
else
106106
# For push/release events, build all platforms
107-
echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"},{"platform":"macos-15-intel","args":"--target x86_64-apple-darwin","name":"macos-x86_64"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT
107+
echo 'matrix={"include":[{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"},{"platform":"macos-15","args":"--target universal-apple-darwin","name":"macos-universal"},{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}]}' >> $GITHUB_OUTPUT
108108
fi
109109
110110
build-jars:
@@ -218,7 +218,23 @@ jobs:
218218
uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
219219
with:
220220
toolchain: stable
221-
targets: ${{ (matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel') && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
221+
targets: ${{ matrix.platform == 'macos-15' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
222+
223+
# x86_64 JDK is set up first so the aarch64 step below can leave its
224+
# JAVA_HOME as the active one. The macOS universal JRE build needs
225+
# jmods from both arches; the x64 path is captured into the env
226+
# before the second setup-java overwrites JAVA_HOME.
227+
- name: Set up x86_64 JDK 25 (macOS universal JRE)
228+
if: matrix.platform == 'macos-15'
229+
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
230+
with:
231+
java-version: "25"
232+
distribution: "temurin"
233+
architecture: "x64"
234+
235+
- name: Capture x86_64 JAVA_HOME
236+
if: matrix.platform == 'macos-15'
237+
run: echo "X64_JAVA_HOME=$JAVA_HOME" >> "$GITHUB_ENV"
222238

223239
- name: Set up JDK 25
224240
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
@@ -234,6 +250,14 @@ jobs:
234250
- name: Install Task
235251
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
236252

253+
# Build the universal JRE before desktop:prepare so the jlink:runtime
254+
# task short-circuits on its `test -d runtime/jre` status check.
255+
- name: Build universal macOS JRE
256+
if: matrix.platform == 'macos-15'
257+
env:
258+
AARCH64_JAVA_HOME: ${{ env.JAVA_HOME }}
259+
run: task desktop:jlink:universal-mac
260+
237261
- name: Prepare desktop build
238262
run: task desktop:prepare
239263
env:
@@ -321,7 +345,7 @@ jobs:
321345
}
322346
323347
- name: Import Apple Developer Certificate
324-
if: (matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel') && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.sign != 'false') || github.ref == 'refs/heads/V2-master')
348+
if: matrix.platform == 'macos-15' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.sign != 'false') || github.ref == 'refs/heads/V2-master')
325349
env:
326350
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
327351
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
@@ -342,7 +366,7 @@ jobs:
342366
rm certificate.p12
343367
344368
- name: Verify Certificate
345-
if: (matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel') && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.sign != 'false') || github.ref == 'refs/heads/V2-master')
369+
if: matrix.platform == 'macos-15' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.sign != 'false') || github.ref == 'refs/heads/V2-master')
346370
run: |
347371
echo "Verifying Apple Developer Certificate..."
348372
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
@@ -557,7 +581,7 @@ jobs:
557581
# Only ship the MSI installer on Windows. The loose exe and WiX toolset exes
558582
# are not the user-facing installer - the MSI contains the signed inner exe.
559583
find . -name "*.msi" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.msi" \;
560-
elif [ "${{ matrix.platform }}" = "macos-15" ] || [ "${{ matrix.platform }}" = "macos-15-intel" ]; then
584+
elif [ "${{ matrix.platform }}" = "macos-15" ]; then
561585
find . -name "*.dmg" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.dmg" \;
562586
find . -name "*.app" -exec cp -r {} "../../../dist/Stirling-PDF-${{ matrix.name }}.app" \;
563587
else

.github/workflows/package-managers.yml

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ jobs:
2222
runs-on: ubuntu-latest
2323
outputs:
2424
version: ${{ steps.info.outputs.version }}
25-
dmg_arm64_sha256: ${{ steps.hashes.outputs.dmg_arm64_sha256 }}
26-
dmg_x86_64_sha256: ${{ steps.hashes.outputs.dmg_x86_64_sha256 }}
25+
dmg_sha256: ${{ steps.hashes.outputs.dmg_sha256 }}
2726
msi_sha256: ${{ steps.hashes.outputs.msi_sha256 }}
2827
deb_sha256: ${{ steps.hashes.outputs.deb_sha256 }}
2928
jar_sha256: ${{ steps.hashes.outputs.jar_sha256 }}
@@ -63,17 +62,15 @@ jobs:
6362
sha256sum "$file" | awk '{print $1}'
6463
}
6564
66-
DMG_ARM64_SHA=$(download_sha256 "${BASE}/Stirling-PDF-macos-aarch64.dmg")
67-
DMG_X64_SHA=$(download_sha256 "${BASE}/Stirling-PDF-macos-x86_64.dmg")
65+
DMG_SHA=$(download_sha256 "${BASE}/Stirling-PDF-macos-universal.dmg")
6866
MSI_SHA=$(download_sha256 "${BASE}/Stirling-PDF-windows-x86_64.msi")
6967
DEB_SHA=$(download_sha256 "${BASE}/Stirling-PDF-linux-x86_64.deb")
7068
JAR_SHA=$(download_sha256 "${BASE}/Stirling-PDF-with-login.jar")
7169
72-
echo "dmg_arm64_sha256=$DMG_ARM64_SHA" >> "$GITHUB_OUTPUT"
73-
echo "dmg_x86_64_sha256=$DMG_X64_SHA" >> "$GITHUB_OUTPUT"
74-
echo "msi_sha256=$MSI_SHA" >> "$GITHUB_OUTPUT"
75-
echo "deb_sha256=$DEB_SHA" >> "$GITHUB_OUTPUT"
76-
echo "jar_sha256=$JAR_SHA" >> "$GITHUB_OUTPUT"
70+
echo "dmg_sha256=$DMG_SHA" >> "$GITHUB_OUTPUT"
71+
echo "msi_sha256=$MSI_SHA" >> "$GITHUB_OUTPUT"
72+
echo "deb_sha256=$DEB_SHA" >> "$GITHUB_OUTPUT"
73+
echo "jar_sha256=$JAR_SHA" >> "$GITHUB_OUTPUT"
7774
7875
update-homebrew-and-scoop:
7976
needs: get-release-info
@@ -93,31 +90,30 @@ jobs:
9390
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
9491
path: tap
9592

93+
# Stirling-PDF now ships a single universal DMG, so the Cask should
94+
# have one sha256 line rather than separate on_arm/on_intel blocks.
95+
# We rewrite every `sha256 "..."` in the Cask to the same value so
96+
# the workflow keeps working through the Cask migration: pre-migration
97+
# both arch blocks get the universal SHA (still correct, since both
98+
# would resolve to the same DMG), post-migration the lone sha256 line
99+
# is updated.
96100
- name: Update Homebrew cask (Casks/stirling-pdf.rb)
97101
env:
98102
VERSION: ${{ needs.get-release-info.outputs.version }}
99-
ARM64_SHA: ${{ needs.get-release-info.outputs.dmg_arm64_sha256 }}
100-
X64_SHA: ${{ needs.get-release-info.outputs.dmg_x86_64_sha256 }}
103+
DMG_SHA: ${{ needs.get-release-info.outputs.dmg_sha256 }}
101104
run: |
102105
CASK="tap/Casks/stirling-pdf.rb"
103-
sed -i "s/version \".*\"/version \"${VERSION}\"/" "$CASK"
104-
awk -v arm="$ARM64_SHA" -v x64="$X64_SHA" '
105-
/on_arm/ { in_arm=1 }
106-
/on_intel/ { in_arm=0; in_intel=1 }
107-
/end/ { in_arm=0; in_intel=0 }
108-
in_arm && /sha256/ { sub(/sha256 ".*"/, "sha256 \"" arm "\"") }
109-
in_intel && /sha256/ { sub(/sha256 ".*"/, "sha256 \"" x64 "\"") }
110-
{ print }
111-
' "$CASK" > tmp && mv tmp "$CASK"
106+
sed -i "s/version \"[^\"]*\"/version \"${VERSION}\"/" "$CASK"
107+
sed -i "s/sha256 \"[^\"]*\"/sha256 \"${DMG_SHA}\"/g" "$CASK"
112108
113109
- name: Update Homebrew formula (Formula/stirling-pdf-server.rb)
114110
env:
115111
VERSION: ${{ needs.get-release-info.outputs.version }}
116112
JAR_SHA: ${{ needs.get-release-info.outputs.jar_sha256 }}
117113
run: |
118114
FORMULA="tap/Formula/stirling-pdf-server.rb"
119-
sed -i "s/version \".*\"/version \"${VERSION}\"/" "$FORMULA"
120-
sed -i "s/sha256 \".*\"/sha256 \"${JAR_SHA}\"/" "$FORMULA"
115+
sed -i "s/version \"[^\"]*\"/version \"${VERSION}\"/" "$FORMULA"
116+
sed -i "s/sha256 \"[^\"]*\"/sha256 \"${JAR_SHA}\"/" "$FORMULA"
121117
122118
- name: Update Scoop stirling-pdf.json
123119
env:

.github/workflows/tauri-build.yml

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Build Tauri Applications
22

3-
# Multi-OS Tauri desktop bundle build matrix (Windows / macOS arm+intel /
3+
# Multi-OS Tauri desktop bundle build matrix (Windows / macOS universal /
44
# Linux). Called from build.yml on PRs that touch desktop sources (gated
55
# via the `tauri` filter in .github/config/.files.yaml). Also runnable
66
# on demand via workflow_dispatch with a per-platform selector.
@@ -48,17 +48,16 @@ jobs:
4848
PLATFORM: ${{ inputs.platform }}
4949
run: |
5050
WINDOWS='{"platform":"windows-latest","args":"--target x86_64-pc-windows-msvc","name":"windows-x86_64"}'
51-
MACOS_ARM='{"platform":"macos-15","args":"--target aarch64-apple-darwin","name":"macos-aarch64"}'
52-
MACOS_INTEL='{"platform":"macos-15-intel","args":"--target x86_64-apple-darwin","name":"macos-x86_64"}'
51+
MACOS='{"platform":"macos-15","args":"--target universal-apple-darwin","name":"macos-universal"}'
5352
LINUX='{"platform":"ubuntu-22.04","args":"","name":"linux-x86_64"}'
5453
5554
# Resolve requested platform — populated by either workflow_dispatch
5655
# or workflow_call inputs; both paths default to "all".
5756
case "$PLATFORM" in
5857
windows) ENTRIES=("$WINDOWS") ;;
59-
macos) ENTRIES=("$MACOS_ARM" "$MACOS_INTEL") ;;
58+
macos) ENTRIES=("$MACOS") ;;
6059
linux) ENTRIES=("$LINUX") ;;
61-
*) ENTRIES=("$WINDOWS" "$MACOS_ARM" "$MACOS_INTEL" "$LINUX") ;;
60+
*) ENTRIES=("$WINDOWS" "$MACOS" "$LINUX") ;;
6261
esac
6362
6463
# Drop macOS entries when Apple certificate secret is unavailable
@@ -111,7 +110,23 @@ jobs:
111110
uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
112111
with:
113112
toolchain: stable
114-
targets: ${{ (matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel') && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
113+
targets: ${{ matrix.platform == 'macos-15' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
114+
115+
# x86_64 JDK is set up first so the aarch64 step below can leave its
116+
# JAVA_HOME as the active one. The macOS universal JRE build needs
117+
# jmods from both arches; the x64 path is captured into the env
118+
# before the second setup-java overwrites JAVA_HOME.
119+
- name: Set up x86_64 JDK 25 (macOS universal JRE)
120+
if: matrix.platform == 'macos-15'
121+
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
122+
with:
123+
java-version: "25"
124+
distribution: "temurin"
125+
architecture: "x64"
126+
127+
- name: Capture x86_64 JAVA_HOME
128+
if: matrix.platform == 'macos-15'
129+
run: echo "X64_JAVA_HOME=$JAVA_HOME" >> "$GITHUB_ENV"
115130

116131
- name: Set up JDK 25
117132
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
@@ -126,6 +141,15 @@ jobs:
126141

127142
- name: Setup Task
128143
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2.0.0
144+
145+
# Build the universal JRE before desktop:prepare so the jlink:runtime
146+
# task short-circuits on its `test -d runtime/jre` status check.
147+
- name: Build universal macOS JRE
148+
if: matrix.platform == 'macos-15'
149+
env:
150+
AARCH64_JAVA_HOME: ${{ env.JAVA_HOME }}
151+
run: task desktop:jlink:universal-mac
152+
129153
- name: Prepare desktop build
130154
run: task desktop:prepare
131155
env:
@@ -213,7 +237,7 @@ jobs:
213237
}
214238
215239
- name: Import Apple Developer Certificate
216-
if: (matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel') && env.APPLE_CERTIFICATE != ''
240+
if: matrix.platform == 'macos-15' && env.APPLE_CERTIFICATE != ''
217241
env:
218242
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
219243
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
@@ -234,7 +258,7 @@ jobs:
234258
rm certificate.p12
235259
236260
- name: Verify Certificate
237-
if: (matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel') && env.APPLE_CERTIFICATE != ''
261+
if: matrix.platform == 'macos-15' && env.APPLE_CERTIFICATE != ''
238262
run: |
239263
echo "Verifying Apple Developer Certificate..."
240264
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
@@ -246,7 +270,7 @@ jobs:
246270
echo "Certificate imported successfully."
247271
248272
- name: Check DMG creation dependencies (macOS only)
249-
if: matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel'
273+
if: matrix.platform == 'macos-15'
250274
run: |
251275
echo "🔍 Checking DMG creation dependencies on ${{ matrix.platform }}..."
252276
echo "hdiutil version: $(hdiutil --version || echo 'NOT FOUND')"
@@ -373,7 +397,7 @@ jobs:
373397
fi
374398
375399
- name: Verify notarization (macOS only)
376-
if: matrix.platform == 'macos-15' || matrix.platform == 'macos-15-intel'
400+
if: matrix.platform == 'macos-15'
377401
run: |
378402
echo "🔍 Verifying notarization status..."
379403
cd ./frontend/src-tauri/target
@@ -398,7 +422,7 @@ jobs:
398422
# Only ship the MSI installer. The loose exe and WiX toolset exes
399423
# are not the user-facing installer - the MSI contains the signed inner exe.
400424
find . -name "*.msi" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.msi" \;
401-
elif [ "${{ matrix.platform }}" = "macos-15" ] || [ "${{ matrix.platform }}" = "macos-15-intel" ]; then
425+
elif [ "${{ matrix.platform }}" = "macos-15" ]; then
402426
find . -name "*.dmg" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.dmg" \;
403427
else
404428
find . -name "*.deb" -exec cp {} "../../../dist/Stirling-PDF-${{ matrix.name }}.deb" \;
@@ -491,7 +515,7 @@ jobs:
491515
echo "❌ No Windows executable found"
492516
exit 1
493517
fi
494-
elif [ "${{ matrix.platform }}" = "macos-15" ] || [ "${{ matrix.platform }}" = "macos-15-intel" ]; then
518+
elif [ "${{ matrix.platform }}" = "macos-15" ]; then
495519
echo "Checking for macOS artifacts..."
496520
find . -name "*.dmg" | head -5
497521
if [ $(find . -name "*.dmg" | wc -l) -eq 0 ]; then
@@ -556,8 +580,7 @@ jobs:
556580
// Map of expected artifact names to display info
557581
const artifactMap = {
558582
'Stirling-PDF-windows-x86_64': { icon: '🪟', platform: 'Windows x64', files: '.exe, .msi' },
559-
'Stirling-PDF-macos-aarch64': { icon: '🍎', platform: 'macOS ARM64', files: '.dmg' },
560-
'Stirling-PDF-macos-x86_64': { icon: '🍎', platform: 'macOS Intel', files: '.dmg' },
583+
'Stirling-PDF-macos-universal': { icon: '🍎', platform: 'macOS Universal', files: '.dmg' },
561584
'Stirling-PDF-linux-x86_64': { icon: '🐧', platform: 'Linux x64', files: '.deb, .rpm, .AppImage' }
562585
};
563586

.taskfiles/desktop.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
version: '3'
22

33
vars:
4-
JLINK_MODULES: "java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported"
4+
# Single source of truth for the jlink module list (also read by
5+
# frontend/scripts/build-universal-mac-jre.sh during CI). The desktop
6+
# taskfile runs with dir: frontend (see Taskfile.yml), so this path
7+
# is relative to that directory.
8+
JLINK_MODULES:
9+
sh: tr -d '\n' < src-tauri/jlink-modules.txt
510

611
tasks:
712
prepare:
@@ -96,7 +101,7 @@ tasks:
96101
jlink
97102
--add-modules {{.JLINK_MODULES}}
98103
--strip-debug
99-
--compress=2
104+
--compress=zip-6
100105
--no-header-files
101106
--no-man-pages
102107
--output src-tauri/runtime/jre
@@ -107,3 +112,18 @@ tasks:
107112
desc: "Remove JLink runtime and bundled JARs"
108113
cmds:
109114
- rm -rf src-tauri/libs src-tauri/runtime
115+
116+
# macOS-only. Replaces jlink:runtime's single-arch JRE with a universal
117+
# (arm64 + x86_64) one for the universal Tauri shell. Runs the x86_64
118+
# jlink under Rosetta on Apple Silicon, so it is opt-in and not part of
119+
# the default desktop:build flow. Requires AARCH64_JAVA_HOME and
120+
# X64_JAVA_HOME to point at matching JDK installations with jmods/.
121+
jlink:universal-mac:
122+
desc: "Create universal (arm64+x86_64) JRE for the macOS Tauri build"
123+
deps: [jlink:jar]
124+
platforms: [darwin]
125+
env:
126+
JLINK_MODULES: "{{.JLINK_MODULES}}"
127+
OUTPUT_DIR: src-tauri/runtime/jre
128+
cmds:
129+
- scripts/build-universal-mac-jre.sh

0 commit comments

Comments
 (0)