diff --git a/lib/tebako/cli_helpers.rb b/lib/tebako/cli_helpers.rb index 0169d1bc..9132954a 100644 --- a/lib/tebako/cli_helpers.rb +++ b/lib/tebako/cli_helpers.rb @@ -41,47 +41,52 @@ module Tebako # Cli helpers module CliHelpers + WARN = <<~WARN + + ****************************************************************************************************************** + * * + * WARNING: You are packaging in-place, i.e.: tebako package will be placed inside application root. * + * It is not an error but we do not recommend it because it is a way to keep packaging old versions recrsively. * + * * + ****************************************************************************************************************** + + WARN + def do_press(options_manager) scenario_manager = Tebako::ScenarioManager.new(options_manager.root, options_manager.fs_entrance) puts options_manager.press_announce(scenario_manager.msys?) - if options_manager.mode == "both" || options_manager.mode == "runtime" || options_manager.mode == "bundle" - do_press_runtime(options_manager, scenario_manager) - end - - if options_manager.mode == "both" || options_manager.mode == "application" - do_press_application(options_manager, scenario_manager) + if options_manager.package_within_root? + puts WARN + sleep 5 end - true + do_press_runtime(options_manager, scenario_manager) + do_press_application(options_manager, scenario_manager) end def do_press_application(options_manager, scenario_manager) + return unless %w[both application].include?(options_manager.mode) + packager = Tebako::PackagerLite.new(options_manager, scenario_manager) packager.create_package end def do_press_runtime(options_manager, scenario_manager) + return unless %w[both runtime bundle].include?(options_manager.mode) + generate_files(options_manager, scenario_manager) - cfg_cmd = "cmake -DSETUP_MODE:BOOLEAN=OFF #{options_manager.cfg_options} #{options_manager.press_options}" - build_cmd = "cmake --build #{options_manager.output_folder} --target tebako --parallel #{Etc.nprocessors}" merged_env = ENV.to_h.merge(options_manager.b_env) - Tebako.packaging_error(103) unless system(merged_env, cfg_cmd) - Tebako.packaging_error(104) unless system(merged_env, build_cmd) - true + Tebako.packaging_error(103) unless system(merged_env, press_cfg_cmd(options_manager)) + Tebako.packaging_error(104) unless system(merged_env, press_build_cmd(options_manager)) end def do_setup(options_manager) puts "Setting up tebako packaging environment" - cfg_cmd = "cmake -DSETUP_MODE:BOOLEAN=ON #{options_manager.cfg_options}" - build_cmd = "cmake --build \"#{options_manager.output_folder}\" --target setup --parallel #{Etc.nprocessors}" merged_env = ENV.to_h.merge(options_manager.b_env) - Tebako.packaging_error(101) unless system(merged_env, cfg_cmd) - Tebako.packaging_error(102) unless system(merged_env, build_cmd) - - fix_mkdwarfs_permissions(options_manager) - true + Tebako.packaging_error(101) unless system(merged_env, setup_cfg_cmd(options_manager)) + Tebako.packaging_error(102) unless system(merged_env, setup_build_cmd(options_manager)) end def generate_files(options_manager, scenario_manager) @@ -93,16 +98,11 @@ def generate_files(options_manager, scenario_manager) Tebako::Codegen.generate_tebako_fs_cpp(options_manager, scenario_manager) Tebako::Codegen.generate_deploy_rb(options_manager, scenario_manager) - return unless options_manager.mode == "both" || options_manager.mode == "runtime" + return unless %w[both runtime].include?(options_manager.mode) Tebako::Codegen.generate_stub_rb(options_manager) end - def fix_mkdwarfs_permissions(options_manager) - scenario_manager = Tebako::ScenarioManager.new(options_manager.root, options_manager.fs_entrance) - FileUtils.chmod("+x", File.join(options_manager.deps_bin_dir, "mkdwarfs#{scenario_manager.exe_suffix}")) - end - def options_from_tebafile(tebafile) ::YAML.load_file(tebafile)["options"] || {} rescue Psych::SyntaxError => e @@ -114,5 +114,21 @@ def options_from_tebafile(tebafile) puts e.message {} end + + def press_build_cmd(options_manager) + "cmake --build #{options_manager.output_folder} --target tebako --parallel #{Etc.nprocessors}" + end + + def press_cfg_cmd(options_manager) + "cmake -DSETUP_MODE:BOOLEAN=OFF #{options_manager.cfg_options} #{options_manager.press_options}" + end + + def setup_build_cmd(options_manager) + "cmake --build \"#{options_manager.output_folder}\" --target setup --parallel #{Etc.nprocessors}" + end + + def setup_cfg_cmd(options_manager) + "cmake -DSETUP_MODE:BOOLEAN=ON #{options_manager.cfg_options}" + end end end diff --git a/lib/tebako/deploy_helper.rb b/lib/tebako/deploy_helper.rb index e70aced6..39b197fb 100644 --- a/lib/tebako/deploy_helper.rb +++ b/lib/tebako/deploy_helper.rb @@ -49,7 +49,7 @@ def initialize(fs_root, fs_entrance, target_dir, pre_dir) @fs_entrance = fs_entrance @target_dir = target_dir @pre_dir = pre_dir - @verbose = ENV["VERBOSE"] == "yes" || ENV["VERBOSE"] == "true" + @verbose = %w[yes true].include?(ENV.fetch("VERBOSE", nil)) @ncores = BuildHelpers.ncores end diff --git a/lib/tebako/options_manager.rb b/lib/tebako/options_manager.rb index c932572d..95fef6ea 100644 --- a/lib/tebako/options_manager.rb +++ b/lib/tebako/options_manager.rb @@ -185,6 +185,15 @@ def package end end + def package_within_root? + package_path = Pathname.new(package.chomp("/")) + root_path = Pathname.new(root.chomp("/")) + package_path.ascend do |path| + return true if path == root_path + end + false + end + def prefix @prefix ||= if @options["prefix"].nil? handle_nil_prefix diff --git a/lib/tebako/packager.rb b/lib/tebako/packager.rb index a8fe2cff..2d2d5d64 100644 --- a/lib/tebako/packager.rb +++ b/lib/tebako/packager.rb @@ -108,6 +108,7 @@ def init(stash_dir, src_dir, pre_dir, bin_dir) def mkdwarfs(deps_bin_dir, data_bin_file, data_src_dir, descriptor = nil) puts "-- Running mkdwarfs script" + FileUtils.chmod("a+x", Dir.glob(File.join(deps_bin_dir, "mkdwarfs*"))) params = [File.join(deps_bin_dir, "mkdwarfs"), "-o", data_bin_file, "-i", data_src_dir, "--no-progress"] params << "--header" << descriptor if descriptor BuildHelpers.run_with_capture_v(params) diff --git a/spec/cli_helpers_spec.rb b/spec/cli_helpers_spec.rb index aedad140..cf2141ad 100644 --- a/spec/cli_helpers_spec.rb +++ b/spec/cli_helpers_spec.rb @@ -37,8 +37,8 @@ { "output" => "/path/to/output", "deps" => "/path/to/deps", "entry-point" => "entrypoint", "root" => "/tmp/path/to/root/" } end - let(:ruby_ver) { "3.2.5" } - let(:ruby_hash) { Tebako::RubyVersion::RUBY_VERSIONS["3.2.5"] } + let(:ruby_ver) { "3.2.6" } + let(:ruby_hash) { Tebako::RubyVersion::RUBY_VERSIONS["3.2.6"] } before do allow_any_instance_of(Pathname).to receive(:realpath) { |instance| instance } @@ -62,7 +62,7 @@ it "executes the press command successfully" do allow_any_instance_of(Tebako::PackagerLite).to receive(:create_package).and_return(true) - expect(do_press(options_manager)).to be_truthy + expect { do_press(options_manager) }.not_to raise_error end end @@ -79,7 +79,7 @@ allow(Tebako::Codegen).to receive(:generate_tebako_version_h).and_return(true) allow(Tebako::Codegen).to receive(:generate_tebako_fs_cpp).and_return(true) - expect(do_press(options_manager)).to be_truthy + expect { do_press(options_manager) }.not_to raise_error end it "raises an error if the press command fails" do @@ -103,7 +103,7 @@ allow(Tebako::Codegen).to receive(:generate_tebako_fs_cpp).and_return(true) allow(Tebako::Codegen).to receive(:generate_package_header).and_return(true) - expect(do_press(options_manager)).to be_truthy + expect { do_press(options_manager) }.not_to raise_error end it "raises an error if the press command fails" do @@ -112,6 +112,26 @@ expect { do_press(options_manager) }.to raise_error(Tebako::Error) end end + + context "when package_within_root? is true" do + before do + options["mode"] = "bundle" + options["output"] = "/tmp/path/to/root/output" + end + + let(:options_manager) { Tebako::OptionsManager.new(options) } + + it "shows a warning and executes the press command successfully" do + allow(FileUtils).to receive(:rm_rf) + allow(self).to receive(:system).and_return(true) + allow(Tebako::Codegen).to receive(:generate_tebako_version_h).and_return(true) + allow(Tebako::Codegen).to receive(:generate_tebako_fs_cpp).and_return(true) + allow(Tebako::Codegen).to receive(:generate_package_header).and_return(true) + + allow(self).to receive(:sleep).with(any_args).and_return(nil) + expect { do_press(options_manager) }.to output(/WARNING/).to_stdout + end + end end describe "#do_setup" do @@ -125,7 +145,7 @@ it "executes the setup command successfully" do allow(FileUtils).to receive(:rm_rf) allow(self).to receive(:system).and_return(true) - expect(do_setup(options_manager)).to be_truthy + expect { do_setup(options_manager) }.not_to raise_error end it "raises an error if the setup command fails" do diff --git a/spec/deploy_helper_spec.rb b/spec/deploy_helper_spec.rb index 76f36f16..841efbf1 100644 --- a/spec/deploy_helper_spec.rb +++ b/spec/deploy_helper_spec.rb @@ -56,6 +56,68 @@ end end + describe "#bundle_config" do + let(:r_v) { "3.2.4" } + let(:ruby_ver) { Tebako::RubyVersion.new(r_v) } + let(:cwd) { "/current/working/dir" } + + context "on linux" do + before do + allow(Tebako::BuildHelpers).to receive(:ncores).and_return(1) if RUBY_PLATFORM =~ /darwin/ + stub_const("RUBY_PLATFORM", "linux") + allow(deploy_helper).to receive(:lookup_files) + allow(deploy_helper).to receive(:configure_scenario) + deploy_helper.configure(ruby_ver, cwd) + end + + it "calls BuildHelpers.run_with_capture_v with the correct commands" do + bundle = "/target/dir/bin/bundle" + expect(Tebako::BuildHelpers) + .to receive(:run_with_capture_v) + .with([bundle, "config", "set", "--local", "build.ffi", "--disable-system-libffi"]) + .once + expect(Tebako::BuildHelpers) + .to receive(:run_with_capture_v) + .with([bundle, "config", "set", "--local", "build.nokogiri", "--no-use-system-libraries"]) + .once + expect(Tebako::BuildHelpers) + .to receive(:run_with_capture_v) + .with([bundle, "config", "set", "--local", "force_ruby_platform", "false"]) + .once + + deploy_helper.send(:bundle_config) + end + end + + context "on msys" do + before do + allow(Tebako::BuildHelpers).to receive(:ncores).and_return(1) if RUBY_PLATFORM =~ /darwin/ + stub_const("RUBY_PLATFORM", "msys") + allow(deploy_helper).to receive(:lookup_files) + allow(deploy_helper).to receive(:configure_scenario) + deploy_helper.configure(ruby_ver, cwd) + end + + it "calls BuildHelpers.run_with_capture_v with the correct commands" do + bundle = "/target/dir/bin/bundle.bat" + expect(Tebako::BuildHelpers) + .to receive(:run_with_capture_v) + .with([bundle, "config", "set", "--local", "build.ffi", "--disable-system-libffi"]) + .once + expect(Tebako::BuildHelpers) + .to receive(:run_with_capture_v) + .with([bundle, "config", "set", "--local", "build.nokogiri", "--use-system-libraries"]) + .once + expect(Tebako::BuildHelpers) + .to receive(:run_with_capture_v) + .with([bundle, "config", "set", "--local", "force_ruby_platform", "true"]) + .once + + deploy_helper.send(:bundle_config) + end + end + end + describe "#configure" do let(:r_v) { "3.2.4" } let(:ruby_ver) { Tebako::RubyVersion.new(r_v) } @@ -110,6 +172,45 @@ end end + describe "#check_entry_point" do + let(:entry_point_root) { "/project/entry_points" } + let(:r_v) { "3.2.4" } + let(:ruby_ver) { Tebako::RubyVersion.new(r_v) } + let(:cwd) { "/current/working/dir" } + + before do + allow(Tebako::BuildHelpers).to receive(:ncores).and_return(1) if RUBY_PLATFORM =~ /darwin/ + stub_const("RUBY_PLATFORM", "linux") + allow(deploy_helper).to receive(:lookup_files) + allow(deploy_helper).to receive(:configure_scenario_inner) + deploy_helper.configure(ruby_ver, cwd) + end + + context "when the entry point file exists" do + before do + allow(File).to receive(:exist?).and_return(true) + end + + it "does not raise an error" do + expect do + deploy_helper.send(:check_entry_point, entry_point_root) + end.not_to raise_error + end + end + + context "when the entry point file does not exist" do + before do + allow(File).to receive(:exist?).and_return(false) + end + + it "raises a Tebako::Error" do + expect do + deploy_helper.send(:check_entry_point, entry_point_root) + end.to raise_error(Tebako::Error, /Entry point/) + end + end + end + describe "#configure_commands" do context "when os_type is msys" do before do @@ -166,6 +267,67 @@ end end + describe "#copy_files" do + let(:destination) { "/fake/dest" } + let(:entry_point_root) { "/project/entry_points" } + let(:r_v) { "3.2.6" } + let(:ruby_ver) { Tebako::RubyVersion.new(r_v) } + let(:cwd) { "/current/working/dir" } + + before do + allow(Tebako::BuildHelpers).to receive(:ncores).and_return(1) if RUBY_PLATFORM =~ /darwin/ + stub_const("RUBY_PLATFORM", "linux") + allow(deploy_helper).to receive(:lookup_files) + allow(deploy_helper).to receive(:configure_scenario_inner) + deploy_helper.configure(ruby_ver, cwd) + end + + context "when @fs_root exists and is readable" do + before do + allow(Dir).to receive(:exist?).with(deploy_helper.instance_variable_get(:@fs_root)).and_return(true) + allow(File).to receive(:readable?).with(deploy_helper.instance_variable_get(:@fs_root)).and_return(true) + allow(FileUtils).to receive(:mkdir_p).with(destination) + end + + it "copies files without raising an error" do + expect(FileUtils) + .to receive(:cp_r) + .with(File.join(deploy_helper.instance_variable_get(:@fs_root), "."), destination) + expect do + deploy_helper.send(:copy_files, destination) + end.not_to raise_error + end + + it "raises a Tebako::Error if FileUtils.cp_r fails" do + allow(FileUtils).to receive(:cp_r).and_raise(StandardError) + expect do + deploy_helper.send(:copy_files, destination) + end.to raise_error(Tebako::Error, /does not exist or is not accessible/) + end + end + + context "when @fs_root does not exist or is unreadable" do + before do + allow(FileUtils).to receive(:mkdir_p).with(destination) + end + + it "raises a Tebako::Error if the directory does not exist" do + allow(Dir).to receive(:exist?).with(deploy_helper.instance_variable_get(:@fs_root)).and_return(false) + expect do + deploy_helper.send(:copy_files, destination) + end.to raise_error(Tebako::Error, /not accessible or is not a directory/) + end + + it "raises a Tebako::Error if the directory is not readable" do + allow(Dir).to receive(:exist?).with(deploy_helper.instance_variable_get(:@fs_root)).and_return(true) + allow(File).to receive(:readable?).with(deploy_helper.instance_variable_get(:@fs_root)).and_return(false) + expect do + deploy_helper.send(:copy_files, destination) + end.to raise_error(Tebako::Error, /not accessible or is not a directory/) + end + end + end + describe "#deploy_env" do let(:gem_home) { File.join(target_dir, "gems") } diff --git a/spec/options_manager_spec.rb b/spec/options_manager_spec.rb index 4efa6545..149462f4 100644 --- a/spec/options_manager_spec.rb +++ b/spec/options_manager_spec.rb @@ -623,6 +623,35 @@ end end end + + describe "#package_within_root?" do + context "when the package path is within the root directory" do + let(:options) { { "root" => "/absolute/path", "output" => "/absolute/path/package" } } + let(:options_manager) { Tebako::OptionsManager.new(options) } + it "returns true" do + result = options_manager.package_within_root? + expect(result).to be(true) + end + end + + context "when the package path is outside the root directory" do + let(:options) { { "root" => "/absolute/path", "output" => "/absolute/otherpath/package" } } + let(:options_manager) { Tebako::OptionsManager.new(options) } + it "returns false" do + result = options_manager.package_within_root? + expect(result).to be(false) + end + end + + context "when the package path is outside the root directory (funcky)" do + let(:options) { { "root" => "/absolute/path/package-dir", "output" => "/absolute/path/package" } } + let(:options_manager) { Tebako::OptionsManager.new(options) } + it "returns false" do + result = options_manager.package_within_root? + expect(result).to be(false) + end + end + end end # rubocop:enable Metrics/BlockLength