Skip to content

Commit a1af594

Browse files
committed
ci: add signed+notarized native host release workflow
1 parent 4ede0a9 commit a1af594

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
name: Release Native Host
2+
3+
# Triggered manually or by pushing a tag like native-host/v1.0.3
4+
on:
5+
workflow_dispatch:
6+
inputs:
7+
version:
8+
description: 'Version to release (e.g. 1.0.3) — must match nativeHostVersion in packages/cli/package.json'
9+
required: true
10+
push:
11+
tags:
12+
- 'native-host/v*'
13+
14+
jobs:
15+
build-sign-notarize-release:
16+
name: Build · Sign · Notarize · Release
17+
# macOS runner is required for codesign and notarytool
18+
runs-on: macos-latest
19+
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
24+
- name: Set up Bun
25+
uses: oven-sh/setup-bun@v2
26+
with:
27+
bun-version: latest
28+
29+
- name: Install native-host dependencies
30+
run: bun install
31+
working-directory: extension/native-host
32+
33+
# ── Build ──────────────────────────────────────────────────────────────
34+
- name: Build all platform binaries
35+
run: bun extension/native-host/build.ts --all
36+
37+
# build.ts outputs darwin-*, but postinstall.js downloads mac-* (getPlatform
38+
# maps process.platform 'darwin' → 'mac'). Rename to match.
39+
- name: Rename darwin→mac for postinstall URL compatibility
40+
run: |
41+
cd extension/native-host/dist
42+
mv thinkbrowse-host-darwin-arm64 thinkbrowse-host-mac-arm64
43+
mv thinkbrowse-host-darwin-x64 thinkbrowse-host-mac-x64
44+
45+
# ── Code signing ───────────────────────────────────────────────────────
46+
- name: Import Developer ID Application certificate
47+
env:
48+
MACOS_SIGNING_CERT_BASE64: ${{ secrets.MACOS_SIGNING_CERT_BASE64 }}
49+
MACOS_SIGNING_CERT_PASSWORD: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}
50+
run: |
51+
echo "$MACOS_SIGNING_CERT_BASE64" | base64 --decode > /tmp/cert.p12
52+
53+
# Create an isolated keychain so we don't touch the runner's login keychain
54+
security create-keychain -p "" build.keychain
55+
security default-keychain -s build.keychain
56+
security unlock-keychain -p "" build.keychain
57+
security set-keychain-settings -lut 7200 build.keychain
58+
59+
security import /tmp/cert.p12 \
60+
-k build.keychain \
61+
-P "$MACOS_SIGNING_CERT_PASSWORD" \
62+
-T /usr/bin/codesign \
63+
-T /usr/bin/security
64+
65+
# Allow codesign to access the key without a UI password prompt
66+
security set-key-partition-list \
67+
-S apple-tool:,apple:,codesign: \
68+
-s -k "" build.keychain
69+
70+
rm /tmp/cert.p12
71+
72+
- name: Sign macOS binaries
73+
env:
74+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
75+
run: |
76+
DIST=extension/native-host/dist
77+
for binary in thinkbrowse-host-mac-arm64 thinkbrowse-host-mac-x64; do
78+
echo "Signing $binary ..."
79+
codesign \
80+
--force \
81+
--options runtime \
82+
--sign "Developer ID Application: David Dundas ($APPLE_TEAM_ID)" \
83+
"$DIST/$binary"
84+
codesign --verify --deep --strict --verbose=2 "$DIST/$binary"
85+
echo " signed OK"
86+
done
87+
88+
# ── Notarization ───────────────────────────────────────────────────────
89+
- name: Notarize macOS binaries
90+
env:
91+
APPLE_ID: ${{ secrets.APPLE_ID }}
92+
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
93+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
94+
run: |
95+
DIST=extension/native-host/dist
96+
for binary in thinkbrowse-host-mac-arm64 thinkbrowse-host-mac-x64; do
97+
echo "Notarizing $binary ..."
98+
zip "/tmp/${binary}.zip" "$DIST/$binary"
99+
100+
xcrun notarytool submit "/tmp/${binary}.zip" \
101+
--apple-id "$APPLE_ID" \
102+
--password "$APPLE_APP_PASSWORD" \
103+
--team-id "$APPLE_TEAM_ID" \
104+
--wait
105+
106+
# Staple embeds the notarization ticket so Gatekeeper works offline
107+
xcrun stapler staple "$DIST/$binary"
108+
xcrun stapler validate "$DIST/$binary"
109+
110+
rm "/tmp/${binary}.zip"
111+
echo " notarized and stapled OK"
112+
done
113+
114+
# ── Checksums ──────────────────────────────────────────────────────────
115+
# postinstall.js downloads checksums.txt from the same release and verifies
116+
# SHA256 before executing the binary. Format: "<hash> <filename>"
117+
- name: Generate checksums.txt
118+
run: |
119+
cd extension/native-host/dist
120+
shasum -a 256 \
121+
thinkbrowse-host-mac-arm64 \
122+
thinkbrowse-host-mac-x64 \
123+
thinkbrowse-host-linux-arm64 \
124+
thinkbrowse-host-linux-x64 \
125+
thinkbrowse-host-windows-x64.exe \
126+
> checksums.txt
127+
echo "checksums.txt:"
128+
cat checksums.txt
129+
130+
# ── Release ────────────────────────────────────────────────────────────
131+
- name: Determine release version
132+
id: ver
133+
run: |
134+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
135+
echo "version=${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT"
136+
else
137+
# Tag format: native-host/v1.0.3 → strip prefix
138+
TAG="${GITHUB_REF_NAME#native-host/v}"
139+
echo "version=$TAG" >> "$GITHUB_OUTPUT"
140+
fi
141+
142+
- name: Create GitHub Release
143+
env:
144+
GH_TOKEN: ${{ github.token }}
145+
run: |
146+
VERSION="${{ steps.ver.outputs.version }}"
147+
DIST=extension/native-host/dist
148+
TAG="native-host/v${VERSION}"
149+
150+
gh release create "$TAG" \
151+
--repo "${{ github.repository }}" \
152+
--title "Native Host v${VERSION}" \
153+
--notes "$(cat <<'NOTES'
154+
## ThinkBrowse Native Host
155+
156+
macOS binaries are **signed** (Developer ID Application) and **notarized** by Apple.
157+
Linux and Windows binaries are unsigned — no extra steps needed.
158+
159+
### Installation
160+
Installed automatically via \`npm install -g @thinkbrowse/cli\`.
161+
To install manually, see the [README](https://github.com/${{ github.repository }}#readme).
162+
163+
### Files
164+
| File | Platform |
165+
|------|----------|
166+
| thinkbrowse-host-mac-arm64 | macOS Apple Silicon |
167+
| thinkbrowse-host-mac-x64 | macOS Intel |
168+
| thinkbrowse-host-linux-arm64 | Linux ARM64 |
169+
| thinkbrowse-host-linux-x64 | Linux x64 |
170+
| thinkbrowse-host-windows-x64.exe | Windows x64 |
171+
| checksums.txt | SHA256 checksums |
172+
NOTES
173+
)" \
174+
"${DIST}/thinkbrowse-host-mac-arm64" \
175+
"${DIST}/thinkbrowse-host-mac-x64" \
176+
"${DIST}/thinkbrowse-host-linux-arm64" \
177+
"${DIST}/thinkbrowse-host-linux-x64" \
178+
"${DIST}/thinkbrowse-host-windows-x64.exe" \
179+
"${DIST}/checksums.txt"
180+
181+
echo ""
182+
echo "Release published: https://github.com/${{ github.repository }}/releases/tag/${TAG}"

0 commit comments

Comments
 (0)