Release Tip #2345
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
on: | |
workflow_run: | |
workflows: [Test] | |
types: [completed] | |
branches: [main] | |
workflow_dispatch: {} | |
name: Release Tip | |
# 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: | |
tag: | |
runs-on: namespace-profile-ghostty-sm | |
needs: [build-macos] | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Tip Tag | |
run: | | |
git config user.name "github-actions[bot]" | |
git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
git tag -fa tip -m "Latest Continuous Release" ${GITHUB_SHA} | |
git push --force origin tip | |
sentry-dsym-debug-slow: | |
runs-on: namespace-profile-ghostty-sm | |
needs: [build-macos-debug-slow] | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Install sentry-cli | |
run: | | |
curl -sL https://sentry.io/get-cli/ | bash | |
- name: Download dSYM | |
run: | | |
GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD) | |
curl -L https://tip.files.ghostty.dev/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow-dsym.zip > dsym.zip | |
- name: Upload dSYM to Sentry | |
env: | |
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
run: | | |
sentry-cli dif upload --project ghostty --wait dsym.zip | |
sentry-dsym-debug-fast: | |
runs-on: namespace-profile-ghostty-sm | |
needs: [build-macos-debug-fast] | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Install sentry-cli | |
run: | | |
curl -sL https://sentry.io/get-cli/ | bash | |
- name: Download dSYM | |
run: | | |
GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD) | |
curl -L https://tip.files.ghostty.dev/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast-dsym.zip > dsym.zip | |
- name: Upload dSYM to Sentry | |
env: | |
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
run: | | |
sentry-cli dif upload --project ghostty --wait dsym.zip | |
sentry-dsym: | |
runs-on: namespace-profile-ghostty-sm | |
needs: [build-macos] | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Install sentry-cli | |
run: | | |
curl -sL https://sentry.io/get-cli/ | bash | |
- name: Download dSYM | |
run: | | |
GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD) | |
curl -L https://tip.files.ghostty.dev/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-dsym.zip > dsym.zip | |
- name: Upload dSYM to Sentry | |
env: | |
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
run: | | |
sentry-cli dif upload --project ghostty --wait dsym.zip | |
source-tarball: | |
if: | | |
${{ | |
github.event_name == 'workflow_dispatch' || | |
( | |
github.event.workflow_run.conclusion == 'success' && | |
github.repository_owner == 'ghostty-org' && | |
github.ref_name == 'main' | |
) | |
}} | |
runs-on: namespace-profile-ghostty-md | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: cachix/install-nix-action@v30 | |
with: | |
nix_path: nixpkgs=channel:nixos-unstable | |
- uses: cachix/cachix-action@v15 | |
with: | |
name: ghostty | |
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" | |
- name: Create Tarball | |
run: git archive --format=tgz --prefix=ghostty-source/ -o ghostty-source.tar.gz HEAD | |
- 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-source.tar.gz -s minisign.key < minisign.password | |
- name: Update Release | |
uses: softprops/action-gh-release@v2 | |
with: | |
name: 'Ghostty Tip ("Nightly")' | |
prerelease: true | |
tag_name: tip | |
target_commitish: ${{ github.sha }} | |
files: | | |
ghostty-source.tar.gz | |
ghostty-source.tar.gz.minisig | |
token: ${{ secrets.GH_RELEASE_TOKEN }} | |
build-macos: | |
if: | | |
${{ | |
github.event_name == 'workflow_dispatch' || | |
( | |
github.event.workflow_run.conclusion == 'success' && | |
github.repository_owner == 'ghostty-org' && | |
github.ref_name == 'main' | |
) | |
}} | |
runs-on: namespace-profile-ghostty-macos | |
timeout-minutes: 90 | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
# Important so that build number generation works | |
fetch-depth: 0 | |
# Install Nix and use that to run our tests so our environment matches exactly. | |
- uses: cachix/install-nix-action@v30 | |
with: | |
nix_path: nixpkgs=channel:nixos-unstable | |
- uses: cachix/cachix-action@v15 | |
with: | |
name: ghostty | |
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" | |
- name: XCode Select | |
run: sudo xcode-select -s /Applications/Xcode_16.0.app | |
# Setup Sparkle | |
- name: Setup Sparkle | |
env: | |
SPARKLE_VERSION: 2.6.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 | |
# Load Build Number | |
- name: Build Number | |
run: | | |
echo "GHOSTTY_BUILD=$(git rev-list --count head)" >> $GITHUB_ENV | |
echo "GHOSTTY_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV | |
echo "GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD)" >> $GITHUB_ENV | |
# 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 | |
# 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 | |
# We inject the "build number" as simply the number of commits since HEAD. | |
# This will be a monotonically always increasing build number that we use. | |
- 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_COMMIT" "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" | |
- 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: | |
PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} | |
PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} | |
PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} | |
run: | | |
# Store the notarization credentials so that we can prevent a UI password dialog | |
# from blocking the CI | |
echo "Create keychain profile" | |
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD" | |
# 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/ | |
# Update Release | |
- name: Release | |
uses: softprops/action-gh-release@v2 | |
with: | |
name: 'Ghostty Tip ("Nightly")' | |
prerelease: true | |
tag_name: tip | |
target_commitish: ${{ github.sha }} | |
files: | | |
ghostty-macos-universal.zip | |
Ghostty.dmg | |
token: ${{ secrets.GH_RELEASE_TOKEN }} | |
# Create our appcast for Sparkle | |
- name: Generate Appcast | |
env: | |
SPARKLE_KEY: ${{ secrets.PROD_MACOS_SPARKLE_KEY }} | |
run: | | |
echo $SPARKLE_KEY > signing.key | |
sign_update -f signing.key Ghostty.dmg > sign_update.txt | |
curl -L https://tip.files.ghostty.org/appcast.xml > appcast.xml | |
python3 ./dist/macos/update_appcast_tip.py | |
test -f appcast_new.xml | |
# Upload our binaries first | |
- name: Prep R2 Storage | |
run: | | |
mkdir blob | |
mkdir -p blob/${GHOSTTY_COMMIT_LONG} | |
cp ghostty-macos-universal.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal.zip | |
cp ghostty-macos-universal-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-dsym.zip | |
cp Ghostty.dmg blob/${GHOSTTY_COMMIT_LONG}/Ghostty.dmg | |
- name: Upload to R2 | |
uses: ryand56/r2-upload-action@latest | |
with: | |
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} | |
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} | |
r2-secret-access-key: ${{ secrets.CF_R2_TIP_SECRET_KEY }} | |
r2-bucket: ghostty-tip | |
source-dir: blob | |
destination-dir: ./ | |
# Now upload our appcast. This ensures that the appcast never | |
# gets out of sync with the binaries. | |
- name: Prep R2 Storage for Appcast | |
run: | | |
rm -r blob | |
mkdir blob | |
cp appcast_new.xml blob/appcast.xml | |
- name: Upload Appcast to R2 | |
uses: ryand56/r2-upload-action@latest | |
with: | |
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} | |
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} | |
r2-secret-access-key: ${{ secrets.CF_R2_TIP_SECRET_KEY }} | |
r2-bucket: ghostty-tip | |
source-dir: blob | |
destination-dir: ./ | |
build-macos-debug-slow: | |
if: | | |
${{ | |
github.event_name == 'workflow_dispatch' || | |
( | |
github.event.workflow_run.conclusion == 'success' && | |
github.repository_owner == 'ghostty-org' && | |
github.ref_name == 'main' | |
) | |
}} | |
runs-on: namespace-profile-ghostty-macos | |
timeout-minutes: 90 | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
# Important so that build number generation works | |
fetch-depth: 0 | |
# Install Nix and use that to run our tests so our environment matches exactly. | |
- uses: cachix/install-nix-action@v30 | |
with: | |
nix_path: nixpkgs=channel:nixos-unstable | |
- uses: cachix/cachix-action@v15 | |
with: | |
name: ghostty | |
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" | |
- name: XCode Select | |
run: sudo xcode-select -s /Applications/Xcode_16.0.app | |
# Setup Sparkle | |
- name: Setup Sparkle | |
env: | |
SPARKLE_VERSION: 2.5.1 | |
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 | |
# Load Build Number | |
- name: Build Number | |
run: | | |
echo "GHOSTTY_BUILD=$(git rev-list --count head)" >> $GITHUB_ENV | |
echo "GHOSTTY_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV | |
echo "GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD)" >> $GITHUB_ENV | |
# 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=Debug | |
# 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 | |
# We inject the "build number" as simply the number of commits since HEAD. | |
# This will be a monotonically always increasing build number that we use. | |
- 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_COMMIT" "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" | |
- 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: "Notarize app bundle" | |
env: | |
PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} | |
PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} | |
PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} | |
run: | | |
# Store the notarization credentials so that we can prevent a UI password dialog | |
# from blocking the CI | |
echo "Create keychain profile" | |
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD" | |
# We can't notarize an app bundle directly, but we need to compress it as an archive. | |
# Therefore, we create a zip file containing our app bundle, so that we can send it to the | |
# notarization service | |
echo "Creating temp notarization archive" | |
ditto -c -k --keepParent "macos/build/Release/Ghostty.app" "notarization.zip" | |
# 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 app" | |
xcrun notarytool submit "notarization.zip" --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. | |
echo "Attach staple" | |
xcrun stapler staple "macos/build/Release/Ghostty.app" | |
# Zip up the app | |
- name: Zip App | |
run: | | |
cd macos/build/Release | |
zip -9 -r --symlinks ../../../ghostty-macos-universal-debug-slow.zip Ghostty.app | |
zip -9 -r --symlinks ../../../ghostty-macos-universal-debug-slow-dsym.zip Ghostty.app.dSYM/ | |
# Update Release | |
- name: Release | |
uses: softprops/action-gh-release@v2 | |
with: | |
name: 'Ghostty Tip ("Nightly")' | |
prerelease: true | |
tag_name: tip | |
target_commitish: ${{ github.sha }} | |
files: ghostty-macos-universal-debug-slow.zip | |
token: ${{ secrets.GH_RELEASE_TOKEN }} | |
# Update Blob Storage | |
- name: Prep R2 Storage | |
run: | | |
mkdir blob | |
mkdir -p blob/${GHOSTTY_COMMIT_LONG} | |
cp ghostty-macos-universal-debug-slow.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow.zip | |
cp ghostty-macos-universal-debug-slow-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow-dsym.zip | |
- name: Upload to R2 | |
uses: ryand56/r2-upload-action@latest | |
with: | |
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} | |
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} | |
r2-secret-access-key: ${{ secrets.CF_R2_TIP_SECRET_KEY }} | |
r2-bucket: ghostty-tip | |
source-dir: blob | |
destination-dir: ./ | |
build-macos-debug-fast: | |
if: | | |
${{ | |
github.event_name == 'workflow_dispatch' || | |
( | |
github.event.workflow_run.conclusion == 'success' && | |
github.repository_owner == 'ghostty-org' && | |
github.ref_name == 'main' | |
) | |
}} | |
runs-on: namespace-profile-ghostty-macos | |
timeout-minutes: 90 | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
# Important so that build number generation works | |
fetch-depth: 0 | |
# Install Nix and use that to run our tests so our environment matches exactly. | |
- uses: cachix/install-nix-action@v30 | |
with: | |
nix_path: nixpkgs=channel:nixos-unstable | |
- uses: cachix/cachix-action@v15 | |
with: | |
name: ghostty | |
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" | |
- name: XCode Select | |
run: sudo xcode-select -s /Applications/Xcode_16.0.app | |
# Setup Sparkle | |
- name: Setup Sparkle | |
env: | |
SPARKLE_VERSION: 2.5.1 | |
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 | |
# Load Build Number | |
- name: Build Number | |
run: | | |
echo "GHOSTTY_BUILD=$(git rev-list --count head)" >> $GITHUB_ENV | |
echo "GHOSTTY_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV | |
echo "GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD)" >> $GITHUB_ENV | |
# 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=ReleaseSafe | |
# 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 | |
# We inject the "build number" as simply the number of commits since HEAD. | |
# This will be a monotonically always increasing build number that we use. | |
- 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_COMMIT" "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" | |
- 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: "Notarize app bundle" | |
env: | |
PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }} | |
PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }} | |
PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }} | |
run: | | |
# Store the notarization credentials so that we can prevent a UI password dialog | |
# from blocking the CI | |
echo "Create keychain profile" | |
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD" | |
# We can't notarize an app bundle directly, but we need to compress it as an archive. | |
# Therefore, we create a zip file containing our app bundle, so that we can send it to the | |
# notarization service | |
echo "Creating temp notarization archive" | |
ditto -c -k --keepParent "macos/build/Release/Ghostty.app" "notarization.zip" | |
# 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 app" | |
xcrun notarytool submit "notarization.zip" --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. | |
echo "Attach staple" | |
xcrun stapler staple "macos/build/Release/Ghostty.app" | |
# Zip up the app | |
- name: Zip App | |
run: | | |
cd macos/build/Release | |
zip -9 -r --symlinks ../../../ghostty-macos-universal-debug-fast.zip Ghostty.app | |
zip -9 -r --symlinks ../../../ghostty-macos-universal-debug-fast-dsym.zip Ghostty.app.dSYM/ | |
# Update Release | |
- name: Release | |
uses: softprops/action-gh-release@v2 | |
with: | |
name: 'Ghostty Tip ("Nightly")' | |
prerelease: true | |
tag_name: tip | |
target_commitish: ${{ github.sha }} | |
files: ghostty-macos-universal-debug-fast.zip | |
token: ${{ secrets.GH_RELEASE_TOKEN }} | |
# Update Blob Storage | |
- name: Prep R2 Storage | |
run: | | |
mkdir blob | |
mkdir -p blob/${GHOSTTY_COMMIT_LONG} | |
cp ghostty-macos-universal-debug-fast.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast.zip | |
cp ghostty-macos-universal-debug-fast-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast-dsym.zip | |
- name: Upload to R2 | |
uses: ryand56/r2-upload-action@latest | |
with: | |
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }} | |
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }} | |
r2-secret-access-key: ${{ secrets.CF_R2_TIP_SECRET_KEY }} | |
r2-bucket: ghostty-tip | |
source-dir: blob | |
destination-dir: ./ |