diff --git a/.github/workflows/release-tip.yml b/.github/workflows/release-tip.yml index a8723d428..4ef6d51b6 100644 --- a/.github/workflows/release-tip.yml +++ b/.github/workflows/release-tip.yml @@ -196,3 +196,156 @@ jobs: r2-bucket: ghostty-tip source-dir: blob destination-dir: ./ + + build-macos-debug: + if: | + ${{ + github.event_name == 'workflow_dispatch' || + ( + github.event.workflow_run.conclusion == 'success' && + github.repository_owner == 'mitchellh' && + 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@v26 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@v14 + with: + name: ghostty + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + + # 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 + + # 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 -Dstatic=true -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.zip Ghostty.app + + # Update Blob Storage + - name: Prep R2 Storage + run: | + mkdir blob + mkdir -p blob/${GHOSTTY_BUILD} + cp ghostty-macos-universal-debug.zip blob/${GHOSTTY_BUILD}/ghostty-macos-universal-debug.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: ./