Skip to content

fix(cua-driver-rs)(install): _install-local-rust.sh symlink swap + macOS 26 codesign - #1716

Merged
f-trycua merged 1 commit into
mainfrom
fix/install-local-rust-symlink-resign
May 26, 2026
Merged

fix(cua-driver-rs)(install): _install-local-rust.sh symlink swap + macOS 26 codesign#1716
f-trycua merged 1 commit into
mainfrom
fix/install-local-rust-symlink-resign

Conversation

@f-trycua

@f-trycua f-trycua commented May 26, 2026

Copy link
Copy Markdown
Collaborator

Two bugs in _install-local-rust.sh

Both surfaced today on macOS 26.4.1 mid-session.

1. Atomic symlink swap was broken on macOS

# Before
mv -Tf "$TMP_LINK" "$CURRENT_LINK" 2>/dev/null \
    || mv -f "$TMP_LINK" "$CURRENT_LINK"

mv -T is GNU-only. On macOS BSD coreutils the -Tf form silently errors (stderr discarded), the fallback mv -f fires, and when the destination is a symlink-to-directory it follows the symlink, dumping the temp inside as current/current.new. Leaves stale current.new orphans at both levels; the actual current symlink never gets repointed.

User-facing repro:

$ ./install-local.sh
...
Staging into ...
mv: .../current.new and .../current/current.new are identical
[exit 1]

Fix: replace with ln -sfn. POSIX, atomic on POSIX-compliant FS, works the same on BSD and GNU. No temp file means no orphan to sweep on partial failure.

2. macOS 26 Taskgated SIGKILLs the freshly-installed binary

After cp planted the new binary, the kernel's cached signature for the new inode didn't match the linker-emitted ad-hoc signature strictly enough under macOS 26's CODESIGNING namespace. Result: SIGKILL (Code Signature Invalid) — Taskgated Invalid Signature on first run, no stderr, exit 137. Buried in ~/Library/Logs/DiagnosticReports/cua-driver-*.ips.

Fix: re-sign in place with codesign --force --sign - immediately after the cp. Macos-only; guarded with command -v codesign. ~50ms cost on a 40MB binary. Prints a warning rather than failing if codesign isn't available, so older macOS / Linux installs aren't blocked.

Repro (this Mac, macOS 26.4.1)

Before this PR:
  $ ./install-local.sh
  ...
  Staging into ...
  mv: ...current.new and .../current/current.new are identical   ← bug 1
  $ ~/.local/bin/cua-driver --version
  [exit 137, no output]                                          ← bug 2

After this PR:
  $ ./install-local.sh
  ...
  Staging into ...
  current -> .../releases/0.0.0-local-debug-arm64-apple-darwin
  ~/.local/bin/cua-driver -> .../packages/current/cua-driver
  Installed.
  $ ls ~/.cua-driver/packages/
  current  releases       ← no stale current.new
  $ ~/.local/bin/cua-driver --version
  cua-driver 0.2.18       ← runs cleanly

Test plan

  • bash -n clean
  • Live test on macOS 26.4.1: install end-to-end clean, no orphans, binary runs
  • Verify on Linux — the codesign step is guarded by command -v codesign, so it's a no-op; the ln -sfn fix applies on both

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Enhanced the local Rust installation script to improve binary handling on macOS with more reliable symlink management during setup.

Review Change Stack

…cOS 26 codesign

Two install-script bugs surfaced by today's macOS 26.4 + slow-path
re-install.

1) Atomic symlink swap was broken on macOS
================================================

Previous code:

    TMP_LINK="$CURRENT_LINK.new"
    rm -f "$TMP_LINK"
    ln -s "$VERSIONED_DIR" "$TMP_LINK"
    mv -Tf "$TMP_LINK" "$CURRENT_LINK" 2>/dev/null \
        || mv -f "$TMP_LINK" "$CURRENT_LINK"

`mv -T` is GNU-only. On macOS BSD coreutils the `-Tf` form silently
errors (redirected to /dev/null), then the fallback `mv -f` fires. When
the destination is a symlink-to-directory (which `$CURRENT_LINK` is on
re-install), BSD `mv -f` *follows the symlink* and drops the temp
symlink INSIDE the directory as `current/current.new`, leaving stale
`current.new` orphans at both the packages/ level AND inside the
versioned release dir, and the actual `current` symlink untouched.

Replacement: `ln -sfn` — POSIX, atomic on POSIX-compliant filesystems,
works identically on macOS BSD and Linux GNU. No temp file, no orphan
to sweep on partial failure. Also adds an `rm -f "$CURRENT_LINK.new"`
to clean up any orphan that a previous (pre-fix) run might've left.

User-facing repro:

    $ ./install-local.sh
    ...
    Staging into ...
    mv: ...current.new and .../current/current.new are identical
    [exit 1, broken state on disk]

2) macOS 26 Taskgated rejects linker-emitted adhoc signatures after cp
========================================================================

macOS 26.4 (the user's host) enforces CODESIGNING-namespace verification
stricter than 14.x and earlier. After `cp` planted the new binary at
the versioned release dir, the kernel's cached signature for the new
inode didn't match the embedded linker-emitted ad-hoc signature
strictly enough, and Taskgated SIGKILLed the binary on first launch
with no stderr output. Exit code 137. Only diagnostic was buried in
~/Library/Logs/DiagnosticReports/cua-driver-*.ips:

    "type": "EXC_CRASH",
    "signal": "SIGKILL (Code Signature Invalid)"
    "namespace": "CODESIGNING",
    "indicator": "Taskgated Invalid Signature"

Fix: re-sign in place with `codesign --force --sign -` immediately
after the `cp`. The fresh embedded ad-hoc signature is keyed to the
on-disk bytes Taskgated will subsequently verify. Cheap (~50ms on a
40MB binary). macOS-only — guarded with `command -v codesign` so
Linux + minimal macOS shells don't blow up if codesign is missing.

If codesign fails for any reason (rare — would need a corrupted dev
toolchain), prints a yellow warning rather than failing the install,
so users on older macOS where the issue doesn't manifest aren't
blocked. The warning text names the symptom (SIGKILL on first run)
so anyone hitting it can correlate.

Test plan
=========
- [x] `bash -n` clean
- [x] Live test on macOS 26.4.1: install-local.sh end-to-end completes,
      no orphan files, `~/.local/bin/cua-driver --version` exits 0
- [ ] Linux verification (the codesign step is guarded; symlink fix
      applies on both)
@vercel

vercel Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored May 26, 2026 1:14pm

Request Review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1403a9c2-2f12-4e18-9072-7554e6da8521

📥 Commits

Reviewing files that changed from the base of the PR and between 02f1f03 and 7ead883.

📒 Files selected for processing (1)
  • libs/cua-driver/scripts/_install-local-rust.sh

📝 Walkthrough

Walkthrough

The local Rust installation script now re-signs the staged cua-driver binary on macOS after copying it into a versioned release directory, and replaces its symlink-update mechanism with an atomic ln -sfn approach to safely repoint the current link, removing the prior temp-swap pattern.

Changes

Local Rust Installation Flow

Layer / File(s) Summary
macOS Code-Signing After Staging
libs/cua-driver/scripts/_install-local-rust.sh
After copying the built binary into the versioned release directory, the script conditionally re-signs it on macOS 26+ using codesign --force --sign - (best-effort, warning if codesign fails); this step is skipped on other platforms.
Atomic Symlink Repointing for Current Link
libs/cua-driver/scripts/_install-local-rust.sh
The current symlink update now ensures the packages directory exists, cleans up any leftover current.new temp file from a prior run, and repoints current to the new versioned directory using ln -sfn, replacing the previous TMP_LINK + mv -Tf/mv -f mechanism.

Possibly Related PRs

  • trycua/cua#1540: Both PRs modify the Rust local-install flow's on-disk "current" symlink behavior (atomic repoint/switch after installing a versioned binary).
  • trycua/cua#1655: Both PRs update local install script "current" link repointing logic after staging a release (this PR in _install-local-rust.sh, the retrieved PR in install-local.ps1), though they differ in platform and approach.

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A binary signs itself with native pride,
while symlinks dance in atomic stride—
no temp-swap chaos, just ln -sfn grace,
macOS codesign keeps time and place.
Rust installation flows, clean and bright! ✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/install-local-rust-symlink-resign

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@f-trycua
f-trycua marked this pull request as ready for review May 26, 2026 15:20
@f-trycua
f-trycua merged commit 727c327 into main May 26, 2026
6 of 7 checks passed
@f-trycua
f-trycua deleted the fix/install-local-rust-symlink-resign branch May 26, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant