diff --git a/.github/workflows/release-macos-app.yml b/.github/workflows/release-macos-app.yml
deleted file mode 100644
index 19b39fc3..00000000
--- a/.github/workflows/release-macos-app.yml
+++ /dev/null
@@ -1,500 +0,0 @@
-name: Release macOS App
-
-# Disabled until GitHub secrets are configured for code signing
-# To enable: add APPLE_CERTIFICATE_P12, APPLE_CERTIFICATE_PASSWORD,
-# APPLE_ID, APPLE_ID_PASSWORD, APPLE_TEAM_ID secrets
-on:
- workflow_dispatch:
- inputs:
- tag:
- description: 'Release tag to attach artifact to (e.g., v0.1.0)'
- required: true
- # Uncomment to auto-trigger on release:
- # release:
- # types: [published]
- workflow_call:
- inputs:
- tag:
- description: 'Release tag to attach artifact to'
- required: true
- type: string
- secrets:
- APPLE_CERTIFICATE_P12:
- required: true
- APPLE_CERTIFICATE_PASSWORD:
- required: true
- APPLE_ID:
- required: true
- APPLE_ID_PASSWORD:
- required: true
- APPLE_TEAM_ID:
- required: true
- SPARKLE_EDDSA_PRIVATE_KEY:
- required: true
-
-env:
- APP_NAME: "Hack Desktop"
- SCHEME: HackDesktop
- PROJECT_PATH: apps/macos
-
-jobs:
- build-and-notarize:
- runs-on: blacksmith-6vcpu-macos-15
- permissions:
- contents: write
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- ref: ${{ inputs.tag }}
-
- - name: Setup Xcode
- uses: maxim-lobanov/setup-xcode@v1
- with:
- xcode-version: "latest-stable"
-
- - name: Resolve release metadata
- id: meta
- run: |
- set -euo pipefail
- TAG="${{ inputs.tag }}"
- VERSION="${TAG#v}"
- ARCH="$(uname -m)"
- if [ "$ARCH" = "x86_64" ] || [ "$ARCH" = "amd64" ]; then
- ARCH="x86_64"
- elif [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then
- ARCH="arm64"
- else
- echo "Unsupported architecture: $ARCH"
- exit 1
- fi
-
- echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- echo "arch=$ARCH" >> "$GITHUB_OUTPUT"
-
- # Important: do this before `xcodegen generate`. XcodeGen snapshots resources at generation time,
- # and we populate BundledCLI dynamically from the release tarball.
- - name: Bundle CLI into app resources
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- RELEASE_TAG: ${{ steps.meta.outputs.tag }}
- VERSION: ${{ steps.meta.outputs.version }}
- ARCH: ${{ steps.meta.outputs.arch }}
- run: |
- set -euo pipefail
- TARBALL="hack-$VERSION-darwin-$ARCH.tar.gz"
-
- mkdir -p "$RUNNER_TEMP/cli"
- gh release download "$RELEASE_TAG" -p "$TARBALL" --dir "$RUNNER_TEMP/cli"
-
- mkdir -p "$RUNNER_TEMP/cli/extract"
- tar -xzf "$RUNNER_TEMP/cli/$TARBALL" -C "$RUNNER_TEMP/cli/extract"
-
- RELEASE_DIR="$RUNNER_TEMP/cli/extract/hack-$VERSION-release"
- if [ ! -d "$RELEASE_DIR" ]; then
- echo "Missing release dir: $RELEASE_DIR"
- exit 1
- fi
-
- BUNDLE_DIR="apps/macos/App/BundledCLI"
- mkdir -p "$BUNDLE_DIR"
- rm -rf "$BUNDLE_DIR/hack" "$BUNDLE_DIR/assets" "$BUNDLE_DIR/binaries"
-
- cp "$RELEASE_DIR/hack" "$BUNDLE_DIR/hack"
- if [ -d "$RELEASE_DIR/assets" ]; then
- cp -R "$RELEASE_DIR/assets" "$BUNDLE_DIR/assets"
- fi
- if [ -d "$RELEASE_DIR/binaries" ]; then
- cp -R "$RELEASE_DIR/binaries" "$BUNDLE_DIR/binaries"
- fi
-
- - name: Install Zig (0.15.2)
- run: |
- set -euo pipefail
- ZIG_VERSION="0.15.2"
-
- HOST_ARCH="$(uname -m)"
- if [ "$HOST_ARCH" = "arm64" ] || [ "$HOST_ARCH" = "aarch64" ]; then
- ZIG_ARCH="aarch64"
- elif [ "$HOST_ARCH" = "x86_64" ] || [ "$HOST_ARCH" = "amd64" ]; then
- ZIG_ARCH="x86_64"
- else
- echo "Unsupported host arch: $HOST_ARCH"
- exit 1
- fi
-
- URL="https://ziglang.org/download/${ZIG_VERSION}/zig-${ZIG_ARCH}-macos-${ZIG_VERSION}.tar.xz"
- curl -fsSL "$URL" -o "$RUNNER_TEMP/zig.tar.xz"
- mkdir -p "$RUNNER_TEMP/zig"
- tar -xf "$RUNNER_TEMP/zig.tar.xz" -C "$RUNNER_TEMP/zig"
-
- ZIG_BIN="$(find "$RUNNER_TEMP/zig" -maxdepth 2 -type f -name zig | head -n 1)"
- if [ -z "$ZIG_BIN" ]; then
- echo "Failed to locate zig binary after extracting."
- exit 1
- fi
-
- ZIG_DIR="$(dirname "$ZIG_BIN")"
- export PATH="$ZIG_DIR:$PATH"
- echo "$ZIG_DIR" >> "$GITHUB_PATH"
- "$ZIG_BIN" version
-
- - name: Build Ghostty VT dylib (universal)
- run: |
- set -euo pipefail
-
- VENDOR_DIR="apps/macos/vendor/ghostty"
- BRIDGE_DIR="apps/macos/Experiments/GhosttyVTBridge"
- REVISION_FILE="$BRIDGE_DIR/GHOSTTY_REVISION"
- OUT_DIR="apps/macos/App/GhosttyVT/ghostty/lib"
- OUT_LIB="$OUT_DIR/libhack_ghostty_vt.dylib"
- GHOSTTY_REVISION="$(tr -d '[:space:]' < "$REVISION_FILE")"
-
- if [ ! -d "$VENDOR_DIR" ]; then
- mkdir -p "$(dirname "$VENDOR_DIR")"
- git clone --filter=blob:none --no-checkout https://github.com/ghostty-org/ghostty "$VENDOR_DIR"
- fi
-
- git -C "$VENDOR_DIR" fetch --depth 1 origin "$GHOSTTY_REVISION"
- git -C "$VENDOR_DIR" checkout --detach --force FETCH_HEAD
-
- ACTUAL_GHOSTTY_REVISION="$(git -C "$VENDOR_DIR" rev-parse HEAD)"
- if [ "$ACTUAL_GHOSTTY_REVISION" != "$GHOSTTY_REVISION" ]; then
- echo "Expected Ghostty $GHOSTTY_REVISION, checked out $ACTUAL_GHOSTTY_REVISION"
- exit 1
- fi
- echo "Ghostty revision: $ACTUAL_GHOSTTY_REVISION"
-
- mkdir -p "$OUT_DIR"
-
- # Patch Ghostty's lib_vt.zig for our C ABI bridge.
- python3 - <<'PY'
- import pathlib
-
- lib_vt = pathlib.Path("apps/macos/vendor/ghostty/src/lib_vt.zig")
- text = lib_vt.read_text(encoding="utf-8")
- guard = 'if (@import("root") == lib) {'
- patched = 'if (@import("root") == lib and terminal.options.c_abi) {'
- if guard in text and patched not in text:
- text = text.replace(guard, patched)
- if "@export(&" in text:
- text = text.replace("@export(&", "@export(")
- lib_vt.write_text(text, encoding="utf-8")
- PY
-
- mkdir -p "$RUNNER_TEMP/ghosttyvt/arm64" "$RUNNER_TEMP/ghosttyvt/x86_64"
-
- pushd "$BRIDGE_DIR" >/dev/null
- GHOSTTY_DIR="$GITHUB_WORKSPACE/$VENDOR_DIR"
-
- zig build -Dghostty="$GHOSTTY_DIR" -Doptimize=ReleaseSafe -Dtarget=aarch64-macos --prefix "$RUNNER_TEMP/ghosttyvt/arm64"
- zig build -Dghostty="$GHOSTTY_DIR" -Doptimize=ReleaseSafe -Dtarget=x86_64-macos --prefix "$RUNNER_TEMP/ghosttyvt/x86_64"
- popd >/dev/null
-
- ARM_LIB="$RUNNER_TEMP/ghosttyvt/arm64/lib/libhack_ghostty_vt.dylib"
- X64_LIB="$RUNNER_TEMP/ghosttyvt/x86_64/lib/libhack_ghostty_vt.dylib"
-
- if [ ! -f "$ARM_LIB" ] || [ ! -f "$X64_LIB" ]; then
- echo "Missing built libs: $ARM_LIB / $X64_LIB"
- ls -la "$RUNNER_TEMP/ghosttyvt/arm64/lib" || true
- ls -la "$RUNNER_TEMP/ghosttyvt/x86_64/lib" || true
- exit 1
- fi
-
- lipo -create "$ARM_LIB" "$X64_LIB" -output "$OUT_LIB"
-
- - name: Install xcodegen
- run: brew install xcodegen
-
- - name: Generate Xcode project
- working-directory: ${{ env.PROJECT_PATH }}
- run: xcodegen generate
-
- - name: Import certificate
- env:
- CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }}
- CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- run: |
- # Create temporary keychain
- KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
- KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
-
- security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
- security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
- security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
-
- # Import certificate
- echo "$CERTIFICATE_P12" | base64 --decode > $RUNNER_TEMP/certificate.p12
- security import $RUNNER_TEMP/certificate.p12 \
- -P "$CERTIFICATE_PASSWORD" \
- -A \
- -t cert \
- -f pkcs12 \
- -k "$KEYCHAIN_PATH"
-
- # Allow codesign to access keychain
- security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
-
- # Add to search list
- security list-keychain -d user -s "$KEYCHAIN_PATH"
-
- echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> $GITHUB_ENV
-
- - name: Resolve codesign identity
- run: |
- set -euo pipefail
- LINE="$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" | grep 'Developer ID Application' | head -n 1 || true)"
- if [ -z "$LINE" ]; then
- echo "No Developer ID Application identity found in keychain: $KEYCHAIN_PATH"
- security find-identity -v -p codesigning "$KEYCHAIN_PATH" || true
- exit 1
- fi
-
- IDENTITY_HASH="$(echo "$LINE" | awk '{print $2}')"
- IDENTITY_NAME="$(echo "$LINE" | awk -F '\"' '{print $2}')"
-
- if [ -z "$IDENTITY_HASH" ] || [ -z "$IDENTITY_NAME" ]; then
- echo "Failed to parse identity from line: $LINE"
- exit 1
- fi
-
- echo "CODESIGN_IDENTITY_HASH=$IDENTITY_HASH" >> "$GITHUB_ENV"
- echo "CODESIGN_IDENTITY_NAME=$IDENTITY_NAME" >> "$GITHUB_ENV"
-
- - name: Codesign bundled CLI
- env:
- BUNDLED_HACK: apps/macos/App/BundledCLI/hack
- run: |
- set -euo pipefail
- if [ ! -f "$BUNDLED_HACK" ]; then
- echo "Missing bundled hack binary: $BUNDLED_HACK"
- exit 1
- fi
-
- codesign --force --options runtime --timestamp \
- --entitlements "apps/macos/App/HackCLIBundled.entitlements" \
- --sign "$CODESIGN_IDENTITY_HASH" \
- --keychain "$KEYCHAIN_PATH" \
- "$BUNDLED_HACK"
-
- codesign -vvv "$BUNDLED_HACK"
-
- - name: Codesign Ghostty VT dylib
- env:
- GHOSTTY_VT: apps/macos/App/GhosttyVT/ghostty/lib/libhack_ghostty_vt.dylib
- run: |
- set -euo pipefail
- if [ ! -f "$GHOSTTY_VT" ]; then
- echo "Missing Ghostty VT dylib: $GHOSTTY_VT"
- exit 1
- fi
-
- codesign --force --options runtime --timestamp \
- --sign "$CODESIGN_IDENTITY_HASH" \
- --keychain "$KEYCHAIN_PATH" \
- "$GHOSTTY_VT"
-
- codesign -vvv "$GHOSTTY_VT"
-
- - name: Build and archive
- working-directory: ${{ env.PROJECT_PATH }}
- env:
- TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- run: |
- xcodebuild archive \
- -scheme "$SCHEME" \
- -configuration Release \
- -archivePath "$RUNNER_TEMP/HackDesktop.xcarchive" \
- CODE_SIGN_STYLE=Manual \
- CODE_SIGN_IDENTITY="$CODESIGN_IDENTITY_NAME" \
- DEVELOPMENT_TEAM="$TEAM_ID" \
- OTHER_CODE_SIGN_FLAGS="--keychain $KEYCHAIN_PATH --timestamp"
-
- - name: Export app
- env:
- TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- run: |
- cat > $RUNNER_TEMP/ExportOptions.plist << EOF
-
-
-
-
- method
- developer-id
- teamID
- $TEAM_ID
- signingStyle
- manual
- signingCertificate
- $CODESIGN_IDENTITY_NAME
-
-
- EOF
-
- xcodebuild -exportArchive \
- -archivePath "$RUNNER_TEMP/HackDesktop.xcarchive" \
- -exportPath "$RUNNER_TEMP/export" \
- -exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist"
-
- - name: Notarize app
- env:
- APPLE_ID: ${{ secrets.APPLE_ID }}
- APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
- APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- run: |
- # Create ZIP for notarization
- ditto -c -k --keepParent "$RUNNER_TEMP/export/$APP_NAME.app" "$RUNNER_TEMP/app.zip"
-
- # Submit for notarization
- xcrun notarytool submit "$RUNNER_TEMP/app.zip" \
- --apple-id "$APPLE_ID" \
- --password "$APPLE_ID_PASSWORD" \
- --team-id "$APPLE_TEAM_ID" \
- --wait
-
- # Staple the notarization ticket
- xcrun stapler staple "$RUNNER_TEMP/export/$APP_NAME.app"
-
- - name: Create DMG
- run: |
- # Install create-dmg
- brew install create-dmg
-
- # Create DMG
- create-dmg \
- --volname "$APP_NAME" \
- --window-pos 200 120 \
- --window-size 600 400 \
- --icon-size 100 \
- --icon "$APP_NAME.app" 150 190 \
- --app-drop-link 450 190 \
- --hide-extension "$APP_NAME.app" \
- "$RUNNER_TEMP/HackDesktop.dmg" \
- "$RUNNER_TEMP/export/$APP_NAME.app" || true
-
- # Fallback if create-dmg fails (it returns non-zero even on success sometimes)
- if [ ! -f "$RUNNER_TEMP/HackDesktop.dmg" ]; then
- hdiutil create -volname "$APP_NAME" \
- -srcfolder "$RUNNER_TEMP/export/$APP_NAME.app" \
- -ov -format UDZO \
- "$RUNNER_TEMP/HackDesktop.dmg"
- fi
-
- - name: Notarize DMG
- env:
- APPLE_ID: ${{ secrets.APPLE_ID }}
- APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
- APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
- run: |
- xcrun notarytool submit "$RUNNER_TEMP/HackDesktop.dmg" \
- --apple-id "$APPLE_ID" \
- --password "$APPLE_ID_PASSWORD" \
- --team-id "$APPLE_TEAM_ID" \
- --wait
-
- xcrun stapler staple "$RUNNER_TEMP/HackDesktop.dmg"
-
- - name: Get version
- id: version
- run: |
- VERSION=$(defaults read "$RUNNER_TEMP/export/$APP_NAME.app/Contents/Info.plist" CFBundleShortVersionString)
- echo "version=$VERSION" >> $GITHUB_OUTPUT
-
- - name: Rename artifacts
- env:
- VERSION: ${{ steps.version.outputs.version }}
- run: |
- mv "$RUNNER_TEMP/HackDesktop.dmg" "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.dmg"
-
- # Also create a ZIP of the app
- ditto -c -k --keepParent \
- "$RUNNER_TEMP/export/$APP_NAME.app" \
- "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.zip"
-
- - name: Generate Sparkle appcast
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
- VERSION: ${{ steps.version.outputs.version }}
- SPARKLE_EDDSA_PRIVATE_KEY: ${{ secrets.SPARKLE_EDDSA_PRIVATE_KEY }}
- run: |
- set -euo pipefail
-
- mkdir -p "$RUNNER_TEMP/sparkle"
- gh release download 2.8.1 -R sparkle-project/Sparkle -p "Sparkle-2.8.1.tar.xz" --dir "$RUNNER_TEMP/sparkle"
- tar -xf "$RUNNER_TEMP/sparkle/Sparkle-2.8.1.tar.xz" -C "$RUNNER_TEMP/sparkle"
-
- ZIP_PATH="$RUNNER_TEMP/HackDesktop-$VERSION-macOS.zip"
- if [ ! -f "$ZIP_PATH" ]; then
- echo "Missing ZIP: $ZIP_PATH"
- exit 1
- fi
-
- SIGN_LINE="$(printf '%s' "$SPARKLE_EDDSA_PRIVATE_KEY" | "$RUNNER_TEMP/sparkle/bin/sign_update" --ed-key-file - "$ZIP_PATH")"
- ED_SIGNATURE="$(echo "$SIGN_LINE" | sed -n 's/.*sparkle:edSignature=\"\([^\"]*\)\".*/\1/p')"
- LENGTH="$(echo "$SIGN_LINE" | sed -n 's/.*length=\"\([0-9]*\)\".*/\1/p')"
-
- if [ -z "$ED_SIGNATURE" ] || [ -z "$LENGTH" ]; then
- echo "Failed to parse signature output: $SIGN_LINE"
- exit 1
- fi
-
- PUB_DATE="$(date -u +"%a, %d %b %Y %H:%M:%S %z")"
- DOWNLOAD_URL="https://github.com/hack-dance/hack/releases/download/$RELEASE_TAG/HackDesktop-$VERSION-macOS.zip"
- NOTES_URL="https://github.com/hack-dance/hack/releases/tag/$RELEASE_TAG"
-
- cat > "$RUNNER_TEMP/appcast.xml" << EOF
-
-
-
- Hack Desktop
- https://github.com/hack-dance/hack
- Hack Desktop updates
- en
- -
- Version $VERSION
- $NOTES_URL
- $PUB_DATE
-
-
-
-
- EOF
-
- - name: Upload artifacts
- uses: actions/upload-artifact@v4
- with:
- name: macos-app
- path: |
- ${{ runner.temp }}/HackDesktop-*.dmg
- ${{ runner.temp }}/HackDesktop-*.zip
- ${{ runner.temp }}/appcast.xml
-
- - name: Upload to release
- if: github.event_name == 'release' || inputs.tag != ''
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
- VERSION: ${{ steps.version.outputs.version }}
- run: |
- gh release upload "$RELEASE_TAG" \
- "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.dmg" \
- "$RUNNER_TEMP/HackDesktop-$VERSION-macOS.zip" \
- "$RUNNER_TEMP/appcast.xml" \
- --clobber
-
- - name: Cleanup keychain
- if: always()
- run: |
- if [ -n "$KEYCHAIN_PATH" ] && [ -f "$KEYCHAIN_PATH" ]; then
- security delete-keychain "$KEYCHAIN_PATH" || true
- fi
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bd886908..47943bfa 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -145,15 +145,6 @@ jobs:
done
done
- macos-app:
- needs: [create-release, build]
- permissions:
- contents: write
- uses: ./.github/workflows/release-macos-app.yml
- with:
- tag: ${{ needs.create-release.outputs.tag }}
- secrets: inherit
-
update-homebrew-tap:
needs: [create-release, build]
runs-on: blacksmith-4vcpu-ubuntu-2404
diff --git a/.releaserc.json b/.releaserc.json
index 26c199a4..1f8ae3f2 100644
--- a/.releaserc.json
+++ b/.releaserc.json
@@ -23,8 +23,7 @@
"packages/cli/package.json",
"packages/db/package.json",
"services/auth-broker/package.json",
- "bun.lock",
- "apps/macos/Config/Base.xcconfig"
+ "bun.lock"
],
"message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
}
diff --git a/apps/macos/README.md b/apps/macos/README.md
index 178bc771..36921750 100644
--- a/apps/macos/README.md
+++ b/apps/macos/README.md
@@ -2,7 +2,9 @@
Native macOS app for managing local hack projects, runtime status, and quick actions.
-Hack v3 keeps the app intentionally small and local-first.
+The source is retained for possible future work, but the app is currently unsupported and is not
+built, signed, or published by Hack's CI/release pipeline. The commands below are local development
+tools only.
## Requirements
diff --git a/scripts/prepare-release.ts b/scripts/prepare-release.ts
index c06d1b39..eea51fd3 100644
--- a/scripts/prepare-release.ts
+++ b/scripts/prepare-release.ts
@@ -62,24 +62,6 @@ async function main({ args }: { readonly args: Args }): Promise {
rootPackageJson: pkg,
});
- // Update macOS app version in Base.xcconfig
- const xconfigPath = resolve(repoRoot, "apps/macos/Config/Base.xcconfig");
- try {
- const xconfigContent = await Bun.file(xconfigPath).text();
- const updatedXconfig = xconfigContent.replace(
- /^MARKETING_VERSION = .*/m,
- `MARKETING_VERSION = ${nextVersion}`
- );
- if (updatedXconfig !== xconfigContent) {
- await Bun.write(xconfigPath, updatedXconfig);
- process.stdout.write(
- `Updated Base.xcconfig: MARKETING_VERSION → ${nextVersion}\n`
- );
- }
- } catch {
- // macOS config may not exist, that's fine
- }
-
return 0;
}
diff --git a/tests/macos-ghostty-source.test.ts b/tests/macos-ghostty-source.test.ts
index de97446d..0b633386 100644
--- a/tests/macos-ghostty-source.test.ts
+++ b/tests/macos-ghostty-source.test.ts
@@ -5,7 +5,7 @@ const repoRoot = path.resolve(import.meta.dir, "..");
const bridgeDir = path.join(repoRoot, "apps/macos/Experiments/GhosttyVTBridge");
describe("Ghostty source pin", () => {
- test("uses one immutable revision in local and release builds", async () => {
+ test("uses one immutable revision in local builds", async () => {
const revision = (
await Bun.file(path.join(bridgeDir, "GHOSTTY_REVISION")).text()
).trim();
@@ -15,10 +15,6 @@ describe("Ghostty source pin", () => {
const bundleScript = await Bun.file(
path.join(repoRoot, "scripts/macos-ghostty-bundle.ts")
).text();
- const releaseWorkflow = await Bun.file(
- path.join(repoRoot, ".github/workflows/release-macos-app.yml")
- ).text();
-
expect(revision).toMatch(/^[0-9a-f]{40}$/);
expect(setupScript).toContain('path.join(bridgeDir, "GHOSTTY_REVISION")');
expect(bundleScript).toContain('path.join(bridgeDir, "GHOSTTY_REVISION")');
@@ -26,12 +22,6 @@ describe("Ghostty source pin", () => {
expect(bundleScript).toContain(
"git -C ${vendorDir} reset --hard ${ghosttyRevision}"
);
- expect(releaseWorkflow).toContain(
- 'REVISION_FILE="$BRIDGE_DIR/GHOSTTY_REVISION"'
- );
expect(setupScript).not.toContain("fetch --depth 1 origin main");
- expect(releaseWorkflow).not.toContain(
- "git clone --depth 1 https://github.com/ghostty-org/ghostty"
- );
});
});
diff --git a/tests/release-boundary.test.ts b/tests/release-boundary.test.ts
new file mode 100644
index 00000000..317d2dfd
--- /dev/null
+++ b/tests/release-boundary.test.ts
@@ -0,0 +1,27 @@
+import { describe, expect, test } from "bun:test";
+import path from "node:path";
+
+const repoRoot = path.resolve(import.meta.dir, "..");
+
+describe("release product boundary", () => {
+ test("does not build or publish the retained macOS app", async () => {
+ const releaseWorkflow = await Bun.file(
+ path.join(repoRoot, ".github/workflows/release.yml")
+ ).text();
+ const macosReleaseWorkflow = Bun.file(
+ path.join(repoRoot, ".github/workflows/release-macos-app.yml")
+ );
+ const releaseConfig = await Bun.file(
+ path.join(repoRoot, ".releaserc.json")
+ ).json();
+ const prepareRelease = await Bun.file(
+ path.join(repoRoot, "scripts/prepare-release.ts")
+ ).text();
+
+ expect(releaseWorkflow).not.toContain("release-macos-app");
+ expect(releaseWorkflow).not.toContain("macos-app:");
+ expect(await macosReleaseWorkflow.exists()).toBe(false);
+ expect(JSON.stringify(releaseConfig)).not.toContain("apps/macos");
+ expect(prepareRelease).not.toContain("Base.xcconfig");
+ });
+});