-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat(cua-driver): add Intel Mac (x86_64) support via cross-compilation #1469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| name: "CI: Cua Driver — PR pre-release binaries" | ||
|
|
||
| # Builds unsigned pre-release binaries for both darwin-arm64 and darwin-x86_64 | ||
| # on every PR that touches libs/cua-driver, then posts download links as a | ||
| # PR comment so Intel Mac users can test before merging. | ||
| # | ||
| # Limitations: | ||
| # - Binaries are UNSIGNED and UNNOTARIZED. macOS Gatekeeper will quarantine | ||
| # them. To run: xattr -d com.apple.quarantine ./cua-driver | ||
| # - Artifact links require a GitHub login and expire after 90 days. | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - "libs/cua-driver/**" | ||
| - ".github/workflows/ci-cua-driver-pr-binaries.yml" | ||
|
|
||
| concurrency: | ||
| group: cua-driver-pr-binaries-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| pull-requests: write # to post the comment | ||
| contents: read | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build (${{ matrix.arch }}) | ||
| runs-on: macos-15 | ||
| strategy: | ||
| matrix: | ||
| arch: [arm64, x86_64] | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Select Xcode 16.3 | ||
| run: sudo xcode-select -s /Applications/Xcode_16.3.app | ||
|
|
||
| - name: Build (${{ matrix.arch }}) | ||
| working-directory: ./libs/cua-driver | ||
| run: | | ||
| if [ "${{ matrix.arch }}" = "arm64" ]; then | ||
| swift build --configuration release --product cua-driver | ||
| BINARY=".build/release/cua-driver" | ||
| else | ||
| swift build --configuration release --product cua-driver --arch x86_64 | ||
| BINARY=".build/x86_64-apple-macosx/release/cua-driver" | ||
| fi | ||
|
|
||
| # Package into a tarball alongside a thin wrapper script so users | ||
| # can just extract and run ./cua-driver without knowing the bundle layout. | ||
| TARBALL="cua-driver-pr${{ github.event.pull_request.number }}-darwin-${{ matrix.arch }}.tar.gz" | ||
| mkdir -p /tmp/cua-driver-dist | ||
| cp "$BINARY" /tmp/cua-driver-dist/cua-driver | ||
| tar -czf "/tmp/$TARBALL" -C /tmp/cua-driver-dist cua-driver | ||
| echo "TARBALL=/tmp/$TARBALL" >> "$GITHUB_ENV" | ||
| echo "TARBALL_NAME=$TARBALL" >> "$GITHUB_ENV" | ||
|
|
||
| - name: Upload artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: cua-driver-pr${{ github.event.pull_request.number }}-darwin-${{ matrix.arch }} | ||
| path: ${{ env.TARBALL }} | ||
| retention-days: 90 | ||
|
|
||
| comment: | ||
| name: Post PR comment with download links | ||
| runs-on: ubuntu-latest | ||
| needs: build | ||
| steps: | ||
| - name: Get artifact URLs | ||
| id: artifacts | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: | | ||
| # GitHub Actions artifacts aren't directly linkable without auth, | ||
| # but we can construct the deep-link to the run's artifact list. | ||
| RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" | ||
| echo "run_url=$RUN_URL" >> "$GITHUB_OUTPUT" | ||
|
|
||
| ARM64_NAME="cua-driver-pr${{ github.event.pull_request.number }}-darwin-arm64" | ||
| X86_NAME="cua-driver-pr${{ github.event.pull_request.number }}-darwin-x86_64" | ||
|
|
||
| # Fetch artifact IDs so we can construct direct zip download URLs | ||
| ARTIFACTS=$(gh api \ | ||
| "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" \ | ||
| --jq '.artifacts[] | {name, id}') | ||
|
|
||
| ARM64_ID=$(echo "$ARTIFACTS" | python3 -c " | ||
| import sys, json | ||
| lines = sys.stdin.read().strip().split('\n') | ||
| import json as j | ||
| for line in lines: | ||
| try: | ||
| obj = j.loads(line) | ||
| if obj['name'] == '$ARM64_NAME': | ||
| print(obj['id']) | ||
| except: pass | ||
| " 2>/dev/null || echo "") | ||
|
|
||
| X86_ID=$(echo "$ARTIFACTS" | python3 -c " | ||
| import sys, json | ||
| lines = sys.stdin.read().strip().split('\n') | ||
| import json as j | ||
| for line in lines: | ||
| try: | ||
| obj = j.loads(line) | ||
| if obj['name'] == '$X86_NAME': | ||
| print(obj['id']) | ||
| except: pass | ||
| " 2>/dev/null || echo "") | ||
|
|
||
| REPO="${{ github.repository }}" | ||
| if [ -n "$ARM64_ID" ]; then | ||
| echo "arm64_url=https://github.com/${REPO}/actions/runs/${{ github.run_id }}/artifacts/${ARM64_ID}" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| if [ -n "$X86_ID" ]; then | ||
| echo "x86_url=https://github.com/${REPO}/actions/runs/${{ github.run_id }}/artifacts/${X86_ID}" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Find existing bot comment | ||
| id: find-comment | ||
| uses: peter-evans/find-comment@v3 | ||
| with: | ||
| issue-number: ${{ github.event.pull_request.number }} | ||
| comment-author: github-actions[bot] | ||
| body-includes: "<!-- cua-driver-pr-binaries -->" | ||
|
|
||
| - name: Post or update PR comment | ||
| uses: peter-evans/create-or-update-comment@v4 | ||
| with: | ||
| comment-id: ${{ steps.find-comment.outputs.comment-id }} | ||
| issue-number: ${{ github.event.pull_request.number }} | ||
| edit-mode: replace | ||
| body: | | ||
| <!-- cua-driver-pr-binaries --> | ||
| ## 🔧 Pre-release binaries (unsigned) | ||
|
|
||
| Built from commit ${{ github.event.pull_request.head.sha }} · [CI run](${{ steps.artifacts.outputs.run_url }}) | ||
|
|
||
| | Platform | Download | | ||
| |----------|----------| | ||
| | macOS — Apple Silicon (arm64) | [${{ github.event.pull_request.number }}-darwin-arm64](${{ steps.artifacts.outputs.arm64_url }}) | | ||
| | macOS — Intel (x86_64) | [${{ github.event.pull_request.number }}-darwin-x86_64](${{ steps.artifacts.outputs.x86_url }}) | | ||
|
|
||
| > **⚠️ Unsigned binary** — macOS will quarantine it on first download. To run: | ||
| > ```bash | ||
| > # After extracting the tarball: | ||
| > xattr -d com.apple.quarantine ./cua-driver | ||
| > chmod +x ./cua-driver | ||
| > ./cua-driver --version | ||
| > ``` | ||
| > Artifact links require a GitHub login and expire after 90 days. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,20 +60,48 @@ cd "$CUA_DRIVER_DIR" | |
| mkdir -p .release | ||
| log "normal" "Ensuring .release directory exists and is accessible" | ||
|
|
||
| # Get the native architecture and allow env override | ||
| NATIVE_ARCH=$(uname -m) | ||
| ARCH=${ARCH:-$(uname -m)} | ||
|
|
||
| # Build the release version | ||
| log "essential" "Building release version..." | ||
| swift build -c release --product cua-driver > /dev/null | ||
| log "essential" "Building release version (arch: $ARCH)..." | ||
| if [ "$ARCH" != "$NATIVE_ARCH" ]; then | ||
| if ! swift build -c release --product cua-driver --arch "$ARCH" > /dev/null; then | ||
| log "error" "swift build failed for ARCH=$ARCH" | ||
| exit 1 | ||
| fi | ||
| else | ||
| if ! swift build -c release --product cua-driver > /dev/null; then | ||
| log "error" "swift build failed for native ARCH=$NATIVE_ARCH" | ||
| exit 1 | ||
| fi | ||
| fi | ||
|
Comment on lines
+69
to
+79
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fail fast if ARCH-specific build output is missing. Right now, a failed Suggested hardening log "essential" "Building release version (arch: $ARCH)..."
if [ "$ARCH" != "$NATIVE_ARCH" ]; then
- swift build -c release --product cua-driver --arch "$ARCH" > /dev/null
+ if ! swift build -c release --product cua-driver --arch "$ARCH" > /dev/null; then
+ log "error" "swift build failed for ARCH=$ARCH"
+ exit 1
+ fi
else
- swift build -c release --product cua-driver > /dev/null
+ if ! swift build -c release --product cua-driver > /dev/null; then
+ log "error" "swift build failed for native ARCH=$NATIVE_ARCH"
+ exit 1
+ fi
fi
@@
if [ "$ARCH" != "$NATIVE_ARCH" ]; then
BINARY_PATH=".build/${ARCH}-apple-macosx/release/cua-driver"
else
BINARY_PATH=".build/release/cua-driver"
fi
+if [ ! -f "$BINARY_PATH" ]; then
+ log "error" "Expected binary not found at $BINARY_PATH"
+ exit 1
+fi
cp -f "$BINARY_PATH" "$APP_BUNDLE/Contents/MacOS/cua-driver"Also applies to: 83-91 🤖 Prompt for AI Agents
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. worth addressing @r33drichards
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| # --- Assemble .app bundle --- | ||
| log "essential" "Assembling .app bundle..." | ||
|
|
||
| APP_BUNDLE=".release/CuaDriver.app" | ||
| # Use an arch-suffixed bundle name so sequential arm64 + x86_64 invocations | ||
| # don't clobber each other's .app in .release/. The CI workflow renames/copies | ||
| # to CuaDriver.app where needed (e.g. the bare-binary packaging step). | ||
| APP_BUNDLE=".release/CuaDriver-${ARCH}.app" | ||
| rm -rf "$APP_BUNDLE" | ||
| mkdir -p "$APP_BUNDLE/Contents/MacOS" | ||
| mkdir -p "$APP_BUNDLE/Contents/Resources" | ||
|
|
||
| # Copy the binary into the bundle | ||
| cp -f .build/release/cua-driver "$APP_BUNDLE/Contents/MacOS/cua-driver" | ||
| # Copy the binary into the bundle. | ||
| # When cross-compiling (ARCH != native), Swift places the output under | ||
| # .build/<triple>/release/ instead of the usual .build/release/ symlink. | ||
| if [ "$ARCH" != "$NATIVE_ARCH" ]; then | ||
| BINARY_PATH=".build/${ARCH}-apple-macosx/release/cua-driver" | ||
| else | ||
| BINARY_PATH=".build/release/cua-driver" | ||
| fi | ||
| if [ ! -f "$BINARY_PATH" ]; then | ||
| log "error" "Expected binary not found at $BINARY_PATH" | ||
| exit 1 | ||
| fi | ||
| cp -f "$BINARY_PATH" "$APP_BUNDLE/Contents/MacOS/cua-driver" | ||
|
|
||
| # Stamp and copy Info.plist — the source plist ships with a static | ||
| # `CFBundleShortVersionString` for dev builds; substitute the release | ||
|
|
@@ -218,16 +246,17 @@ fi | |
|
|
||
| # --- Create release archives --- | ||
|
|
||
| # Get architecture and create OS identifier | ||
| ARCH=$(uname -m) | ||
| # ARCH is already set above (env override or native); derive OS identifier | ||
| OS_IDENTIFIER="darwin-${ARCH}" | ||
|
Comment on lines
+249
to
250
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scope archive cleanup to the current architecture. With dual invocations, the wildcard cleanup on Line 244 deletes artifacts from the first pass (native) before the x86_64 pass finishes, so native upload paths can disappear. Suggested fix-# Clean up any existing artifacts first to avoid conflicts
-rm -f cua-driver-*.tar.gz cua-driver-*.pkg.tar.gz
+# Clean up only this arch's artifacts to keep outputs from other arch passes
+rm -f "cua-driver-${VERSION}-${OS_IDENTIFIER}.tar.gz" \
+ "cua-driver-${VERSION}-${OS_IDENTIFIER}.pkg.tar.gz"🤖 Prompt for AI Agents
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| RELEASE_DIR="$(cd .release && pwd)" | ||
|
|
||
| log "essential" "Creating archives in $RELEASE_DIR..." | ||
| cd "$RELEASE_DIR" | ||
|
|
||
| # Clean up any existing artifacts first to avoid conflicts | ||
| rm -f cua-driver-*.tar.gz cua-driver-*.pkg.tar.gz | ||
| # Clean up only this arch's artifacts — a wildcard glob would wipe artifacts | ||
| # from a previous arch pass (e.g. the arm64 tarballs before x86_64 finishes). | ||
| rm -f "cua-driver-${VERSION}-${OS_IDENTIFIER}.tar.gz" \ | ||
| "cua-driver-${VERSION}-${OS_IDENTIFIER}.pkg.tar.gz" | ||
|
|
||
| # Create a backward-compatible wrapper script at the tarball root so | ||
| # extracting the tarball and running `./cua-driver <tool>` works | ||
|
|
@@ -238,11 +267,23 @@ exec "$(dirname "$0")/CuaDriver.app/Contents/MacOS/cua-driver" "$@" | |
| WRAPPER_EOF | ||
| chmod +x cua-driver | ||
|
|
||
| # Create version-specific archives | ||
| # Create version-specific archives. | ||
| # The arch-suffixed bundle (CuaDriver-${ARCH}.app) is packaged as CuaDriver.app | ||
| # inside the tarball so install.sh — which always extracts and looks for | ||
| # CuaDriver.app — works without any changes. | ||
| # We create a temporary symlink CuaDriver.app → CuaDriver-${ARCH}.app, tar it | ||
| # (tar follows symlinks to directories by default on macOS), then remove the link. | ||
| log "essential" "Creating version-specific archives (${VERSION})..." | ||
|
|
||
| # Package the .app bundle and wrapper script | ||
| tar -czf "cua-driver-${VERSION}-${OS_IDENTIFIER}.tar.gz" cua-driver CuaDriver.app > /dev/null 2>&1 | ||
| # Temporary CuaDriver.app symlink so the tarball layout is stable for install.sh | ||
| ln -sfn "CuaDriver-${ARCH}.app" CuaDriver.app | ||
|
|
||
| # Package the .app bundle (via the CuaDriver.app symlink) and wrapper script | ||
| tar -czf "cua-driver-${VERSION}-${OS_IDENTIFIER}.tar.gz" \ | ||
| cua-driver CuaDriver.app > /dev/null 2>&1 | ||
|
|
||
| # Remove the symlink; the arch-suffixed bundle stays for the bare-binary step | ||
| rm -f CuaDriver.app | ||
|
|
||
| # Package the installer | ||
| tar -czf "cua-driver-${VERSION}-${OS_IDENTIFIER}.pkg.tar.gz" cua-driver.pkg > /dev/null 2>&1 | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second pass overwrites
.release/CuaDriver.app, which can mislabel the bare-binary artifact.After Line 176,
.release/CuaDriver.appis x86_64, but the later bare-binary step names output using nativeuname -m(arm64 on this runner). That can publish a wrongly labeled binary tarball.One practical approach
LOG_LEVEL=minimal ./build-release-notarized.sh +cp -R .release/CuaDriver.app .release/CuaDriver-arm64.app # Cross-compile for Intel Mac (x86_64) on this arm64 runner ARCH=x86_64 LOG_LEVEL=minimal ./build-release-notarized.shThen point the bare-binary packaging step to
.release/CuaDriver-arm64.app/...for the native artifact (or produce explicitly arch-suffixed bare binaries from each app).🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
worth addressing @r33drichards
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.