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

bundle install does not run the gems hooks #3239

Open
ankitsny opened this issue Mar 6, 2020 · 16 comments
Open

bundle install does not run the gems hooks #3239

ankitsny opened this issue Mar 6, 2020 · 16 comments

Comments

@ankitsny
Copy link

ankitsny commented Mar 6, 2020

I am using rusky gem in my project for git hooks. and rusky has post-install script to create the git hooks.

but when I do bundle install then hooks are not getting created
but when I do gem install rusky it runs the postscript and creates the git-hooks.

so, for now, I am running rusky install explicitly, which is a little annoying.
do let me know how to invoke postscript of gems or any script after bundle install

@deivid-rodriguez
Copy link
Member

Hi @anks333! Please provide steps so we can reproduce this issue in our environment. Thanks!

@ankitsny
Copy link
Author

ankitsny commented Mar 6, 2020

# Gemfile

ruby '>= 2.5.0', engine: 'jruby', engine_version: '9.2.6.0'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.2.3'
# Use jdbcpostgresql as the database for Active Record
gem 'activerecord-jdbcpostgresql-adapter', '~> 52.2'
# Use Puma as the app server
gem 'puma', '~> 3.11'
gem 'figaro'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'

# Use ActiveStorage variant
# gem 'mini_magick', '~> 4.8'

# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development

# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
# gem 'rack-cors'
gem 'redis'
gem 'statsd-ruby'
gem 'slim'

group :development do
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'rusky'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

@ankitsny
Copy link
Author

ankitsny commented Mar 6, 2020

this is my gem file when I run bundle install
it installs all the gems, but it does not run the post-install scripts of the gems.

@ankitsny
Copy link
Author

ankitsny commented Mar 6, 2020

if I do gem install rusky, it installs the gem and also executes the post-install scripts of the rusky gem.

in the context of node.js, modules have postinstall scripts, so when I run npm install it installs the module and runs the postinstall scripts.

@deivid-rodriguez
Copy link
Member

I think this is actually a rubygems issue. Post install hooks only fire for gems that are already installed when ruby boots and requires rubygems:

$ # no rusky gem installed on my system

$ gem install rusky
Fetching rusky-0.2.5.gem
Successfully installed rusky-0.2.5
Parsing documentation for rusky-0.2.5
Installing ri documentation for rusky-0.2.5
Done installing documentation for rusky after 0 seconds
1 gem installed

$ # no post_install hooks were fired

$ gem install rusky
rusky > can't find .git directory, so skipping rusky process
Successfully installed rusky-0.2.5
Parsing documentation for rusky-0.2.5
Done installing documentation for rusky after 0 seconds
1 gem installed

$ # post_install hooks were now fired because rusky was already installed

@ankitsny
Copy link
Author

ankitsny commented Mar 6, 2020

if so, then if I run bundle install more than once then it should invoke the hooks

@deivid-rodriguez
Copy link
Member

Yep, if bundle install actually installs something new, yeah.

@hsbt hsbt transferred this issue from rubygems/bundler Mar 14, 2020
@rreinhardt9
Copy link
Contributor

From my understanding, it appears that the Rusky gem is using rubygems plugins in a way that is not intended 🤔 here is what makes me think that.

Plugins are "Extensions that use the RubyGems plugin API" and so are ways that you can extend and modify the behavior of Rubygems in general and not just for one specific gem. One way that you can share these plugins would be to gemify it and share it with others. Once the gem is installed, rubygems would find the plugin (and any hooks, commands, etc. it adds) during future installs.

Rusky attempts to use the plugin API to add it's own post install behavior https://github.com/ohbarye/rusky/blob/3edc4ddae4172a5bf5407226d2fe1f71434d64d7/lib/rubygems_plugin.rb#L6-L8 but from what I understand, this would not have the expected behavior. As we were seeing in the example above, installing the gem the first time will add this plugin behavior for subsequent installs, but on the initial install would not run (because during that install, the plugin was not installed or active yet).

If this understanding is correct, then the rubygems plugin API is working as expected and the observed issue is due to the way the Rusky gem is attempting to use it to run it's own post install behavior in a way the plugin API is not intended to be used.

Does this understanding seem correct to others that might have more context on the rubygems plugin API?

@simi
Copy link
Member

simi commented Sep 13, 2020

You're right @rreinhardt9.

@deivid-rodriguez
Copy link
Member

I understand that the rusky gem might be misusing this feature, but even in the case that the feature was being correctly used, we could still maybe do better?

Take yard for example, which provides a rubygems plugin to install the YARD documentation for a gem right after it is installed. Should yard be able to install YARD documentation for itself? It'd probably be nice? Making it work would be a matter of adding the plugins of the gem that was just installed to the list of loaded plugins, to give after_install hooks the chance to run for the newly installed gem.

So, instead of:

$ gem install yard
Fetching yard-0.9.25.gem
Successfully installed yard-0.9.25
1 gem installed

$ gem install yard:0.9.24
Fetching yard-0.9.24.gem
Successfully installed yard-0.9.24
Building YARD (yri) index for yard-0.9.24...
Installing YARD documentation for yard-0.9.24...
Done installing documentation for yard after 9 seconds
1 gem installed

we would get

$ gem install yard
Fetching yard-0.9.25.gem
Successfully installed yard-0.9.25
Building YARD (yri) index for yard-0.9.25...
Installing YARD documentation for yard-0.9.25...
Done installing documentation for yard after 9 seconds
1 gem installed

$ gem install yard:0.9.24
Fetching yard-0.9.24.gem
Successfully installed yard-0.9.24
Building YARD (yri) index for yard-0.9.24...
Installing YARD documentation for yard-0.9.24...
Done installing documentation for yard after 9 seconds
1 gem installed

By the way, we should review the https://guides.rubygems.org/plugins/ guide, since rubygems no longer uses Gem.find_files to load plugins since #3108.

@simi
Copy link
Member

simi commented Sep 14, 2020

I think you can create "extension" to run custom script during gem install. Rakefile extension would be enough here.

@deivid-rodriguez
Copy link
Member

deivid-rodriguez commented Sep 14, 2020

Yes, you can.

In my opinion though, that's unrelated to whether we should consider this as an issue or not. I was personally surprised by this behaviour when I first found it, and only made sense after seeing the implementation. I believe we should either make this case work as I suggested, or improve our documentation about it.

@rreinhardt9
Copy link
Contributor

This is super fascinating to me really; I'm just learning about this cool plugin system from this issue 🎉

I was personally surprised by this behaviour when I first found it, and only made sense after seeing the implementation.
Same here! After I dug in it made sense, but at first I was confused as to why it didn't work.

I wonder if it could start to be a bit of a rabbit hole to try and make sure that when a plugin is installed from a gem it immediately takes effect. For example:

post_install

In this case, let's say running bundler is going to install 4 gems and our plugin is in the 3rd. Would gems 1 and 2 not get the post_install behavior but gems 3 and 4 would because the plugin would be required after it's installed as the 3rd gem?

pre_install

In this case, let's again say running bundler is going to install 4 gems and our plugin is the 3rd. Would gems 1, 2, and 3 not get the pre_install behavior, but gem 4 would because it would now be present after gem 3 is installed and required?

For the Rusky gem, I believe that since it's installed itself as a "plugin" that post_install hook is going to run for every gem that is installed, not just the Rusky gem, on subsequent bundle runs 🤔 Which doesn't appear to have been the intended behaviour (they just wanted some post install setup to happen when that single gem was installed it appeared).

I'm kind of thinking that like you said, better clarity through documentation might be a good place to start here. I'd love to help out with that if I can! Would a good start be to open some issues for clarifying plugin documentation on the https://github.com/rubygems/guides repo and linking back to this issue?

@deivid-rodriguez
Copy link
Member

Thanks for the input @rreinhardt9, I'll be offline for a few days but I'm marking this thread as unread and will get back to your post when I come back online next Wednesday.

@deivid-rodriguez
Copy link
Member

In this case, let's say running bundler is going to install 4 gems and our plugin is in the 3rd. Would gems 1 and 2 not get the post_install behavior but gems 3 and 4 would because the plugin would be required after it's installed as the 3rd gem?

There's some interaction between bundler and rubygems here that I want to review at some point, but from the current state of the code I think the idea is that post_install hooks are run manually by bundler after all gems are installed. So I think all gems should get all post_install hooks. There's an open PR that affects the way bundler runs rubygems plugins, although in that case it handles plugin files directly put in the $LOAD_PATH, not as part of gems.

In this case, let's again say running bundler is going to install 4 gems and our plugin is the 3rd. Would gems 1, 2, and 3 not get the pre_install behavior, but gem 4 would because it would now be present after gem 3 is installed and required?

I think in this case I'd expect no new pre-install hooks to be run at all. Essentially consider the whole bundle as a unit and only run pre_install and post_install hooks before or after bundle install, without considering each gem separately.

For the Rusky gem, I believe that since it's installed itself as a "plugin" that post_install hook is going to run for every gem that is installed, not just the Rusky gem, on subsequent bundle runs thinking Which doesn't appear to have been the intended behaviour (they just wanted some post install setup to happen when that single gem was installed it appeared).

Yes, it seems like misusing rubygems hooks, and it sounds like the Rakefile alternative @simi suggested would be the better thing to do.

I'm kind of thinking that like you said, better clarity through documentation might be a good place to start here. I'd love to help out with that if I can! Would a good start be to open some issues for clarifying plugin documentation on the https://github.com/rubygems/guides repo and linking back to this issue?

Sure. Whatever the expected behaviour is, documenting the current way things work would be a nice improvement anyways.

@richiethomas
Copy link

Is this discussion question possibly related? Sounds like it might be.

TL;DR- RBENV is no longer able to rehash its shims after bundle install, presumably due to this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants