forked from ghostty-org/ghostty
-
Notifications
You must be signed in to change notification settings - Fork 0
397 lines (350 loc) · 16.5 KB
/
release-tag.yml
File metadata and controls
397 lines (350 loc) · 16.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
on:
workflow_dispatch:
inputs:
version:
description: "Version to deploy (format: vX.Y.Z)"
required: true
upload:
description: "Upload final artifacts to R2"
default: false
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
name: Release Tag
# We must only run one release workflow at a time to prevent corrupting
# our release artifacts.
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
jobs:
setup:
runs-on: namespace-profile-ghostty-sm
outputs:
version: ${{ steps.extract_version.outputs.version }}
build: ${{ steps.extract_build_info.outputs.build }}
commit: ${{ steps.extract_build_info.outputs.commit }}
commit_long: ${{ steps.extract_build_info.outputs.commit_long }}
steps:
- name: Validate Version Input
if: github.event_name == 'workflow_dispatch'
run: |
if [[ ! "${{ github.event.inputs.version }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must follow the format vX.Y.Z (e.g., v1.0.0)."
exit 1
fi
echo "Version is valid: ${{ github.event.inputs.version }}"
- name: Extract the Version
id: extract_version
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
# Remove the leading 'v' from the tag
VERSION=${GITHUB_REF#refs/tags/v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
VERSION=${{ github.event.inputs.version }}
VERSION=${VERSION#v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
else
echo "Error: Unsupported event type."
exit 1
fi
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Important so that build number generation works
fetch-depth: 0
- name: Extract build info
id: extract_build_info
run: |
GHOSTTY_BUILD=$(git rev-list --count HEAD)
GHOSTTY_COMMIT=$(git rev-parse --short HEAD)
GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD)
echo "build=$GHOSTTY_BUILD" >> $GITHUB_OUTPUT
echo "commit=$GHOSTTY_COMMIT" >> $GITHUB_OUTPUT
echo "commit_long=$GHOSTTY_COMMIT_LONG" >> $GITHUB_OUTPUT
cat $GITHUB_OUTPUT
source-tarball:
runs-on: namespace-profile-ghostty-md
needs: [setup]
env:
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@4d61c33d0b4333a518e975a0c4de7633d28713bb # v1.4.1
with:
path: |
/nix
/zig
- uses: cachix/install-nix-action@2126ae7fc54c9df00dd18f7f18754393182c73cd # v31.9.1
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Create Tarball
run: |
rm -rf zig-out/dist
nix develop -c zig build distcheck
cp zig-out/dist/ghostty-${GHOSTTY_VERSION}.tar.gz .
cp zig-out/dist/ghostty-${GHOSTTY_VERSION}.tar.gz ghostty-source.tar.gz
- name: Sign Tarball
run: |
echo -n "${{ secrets.MINISIGN_KEY }}" > minisign.key
echo -n "${{ secrets.MINISIGN_PASSWORD }}" > minisign.password
nix develop -c minisign -S -m "ghostty-${GHOSTTY_VERSION}.tar.gz" -s minisign.key < minisign.password
nix develop -c minisign -S -m "ghostty-source.tar.gz" -s minisign.key < minisign.password
- name: Upload artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: source-tarball
path: |-
ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz
ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz.minisig
ghostty-source.tar.gz
ghostty-source.tar.gz.minisig
build-macos:
needs: [setup]
runs-on: namespace-profile-ghostty-macos-tahoe
timeout-minutes: 90
env:
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
GHOSTTY_BUILD: ${{ needs.setup.outputs.build }}
GHOSTTY_COMMIT: ${{ needs.setup.outputs.commit }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: XCode Select
run: sudo xcode-select -s /Applications/Xcode_26.2.app
- name: Xcode Version
run: xcodebuild -version
- name: Setup Sparkle
env:
SPARKLE_VERSION: 2.7.3
run: |
mkdir -p .action/sparkle
cd .action/sparkle
curl -L https://github.com/sparkle-project/Sparkle/releases/download/${SPARKLE_VERSION}/Sparkle-for-Swift-Package-Manager.zip > sparkle.zip
unzip sparkle.zip
echo "$(pwd)/bin" >> $GITHUB_PATH
# GhosttyKit is the framework that is built from Zig for our native
# Mac app to access. Build this in release mode.
- name: Build GhosttyKit
run: |
nix develop -c \
zig build \
-Doptimize=ReleaseFast \
-Demit-macos-app=false \
-Dversion-string=${GHOSTTY_VERSION}
# The native app is built with native XCode tooling. This also does
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
# Nix breaks xcodebuild so this has to be run outside.
- name: Build Ghostty.app
run: |
cd macos
xcodebuild -target Ghostty -configuration Release
# Add all our metadata to Info.plist so we can reference it later.
- name: Update Info.plist
env:
SPARKLE_KEY_PUB: ${{ secrets.PROD_MACOS_SPARKLE_KEY_PUB }}
run: |
# Version Info
/usr/libexec/PlistBuddy -c "Set :GhosttyCommit $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $GHOSTTY_BUILD" "macos/build/Release/Ghostty.app/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $GHOSTTY_VERSION" "macos/build/Release/Ghostty.app/Contents/Info.plist"
# Updater
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"
- name: Codesign app bundle
env:
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
run: |
# Turn our base64-encoded certificate back to a regular .p12 file
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
# We need to create a new keychain, otherwise using the certificate will prompt
# with a UI dialog asking for the certificate password, which we can't
# use in a headless CI environment
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain
# Codesign Sparkle. Some notes here:
# - The XPC services aren't used since we don't sandbox Ghostty,
# but since they're part of the build, they still need to be
# codesigned.
# - The binaries in the "Versions" folders need to NOT be symlinks.
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc"
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Installer.xpc"
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate"
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app"
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework"
# Codesign the app bundle
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime --entitlements "macos/Ghostty.entitlements" macos/build/Release/Ghostty.app
- name: Create DMG
env:
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
run: |
npm install --global create-dmg
create-dmg \
--identity="$MACOS_CERTIFICATE_NAME" \
./macos/build/Release/Ghostty.app \
./
mv ./Ghostty*.dmg ./Ghostty.dmg
- name: "Notarize DMG"
env:
APPLE_NOTARIZATION_ISSUER: ${{ secrets.APPLE_NOTARIZATION_ISSUER }}
APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
run: |
# Store the notarization credentials so that we can prevent a UI password dialog
# from blocking the CI
echo "Create keychain profile"
echo "$APPLE_NOTARIZATION_KEY" > notarization_key.p8
xcrun notarytool store-credentials "notarytool-profile" --key notarization_key.p8 --key-id "$APPLE_NOTARIZATION_KEY_ID" --issuer "$APPLE_NOTARIZATION_ISSUER"
rm notarization_key.p8
# Here we send the notarization request to the Apple's Notarization service, waiting for the result.
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App
# characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
# you're curious
echo "Notarize dmg"
xcrun notarytool submit "Ghostty.dmg" --keychain-profile "notarytool-profile" --wait
# Finally, we need to "attach the staple" to our executable, which will allow our app to be
# validated by macOS even when an internet connection is not available. We do this to
# both the app and the dmg
echo "Attach staple"
xcrun stapler staple "Ghostty.dmg"
xcrun stapler staple "macos/build/Release/Ghostty.app"
# Zip up the app and symbols
- name: Zip App
run: |
cd macos/build/Release
zip -9 -r --symlinks ../../../ghostty-macos-universal.zip Ghostty.app
zip -9 -r --symlinks ../../../ghostty-macos-universal-dsym.zip Ghostty.app.dSYM/
- name: Upload artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: macos
path: |-
Ghostty.dmg
ghostty-macos-universal.zip
ghostty-macos-universal-dsym.zip
sentry-dsym:
runs-on: namespace-profile-ghostty-sm
needs: [build-macos]
steps:
- name: Install sentry-cli
run: |
curl -sL https://sentry.io/get-cli/ | bash
- name: Download macOS Artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: macos
- name: Upload dSYM to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: |
sentry-cli dif upload --project ghostty --wait ghostty-macos-universal-dsym.zip
appcast:
needs: [setup, build-macos]
runs-on: namespace-profile-ghostty-macos-tahoe
env:
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
GHOSTTY_BUILD: ${{ needs.setup.outputs.build }}
GHOSTTY_COMMIT: ${{ needs.setup.outputs.commit }}
GHOSTTY_COMMIT_LONG: ${{ needs.setup.outputs.commit_long }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download macOS Artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: macos
- name: Setup Sparkle
env:
SPARKLE_VERSION: 2.7.3
run: |
mkdir -p .action/sparkle
cd .action/sparkle
curl -L https://github.com/sparkle-project/Sparkle/releases/download/${SPARKLE_VERSION}/Sparkle-for-Swift-Package-Manager.zip > sparkle.zip
unzip sparkle.zip
echo "$(pwd)/bin" >> $GITHUB_PATH
- name: Generate Appcast
env:
SPARKLE_KEY: ${{ secrets.PROD_MACOS_SPARKLE_KEY }}
run: |
echo "GHOSTTY_VERSION=$GHOSTTY_VERSION"
echo "GHOSTTY_BUILD=$GHOSTTY_BUILD"
echo "GHOSTTY_COMMIT=$GHOSTTY_COMMIT"
echo "GHOSTTY_COMMIT_LONG=$GHOSTTY_COMMIT_LONG"
echo $SPARKLE_KEY > signing.key
sign_update -f signing.key Ghostty.dmg > sign_update.txt
curl -L https://release.files.ghostty.org/appcast.xml > appcast.xml
python3 ./dist/macos/update_appcast_tag.py
test -f appcast_new.xml
mv appcast_new.xml appcast.xml
- name: Upload artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: sparkle
path: |-
appcast.xml
upload:
if: |-
(github.event_name == 'workflow_dispatch' &&
github.event.inputs.upload == 'true') ||
github.event_name == 'push'
needs: [setup, source-tarball, build-macos, appcast]
runs-on: namespace-profile-ghostty-sm
env:
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Download macOS Artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: macos
- name: Download Sparkle Artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: sparkle
- name: Download Source Tarball Artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: source-tarball
# Upload all of our files EXCEPT the appcast. The appcast triggers
# updates in clients and we don't want to do that until we're
# sure these are uploaded.
- name: Prep Files
run: |
mkdir blob
mkdir -p blob/${GHOSTTY_VERSION}
mv "ghostty-${GHOSTTY_VERSION}.tar.gz" blob/${GHOSTTY_VERSION}/ghostty-${GHOSTTY_VERSION}.tar.gz
mv "ghostty-${GHOSTTY_VERSION}.tar.gz.minisig" blob/${GHOSTTY_VERSION}/ghostty-${GHOSTTY_VERSION}.tar.gz.minisig
mv ghostty-source.tar.gz blob/${GHOSTTY_VERSION}/ghostty-source.tar.gz
mv ghostty-source.tar.gz.minisig blob/${GHOSTTY_VERSION}/ghostty-source.tar.gz.minisig
mv ghostty-macos-universal.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal.zip
mv ghostty-macos-universal-dsym.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal-dsym.zip
mv Ghostty.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg
mv appcast.xml blob/${GHOSTTY_VERSION}/appcast-staged.xml
- name: Upload to R2
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
with:
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }}
r2-bucket: ghostty-release
source-dir: blob
destination-dir: ./