diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..37e64e75 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,92 @@ +name: Release macOS app + +on: + push: + tags: + - "v*" + workflow_dispatch: + +permissions: + contents: write + +jobs: + build-sign-notarize: + runs-on: self-hosted + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install build deps + run: | + brew update + brew install zig + + - name: Build GhosttyKit.xcframework + run: | + cd ghostty + zig build -Demit-xcframework=true -Dxcframework-target=native -Doptimize=ReleaseFast + cd .. + rm -rf GhosttyKit.xcframework + cp -R ghostty/macos/GhosttyKit.xcframework GhosttyKit.xcframework + + - name: Build app (Release) + run: | + xcodebuild -scheme GhosttyTabs -configuration Release -derivedDataPath build CODE_SIGNING_ALLOWED=NO build + + - name: Import signing cert + env: + APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + run: | + if [ -z "$APPLE_CERTIFICATE_BASE64" ]; then + echo "Missing APPLE_CERTIFICATE_BASE64 secret" >&2 + exit 1 + fi + if [ -z "$APPLE_CERTIFICATE_PASSWORD" ]; then + echo "Missing APPLE_CERTIFICATE_PASSWORD secret" >&2 + exit 1 + fi + KEYCHAIN_PASSWORD="$(uuidgen)" + echo "$APPLE_CERTIFICATE_BASE64" | base64 --decode > /tmp/cert.p12 + security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain + security set-keychain-settings -lut 21600 build.keychain + security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain + security import /tmp/cert.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security + security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain + security list-keychains -d user -s build.keychain + + - name: Codesign app + env: + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + run: | + if [ -z "$APPLE_SIGNING_IDENTITY" ]; then + echo "Missing APPLE_SIGNING_IDENTITY secret" >&2 + exit 1 + fi + APP_PATH="build/Build/Products/Release/GhosttyTabs.app" + /usr/bin/codesign --force --options runtime --timestamp --sign "$APPLE_SIGNING_IDENTITY" --deep "$APP_PATH" + /usr/bin/codesign --verify --deep --strict --verbose=2 "$APP_PATH" + + - name: Notarize app + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: | + if [ -z "$APPLE_ID" ] || [ -z "$APPLE_APP_SPECIFIC_PASSWORD" ] || [ -z "$APPLE_TEAM_ID" ]; then + echo "Missing notarization secrets (APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID)" >&2 + exit 1 + fi + APP_PATH="build/Build/Products/Release/GhosttyTabs.app" + ZIP_PATH="GhosttyTabs-macos.zip" + ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$ZIP_PATH" + xcrun notarytool submit "$ZIP_PATH" --apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" --password "$APPLE_APP_SPECIFIC_PASSWORD" --wait + xcrun stapler staple "$APP_PATH" + + - name: Upload release asset + uses: softprops/action-gh-release@v2 + with: + files: GhosttyTabs-macos.zip + generate_release_notes: true diff --git a/README.md b/README.md new file mode 100644 index 00000000..f1e49300 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# GhosttyTabs + +Vertical tabs for Ghostty on macOS, built on libghostty. + +[![Download macOS](https://img.shields.io/badge/Download-macOS-1b5fdd?style=for-the-badge&logo=apple)](releases/latest/download/GhosttyTabs-macos.zip) + +## Releases + +Tag a version like `v0.1.0` and push it to trigger the GitHub Actions release workflow. +The workflow builds `GhosttyKit.xcframework`, builds the Release app, signs, notarizes, +staples, and uploads `GhosttyTabs-macos.zip` to the release. + +### Required GitHub secrets + +- `APPLE_CERTIFICATE_BASE64`: Base64-encoded Developer ID Application .p12 +- `APPLE_CERTIFICATE_PASSWORD`: Password for the .p12 +- `APPLE_SIGNING_IDENTITY`: e.g. `Developer ID Application: Your Name (TEAMID)` +- `APPLE_ID`: Apple ID used for notarization +- `APPLE_APP_SPECIFIC_PASSWORD`: App-specific password for the Apple ID +- `APPLE_TEAM_ID`: Apple Developer Team ID