Skip to content

Commit

Permalink
feat: load Ruby version from Gemfile
Browse files Browse the repository at this point in the history
  • Loading branch information
maxirmx committed Jan 24, 2025
1 parent c945231 commit 3ec6378
Show file tree
Hide file tree
Showing 15 changed files with 641 additions and 136 deletions.
43 changes: 34 additions & 9 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ can opt to run `strip -S` manually, it most cases it works.
MacOS build caveats:
* We saw `clang` compiler segmentation fault when during packaging of very large
* We saw `clang` compiler segmentation fault when packaging of very large
projects with XCode 14.3.1. This issue is not reproducible with XCode 15.0.1 or
higher.
====
Expand Down Expand Up @@ -905,7 +905,8 @@ Where:

`<tebako-root-folder>`:: the Tebako root folder (see details: <<root-folder-selection>>)

`Ruby`:: parameter defines Ruby version that will be packaged (optional, defaults to `3.2.6`)
`Ruby`:: parameter defines package Ruby version (optional). This version is used in conjuction with requirements
from the Gemfile as explained below in 'Package Ruby version selection rules' section.

`tebafile`::
the tebako configuration file (optional, defaults to `$PWD/.tebako.yml`).
Expand Down Expand Up @@ -1056,13 +1057,37 @@ operation. The following table lists the possible exit codes and their meanings.

| 0 | No error
| 1 | Invalid command line
| 101 | `tebako setup` failed at configuration step
| 102 | `tebako setup` failed at build step
| 103 | `tebako press` failed at configuration step
| 104 | `tebako press` failed at build step
| 253 | Unsupported Ruby version
| 254 | Unsupported operating systems
| 255 | Internal error
| 101 | 'tebako setup' configure step failed
| 102 | 'tebako setup' build step failed
| 103 | 'tebako press' configure step failed
| 104 | 'tebako press' build step failed
| 105 | Failed to map MSys path to Windows
| 106 | Entry point does not exist or is not accessible
| 107 | Project root does not exist or is not accessible
| 108 | Package working directory does not exist
| 109 | Invalid Ruby version format
| 110 | Ruby version is not supported
| 111 | Ruby version is not supported on Windows
| 112 | OS is not supported
| 113 | Path to root shall be absolute. Relative path is not allowed
| 114 | Entry point is not within the project root
| 115 | Failed to load Gemfile
| 116 | Ruby version does not satify Gemfile requirements
|===

== Package Ruby version selection rules

During packaging tebako creates its own Ruby execution environment that is independent from the host Ruby environment.
The version of Ruby that is used in the package is defined by the combination of the `--Ruby` option and ruby requirement
specified in Gemfile.

.Package Ruby version selection Rules
[cols="3", options="header"]
|===
| Gemfile requirement\--Ruby option | Specified | Not specified

| Specified | The version specified by --Ruby option if it is supported and satisfies Gemfile requirement; error otherwise | The minimal supported Ruby option that satisfies Gemfile requirement; error otherwise
| Not specified | The version specified by --Ruby option if it is supported; error otherwise | Default Tebako Ruby version (3.2.6).

|===

Expand Down
4 changes: 3 additions & 1 deletion lib/tebako/cli_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ module CliHelpers

def do_press(options_manager)
scenario_manager = Tebako::ScenarioManager.new(options_manager.root, options_manager.fs_entrance)
scenario_manager.configure_scenario
options_manager.process_gemfile(scenario_manager.gemfile_path) if scenario_manager.with_gemfile

puts options_manager.press_announce(scenario_manager.msys?)

if options_manager.package_within_root?
Expand Down Expand Up @@ -91,7 +94,6 @@ def do_setup(options_manager)

def generate_files(options_manager, scenario_manager)
puts "-- Generating files"
scenario_manager.configure_scenario

v_parts = Tebako::VERSION.split(".")
Tebako::Codegen.generate_tebako_version_h(options_manager, v_parts)
Expand Down
2 changes: 1 addition & 1 deletion lib/tebako/deploy_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def install_gem(name, ver = nil)
end

def needs_bundler?
@gf_length.positive? && !@ruby_ver.ruby31?
@with_gemfile && !@ruby_ver.ruby31?
end

def update_rubygems
Expand Down
7 changes: 5 additions & 2 deletions lib/tebako/error.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Copyright (c) 2023-204 [Ribose Inc](https://www.ribose.com).
# Copyright (c) 2023-2025 [Ribose Inc](https://www.ribose.com).
# All rights reserved.
# This file is a part of tebako
#
Expand Down Expand Up @@ -42,12 +42,15 @@ module Tebako
112 => "OS is not supported",
113 => "Path to root shall be absolute. Relative path is not allowed",
114 => "Entry point is not within the project root",
115 => "Failed to load Gemfile",
116 => "Ruby version does not satify Gemfile requirements",
201 => "Warning. Could not create cache version file"
}.freeze

class << self
def packaging_error(code)
def packaging_error(code, extm = nil)
msg = PACKAGING_ERRORS[code]
msg += ": #{extm}" unless extm.nil?
msg = "Unknown packaging error" if msg.nil?
raise Tebako::Error.new msg, code
end
Expand Down
13 changes: 13 additions & 0 deletions lib/tebako/options_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,19 @@ def press_options
@press_options ||= "-DPCKG:STRING='#{package}' -DLOG_LEVEL:STRING='#{l_level}' " \
end

def process_gemfile(gemfile_path)
folder = File.dirname(gemfile_path)
filename = File.basename(gemfile_path)
# Change directory to the folder containing the Gemfile
# Because Bundler::Definition.build *sometimes* requires to be in
# the Gemfile directory
Dir.chdir(folder) do
@rv = Tebako::RubyVersionWithGemfile.new(@options["Ruby"], filename)
end
@ruby_ver, @ruby_hash = @rv.extend_ruby_version
@ruby_src_dir = nil
end

def relative?(path)
Pathname.new(path).relative?
end
Expand Down
80 changes: 68 additions & 12 deletions lib/tebako/ruby_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

require "bundler"
require_relative "error"

# Tebako - an executable packager
module Tebako
# Ruby version checks
# Ruby version
class RubyVersion
RUBY_VERSIONS = {
"2.7.8" => "c2dab63cbc8f2a05526108ad419efa63a67ed4074dbbcf9fc2b1ca664cb45ba0",
Expand All @@ -51,13 +52,23 @@ class RubyVersion
def initialize(ruby_version)
@ruby_version = ruby_version.nil? ? DEFAULT_RUBY_VERSION : ruby_version

version_check_format
version_check
version_check_msys
run_checks
end

attr_reader :ruby_version

def api_version
@api_version ||= "#{@ruby_version.split(".")[0..1].join(".")}.0"
end

def extend_ruby_version
@extend_ruby_version ||= [@ruby_version, RUBY_VERSIONS[@ruby_version]]
end

def lib_version
@lib_version ||= "#{@ruby_version.split(".")[0..1].join}0"
end

def ruby3x?
@ruby3x ||= @ruby_version[0] == "3"
end
Expand All @@ -82,12 +93,10 @@ def ruby34?
@ruby34 ||= ruby3x? && @ruby_version[2].to_i >= 4
end

def api_version
@api_version ||= "#{@ruby_version.split(".")[0..1].join(".")}.0"
end

def lib_version
@lib_version ||= "#{@ruby_version.split(".")[0..1].join}0"
def run_checks
version_check_format
version_check
version_check_msys
end

def version_check
Expand All @@ -111,9 +120,56 @@ def version_check_msys
raise Tebako::Error.new("Ruby version #{@ruby_version} is not supported on Windows", 111)
end
end
end

def extend_ruby_version
@extend_ruby_version ||= [@ruby_version, RUBY_VERSIONS[@ruby_version]]
# Ruby version with Gemfile definition
class RubyVersionWithGemfile < RubyVersion
def initialize(ruby_version, gemfile_path)
# Assuming that it does not attempt to load any gems or resolve dependencies
# this can be done with any bundler version
gemfile = Bundler::Definition.build(gemfile_path, nil, nil)
ruby_v = gemfile.ruby_version&.versions
if ruby_v.nil?
super(ruby_version)
else
process_gemfile_ruby_version(ruby_version, ruby_v)
run_checks
end
rescue StandardError => e
Tebako.packaging_error(115, e.message)
end

def process_gemfile_ruby_version(ruby_version, ruby_v)
puts "-- Found Gemfile with Ruby requirements #{ruby_v}"
requirement = Gem::Requirement.new(ruby_v)

if ruby_version.nil?
process_gemfile_ruby_version_ud(requirement)
else
process_gemfile_ruby_version_d(ruby_version, requirement)
end
end

def process_gemfile_ruby_version_d(ruby_version, requirement)
current_version = Gem::Version.new(ruby_version)
unless requirement.satisfied_by?(current_version)
raise Tebako::Error.new("Ruby version #{ruby_version} does not satisfy requirement #{ruby_v}", 116)
end

@ruby_version = ruby_version
end

def process_gemfile_ruby_version_ud(requirement)
available_versions = RUBY_VERSIONS.keys.map { |v| Gem::Version.new(v) }
matching_version = available_versions.find { |v| requirement.satisfied_by?(v) }
puts "-- Found matching Ruby version #{matching_version}" if matching_version

unless matching_version
raise Tebako::Error.new("No available Ruby version satisfies requirement #{requirement}",
116)
end

@ruby_version = matching_version.to_s
end
end
end
13 changes: 8 additions & 5 deletions lib/tebako/scenario_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
# POSSIBILITY OF SUCH DAMAGE.

require "pathname"
require "bundler"

require_relative "error"

Expand All @@ -34,11 +35,12 @@ module Tebako
# Manages packaging scenario based on input files (gemfile, gemspec, etc)
class ScenarioManager
def initialize(fs_root, fs_entrance)
@with_gemfile = false
initialize_root(fs_root)
initialize_entry_point(fs_entrance || "stub.rb")
end

attr_reader :fs_entry_point, :fs_mount_point, :fs_entrance
attr_reader :fs_entry_point, :fs_mount_point, :fs_entrance, :gemfile_path, :with_gemfile

def configure_scenario
@fs_mount_point = if msys?
Expand Down Expand Up @@ -92,16 +94,16 @@ def configure_scenario_inner
when 0
configure_scenario_no_gemspec
when 1
@scenario = @gf_length.positive? ? :gemspec_and_gemfile : :gemspec
@scenario = @with_gemfile ? :gemspec_and_gemfile : :gemspec
else
raise Tebako::Error, "Multiple Ruby gemspecs found in #{@fs_root}"
end
end

def configure_scenario_no_gemspec
@fs_entry_point = "/local/#{@fs_entrance}" if @gf_length.positive? || @g_length.zero?
@fs_entry_point = "/local/#{@fs_entrance}" if @with_gemfile || @g_length.zero?

@scenario = if @gf_length.positive?
@scenario = if @with_gemfile
:gemfile
elsif @g_length.positive?
:gem
Expand All @@ -111,8 +113,9 @@ def configure_scenario_no_gemspec
end

def lookup_files
@gemfile_path = File.join(@fs_root, "Gemfile")
@gs_length = Dir.glob(File.join(@fs_root, "*.gemspec")).length
@gf_length = Dir.glob(File.join(@fs_root, "Gemfile")).length
@with_gemfile = File.exist?(@gemfile_path)
@g_length = Dir.glob(File.join(@fs_root, "*.gem")).length
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/tebako/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@
# POSSIBILITY OF SUCH DAMAGE.

module Tebako
VERSION = "0.12.6"
VERSION = "0.12.7"
end
Loading

0 comments on commit 3ec6378

Please sign in to comment.