Skip to content

fix: stop Homebrew rewriting bundled dylib IDs on macOS - #335

Merged
cloudsmith-iduffy merged 1 commit into
masterfrom
fix/eng-13692-preserve-rpath-in-homebrew-template
Jul 31, 2026
Merged

fix: stop Homebrew rewriting bundled dylib IDs on macOS#335
cloudsmith-iduffy merged 1 commit into
masterfrom
fix/eng-13692-preserve-rpath-in-homebrew-template

Conversation

@cloudsmith-iduffy

Copy link
Copy Markdown
Contributor

Adds preserve_rpath to the Homebrew formula template so macOS installs stop failing.

Companion to cloudsmith-io/homebrew-cloudsmith-cli#28, which fixed the tap. This change is what stops the next release reverting that fix, because publish-homebrew copies the rendered template over Formula/cloudsmith-cli.rb wholesale.

The bug

Since 1.20.1 the Homebrew formula ships the PyInstaller onedir bundle, so the keg contains 65 prebuilt Mach-O files where the zipapp had none. Homebrew rewrites the dylib ID of every one to an absolute Cellar path, and that name does not fit the header padding of pydantic_core's prebuilt wheel:

Error: Failed changing dylib ID of .../libexec/_internal/pydantic_core/_pydantic_core.cpython-312-darwin.so
  from @rpath/pydantic_core._pydantic_core.cpython-312-darwin.so
    to /opt/homebrew/opt/cloudsmith-cli/libexec/_internal/pydantic_core/pydantic_core._pydantic_core...
Error: Failed to fix install linkage

Measured against the published 1.20.2 archives — the new ID needs 56 more bytes than are available:

archive header slack needed result
arm64 _pydantic_core.so 48 bytes 56 fails
x86_64 _pydantic_core.so 32 bytes 56 fails
arm64 rpds.so 48 bytes 40 passes by 8 bytes

Both macOS architectures are affected, and the margin elsewhere is thin. The install does link and the CLI runs, but brew exits non-zero, which breaks brew upgrade and any automation around it. Reported by a customer upgrading 1.19.0 → 1.20.1.

Why preserve_rpath rather than a build change

_pydantic_core.so is a prebuilt maturin wheel from PyPI that PyInstaller copies verbatim, so linking it with -headerpad_max_install_names would mean building pydantic-core from source.

The rewrite is also pointless. Every non-system linkage in the bundle is @rpath-relative and private to it, and nothing outside links against them. Homebrew already skips @rpath install names and rpaths via grep_v(VARIABLE_REFERENCE_RX); the dylib ID is the one path lacking that exemption, and preserve_rpath supplies it. All six dylibs in the bundle use @rpath IDs, so it covers every one.

Verification

Rendered with 1.20.2's version and checksums, this template produces a formula byte-identical to the one merged in the tap, and:

  • brew style — no offenses
  • brew audit — clean
  • the tap's macOS CI installs, uninstalls, upgrades, downgrades and pins it on both arm64 and Intel

Before the fix the install reproduced the error above and exited 1; after it, exit 0 with all six dylib IDs left as @rpath.

🤖 Generated with Claude Code

Since 1.20.1 the Homebrew formula ships the PyInstaller onedir bundle, so the keg
contains 65 prebuilt Mach-O files where the zipapp had none. Homebrew rewrites the
dylib ID of every one of them to an absolute Cellar path, and that name does not
fit in the header padding of pydantic_core's prebuilt wheel:

  Failed changing dylib ID of .../_pydantic_core.cpython-312-darwin.so
    from @rpath/pydantic_core._pydantic_core.cpython-312-darwin.so
      to /opt/homebrew/opt/cloudsmith-cli/libexec/_internal/...
  Error: Failed to fix install linkage

The install still links and the CLI runs, but brew exits non-zero, which breaks
`brew upgrade` and the automation around it. Both macOS architectures are
affected; the Intel bundle has less padding to spare (32 bytes against the 56 the
new name needs, versus 48 on arm64).

Rewriting these IDs achieves nothing: every non-system linkage in the bundle is
@rpath-relative and private to it, and nothing outside links against them.
Homebrew already skips @rpath install names and rpaths; the dylib ID is the one
path lacking that exemption, and preserve_rpath supplies it.

The tap carries this fix already (cloudsmith-io/homebrew-cloudsmith-cli#28), but
publish-homebrew copies the rendered template over Formula/cloudsmith-cli.rb
wholesale, so without the same change here the next release would silently revert
it. Rendered with 1.20.2's values this template now produces a formula
byte-identical to the tap's, and brew style and brew audit both pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Ready to approve

The change is narrowly scoped to the Homebrew formula template and aligns with the stated install failure mode without introducing additional logic or surface area.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Updates the Homebrew formula template used by the release workflow so macOS Homebrew installs of the PyInstaller onedir bundle don’t fail due to Homebrew rewriting Mach-O linkage metadata.

Changes:

  • Adds the preserve_rpath directive to the Homebrew formula template to prevent rewriting @rpath-based linkage.
  • Documents (in-formula) the rationale tied to Mach-O header padding constraints in prebuilt wheels (e.g., pydantic_core).
File summaries
File Description
packaging/homebrew/cloudsmith-cli.rb.tmpl Adds preserve_rpath (with explanatory comment) to avoid Homebrew rewriting @rpath-based dylib IDs during macOS installs.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@cloudsmith-iduffy
cloudsmith-iduffy merged commit 2caa6e2 into master Jul 31, 2026
27 checks passed
@cloudsmith-iduffy
cloudsmith-iduffy deleted the fix/eng-13692-preserve-rpath-in-homebrew-template branch July 31, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants