heroku / buildpacks-ruby

Heroku's Cloud Native Buildpack for Ruby applications.
BSD 3-Clause "New" or "Revised" License
1 stars 1 forks source link

Mention we won't install a version of nodejs unless a `package.json` is found #295

Open schneems opened 4 months ago

schneems commented 4 months ago

In https://github.com/heroku/jruby-getting-started/issues/26 it was reported that the jruby getting started guide did not work "out of the box" with the ruby CNB because it was missing a package.json. However it works with the classic buildpack:

$ ls
Gemfile         Procfile.windows    app         db          public
Gemfile.lock        README.md       config          lib         test
Procfile        Rakefile        config.ru       log         vendor
⛄️ 3.3.1 🚀 /tmp/150ca2015abd0c42ade576ee15691dc6/jruby-getting-started (main)
$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
⛄️ 3.3.1 🚀 /tmp/150ca2015abd0c42ade576ee15691dc6/jruby-getting-started (main)
$ heroku create
Creating app... done, ⬢ serene-badlands-65663
https://serene-badlands-65663-2a3a8da4d325.herokuapp.com/ | https://git.heroku.com/serene-badlands-65663.git
⛄️ 3.3.1 🚀 /tmp/150ca2015abd0c42ade576ee15691dc6/jruby-getting-started (main)
$ git push heroku main
Enumerating objects: 316, done.
Counting objects: 100% (316/316), done.
Delta compression using up to 12 threads
Compressing objects: 100% (193/193), done.
Writing objects: 100% (316/316), 71.12 KiB | 35.56 MiB/s, done.
Total 316 (delta 99), reused 315 (delta 99), pack-reused 0
remote: Resolving deltas: 100% (99/99), done.
remote: Updated 89 paths from 5cbf940
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Building on the Heroku-22 stack
remote: -----> Determining which buildpack to use for this app
remote: -----> Ruby app detected
remote:
remote:        ## Warning: Your app needs java
remote:
remote:        The Ruby buildpack determined your app needs java installed
remote:        we recommend you add the jvm buildpack to your application:
remote:
remote:          $ heroku buildpacks:add heroku/jvm --index=1
remote:
remote: -----> Installing Java
remote:
remote: -----> Downloading Buildpack: heroku/jvm
remote: -----> Detected Framework: JVM Common
remote:
remote:  !     WARNING: No OpenJDK version specified
remote:        Your application does not explicitly specify an OpenJDK
remote:        version. OpenJDK 1.8 will be installed.
remote:
remote:        This default version will change at some point. Your
remote:        application might fail to build with the new version. We
remote:        recommend explicitly setting the required OpenJDK version for
remote:        your application.
remote:
remote:        To set the OpenJDK version, add or edit the system.properties
remote:        file in the root directory of your application to contain:
remote:
remote:        java.runtime.version = 1.8
remote:
remote:
remote: -----> Installing OpenJDK 1.8... done
remote: -----> Installing bundler 2.4.22
remote: -----> Removing BUNDLED WITH version in the Gemfile.lock
remote: -----> Compiling Ruby/Rails
remote: -----> Using Ruby version: ruby-3.1.0-jruby-9.4.1.0
remote: -----> Installing dependencies using bundler 2.4.22
remote:        Running: BUNDLE_WITHOUT='development:test' BUNDLE_PATH=vendor/bundle BUNDLE_BIN=vendor/bundle/bin BUNDLE_DEPLOYMENT=1 bundle install -j4
remote:        Fetching gem metadata from https://rubygems.org/.........
remote:        Fetching rake 13.0.6
remote:        Installing rake 13.0.6
remote:        Fetching minitest 5.18.0
remote:        Fetching builder 3.2.4
# ...
remote:        Installing sass-rails 6.0.0
remote:        Bundle complete! 13 Gemfile dependencies, 74 gems now installed.
remote:        Gems in the groups 'development' and 'test' were not installed.
remote:        Bundled gems are installed into `./vendor/bundle`
remote:        Bundle completed (87.92s)
remote:        Cleaning up the bundler cache.
remote:
remote: ###### WARNING:
remote:
remote:        Installing a default version (20.9.0) of Node.js.
remote:        This version is not pinned and can change over time, causing unexpected failures.
remote:
remote:        Heroku recommends placing the `heroku/nodejs` buildpack in front of
remote:        `heroku/ruby` to install a specific version of node:
remote:
remote:        https://devcenter.heroku.com/articles/ruby-support#node-js-support
remote:
remote: -----> Installing node-v20.9.0-linux-x64
remote: -----> Detecting rake tasks
remote: -----> Preparing app for Rails asset pipeline
remote:        Running: rake assets:precompile

The reason for this is that the two buildpacks have different behavior. And when there's a difference in behavior it should be called out in https://github.com/heroku/buildpacks-ruby/blob/main/docs/upgrading.md which it isn't.

Difference

The CNB ONLY looks for a package.json to install a node version and then relies on the node buildpack to install it.

When node support was added to the classic buildpack, a heroku/nodejs buildpack didn't exist. The asset pipeline (aka sprockets) needed A version of node so the ruby buildpack detected when execjs gem is present and installs node https://github.com/heroku/heroku-buildpack-ruby/blob/168cb5d556f154e4eba57c797d04b18a4d8b354c/lib/language_pack/ruby.rb#L944-L946. So the reason this works on classic is that it detects that jruby has execjs and it installs a version of node.

Besides documenting this difference we could try to bring the classic behavior closer to the CNB. by using the compile_buildpack_v2 bash function to install the nodejs buildpack when a package.json is present similar to how java is installed https://github.com/heroku/heroku-buildpack-ruby/blob/168cb5d556f154e4eba57c797d04b18a4d8b354c/bin/compile#L18-L33. This is a riskier change.

Improve the experience

In https://github.com/heroku/jruby-getting-started/issues/26 the error was confusing. The message said

[builder]     ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.

However it was unclear what the user should do to install a node runtime. The buildpack should "just work" ™️ .

We should replicate the logic in the detect phase and re-play what we're not doing. I.e. something like "Skipping nodejs install (no package.json found)`. So if the user searches the logs for "node" they'll find that breadcrumb.