Skip to content
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

/opt/homebrew/.ruby-version overrides rbenv global and causes errors for scripts using #!/usr/bin/env ruby #17390

Closed
4 tasks done
ddribin opened this issue May 30, 2024 · 15 comments · Fixed by #17441
Closed
4 tasks done
Assignees
Labels
bug Reproducible Homebrew/brew bug outdated PR was locked due to age

Comments

@ddribin
Copy link

ddribin commented May 30, 2024

brew gist-logs <formula> link OR brew config AND brew doctor output

> brew config
HOMEBREW_VERSION: 4.3.2-24-g443e4e5
ORIGIN: https://github.com/Homebrew/brew
HEAD: 443e4e5dacd047ca86155e252ff7b9c43b01459b
Last commit: 12 hours ago
Core tap JSON: 30 May 04:02 UTC
Core cask tap JSON: 30 May 04:02 UTC
HOMEBREW_PREFIX: /opt/homebrew
HOMEBREW_AUTO_UPDATE_SECS: 86400
HOMEBREW_CASK_OPTS: []
HOMEBREW_EDITOR: mg
HOMEBREW_MAKE_JOBS: 16
HOMEBREW_NO_ANALYTICS: set
HOMEBREW_SORBET_RUNTIME: set
Homebrew Ruby: 3.3.1 => /opt/homebrew/Library/Homebrew/vendor/portable-ruby/3.3.1/bin/ruby
CPU: 16-core 64-bit arm_palma
Clang: 15.0.0 build 1500
Git: 2.45.1 => /opt/homebrew/bin/git
Curl: 8.4.0 => /usr/bin/curl
macOS: 14.4.1-arm64
CLT: 15.3.0.0.1.1708646388
Xcode: 15.4
Rosetta 2: false
> brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!

Warning: Some installed formulae are deprecated or disabled.
You should find replacements for the following formulae:
  python-lxml

I tried removing python-lxml, but it is used by another formula:

> brew uninstall python-lxml
Error: Refusing to uninstall /opt/homebrew/Cellar/python-lxml/5.2.2
because it is required by img2pdf, which is currently installed.
You can override this and force removal with:
  brew uninstall --ignore-dependencies python-lxml

Verification

  • My brew doctor output says Your system is ready to brew. and am still able to reproduce my issue.
  • I ran brew update and am still able to reproduce my issue.
  • I have resolved all warnings from brew doctor and that did not fix my problem.
  • I searched for recent similar issues at https://github.com/Homebrew/homebrew-core/issues?q=is%3Aissue and found no duplicates.

What were you trying to do (and why)?

I have rbenv installed via Homebrew and I have some personal scripts from a personal tap installed that use #!/usr/bin/env ruby, for portability. For example:

> where archive
/opt/homebrew/bin/archive
> head -5 /opt/homebrew/bin/archive
#!/usr/bin/env ruby

require 'optparse'
require 'ostruct'
require 'pathname'

What happened (include all command output)?

Running these scripts results in an error due to /opt/homebrew/.ruby-version. This is a recent issue, and has worked for years up until I updated `brew today.

> archive -h
rbenv: version `3.3.1' is not installed (set by /opt/homebrew/.ruby-version)

Even ruby fails:

> ruby --version
rbenv: version `3.3.1' is not installed (set by /opt/homebrew/.ruby-version)

Somehow /opt/homebrew/.ruby-version is overriding rbenv global:

> where ruby
/Users/dave/.rbenv/shims/ruby
/usr/bin/ruby
> rbenv versions
rbenv: version `3.3.1' is not installed (set by /opt/homebrew/.ruby-version)
  system
  3.2.3
  3.2.4
> rbenv global
system
> rbenv version
rbenv: version `3.3.1' is not installed (set by /opt/homebrew/.ruby-version)

What did you expect to happen?

I have rbenv setup to use the system version, so these should end up using /usr/bin/ruby, but /opt/homebrew/.ruby-version overrides this:

> rbenv versions
rbenv: version `3.3.1' is not installed (set by /opt/homebrew/.ruby-version)
  system
  3.2.3
  3.2.4
> rbenv global
system
> rbenv version
rbenv: version `3.3.1' is not installed (set by /opt/homebrew/.ruby-version)

This worked until .ruby-version was added with 3392226

Step-by-step reproduction instructions (by running brew commands)

`brew install rbenv`

Setup your shell for `rbenv`, then run:

`ruby --version`
@ddribin ddribin added the bug Reproducible Homebrew/brew bug label May 30, 2024
@ddribin
Copy link
Author

ddribin commented May 30, 2024

Sorry, this issue probably belongs in https://github.com/Homebrew/brew not homebrew-core

@carlocab carlocab transferred this issue from Homebrew/homebrew-core May 30, 2024
@carlocab
Copy link
Member

Yea, we should probably move that .ruby-version file somewhere better.

@MikeMcQuaid
Copy link
Member

Yea, we should probably move that .ruby-version file somewhere better.

It needs to live in the repository root if we want it to work as a .ruby-version file.

We could move it to Library but that means it won't typically be picked up appropriately.

Ideally we can fix this without moving but it can be moved if that's the only way to resolve this.

@ddribin
Copy link
Author

ddribin commented Jun 1, 2024

The only workaround here is to uninstall rbenv or disable its shell hooks. There's no way around this, as this is how rbenv is intended to work. Having a /opt/homebrew/.ruby-version file means this is used for everything in /opt/homebrew/bin.

I'm not sure what you are trying to do, but can you somehow use the RBENV_VERSION or RBENV_DIR environment variable instead of .ruby-version?

https://github.com/rbenv/rbenv?tab=readme-ov-file#environment-variables

@MikeMcQuaid
Copy link
Member

What about having a .ruby-version with system in subfolders like bin?

@ddribin
Copy link
Author

ddribin commented Jun 2, 2024

That would work fine, if rbenv global was set to system. But if someone overrode that, I think they would expect bin scripts to use that Ruby version, not system.

I wonder if there's a possible solution here using something like direnv, so that the Ruby version is set only if you cd into /opt/homebrew/. That could still break scripts in bin if you tried to run them when in that directory, but maybe that's fine? If you're hacking on Homebrew you likely have the correct Ruby version installed into rbenv.

@MikeMcQuaid
Copy link
Member

I've been thinking about why no-one else has complained about this and I think this is the main culprit after rereading:

I have rbenv installed via Homebrew and I have some personal scripts from a personal tap installed that use #!/usr/bin/env ruby, for portability.

This is why Homebrew's packages either avoid (or attempt to avoid) the #!/usr/bin/env ruby pattern: it's way to permissive in cases like this.

We'll consider making changes if more people chime in here but I think for now the best workarounds for you would be one of:

  • add .ruby-version into bin manually yourself
  • change the shebangs on these scripts

I'm not sure what you are trying to do

Basically: the same as any other Ruby project: have Ruby tooling use the correct/consistent Ruby version in the Homebrew repository.

@MikeMcQuaid MikeMcQuaid closed this as not planned Won't fix, can't repro, duplicate, stale Jun 4, 2024
@ddribin
Copy link
Author

ddribin commented Jun 5, 2024

This is why Homebrew's packages either avoid (or attempt to avoid) the #!/usr/bin/env ruby pattern: it's way to permissive in cases like this.

The #!/usr/bin/env ruby pattern is the standard way of writing portable scripts. The ruby might be in /usr/bin/ on one machine, /usr/local/bin/ on another, etc. This allows the user of the script to setup Ruby as they (or their OS) sees fit.

If this is discouraged for Homebrew, what is the portable alternative? I use these scripts in non-Homebrew environments, so they need to be agnostic from the real location of the ruby binary. How should these shebang lines be written to work both inside and outside of Homebrew?

@MikeMcQuaid
Copy link
Member

How should these shebang lines be written to work both inside and outside of Homebrew?

That's up to you. I see many potential solutions including not using rbenv in this case, installing the relevant Ruby version Homebrew wants, adjusting your PATH, installing these scripts differently inside/outside Homebrew or not installing them to inside the Homebrew prefix.

@reitermarkus
Copy link
Member

reitermarkus commented Jun 5, 2024

I think this is the usual pattern for things like this:

depends_on "ruby"

def install
  libexec.install "myscript.rb"
  (bin/"myscript").write_env_script libexec/"myscript.rb", PATH: "#{Formula["ruby"].opt_bin}:$PATH"
end

This way the script is guaranteed to run in a predictable way with the Homebrew ruby formula.

@carlocab
Copy link
Member

carlocab commented Jun 6, 2024

Related: Homebrew/homebrew-core#173816

@ddribin
Copy link
Author

ddribin commented Jun 6, 2024

@MikeMcQuaid said:

I see many potential solutions including not using rbenv in this case

For clarification, I'm not using rbenv for these scripts. But I do have projects that use rbenv, so I install it for those. I keep rbenv global tied to system, and only use .ruby-version in project directories. The only way to avoid this issue is to not use rbenv at all, which is not really viable.

As a workaround, I think I can use direnv + use rbenv to conditionally hook rbenv into my shell, but I haven't gotten around to trying that, yet, and would require me to use it for every project that used rbenv.

installing the relevant Ruby version Homebrew wants

Yes, but now I am at the whim of whatever version Homebrew is using. Since I opened this issue, .ruby-version in Homebrew has already changed from 3.3.1 to 3.3.2. Now every time I run brew update, these scripts could potentially break if .ruby-version happens to bump, until I install whatever version Homebrew wants.

@reitermarkus: That pattern would unfortunately not work well for my tap. FWIW, my tap is just a single directory with a bunch of relatively small single-file scripts in it. Some are Ruby, some are Python, and some are Perl. They all use #!/usr/bin/env whatever and they all are only dependent on their respective standard libraries. The install step is just a simple Makefile which copies all the scripts into #{prefix}/bin/:

  def install
    system "make", "install", "PREFIX=#{prefix}"
  end

Using libexec.install + write_env_script would mean making this installation a lot more complicated. It would need to enumerate only the Ruby scripts somehow so they can be installed into libexec and then stubs added to bin.

I'm sorry, I'm not trying to be difficult, but just pointing out how inconvenient this change is.

@MikeMcQuaid MikeMcQuaid reopened this Jun 6, 2024
MikeMcQuaid added a commit that referenced this issue Jun 6, 2024
This will avoid issues with `#!/usr/bin/env ruby` shebangs in Homebrew's
prefix.

Fixes #17390
@MikeMcQuaid MikeMcQuaid self-assigned this Jun 6, 2024
@MikeMcQuaid
Copy link
Member

@ddribin you (plus @mfdj's issue) have convinced me and I've changed my mind. #17441 should address this.

@ddribin
Copy link
Author

ddribin commented Jun 7, 2024

Thank you!

@MikeMcQuaid In the spirit of saying nice things when filing bugs, Homebrew is really fantastic and the first thing I install on a new Mac. It one of my few "must have" tools. Thank you (and everyone else) for all the work you've put into it!

@MikeMcQuaid
Copy link
Member

Thanks for the kind words @ddribin and for politely explaining the situation and bearing with me while I change my mind 😁

@github-actions github-actions bot added the outdated PR was locked due to age label Jul 8, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 8, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Reproducible Homebrew/brew bug outdated PR was locked due to age
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants