Skip to content

Commit

Permalink
chore: extend clean_ruby feature to deal with NUL files on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
maxirmx committed Feb 5, 2025
1 parent 27813b4 commit a431af3
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 90 deletions.
35 changes: 28 additions & 7 deletions lib/tebako/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

require "digest"
require "fileutils"
require "find"
require "pathname"
require "open3"
require "thor"
require "yaml"
Expand All @@ -36,6 +38,7 @@
require_relative "cli_helpers"
require_relative "error"
require_relative "ruby_version"
require_relative "scenario_manager"
require_relative "version"

# Tebako - an executable packager
Expand Down Expand Up @@ -66,10 +69,11 @@ def clean_ruby
(om,) = bootstrap(clean: true)

suffix = options["Ruby"].nil? ? "" : "_#{options["Ruby"]}"
nmr = "src/_ruby#{suffix}*"
nms = "stash#{suffix}*"
FileUtils.rm_rf(Dir.glob(File.join(om.deps, nmr)), secure: true)
FileUtils.rm_rf(Dir.glob(File.join(om.deps, nms)), secure: true)
nmr = Dir.glob(File.join(om.deps, "src", "_ruby#{suffix}*"))
nms = Dir.glob(File.join(om.deps, "stash#{suffix}*"))

FileUtils.rm_rf(nmr + nms, secure: true)
extra_win_clean(nmr)
end

desc "hash", "Print build script hash (ci cache key)"
Expand Down Expand Up @@ -147,13 +151,29 @@ def bootstrap(clean: false)
[options_manager, cache_manager]
end

# Ruby extension maker sometimes creates files with 'NUL' name on Windows
# This method removes such files
def extra_win_clean(nmr)
return unless nmr.any? && ScenarioManagerBase.new.msys?

nmr.each do |path|
if File.basename(path) == "NUL"
full_path = "//?/#{path}"
FileUtils.rm_f(full_path)
end
end
FileUtils.rm_rf(nmr, secure: true)
end

def initialize(*args)
super
return if args[2][:current_command].name.include?("hash")

puts "Tebako executable packager version #{Tebako::VERSION}"
end
end

no_commands do
def options
original_options = super
tebafile = original_options["tebafile"].nil? ? DEFAULT_TEBAFILE : original_options["tebafile"]
Expand All @@ -164,9 +184,7 @@ def options
original_options
end
end
end

no_commands do
def source
c_path = Pathname.new(__FILE__).realpath
@source ||= File.expand_path("../../..", c_path)
Expand All @@ -177,7 +195,10 @@ def validate_press_options

opts = ""
opts += " '--root'" if options["root"].nil?
opts += " '--entry-point'" if options["entry-point"].nil?
if options["entry-point"].nil?
opts += ", " unless opts.empty?
opts += " '--entry-point'"
end
raise Thor::Error, "No value provided for required options #{opts}" unless opts.empty?
end
end
Expand Down
55 changes: 30 additions & 25 deletions lib/tebako/scenario_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,47 +38,50 @@ module Tebako
BUNDLER_VERSION = "2.4.22"
RUBYGEMS_VERSION = "3.4.22"

# A couple of static Scenario definitions
class ScenarioManagerBase
def initialize
@macos = RUBY_PLATFORM =~ /darwin/ ? true : false
@msys = RUBY_PLATFORM =~ /msys|mingw|cygwin/ ? true : false

@fs_mount_point = if @msys
"A:/__tebako_memfs__"
else
"/__tebako_memfs__"
end
@exe_suffix = @msys ? ".exe" : ""
end

attr_reader :fs_mount_point, :exe_suffix

def macos?
@macos
end

def msys?
@msys
end
end

# Manages packaging scenario based on input files (gemfile, gemspec, etc)
class ScenarioManager
class ScenarioManager < ScenarioManagerBase
def initialize(fs_root, fs_entrance)
super()
@with_gemfile = @with_lockfile = @needs_bundler = false
@bundler_version = BUNDLER_VERSION
initialize_root(fs_root)
initialize_entry_point(fs_entrance || "stub.rb")
end

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

def bundler_reference
@needs_bundler ? "_#{@bundler_version}_" : nil
end

def configure_scenario
@fs_mount_point = if msys?
"A:/__tebako_memfs__"
else
"/__tebako_memfs__"
end

lookup_files
configure_scenario_inner
end

def exe_suffix
@exe_suffix ||= msys? ? ".exe" : ""
end

def macos?
@macos ||= RUBY_PLATFORM =~ /darwin/ ? true : false
end

def msys?
@msys ||= RUBY_PLATFORM =~ /msys|mingw|cygwin/ ? true : false
end

private

def configure_scenario_inner
case @gs_length
when 0
configure_scenario_no_gemspec
Expand All @@ -89,6 +92,8 @@ def configure_scenario_inner
end
end

private

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

Expand Down
6 changes: 2 additions & 4 deletions spec/deploy_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,7 @@
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)
allow(deploy_helper).to receive(:configure_scenario)
deploy_helper.configure(ruby_ver, cwd)
end

Expand Down Expand Up @@ -338,8 +337,7 @@
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)
allow(deploy_helper).to receive(:configure_scenario)
deploy_helper.configure(ruby_ver, cwd)
end

Expand Down
104 changes: 104 additions & 0 deletions spec/scenario_manager_base_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# frozen_string_literal: true

# Copyright (c) 2024-2025 [Ribose Inc](https://www.ribose.com).
# All rights reserved.
# This file is a part of tebako
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

require "tmpdir"
require_relative "../lib/tebako/scenario_manager"

# rubocop:disable Metrics/BlockLength
RSpec.describe Tebako::ScenarioManagerBase do
describe "#initialize" do
context "on msys platform" do
before do
stub_const("RUBY_PLATFORM", "msys")
end

it "sets correct fs_mount_point and exe_suffix" do
manager = described_class.new
expect(manager.fs_mount_point).to eq("A:/__tebako_memfs__")
expect(manager.exe_suffix).to eq(".exe")
end
end

context "on non-msys platform" do
before do
stub_const("RUBY_PLATFORM", "linux")
end

it "sets correct fs_mount_point and exe_suffix" do
manager = described_class.new
expect(manager.fs_mount_point).to eq("/__tebako_memfs__")
expect(manager.exe_suffix).to eq("")
end
end
end

describe "#macos?" do
context "on macos platform" do
before do
stub_const("RUBY_PLATFORM", "darwin")
end

it "returns true" do
expect(described_class.new.macos?).to be true
end
end

context "on non-macos platform" do
before do
stub_const("RUBY_PLATFORM", "linux")
end

it "returns false" do
expect(described_class.new.macos?).to be false
end
end
end

describe "#msys?" do
context "on msys platform" do
before do
stub_const("RUBY_PLATFORM", "msys")
end

it "returns true" do
expect(described_class.new.msys?).to be true
end
end

context "on non-msys platform" do
before do
stub_const("RUBY_PLATFORM", "darwin")
end

it "returns false" do
expect(described_class.new.msys?).to be false
end
end
end
end

# rubocop:enable Metrics/BlockLength
Loading

0 comments on commit a431af3

Please sign in to comment.