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

Look for absolute path when removing bundler/setup from RUBYOPT in Bundler.unbundled_env method #3877

Merged

Conversation

rreinhardt9
Copy link
Contributor

@rreinhardt9 rreinhardt9 commented Aug 4, 2020

Why?

Resolves #3705

Bundler no longer supplies the require in RUBYOPT as a relative path but as an absolute path (see afc00b1 )

What?

This updates unbundled_env to match that behavior and looks for a require with an absolute path to bundler/setup when removing traces of bundler from the environment variable RUBYOPT. It will also continue to look for relative paths to remove as well in order to not create issues with existing scripts people might have that sets it as a relative path.

Testing Notes

In the examples below, to follow along you'll have to change the absolute paths in the commands based on where your ruby gems and bundler repo are located on your computer. I found $ gem list -d bundler helpful to see the path bundler is installed at.

I would expect this issue to appear only on 2.2.0.rc.1 and later because that is the release the commit was in changing RUBYOPT to have an absolute and not relative path and it was:

$ RUBYOPT=-r/Users/ross/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/bundler-2.2.0.rc.1/lib/bundler/setup bundler/bin/bundle exec ruby -rbundler -e'puts Bundler::VERSION'
=> 2.2.0.rc.1

$ RUBYOPT=-r/Users/ross/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/bundler-2.2.0.rc.1/lib/bundler/setup bundle exec ruby -rbundler -e'puts Bundler.unbundled_env["RUBYOPT"]'
=> -r/Users/ross/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/bundler-2.2.0.rc.1/lib/bundler/setup

I then confirmed that the -r statement in RUBYOPT was removed when running the bundler code from this feature branch:

$ RUBYOPT=-r/Users/ross/dev/rubygems/bundler/lib/bundler/setup bundle exec ruby -rbundler -e'puts Bundler::VERSION'
=> 2.2.0.rc.2

$ RUBYOPT=-r/Users/ross/dev/rubygems/bundler/lib/bundler/setup bundle exec ruby -rbundler -e'puts Bundler.unbundled_env["RUBYOPT"]'
=> # Nothing returned

I went back to version 2.1.2 to see what the behavior was before:

$ bundle exec ruby -rbundler -e'puts Bundler::VERSION'
=> 2.1.2

$ bundle exec ruby -rbundler -e'puts ENV["RUBYOPT"]'
=> -r/Users/ross/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/bundler-2.1.4/lib/bundler/setup

$ bundle exec ruby -rbundler -e'puts Bundler.unbundled_env["RUBYOPT"]'
=> # Nothing

$ RUBYOPT=-rbundler/setup bundle exec ruby -rbundler -e'puts Bundler.unbundled_env["RUBYOPT"]'
=> # Still nothing

# Doesn't remove an absolute path
$ RUBYOPT=-r/Users/ross/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/bundler-2.1.4/lib/bundler/setup bundle exec ruby -rbundler -e'puts Bundler.unbundled_env["RUBYOPT"]'
=> -r/Users/ross/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/bundler-2.1.4/lib/bundler/setup

This gives me confidence that:

  • The issue was only present in and after 2.2.0.rc.1
  • The change in this behavior addresses the issues seen in 2.2.0.rc.1

Caveats

One caveat to mention is that before using unbundled_env would always remove bundler/setup from RUBYOPT regardless of the version of bundler the entry in RUBYOPT was targeted at. After this change, unbundled_env only removes the absolute path for bundler/setup that points to the currently activated version of bundler. We discussed making a broader regex for trying to clean up any -r for bundler/setup but were concerned that might be too broad and might lead to trouble.

@rreinhardt9 rreinhardt9 changed the title Strip out bundle/setup from RUBYOPT for unbundled_env Remove specific manipulation of RUBYLIB, RUBYOPT, and MANPATH Aug 28, 2020
bundler/lib/bundler.rb Outdated Show resolved Hide resolved
bundler/spec/runtime/with_unbundled_env_spec.rb Outdated Show resolved Hide resolved
bundler/spec/runtime/with_unbundled_env_spec.rb Outdated Show resolved Hide resolved
bundler/spec/runtime/with_unbundled_env_spec.rb Outdated Show resolved Hide resolved
bundler/spec/runtime/with_unbundled_env_spec.rb Outdated Show resolved Hide resolved
bundler/lib/bundler.rb Outdated Show resolved Hide resolved
@deivid-rodriguez
Copy link
Member

Hi @rreinhardt9. After a second look, I don't think this is correct.

There's two helpers here, meant to be used for different things:

  • Bundler.with_unbundled_env is meant to cleanup every bundler related variable, not matter what the original environment is.
  • Bundler.with_original_env is meant to restore the environment as it was before bundler was loaded.

So, for example,

$ RUBYOPT=-rbundler/setup ruby -rbundler -e'puts Bundler.unbundled_env["RUBYOPT"]'

should print nothing, as opposed to -rbundler/setup. I believe your changes essentially remove the distinction between these two helpers.

@rreinhardt9 rreinhardt9 marked this pull request as ready for review August 28, 2020 12:24
@rreinhardt9
Copy link
Contributor Author

rreinhardt9 commented Aug 28, 2020

@deivid-rodriguez Ah, gotcha thanks!!! That helps give me clarity... so the intention of Bundler.with_unbundled_env is to go even further than Bundler.with_original_env and try to clear out any bundler related environment variables that were set, even if they were present in the original environment.

I'll look at it again from that perspective, I'm thinking in that case we for RUBYOPT we change:

      if env.key?("RUBYOPT")
        env["RUBYOPT"] = env["RUBYOPT"].sub "-rbundler/setup", ""
      end

so that the regex matches any path ending in bundler/setup (something like `env["RUBYOPT"].sub /-r.*bundler/setup/, "")

I'll:

  • Put back check for RUBYOPT and update the regex
  • Undo the changes to the RUBYLIB and MANPATH code
  • move the tests back to where they were/ update RUBYOPT test for the new regex check

@deivid-rodriguez
Copy link
Member

Yes, exactly.

Only thing I'm not sure about is whether to use a regexp to detect bundler/setup like you suggested, or add a separate substitution to look for an exact match of the expanded bundler/setup path that we previously added.

@rreinhardt9
Copy link
Contributor Author

rreinhardt9 commented Aug 28, 2020

I'm wondering about that too...

On one hand, if the method is intended to remove any traces of bundler that were in the original environment the regex would catch bundler/setup entries that might not be the same as that from the currently running version of bundler. But I'd be a little concerned the regex could carry some risk because its effects would be more broad.

Additionally, while dealing with RUBYLIB in the same method it looks for an exact expanded match to remove; so doing the same for RUBYOPT would be consistent with that behavior.

Based on that removing the exact expanded path for RUBYOPT seems like the way to go. 🤔

@deivid-rodriguez
Copy link
Member

Agreed!

@deivid-rodriguez
Copy link
Member

If you remember, when you update the PR, could you also update the title to reflect what it's finally doing?

@rreinhardt9 rreinhardt9 changed the title Remove specific manipulation of RUBYLIB, RUBYOPT, and MANPATH Look for absolute path when removing bundler/setup from RUBYOPT in Bundler.unbundled_env method Aug 29, 2020
@rreinhardt9
Copy link
Contributor Author

@deivid-rodriguez Thanks so much for your patience helping me through this! I've made updates we talked about and updated the PR description/title to reflect the new approach and it's ready for another look 🎉

@deivid-rodriguez
Copy link
Member

Looks good, but I feel we should still keep the old -rbundler/setup check as well. I believe people putting -rbundler/setup in RUBYOPT on their scripts will still expect Bundler.with_unbundled_env to remove that.

@rreinhardt9
Copy link
Contributor Author

That is a great point 😬 🤦 thanks for catching that!!! I just added a commit handling relative in addition to absolute paths. Once you get a chance to check it out let me know if you want that squashed before the merge and I can do that 🎉

Copy link
Member

@deivid-rodriguez deivid-rodriguez left a comment

Choose a reason for hiding this comment

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

Looks good!

If you wanted to curate the commit history, I'd split the three different improvements you ended up making into separate commits (and move your comments on the code to the commit message of each individual commit). Namely:

  • Improve space handling when removing entries from RUBYOPT, as you explained here.
  • Improve coverage of existing specs to better simulate the original presence of -rbundler/setup in RUBYOPT, as you explained here.
  • The bug fix itself with the new spec.

But I'm aware of my overpickyness about git history, so this is also fine as is 😅.

@rreinhardt9
Copy link
Contributor Author

I love it! I'm a fan of an informative git history as well and don't mind splitting those 🎉 I'll do that!

Using `sub` to replace the entry with an empty string "" will only work
cleanly if the entry we are removing is the ONLY entry in RUBYOPT. If
it's not the only entry, it will leave behind stray whitespace.

For example, if RUBYOPT contained-r/path/to/bundler/setup
-r/something/else removing -r/path/to/bundler/setup using `sub` would
leave a leading space in RUBYOPT.

If we tried to include a trailing space when using `sub`, it would solve
that issue for when there are more than one entry but it would not work
when the entry we are removing is the ONLY entry or when it's the last
entry.

The new method of splitting on " " helps us avoid all of that because it
will "just work" for any of the above cases and it won't leave the
leading space behind in RUBYOPT. It also is consistent with the method
used to clean RUBYLIB in `unbundled_env` directly beneath this code.

The default for String#split is " " so it not technically necessary to
specify it, but it's explicitly stated here to give clarity to future
readers of this line.
Because we are trying to simulate that the RUBYOPT contained this
bundler/setup value IN the original env, we actually have to set it in
BUNDLER_ORIG_RUBYOPT for the test. We are simulating that this
bundler/setup entry in RUBYOPT existed BEFORE bundler was activated
(even though it didn't).

If we set it in RUBYOPT it will already be removed even without the code
in unbundled_env (the spec still passed when the code in unbundled_env
for handling RUBYOPT was commented out).

That is because unbundled_env calls original_env as it's starting point,
which will remove whatever we set here in RUBYOPT because it was not in
the original env when the test suite first activated bundler.
Bundler no longer supplies the require in RUBYOPT as a relative path but
as an absolute path (see
rubygems@afc00b1
)

This updates `unbundled_env` to match that behavior and looks for a
require with an absolute path to `bundler/setup` when removing traces of
bundler from the environment variable RUBYOPT. It will also continue to
look for relative paths to remove as well in order to not create issues
with existing scripts people might have that set it as a relative path.
Copy link
Member

@deivid-rodriguez deivid-rodriguez left a comment

Choose a reason for hiding this comment

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

Looks great!

@deivid-rodriguez deivid-rodriguez merged commit 43ecbe8 into rubygems:master Sep 1, 2020
@deivid-rodriguez
Copy link
Member

Thanks!!!

@rreinhardt9 rreinhardt9 deleted the unbundled-env-rubyopts branch September 1, 2020 22:41
deivid-rodriguez added a commit that referenced this pull request Oct 5, 2020
Look for absolute path when removing bundler/setup from RUBYOPT in Bundler.unbundled_env method

(cherry picked from commit 43ecbe8)
deivid-rodriguez added a commit that referenced this pull request Oct 5, 2020
Look for absolute path when removing bundler/setup from RUBYOPT in Bundler.unbundled_env method

(cherry picked from commit 43ecbe8)
deivid-rodriguez added a commit that referenced this pull request Oct 5, 2020
Look for absolute path when removing bundler/setup from RUBYOPT in Bundler.unbundled_env method

(cherry picked from commit 43ecbe8)
deivid-rodriguez added a commit that referenced this pull request Oct 6, 2020
Look for absolute path when removing bundler/setup from RUBYOPT in Bundler.unbundled_env method

(cherry picked from commit 43ecbe8)
deivid-rodriguez added a commit that referenced this pull request Oct 6, 2020
Look for absolute path when removing bundler/setup from RUBYOPT in Bundler.unbundled_env method

(cherry picked from commit 43ecbe8)
deivid-rodriguez added a commit that referenced this pull request Oct 6, 2020
Look for absolute path when removing bundler/setup from RUBYOPT in Bundler.unbundled_env method

(cherry picked from commit 43ecbe8)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

'RUBYOPT' still contain "-r bundler/setup" when using unbundled_env
3 participants