From 14cfdccdaec9a14371cabd39956df7eb646fe1f9 Mon Sep 17 00:00:00 2001 From: Richard Schneeman Date: Tue, 16 Apr 2024 10:25:06 -0500 Subject: [PATCH 1/3] Remove RBX and "build" support - Stacked 2/3 (#1440) * Fix tests after bundler change The Ruby buildpack uses the Ruby buildpack on CI. When the bundler versions changed in https://github.com/heroku/heroku-buildpack-ruby/commit/56ff138eea331620d3579370693dd39140a642cd it also changed the behavior of these tests (which use the local bundler version). That means that after the buildpack was deployed, it retroactively meant that tests on main were failing. The issue with these tests is that they're using a feature of bundler that's been removed, specifically this change https://devcenter.heroku.com/changelog-items/2809. In these tests the Ruby version is specified in the Gemfile but not the Gemfile.lock: - Gemfile https://github.com/sharpstone/mri_193/blob/master/Gemfile - Gemfile.lock https://github.com/sharpstone/mri_193/blob/master/Gemfile.lock With the newer version of bundler, these tests now fail. To fix this tests will need to be updated or removed (based on their current relevance). * Clean up unused hatchet apps * Remove RBX and "build" support Rubinius is a Ruby implementation that was available to use on the platform a long time ago but has not been supported for some time: Ruby 1.9.2 requires a "build" branch of logic that can be removed as well. It is not on any existing stacks and was not present on Heroku-18: ``` $ curl -I https://heroku-buildpack-ruby.s3.us-east-1.amazonaws.com/heroku-18/ruby-2.5.7.tgz HTTP/1.1 200 OK x-amz-id-2: Hl89PTav6LfvU90vP97YsBc5DEBptax9s4PZU+3ubIrIUBY54lD0f2FXMOCgh9XQEQRU+5ia1x4= x-amz-request-id: KVDZM54WRBN5ASGT Date: Mon, 15 Apr 2024 15:55:27 GMT Last-Modified: Tue, 01 Oct 2019 14:46:30 GMT ETag: "93ff97625abfc6ccd1a071bb0fda8e66" x-amz-version-id: null Accept-Ranges: bytes Content-Type: Server: AmazonS3 Content-Length: 11995820 $ curl -I https://heroku-buildpack-ruby.s3.us-east-1.amazonaws.com/heroku-18/ruby-1.9.2.tgz HTTP/1.1 404 Not Found x-amz-request-id: 1NKVW4VD3T94T71R x-amz-id-2: tTow7CFW9BcWmAbkEWvrqPz5zJdsy7ry8Jm39v+EURa4RN/UpslBhhGf1iHzOrpJ4n1BJxiGGoI= Content-Type: application/xml Date: Mon, 15 Apr 2024 15:55:31 GMT Server: AmazonS3 ``` Other unused functionality that's being removed: - CDN support for downloading Ruby binaries. This provided no meaningful download improvement when deploying as the binaries are on S3 in the same data center. This was disabled a long time ago. - A "default cache" that was warmed with common gems for the most common Ruby version. This was effective at reducing "first build" time but the complexity exploded if we were to try and support N versions of Rails and M versions of Ruby for this feature. Additionally the community has moved to pre-built binaries for some expensive components such as nokogiri. * Update lib/language_pack/installers/heroku_ruby_installer.rb Co-authored-by: Josh W Lewis --------- Co-authored-by: Josh W Lewis --- CHANGELOG.md | 1 + Gemfile.lock | 2 +- Rakefile | 15 --- config/cdn.yml | 1 - hatchet.json | 8 +- hatchet.lock | 12 -- lib/language_pack.rb | 2 - lib/language_pack/fetcher.rb | 16 +-- lib/language_pack/helpers/bundler_wrapper.rb | 1 - .../helpers/download_presence.rb | 10 +- .../helpers/outdated_ruby_version.rb | 3 +- .../installers/heroku_ruby_installer.rb | 52 ++++++-- lib/language_pack/installers/rbx_installer.rb | 20 ---- .../installers/ruby_installer.rb | 53 -------- lib/language_pack/ruby.rb | 53 ++------ lib/language_pack/ruby_version.rb | 20 +--- lib/language_pack/test/ruby.rb | 2 +- spec/helpers/download_presence_spec.rb | 31 +++-- spec/helpers/outdated_ruby_version_spec.rb | 16 +-- spec/helpers/ruby_version_spec.rb | 113 ++++++++++-------- spec/installers/heroku_ruby_installer_spec.rb | 22 +--- spec/installers/rbx_installer_spec.rb | 20 ---- 22 files changed, 150 insertions(+), 323 deletions(-) delete mode 100644 config/cdn.yml delete mode 100644 lib/language_pack/installers/rbx_installer.rb delete mode 100644 lib/language_pack/installers/ruby_installer.rb delete mode 100644 spec/installers/rbx_installer_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e21eaa58..4ef414361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] +- Remove unused Rubinius and Ruby 1.9.2 codepaths (https://github.com/heroku/heroku-buildpack-ruby/pull/1440) ## [v267] - 2024-02-28 diff --git a/Gemfile.lock b/Gemfile.lock index b9fd360c4..0f753a918 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,4 +72,4 @@ RUBY VERSION ruby 3.1.4p223 BUNDLED WITH - 2.3.10 + 2.5.8 diff --git a/Rakefile b/Rakefile index 6220d0b7e..4372a9bb7 100644 --- a/Rakefile +++ b/Rakefile @@ -106,21 +106,6 @@ task "gem:install", :gem, :version do |t, args| install_gem(gem, version) end -desc "generate ruby versions manifest" -task "ruby:manifest" do - require 'rexml/document' - require 'yaml' - - document = REXML::Document.new(`curl https://#{S3_BUCKET_NAME}.s3.#{S3_BUCKET_REGION}.amazonaws.com`) - rubies = document.elements.to_a("//Contents/Key").map {|node| node.text }.select {|text| text.match(/^(ruby|rbx|jruby)-\\\\d+\\\\.\\\\d+\\\\.\\\\d+(-p\\\\d+)?/) } - - Dir.mktmpdir("ruby_versions-") do |tmpdir| - name = 'ruby_versions.yml' - File.open(name, 'w') {|file| file.puts(rubies.to_yaml) } - sh("#{s3_tools_dir}/s3 put #{S3_BUCKET_NAME} #{name} #{name}") - end -end - begin require 'rspec/core/rake_task' diff --git a/config/cdn.yml b/config/cdn.yml deleted file mode 100644 index ed97d539c..000000000 --- a/config/cdn.yml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/hatchet.json b/hatchet.json index 921ae7886..6968997e6 100644 --- a/hatchet.json +++ b/hatchet.json @@ -8,7 +8,6 @@ "sharpstone/default_with_rakefile" ], "bundler": [ - "sharpstone/bad_gemfile_on_platform", "sharpstone/problem_gemfile_version", "sharpstone/git_gemspec", "sharpstone/no_lockfile", @@ -17,10 +16,7 @@ ], "ruby": [ "sharpstone/ruby_version_does_not_exist", - "sharpstone/ruby_193_jruby_173", - "sharpstone/ruby_193_jruby_176", "sharpstone/ruby_193_jruby_17161_jdk7", - "sharpstone/ruby_193_bad_patch_cedar_14", "sharpstone/ruby_25", "sharpstone/jruby-minimal", "sharpstone/empty-procfile", @@ -34,9 +30,7 @@ "rack": [ "sharpstone/default_ruby", "sharpstone/mri_187_nokogiri", - "sharpstone/mri_192", - "sharpstone/mri_193", - "sharpstone/mri_200" + "sharpstone/mri_192" ], "rails_versions": [ "sharpstone/rails_lts_23_default_ruby", diff --git a/hatchet.lock b/hatchet.lock index c8fe801e9..b1fa5cbc5 100644 --- a/hatchet.lock +++ b/hatchet.lock @@ -1,6 +1,4 @@ --- -- - "./repos/bundler/bad_gemfile_on_platform" - - 5dd45c83631f58d121afb48b513016cc9e2d3432 - - "./repos/bundler/git_gemspec" - 7755e19caf122e1373bce73ffe9b333e9411d732 - - "./repos/bundler/no_lockfile" @@ -33,10 +31,6 @@ - 3fcce9c85bf560dba285cc385ae9845729195826 - - "./repos/rack/mri_192" - ef6b5ccf8aa2a8f5e3745934e3580dc0bd2d6d4b -- - "./repos/rack/mri_193" - - 2e9ed455517c57b0dc953f54fbf9fef641c7aadb -- - "./repos/rack/mri_200" - - f9922cbd9c6f44fdded54912f66038db37cb4b8f - - "./repos/rails_versions/active_storage_local" - 18853ba7dda61745995740b4ca6f5f90bbd8afba - - "./repos/rails_versions/active_storage_non_local" @@ -77,14 +71,8 @@ - f79860bc2866449fe065484f1542aaadd3f7cfd2 - - "./repos/ruby/libpq_connection_error" - c211c245f09d8335a520cd7a0b5360897d4988eb -- - "./repos/ruby/ruby_193_bad_patch_cedar_14" - - 6948c1460c596f08c4642d616ae73a45dc0ae51a - - "./repos/ruby/ruby_193_jruby_17161_jdk7" - c1b632f8a96cf9b902f5cdcd7a190d46544a8144 -- - "./repos/ruby/ruby_193_jruby_173" - - a08e8b2cc61d08bd611accaa455dbcbfff80f831 -- - "./repos/ruby/ruby_193_jruby_176" - - 9fcbc184cb386abc8784e9935d52d403e35c532c - - "./repos/ruby/ruby_25" - 0cb3df80d55b61e9417f2ac00adb06e15ae37982 - - "./repos/ruby/ruby_version_does_not_exist" diff --git a/lib/language_pack.rb b/lib/language_pack.rb index 05703038d..2e83d4e2d 100644 --- a/lib/language_pack.rb +++ b/lib/language_pack.rb @@ -31,9 +31,7 @@ def self.detect(*args) require "language_pack/helpers/bundler_wrapper" require "language_pack/helpers/outdated_ruby_version" require "language_pack/helpers/download_presence" -require "language_pack/installers/ruby_installer" require "language_pack/installers/heroku_ruby_installer" -require "language_pack/installers/rbx_installer" require "language_pack/ruby" require "language_pack/rack" diff --git a/lib/language_pack/fetcher.rb b/lib/language_pack/fetcher.rb index 37709edab..a4e491a87 100644 --- a/lib/language_pack/fetcher.rb +++ b/lib/language_pack/fetcher.rb @@ -6,11 +6,10 @@ class Fetcher class FetchError < StandardError; end include ShellHelpers - CDN_YAML_FILE = File.expand_path("../../../config/cdn.yml", __FILE__) - def initialize(host_url, stack = nil) - @config = load_config - @host_url = fetch_cdn(host_url) + def initialize(host_url, stack: nil) + @host_url = Pathname.new(host_url) + # File.basename prevents accidental directory traversal @host_url += File.basename(stack) if stack end @@ -50,14 +49,5 @@ def curl_timeout_in_seconds def curl_connect_timeout_in_seconds env('CURL_CONNECT_TIMEOUT') || 3 end - - def load_config - YAML.load_file(CDN_YAML_FILE) || {} - end - - def fetch_cdn(url) - url = @config[url] || url - Pathname.new(url) - end end end diff --git a/lib/language_pack/helpers/bundler_wrapper.rb b/lib/language_pack/helpers/bundler_wrapper.rb index 6caa5eca8..fe9bd70b8 100644 --- a/lib/language_pack/helpers/bundler_wrapper.rb +++ b/lib/language_pack/helpers/bundler_wrapper.rb @@ -256,5 +256,4 @@ def parse_gemfile_lock gemfile_contents = File.read(@gemfile_lock_path) Bundler::LockfileParser.new(gemfile_contents) end - end diff --git a/lib/language_pack/helpers/download_presence.rb b/lib/language_pack/helpers/download_presence.rb index 68e35d99b..ee8ae0cef 100644 --- a/lib/language_pack/helpers/download_presence.rb +++ b/lib/language_pack/helpers/download_presence.rb @@ -18,13 +18,13 @@ class LanguagePack::Helpers::DownloadPresence STACKS = ['heroku-20', 'heroku-22'] - def initialize(path, stacks: STACKS) - @path = path + def initialize(file_name:, stacks: STACKS) + @file_name = file_name @stacks = stacks @fetchers = [] @threads = [] @stacks.each do |stack| - @fetchers << LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack) + @fetchers << LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: stack) end end @@ -43,7 +43,7 @@ def exists_on_next_stack?(current_stack: ) return false unless supported_stack?(current_stack: current_stack) next_index = @stacks.index(current_stack) + 1 - @threads[next_index] + @threads[next_index].value end def valid_stack_list @@ -67,7 +67,7 @@ def does_not_exist? def call @fetchers.map do |fetcher| @threads << Thread.new do - fetcher.exists?(@path, 3) + fetcher.exists?(@file_name, 3) end end end diff --git a/lib/language_pack/helpers/outdated_ruby_version.rb b/lib/language_pack/helpers/outdated_ruby_version.rb index 4d0348316..548503491 100644 --- a/lib/language_pack/helpers/outdated_ruby_version.rb +++ b/lib/language_pack/helpers/outdated_ruby_version.rb @@ -7,7 +7,7 @@ # ruby_version = LanguagePack::RubyVersion.new("ruby-2.2.5") # outdated = LanguagePack::Helpers::OutdatedRubyVersion.new( # current_ruby_version: ruby_version, -# fetcher: LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, "heroku-20") +# fetcher: LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: "heroku-22") # ) # # outdated.call @@ -34,7 +34,6 @@ def initialize(current_ruby_version: , fetcher:) def can_check? return false if current_ruby_version.patchlevel_is_significant? - return false if current_ruby_version.rbx? return false if current_ruby_version.jruby? true diff --git a/lib/language_pack/installers/heroku_ruby_installer.rb b/lib/language_pack/installers/heroku_ruby_installer.rb index e306c812b..ebd52b371 100644 --- a/lib/language_pack/installers/heroku_ruby_installer.rb +++ b/lib/language_pack/installers/heroku_ruby_installer.rb @@ -1,25 +1,55 @@ -require 'language_pack/installers/ruby_installer' require 'language_pack/base' require 'language_pack/shell_helpers' -class LanguagePack::Installers::HerokuRubyInstaller - include LanguagePack::ShellHelpers, LanguagePack::Installers::RubyInstaller +module LanguagePack::Installers; end +class LanguagePack::Installers::HerokuRubyInstaller BASE_URL = LanguagePack::Base::VENDOR_URL + BIN_DIR = Pathname("bin") + + include LanguagePack::ShellHelpers + attr_reader :fetcher - def initialize(stack) - @fetcher = LanguagePack::Fetcher.new(BASE_URL, stack) + def initialize(stack: ) + @fetcher = LanguagePack::Fetcher.new(BASE_URL, stack: stack) end - def fetch_unpack(ruby_version, install_dir, build = false) + def install(ruby_version, install_dir) + fetch_unpack(ruby_version, install_dir) + setup_binstubs(install_dir) + end + + def fetch_unpack(ruby_version, install_dir) FileUtils.mkdir_p(install_dir) Dir.chdir(install_dir) do - file = "#{ruby_version.version_for_download}.tgz" - if build - file.sub!("ruby", "ruby-build") + @fetcher.fetch_untar("#{ruby_version.version_for_download}.tgz") + end + end + + private def setup_binstubs(install_dir) + BIN_DIR.mkpath + run("ln -s ruby #{install_dir}/bin/ruby.exe") + + install_pathname = Pathname.new(install_dir) + Dir["#{install_dir}/bin/*"].each do |vendor_bin| + # for Ruby 2.6.0+ don't symlink the Bundler bin so our shim works + next if vendor_bin.include?("bundle") + + # The bin/rake binstub generated when compiling ruby does not load bundler + # which can cause unexpected failures. Deleting this binstub allows two things: + # + # - If the app includes a custom binstub allows it to be used + # - If the app does not include a custom binstub, then it will fall back to vendor/bundle/bin/rake + # which is generated by bundler + # + # Discussion: https://github.com/heroku/heroku-buildpack-ruby/issues/1025#issuecomment-653102430 + next if vendor_bin.include?("rake") + + if install_pathname.absolute? + run("ln -s #{vendor_bin} #{BIN_DIR}") + else + run("ln -s ../#{vendor_bin} #{BIN_DIR}") end - @fetcher.fetch_untar(file) end end end - diff --git a/lib/language_pack/installers/rbx_installer.rb b/lib/language_pack/installers/rbx_installer.rb deleted file mode 100644 index 800f92d3a..000000000 --- a/lib/language_pack/installers/rbx_installer.rb +++ /dev/null @@ -1,20 +0,0 @@ -require "language_pack/installers/ruby_installer" -require "language_pack/shell_helpers" - -class LanguagePack::Installers::RbxInstaller - include LanguagePack::ShellHelpers, LanguagePack::Installers::RubyInstaller - - BASE_URL = "https://rubinius-binaries-rubinius-com.s3.us-west-2.amazonaws.com/ubuntu/14.04/x86_64/" - - def initialize(stack) - @fetcher = LanguagePack::Fetcher.new(BASE_URL) - end - - def fetch_unpack(ruby_version, install_dir) - file = "#{ruby_version.version_for_download}.tar.bz2" - @fetcher.fetch_bunzip2(file) - FileUtils.mv(Dir.glob("rubinius/#{ruby_version.engine_version}/*"), ".") - FileUtils.rm_rf("rubinius") - end -end - diff --git a/lib/language_pack/installers/ruby_installer.rb b/lib/language_pack/installers/ruby_installer.rb deleted file mode 100644 index 08714631e..000000000 --- a/lib/language_pack/installers/ruby_installer.rb +++ /dev/null @@ -1,53 +0,0 @@ -require "language_pack/shell_helpers" -module LanguagePack::Installers; end - -# This is a base module that is later included by other -# classes such as LanguagePack::Installers::HerokuRubyInstaller -# -module LanguagePack::Installers::RubyInstaller - include LanguagePack::ShellHelpers - - attr_reader :fetcher - - DEFAULT_BIN_DIR = "bin" - - def self.installer(ruby_version) - if ruby_version.rbx? - LanguagePack::Installers::RbxInstaller - else - LanguagePack::Installers::HerokuRubyInstaller - end - end - - def install(ruby_version, install_dir) - fetch_unpack(ruby_version, install_dir) - setup_binstubs(install_dir) - end - - def setup_binstubs(install_dir) - FileUtils.mkdir_p DEFAULT_BIN_DIR - run("ln -s ruby #{install_dir}/bin/ruby.exe") - - install_pathname = Pathname.new(install_dir) - Dir["#{install_dir}/bin/*"].each do |vendor_bin| - # for Ruby 2.6.0+ don't symlink the Bundler bin so our shim works - next if vendor_bin.include?("bundle") - - # The bin/rake binstub generated when compiling ruby does not load bundler - # which can cause unexpected failures. Deleting this binstub allows two things: - # - # - If the app includes a custom binstub allows it to be used - # - If the app does not include a custom binstub, then it will fall back to vendor/bundle/bin/rake - # which is generated by bundler - # - # Discussion: https://github.com/heroku/heroku-buildpack-ruby/issues/1025#issuecomment-653102430 - next if vendor_bin.include?("rake") - - if install_pathname.absolute? - run("ln -s #{vendor_bin} #{DEFAULT_BIN_DIR}") - else - run("ln -s ../#{vendor_bin} #{DEFAULT_BIN_DIR}") - end - end - end -end diff --git a/lib/language_pack/ruby.rb b/lib/language_pack/ruby.rb index b9574dba6..4086d3885 100644 --- a/lib/language_pack/ruby.rb +++ b/lib/language_pack/ruby.rb @@ -17,7 +17,6 @@ class LanguagePack::Ruby < LanguagePack::Base NAME = "ruby" LIBYAML_VERSION = "0.1.7" LIBYAML_PATH = "libyaml-#{LIBYAML_VERSION}" - RBX_BASE_URL = "http://binaries.rubini.us/heroku" NODE_BP_PATH = "vendor/node/bin" Layer = LanguagePack::Helpers::Layer @@ -38,8 +37,6 @@ def bundler def initialize(*args) super(*args) - @fetchers[:mri] = LanguagePack::Fetcher.new(VENDOR_URL, @stack) - @fetchers[:rbx] = LanguagePack::Fetcher.new(RBX_BASE_URL, @stack) @node_installer = LanguagePack::Helpers::NodeInstaller.new @yarn_installer = LanguagePack::Helpers::YarnInstaller.new end @@ -86,7 +83,7 @@ def compile remove_vendor_bundle warn_bundler_upgrade warn_bad_binstubs - install_ruby(slug_vendor_ruby, build_ruby_path) + install_ruby(slug_vendor_ruby) setup_language_pack_environment( ruby_layer_path: File.expand_path("."), gem_layer_path: File.expand_path("."), @@ -230,12 +227,6 @@ def slug_vendor_ruby "vendor/#{ruby_version.version_without_patchlevel}" end - # the absolute path of the build ruby to use during the buildpack - # @return [String] resulting path - def build_ruby_path - "/tmp/#{ruby_version.version_without_patchlevel}" - end - # fetch the ruby version from bundler # @return [String, nil] returns the ruby version if detected or nil if none is detected def ruby_version @@ -518,23 +509,24 @@ def warn_outdated_minor # install the vendored ruby # @return [Boolean] true if it installs the vendored ruby and false otherwise - def install_ruby(install_path, build_ruby_path = nil) + def install_ruby(install_path) # Could do a compare operation to avoid re-downloading ruby return false unless ruby_version - installer = LanguagePack::Installers::RubyInstaller.installer(ruby_version).new(@stack) - @ruby_download_check = LanguagePack::Helpers::DownloadPresence.new(ruby_version.file_name) - @ruby_download_check.call + installer = LanguagePack::Installers::HerokuRubyInstaller.new( + stack: @stack, + ) - if ruby_version.build? - installer.fetch_unpack(ruby_version, build_ruby_path, true) - end + @ruby_download_check = LanguagePack::Helpers::DownloadPresence.new( + file_name: ruby_version.file_name, + ) + @ruby_download_check.call installer.install(ruby_version, install_path) @outdated_version_check = LanguagePack::Helpers::OutdatedRubyVersion.new( current_ruby_version: ruby_version, - fetcher: installer.fetcher + fetcher: installer.fetcher, ) @outdated_version_check.call @@ -620,9 +612,7 @@ def new_app? # @return [String] resulting path or empty string if ruby is not vendored def ruby_install_binstub_path(ruby_layer_path = ".") @ruby_install_binstub_path ||= - if ruby_version.build? - "#{build_ruby_path}/bin" - elsif ruby_version + if ruby_version "#{ruby_layer_path}/#{slug_vendor_ruby}/bin" else "" @@ -693,20 +683,6 @@ def uninstall_binary(path) FileUtils.rm File.join('bin', File.basename(path)), :force => true end - def load_default_cache? - new_app? && ruby_version.default? - end - - # loads a default bundler cache for new apps to speed up initial bundle installs - def load_default_cache - if false # load_default_cache? - puts "New app detected loading default bundler cache" - patchlevel = run("ruby -e 'puts RUBY_PATCHLEVEL'").strip - cache_name = "#{LanguagePack::RubyVersion::DEFAULT_VERSION}-p#{patchlevel}-default-cache" - @fetchers[:buildpack].fetch_untar("#{cache_name}.tgz") - end - end - # remove `vendor/bundle` that comes from the git repo # in case there are native ext. # users should be using `bundle pack` instead. @@ -837,12 +813,7 @@ def build_bundler puts "Bundle completed (#{"%.2f" % bundle_time}s)" log "bundle", :status => "success" puts "Cleaning up the bundler cache." - # Only show bundle clean output when not using default cache - if load_default_cache? - run("bundle clean > /dev/null", user_env: true, env: env_vars) - else - pipe("bundle clean", out: "2> /dev/null", user_env: true, env: env_vars) - end + pipe("bundle clean", out: "2> /dev/null", user_env: true, env: env_vars) @bundler_cache.store # Keep gem cache out of the slug diff --git a/lib/language_pack/ruby_version.rb b/lib/language_pack/ruby_version.rb index 3d5c17fc3..58c25c1d0 100644 --- a/lib/language_pack/ruby_version.rb +++ b/lib/language_pack/ruby_version.rb @@ -52,9 +52,7 @@ def ruby_192_or_lower? # https://github.com/bundler/bundler/issues/4621 def version_for_download - if rbx? - "rubinius-#{engine_version}" - elsif patchlevel_is_significant? && @patchlevel && @patchlevel.sub(/p/, '').to_i >= 0 + if patchlevel_is_significant? && @patchlevel && @patchlevel.sub(/p/, '').to_i >= 0 @version else version_without_patchlevel @@ -62,9 +60,7 @@ def version_for_download end def file_name - file = "#{version_for_download}.tgz" - file.sub!("ruby", "ruby-build") if build? - file + "#{version_for_download}.tgz" end # Before Ruby 2.1 patch releases were done via patchlevel i.e. 1.9.3-p426 versus 1.9.3-p448 @@ -87,18 +83,6 @@ def jruby? engine == :jruby end - # determine if we're using rbx - # @return [Boolean] true if we are and false if we aren't - def rbx? - engine == :rbx - end - - # determines if a build ruby is required - # @return [Boolean] true if a build ruby is required - def build? - engine == :ruby && %w(1.8.7 1.9.2).include?(ruby_version) - end - # convert to a Gemfile ruby DSL incantation # @return [String] the string representation of the Gemfile ruby DSL def to_gemfile diff --git a/lib/language_pack/test/ruby.rb b/lib/language_pack/test/ruby.rb index 43498d67e..e3a2f25c7 100644 --- a/lib/language_pack/test/ruby.rb +++ b/lib/language_pack/test/ruby.rb @@ -9,7 +9,7 @@ def compile Dir.chdir(build_path) remove_vendor_bundle warn_bad_binstubs - install_ruby(slug_vendor_ruby, build_ruby_path) + install_ruby(slug_vendor_ruby) setup_language_pack_environment( ruby_layer_path: File.expand_path("."), gem_layer_path: File.expand_path("."), diff --git a/spec/helpers/download_presence_spec.rb b/spec/helpers/download_presence_spec.rb index 30a5959ad..7d3780621 100644 --- a/spec/helpers/download_presence_spec.rb +++ b/spec/helpers/download_presence_spec.rb @@ -3,23 +3,22 @@ describe LanguagePack::Helpers::DownloadPresence do it "knows if exists on the next stack" do download = LanguagePack::Helpers::DownloadPresence.new( - 'ruby-1.9.3.tgz', - stacks: ['cedar-14', 'heroku-16', 'heroku-18'] + file_name: 'ruby-3.1.4.tgz', + stacks: ['heroku-20', 'heroku-22'], ) download.call - expect(download.next_stack(current_stack: "cedar-14")).to eq("heroku-16") - expect(download.next_stack(current_stack: "heroku-16")).to eq("heroku-18") - expect(download.next_stack(current_stack: "heroku-18")).to be_falsey + expect(download.next_stack(current_stack: "heroku-20")).to eq("heroku-22") + expect(download.next_stack(current_stack: "heroku-22")).to be_falsey - expect(download.exists_on_next_stack?(current_stack:"cedar-14")).to be_truthy + expect(download.exists_on_next_stack?(current_stack:"heroku-20")).to be_truthy end it "detects when a package is present on higher stacks" do download = LanguagePack::Helpers::DownloadPresence.new( - 'ruby-2.6.5.tgz', - stacks: ['cedar-14', 'heroku-16', 'heroku-18'] + file_name: 'ruby-2.6.5.tgz', + stacks: ['cedar-14', 'heroku-16', 'heroku-18'], ) download.call @@ -33,8 +32,8 @@ it "detects when a package is not present on higher stacks" do download = LanguagePack::Helpers::DownloadPresence.new( - 'ruby-1.9.3.tgz', - stacks: ['cedar-14', 'heroku-16', 'heroku-18'] + file_name: 'ruby-1.9.3.tgz', + stacks: ['cedar-14', 'heroku-16', 'heroku-18'], ) download.call @@ -45,8 +44,8 @@ it "detects when a package is present on two stacks but not a third" do download = LanguagePack::Helpers::DownloadPresence.new( - 'ruby-2.3.0.tgz', - stacks: ['cedar-14', 'heroku-16', 'heroku-18'] + file_name: 'ruby-2.3.0.tgz', + stacks: ['cedar-14', 'heroku-16', 'heroku-18'], ) download.call @@ -57,8 +56,8 @@ it "detects when a package does not exist" do download = LanguagePack::Helpers::DownloadPresence.new( - 'does-not-exist.tgz', - stacks: ['cedar-14', 'heroku-16', 'heroku-18'] + file_name: 'does-not-exist.tgz', + stacks: ['cedar-14', 'heroku-16', 'heroku-18'], ) download.call @@ -69,7 +68,7 @@ it "detects default ruby version" do download = LanguagePack::Helpers::DownloadPresence.new( - "ruby-3.1.1.tgz", + file_name: "ruby-3.1.1.tgz", ) download.call @@ -80,7 +79,7 @@ it "handles the current stack not being in the known stacks list" do download = LanguagePack::Helpers::DownloadPresence.new( - "#{LanguagePack::RubyVersion::DEFAULT_VERSION}.tgz", + file_name: "#{LanguagePack::RubyVersion::DEFAULT_VERSION}.tgz", ) download.call diff --git a/spec/helpers/outdated_ruby_version_spec.rb b/spec/helpers/outdated_ruby_version_spec.rb index 69db8925f..63e5ffaaf 100644 --- a/spec/helpers/outdated_ruby_version_spec.rb +++ b/spec/helpers/outdated_ruby_version_spec.rb @@ -2,7 +2,9 @@ describe LanguagePack::Helpers::OutdatedRubyVersion do let(:stack) { "heroku-16" } - let(:fetcher) { LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack) } + let(:fetcher) { + LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: stack) + } it "finds the latest version on a stack" do ruby_version = LanguagePack::RubyVersion.new("ruby-2.2.5") @@ -29,18 +31,6 @@ expect(outdated.latest_minor_version?).to be_truthy end - it "Doesn't do anything when using a patch significant version" do - ruby_version = LanguagePack::RubyVersion.new("ruby-1.9.3p123") - outdated = LanguagePack::Helpers::OutdatedRubyVersion.new( - current_ruby_version: ruby_version, - fetcher: fetcher - ) - - outdated.call - expect(outdated.can_check?).to eq(false) - expect(outdated.suggested_ruby_minor_version).to eq(nil) - end - it "recommends a non EOL version of Ruby" do ruby_version_one = LanguagePack::RubyVersion.new("ruby-2.1.10") ruby_version_two = LanguagePack::RubyVersion.new("ruby-2.2.10") diff --git a/spec/helpers/ruby_version_spec.rb b/spec/helpers/ruby_version_spec.rb index 46a8d28cb..ffad5a5f6 100644 --- a/spec/helpers/ruby_version_spec.rb +++ b/spec/helpers/ruby_version_spec.rb @@ -60,20 +60,6 @@ expect(ruby_version.warn_ruby_26_bundler?).to be false end - it "correctly sets ruby version for bundler specified versions" do - Hatchet::App.new("mri_193").in_directory_fork do |dir| - Bundler.with_unbundled_env do - ruby_version = LanguagePack::RubyVersion.new(@bundler.install.ruby_version, is_new: true) - version_number = "1.9.3" - version = "ruby-#{version_number}" - expect(ruby_version.version_without_patchlevel).to eq(version) - expect(ruby_version.engine_version).to eq(version_number) - expect(ruby_version.to_gemfile).to eq("ruby '#{version_number}'") - expect(ruby_version.engine).to eq(:ruby) - end - end - end - it "correctly sets default ruby versions" do Hatchet::App.new("default_ruby").in_directory_fork do |dir| Bundler.with_unbundled_env do @@ -89,60 +75,83 @@ end end - it "correctly sets default legacy version" do - Hatchet::App.new("default_ruby").in_directory_fork do |dir| + it "detects Ruby from Gemfile.lock" do + Hatchet::App.new("default_ruby").in_directory_fork do |_| + dir = Pathname(Dir.pwd) Bundler.with_unbundled_env do - ruby_version = LanguagePack::RubyVersion.new(@bundler.install.ruby_version, is_new: false) - version_number = LanguagePack::RubyVersion::LEGACY_VERSION_NUMBER - version = LanguagePack::RubyVersion::LEGACY_VERSION - expect(ruby_version.version_without_patchlevel).to eq(version) - expect(ruby_version.engine_version).to eq(version_number) - expect(ruby_version.to_gemfile).to eq("ruby '#{version_number}'") - expect(ruby_version.engine).to eq(:ruby) - end - end - end + dir.join("Gemfile").write(<<~EOF) + source "https://rubygems.org" + + gem 'rake' + ruby '3.2.3' + EOF + dir.join("Gemfile.lock").write(<<~EOF) + GEM + remote: https://rubygems.org/ + specs: + rake (13.2.1) + + PLATFORMS + arm64-darwin-22 + ruby + x86_64-linux + + DEPENDENCIES + rake + + RUBY VERSION + ruby 3.2.3p157 + + BUNDLED WITH + 2.4.19 + EOF - it "detects Ruby 2.0.0" do - Hatchet::App.new("mri_200").in_directory_fork do |dir| - Bundler.with_unbundled_env do ruby_version = LanguagePack::RubyVersion.new(@bundler.install.ruby_version, is_new: true) - version_number = "2.0.0" + version_number = "3.2.3" version = "ruby-#{version_number}" expect(ruby_version.version_without_patchlevel).to eq(version) expect(ruby_version.engine_version).to eq(version_number) - expect(ruby_version.to_gemfile).to eq("ruby '#{version_number}'") expect(ruby_version.engine).to eq(:ruby) end end end - it "detects non mri engines" do - Hatchet::App.new("ruby_193_jruby_173").in_directory_fork do |dir| + Hatchet::App.new("default_ruby").in_directory_fork do |_| + dir = Pathname(Dir.pwd) Bundler.with_unbundled_env do - ruby_version = LanguagePack::RubyVersion.new(@bundler.install.ruby_version, is_new: true) - version_number = "1.9.3" - engine_version = "1.7.3" - engine = :jruby - version = "ruby-#{version_number}-#{engine}-#{engine_version}" - to_gemfile = "ruby '#{version_number}', :engine => '#{engine}', :engine_version => '#{engine_version}'" - expect(ruby_version.version_without_patchlevel).to eq(version) + dir.join("Gemfile").write(<<~EOF) + source "https://rubygems.org" + + ruby '2.6.8', engine: 'jruby', engine_version: '9.3.6.0' + EOF + dir.join("Gemfile.lock").write(<<~EOF) + GEM + remote: https://rubygems.org/ + specs: + PLATFORMS + java + + DEPENDENCIES + + RUBY VERSION + ruby 2.6.8p001 (jruby 9.3.6.0) + + BUNDLED WITH + 2.3.25 + EOF + + ruby_version = LanguagePack::RubyVersion.new( + @bundler.install.ruby_version, + is_new: true + ) + version_number = "2.6.8" + engine_version = "9.3.6.0" + engine = :jruby + expect(ruby_version.version_without_patchlevel).to eq("ruby-#{version_number}-#{engine}-#{engine_version}") expect(ruby_version.engine_version).to eq(engine_version) - expect(ruby_version.to_gemfile).to eq(to_gemfile) expect(ruby_version.engine).to eq(engine) end end end - - it "surfaces error message from bundler" do - bundle_error_msg = "Zomg der was a problem in da gemfile" - error_klass = LanguagePack::Helpers::BundlerWrapper::GemfileParseError - Hatchet::App.new("bad_gemfile_on_platform").in_directory_fork do |dir| - Bundler.with_unbundled_env do - @bundler = LanguagePack::Helpers::BundlerWrapper.new().install - expect { LanguagePack::RubyVersion.new(@bundler.ruby_version) }.to raise_error(error_klass, /#{Regexp.escape(bundle_error_msg)}/) - end - end - end end diff --git a/spec/installers/heroku_ruby_installer_spec.rb b/spec/installers/heroku_ruby_installer_spec.rb index 2fa788a69..3f37e99c1 100644 --- a/spec/installers/heroku_ruby_installer_spec.rb +++ b/spec/installers/heroku_ruby_installer_spec.rb @@ -1,7 +1,9 @@ require "spec_helper" describe LanguagePack::Installers::HerokuRubyInstaller do - let(:installer) { LanguagePack::Installers::HerokuRubyInstaller.new("cedar-14") } + let(:installer) { LanguagePack::Installers::HerokuRubyInstaller.new( + stack: "cedar-14" + ) } let(:ruby_version) { LanguagePack::RubyVersion.new("ruby-2.3.3") } describe "#fetch_unpack" do @@ -14,26 +16,9 @@ end end end - - context "build rubies" do - let(:ruby_version) { LanguagePack::RubyVersion.new("ruby-1.9.2") } - - it "should fetch and unpack the build ruby" do - Dir.mktmpdir do |dir| - Dir.chdir(dir) do - installer.fetch_unpack(ruby_version, dir, true) - - expect(File.read("lib/ruby/1.9.1/x86_64-linux/rbconfig.rb")).to include(%q{CONFIG["prefix"] = (TOPDIR || DESTDIR + "/tmp/ruby-1.9.2")}) - expect(File).to exist("bin/ruby") - end - end - end - - end end describe "#install" do - it "should install ruby and setup binstubs" do Dir.mktmpdir do |dir| Dir.chdir(dir) do @@ -45,6 +30,5 @@ end end end - end end diff --git a/spec/installers/rbx_installer_spec.rb b/spec/installers/rbx_installer_spec.rb deleted file mode 100644 index d446ed5d1..000000000 --- a/spec/installers/rbx_installer_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require "spec_helper" - -describe LanguagePack::Installers::RbxInstaller do - let(:installer) { LanguagePack::Installers::RbxInstaller.new("cedar-14") } - let(:ruby_version) { LanguagePack::RubyVersion.new("ruby-2.3.1-rbx-3.69") } - - describe "#fetch_unpack" do - - it "should fetch and unpack rbx" do - Dir.mktmpdir do |dir| - Dir.chdir(dir) do - installer.fetch_unpack(ruby_version, dir) - - expect(File).to exist("bin/ruby") - end - end - end - - end -end From 935fd0feb34a0a5584afdd0384a9a4d526c5e1e3 Mon Sep 17 00:00:00 2001 From: Richard Schneeman Date: Wed, 17 Apr 2024 11:37:32 -0500 Subject: [PATCH 2/3] Support Heroku-24 and multi-arch (#1439) Heroku-24 base image supports two architectures amd64 and arm64. We've built binaries for these two architectures https://github.com/heroku/docker-heroku-ruby-builder/pull/38. Effectively this means that the s3 bucket that holds the Ruby binaries has an additional folder. Previously files were at `/ruby-.tgz` now they are at `//ruby-.tgz` but only for `heroku-24` and future stacks moving forward. To support multiple architectures, the buildpack needs to detect the current architecture and whether or not the current stack supports multiple architectures. Beyond downloading binaries, the buildpack is aware of the S3 structure to travers version numbers in order to warn customers when a newer version of a ruby version is available. We also warn customers if their current Ruby version is not available on the next stack. This behavior is implemented and tested in this commit, however we're not turning on warnings for `heroku-24` yet as customers cannot currently use it. --- CHANGELOG.md | 1 + lib/language_pack/base.rb | 17 +++++++ lib/language_pack/fetcher.rb | 3 +- .../helpers/download_presence.rb | 8 +++- .../helpers/outdated_ruby_version.rb | 1 + .../installers/heroku_ruby_installer.rb | 8 +++- lib/language_pack/ruby.rb | 4 ++ spec/helpers/download_presence_spec.rb | 46 +++++++++++++++++++ spec/helpers/outdated_ruby_version_spec.rb | 32 +++++++++++++ spec/installers/heroku_ruby_installer_spec.rb | 10 ++-- 10 files changed, 122 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ef414361..88e9e7b3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] +- Heroku-24 stack initial support. Includes multi-architecture (arm64/amd64) logic that has not been tested on the platform (https://github.com/heroku/heroku-buildpack-ruby/pull/1439) - Remove unused Rubinius and Ruby 1.9.2 codepaths (https://github.com/heroku/heroku-buildpack-ruby/pull/1440) ## [v267] - 2024-02-28 diff --git a/lib/language_pack/base.rb b/lib/language_pack/base.rb index 135e88aea..82187e162 100644 --- a/lib/language_pack/base.rb +++ b/lib/language_pack/base.rb @@ -20,6 +20,8 @@ class LanguagePack::Base VENDOR_URL = ENV['BUILDPACK_VENDOR_URL'] || "https://heroku-buildpack-ruby.s3.us-east-1.amazonaws.com" DEFAULT_LEGACY_STACK = "cedar" ROOT_DIR = File.expand_path("../../..", __FILE__) + MULTI_ARCH_STACKS = ["heroku-24"] + KNOWN_ARCHITECTURES = ["amd64", "arm64"] attr_reader :build_path, :cache, :stack @@ -35,10 +37,25 @@ def initialize(build_path, cache_path = nil, layer_dir=nil) @id = Digest::SHA1.hexdigest("#{Time.now.to_f}-#{rand(1000000)}")[0..10] @fetchers = {:buildpack => LanguagePack::Fetcher.new(VENDOR_URL) } @layer_dir = layer_dir + @arch = get_arch Dir.chdir build_path end + def get_arch + command = "dpkg --print-architecture" + arch = run!(command, silent: true).strip + + if !KNOWN_ARCHITECTURES.include?(arch) + raise <<~EOF + Architecture '#{arch}' returned from command `#{command}` is unknown. + Known architectures include: #{KNOWN_ARCHITECTURES.inspect}" + EOF + end + + arch + end + def self.===(build_path) raise "must subclass" end diff --git a/lib/language_pack/fetcher.rb b/lib/language_pack/fetcher.rb index a4e491a87..f16390720 100644 --- a/lib/language_pack/fetcher.rb +++ b/lib/language_pack/fetcher.rb @@ -7,10 +7,11 @@ class FetchError < StandardError; end include ShellHelpers - def initialize(host_url, stack: nil) + def initialize(host_url, stack: nil, arch: nil) @host_url = Pathname.new(host_url) # File.basename prevents accidental directory traversal @host_url += File.basename(stack) if stack + @host_url += File.basename(arch) if arch end def exists?(path, max_attempts = 1) diff --git a/lib/language_pack/helpers/download_presence.rb b/lib/language_pack/helpers/download_presence.rb index ee8ae0cef..18e865656 100644 --- a/lib/language_pack/helpers/download_presence.rb +++ b/lib/language_pack/helpers/download_presence.rb @@ -18,13 +18,17 @@ class LanguagePack::Helpers::DownloadPresence STACKS = ['heroku-20', 'heroku-22'] - def initialize(file_name:, stacks: STACKS) + def initialize(file_name:, arch: , multi_arch_stacks:, stacks: STACKS ) @file_name = file_name @stacks = stacks @fetchers = [] @threads = [] @stacks.each do |stack| - @fetchers << LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: stack) + if multi_arch_stacks.include?(stack) + @fetchers << LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: stack, arch: arch) + else + @fetchers << LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: stack) + end end end diff --git a/lib/language_pack/helpers/outdated_ruby_version.rb b/lib/language_pack/helpers/outdated_ruby_version.rb index 548503491..9f23f0c7d 100644 --- a/lib/language_pack/helpers/outdated_ruby_version.rb +++ b/lib/language_pack/helpers/outdated_ruby_version.rb @@ -8,6 +8,7 @@ # outdated = LanguagePack::Helpers::OutdatedRubyVersion.new( # current_ruby_version: ruby_version, # fetcher: LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: "heroku-22") +# fetcher: LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: "heroku-22", arch: "amd64") # ) # # outdated.call diff --git a/lib/language_pack/installers/heroku_ruby_installer.rb b/lib/language_pack/installers/heroku_ruby_installer.rb index ebd52b371..2b2d4b976 100644 --- a/lib/language_pack/installers/heroku_ruby_installer.rb +++ b/lib/language_pack/installers/heroku_ruby_installer.rb @@ -10,8 +10,12 @@ class LanguagePack::Installers::HerokuRubyInstaller include LanguagePack::ShellHelpers attr_reader :fetcher - def initialize(stack: ) - @fetcher = LanguagePack::Fetcher.new(BASE_URL, stack: stack) + def initialize(stack: , multi_arch_stacks: , arch: ) + if multi_arch_stacks.include?(stack) + @fetcher = LanguagePack::Fetcher.new(BASE_URL, stack: stack, arch: arch) + else + @fetcher = LanguagePack::Fetcher.new(BASE_URL, stack: stack) + end end def install(ruby_version, install_dir) diff --git a/lib/language_pack/ruby.rb b/lib/language_pack/ruby.rb index 4086d3885..4a3009b7e 100644 --- a/lib/language_pack/ruby.rb +++ b/lib/language_pack/ruby.rb @@ -514,11 +514,15 @@ def install_ruby(install_path) return false unless ruby_version installer = LanguagePack::Installers::HerokuRubyInstaller.new( + multi_arch_stacks: MULTI_ARCH_STACKS, stack: @stack, + arch: @arch ) @ruby_download_check = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: MULTI_ARCH_STACKS, file_name: ruby_version.file_name, + arch: @arch ) @ruby_download_check.call diff --git a/spec/helpers/download_presence_spec.rb b/spec/helpers/download_presence_spec.rb index 7d3780621..7d0da9651 100644 --- a/spec/helpers/download_presence_spec.rb +++ b/spec/helpers/download_presence_spec.rb @@ -1,10 +1,44 @@ require "spec_helper" describe LanguagePack::Helpers::DownloadPresence do + it "handles multi-arch transitions for files that exist" do + download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: ["heroku-24"], + file_name: 'ruby-3.1.4.tgz', + stacks: ["heroku-22", "heroku-24"], + arch: "amd64" + ) + + download.call + + expect(download.next_stack(current_stack: "heroku-22")).to eq("heroku-24") + expect(download.next_stack(current_stack: "heroku-24")).to be_falsey + + expect(download.exists_on_next_stack?(current_stack:"heroku-22")).to be_truthy + end + + it "handles multi-arch transitions for files that do not exist" do + download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: ["heroku-24"], + file_name: 'ruby-3.0.5.tgz', + stacks: ["heroku-20", "heroku-24"], + arch: "amd64" + ) + + download.call + + expect(download.next_stack(current_stack: "heroku-20")).to eq("heroku-24") + expect(download.next_stack(current_stack: "heroku-24")).to be_falsey + + expect(download.exists_on_next_stack?(current_stack:"heroku-20")).to be_falsey + end + it "knows if exists on the next stack" do download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: [], file_name: 'ruby-3.1.4.tgz', stacks: ['heroku-20', 'heroku-22'], + arch: nil ) download.call @@ -17,8 +51,10 @@ it "detects when a package is present on higher stacks" do download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: [], file_name: 'ruby-2.6.5.tgz', stacks: ['cedar-14', 'heroku-16', 'heroku-18'], + arch: nil ) download.call @@ -32,8 +68,10 @@ it "detects when a package is not present on higher stacks" do download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: [], file_name: 'ruby-1.9.3.tgz', stacks: ['cedar-14', 'heroku-16', 'heroku-18'], + arch: nil ) download.call @@ -44,8 +82,10 @@ it "detects when a package is present on two stacks but not a third" do download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: [], file_name: 'ruby-2.3.0.tgz', stacks: ['cedar-14', 'heroku-16', 'heroku-18'], + arch: nil ) download.call @@ -56,8 +96,10 @@ it "detects when a package does not exist" do download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: [], file_name: 'does-not-exist.tgz', stacks: ['cedar-14', 'heroku-16', 'heroku-18'], + arch: nil ) download.call @@ -68,7 +110,9 @@ it "detects default ruby version" do download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: [], file_name: "ruby-3.1.1.tgz", + arch: nil ) download.call @@ -79,7 +123,9 @@ it "handles the current stack not being in the known stacks list" do download = LanguagePack::Helpers::DownloadPresence.new( + multi_arch_stacks: [], file_name: "#{LanguagePack::RubyVersion::DEFAULT_VERSION}.tgz", + arch: nil ) download.call diff --git a/spec/helpers/outdated_ruby_version_spec.rb b/spec/helpers/outdated_ruby_version_spec.rb index 63e5ffaaf..d932f80cb 100644 --- a/spec/helpers/outdated_ruby_version_spec.rb +++ b/spec/helpers/outdated_ruby_version_spec.rb @@ -6,6 +6,38 @@ LanguagePack::Fetcher.new(LanguagePack::Base::VENDOR_URL, stack: stack) } + it "handles amd ↗️ architecture on heroku-24" do + ruby_version = LanguagePack::RubyVersion.new("ruby-3.1.0") + fetcher = LanguagePack::Fetcher.new( + LanguagePack::Base::VENDOR_URL, + stack: "heroku-24", + arch: "amd64" + ) + outdated = LanguagePack::Helpers::OutdatedRubyVersion.new( + current_ruby_version: ruby_version, + fetcher: fetcher + ) + + outdated.call + expect(outdated.suggested_ruby_minor_version).to eq("3.1.4") + end + + it "handles arm 💪 architecture on heroku-24" do + ruby_version = LanguagePack::RubyVersion.new("ruby-3.1.0") + fetcher = LanguagePack::Fetcher.new( + LanguagePack::Base::VENDOR_URL, + stack: "heroku-24", + arch: "arm64" + ) + outdated = LanguagePack::Helpers::OutdatedRubyVersion.new( + current_ruby_version: ruby_version, + fetcher: fetcher + ) + + outdated.call + expect(outdated.suggested_ruby_minor_version).to eq("3.1.4") + end + it "finds the latest version on a stack" do ruby_version = LanguagePack::RubyVersion.new("ruby-2.2.5") outdated = LanguagePack::Helpers::OutdatedRubyVersion.new( diff --git a/spec/installers/heroku_ruby_installer_spec.rb b/spec/installers/heroku_ruby_installer_spec.rb index 3f37e99c1..faf0f87e5 100644 --- a/spec/installers/heroku_ruby_installer_spec.rb +++ b/spec/installers/heroku_ruby_installer_spec.rb @@ -1,9 +1,13 @@ require "spec_helper" describe LanguagePack::Installers::HerokuRubyInstaller do - let(:installer) { LanguagePack::Installers::HerokuRubyInstaller.new( - stack: "cedar-14" - ) } + let(:installer) { + LanguagePack::Installers::HerokuRubyInstaller.new( + multi_arch_stacks: [], + stack: "cedar-14", + arch: nil, + ) + } let(:ruby_version) { LanguagePack::RubyVersion.new("ruby-2.3.3") } describe "#fetch_unpack" do From 5ce2c2eb026d4c72d0df82cb1bfc028bc2e2bb7b Mon Sep 17 00:00:00 2001 From: "heroku-linguist[bot]" <136119646+heroku-linguist[bot]@users.noreply.github.com> Date: Wed, 17 Apr 2024 18:42:25 +0000 Subject: [PATCH 3/3] Prepare release v268 (#1442) Co-authored-by: heroku-linguist[bot] <136119646+heroku-linguist[bot]@users.noreply.github.com> --- CHANGELOG.md | 6 +++++- lib/language_pack/version.rb | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88e9e7b3f..3d957b0ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] + +## [v268] - 2024-04-17 + - Heroku-24 stack initial support. Includes multi-architecture (arm64/amd64) logic that has not been tested on the platform (https://github.com/heroku/heroku-buildpack-ruby/pull/1439) - Remove unused Rubinius and Ruby 1.9.2 codepaths (https://github.com/heroku/heroku-buildpack-ruby/pull/1440) @@ -1512,7 +1515,8 @@ Bugfixes: * Change gem detection to use lockfile parser * use `$RACK_ENV` when thin is detected for rack apps -[unreleased]: https://github.com/heroku/heroku-buildpack-ruby/compare/v267...main +[unreleased]: https://github.com/heroku/heroku-buildpack-ruby/compare/v268...main +[v268]: https://github.com/heroku/heroku-buildpack-ruby/compare/v267...v268 [v267]: https://github.com/heroku/heroku-buildpack-ruby/compare/v266...v267 [v266]: https://github.com/heroku/heroku-buildpack-ruby/compare/v265...v266 [v265]: https://github.com/heroku/heroku-buildpack-ruby/compare/v264...v265 diff --git a/lib/language_pack/version.rb b/lib/language_pack/version.rb index 37b02edb5..35eaf2369 100644 --- a/lib/language_pack/version.rb +++ b/lib/language_pack/version.rb @@ -2,6 +2,6 @@ module LanguagePack class LanguagePack::Base - BUILDPACK_VERSION = "v267" + BUILDPACK_VERSION = "v268" end end