kompo 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
3
+ require "open3"
4
4
 
5
5
  module Kompo
6
6
  # Section to get the Cargo path.
@@ -16,15 +16,15 @@ module Kompo
16
16
  class Installed < Taski::Task
17
17
  def run
18
18
  # First check PATH, then fallback to default rustup location
19
- cargo_in_path, = Open3.capture2('which', 'cargo', err: File::NULL)
19
+ cargo_in_path, = Open3.capture2("which", "cargo", err: File::NULL)
20
20
  cargo_in_path = cargo_in_path.chomp
21
21
  @path = if cargo_in_path.empty?
22
- File.expand_path('~/.cargo/bin/cargo')
23
- else
24
- cargo_in_path
25
- end
22
+ File.expand_path("~/.cargo/bin/cargo")
23
+ else
24
+ cargo_in_path
25
+ end
26
26
  puts "Cargo path: #{@path}"
27
- version_output, = Open3.capture2e(@path, '--version')
27
+ version_output, = Open3.capture2e(@path, "--version")
28
28
  puts "Cargo version: #{version_output.chomp}"
29
29
  end
30
30
  end
@@ -32,14 +32,14 @@ module Kompo
32
32
  # Install Cargo via rustup and return the path
33
33
  class Install < Taski::Task
34
34
  def run
35
- puts 'Cargo not found. Installing via rustup...'
35
+ puts "Cargo not found. Installing via rustup..."
36
36
  system("curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y")
37
37
 
38
- @path = File.expand_path('~/.cargo/bin/cargo')
39
- raise 'Failed to install Cargo' unless File.executable?(@path)
38
+ @path = File.expand_path("~/.cargo/bin/cargo")
39
+ raise "Failed to install Cargo" unless File.executable?(@path)
40
40
 
41
41
  puts "Cargo installed at: #{@path}"
42
- version_output, = Open3.capture2e(@path, '--version')
42
+ version_output, = Open3.capture2e(@path, "--version")
43
43
  puts "Cargo version: #{version_output.chomp}"
44
44
  end
45
45
  end
@@ -48,11 +48,11 @@ module Kompo
48
48
 
49
49
  def cargo_installed?
50
50
  # Check if cargo is in PATH
51
- cargo_in_path, = Open3.capture2('which', 'cargo', err: File::NULL)
51
+ cargo_in_path, = Open3.capture2("which", "cargo", err: File::NULL)
52
52
  return true unless cargo_in_path.chomp.empty?
53
53
 
54
54
  # Check default rustup installation location
55
- home_cargo = File.expand_path('~/.cargo/bin/cargo')
55
+ home_cargo = File.expand_path("~/.cargo/bin/cargo")
56
56
  File.executable?(home_cargo)
57
57
  end
58
58
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
3
+ require "open3"
4
4
 
5
5
  module Kompo
6
6
  # Get Ruby standard library paths from installed Ruby
@@ -12,7 +12,7 @@ module Kompo
12
12
  no_stdlib = Taski.args.fetch(:no_stdlib, false)
13
13
  if no_stdlib
14
14
  @paths = []
15
- puts 'Skipping standard library (--no-stdlib)'
15
+ puts "Skipping standard library (--no-stdlib)"
16
16
  return
17
17
  end
18
18
 
@@ -24,9 +24,9 @@ module Kompo
24
24
  # Include the Ruby standard library root directory
25
25
  # This includes bundler and other default gems that are not in $:
26
26
  # Ruby uses "X.Y.0" format for lib/ruby paths (e.g., "3.4.0" not "3.4")
27
- stdlib_root = File.join(ruby_install_dir, 'lib', 'ruby', "#{ruby_major_minor}.0")
27
+ stdlib_root = File.join(ruby_install_dir, "lib", "ruby", "#{ruby_major_minor}.0")
28
28
  # RubyGems needs gemspec files in specifications/ directory
29
- gems_specs_root = File.join(ruby_install_dir, 'lib', 'ruby', 'gems', "#{ruby_major_minor}.0", 'specifications')
29
+ gems_specs_root = File.join(ruby_install_dir, "lib", "ruby", "gems", "#{ruby_major_minor}.0", "specifications")
30
30
 
31
31
  if Dir.exist?(stdlib_root)
32
32
  @paths = [stdlib_root, gems_specs_root].select { |p| Dir.exist?(p) }
@@ -34,7 +34,7 @@ module Kompo
34
34
  puts "Including gem specifications: #{gems_specs_root}" if Dir.exist?(gems_specs_root)
35
35
  else
36
36
  # Fallback to $: paths if stdlib root doesn't exist
37
- output, status = Open3.capture2(ruby, '-e', 'puts $:', err: File::NULL)
37
+ output, status = Open3.capture2(ruby, "-e", "puts $:", err: File::NULL)
38
38
  unless status.success?
39
39
  raise "Failed to get Ruby standard library paths: exit code #{status.exitstatus}, output: #{output}"
40
40
  end
@@ -42,7 +42,7 @@ module Kompo
42
42
  raw_paths = output.split("\n").reject(&:empty?)
43
43
 
44
44
  @paths = raw_paths.map do |path|
45
- next nil unless path.start_with?('/')
45
+ next nil unless path.start_with?("/")
46
46
 
47
47
  if original_ruby_install_dir != ruby_install_dir && path.start_with?(original_ruby_install_dir)
48
48
  path.sub(original_ruby_install_dir, ruby_install_dir)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fileutils'
3
+ require "fileutils"
4
4
 
5
5
  module Kompo
6
6
  # Collect all dependencies for packing.
@@ -20,7 +20,7 @@ module Kompo
20
20
  project_dir = Taski.args.fetch(:project_dir, Taski.env.working_directory) || Taski.env.working_directory
21
21
  output_arg = Taski.args.fetch(:output_dir, Taski.env.working_directory) || Taski.env.working_directory
22
22
 
23
- @deps = group('Collecting dependencies') do
23
+ @deps = group("Collecting dependencies") do
24
24
  collect_dependencies
25
25
  end
26
26
 
@@ -36,33 +36,31 @@ module Kompo
36
36
 
37
37
  @ext_paths = get_unique_ext_paths(@work_dir, @deps.ruby_build_path, @deps.ruby_version, @deps.exts_dir)
38
38
  @enc_files = [
39
- File.join(@deps.ruby_build_path, "ruby-#{@deps.ruby_version}", 'enc/encinit.o'),
40
- File.join(@deps.ruby_build_path, "ruby-#{@deps.ruby_version}", 'enc/libenc.a'),
41
- File.join(@deps.ruby_build_path, "ruby-#{@deps.ruby_version}", 'enc/libtrans.a')
39
+ File.join(@deps.ruby_build_path, "ruby-#{@deps.ruby_version}", "enc/encinit.o"),
40
+ File.join(@deps.ruby_build_path, "ruby-#{@deps.ruby_version}", "enc/libenc.a"),
41
+ File.join(@deps.ruby_build_path, "ruby-#{@deps.ruby_version}", "enc/libtrans.a")
42
42
  ]
43
43
 
44
- puts 'All dependencies collected for packing'
44
+ puts "All dependencies collected for packing"
45
45
  end
46
46
 
47
47
  private
48
48
 
49
49
  def collect_dependencies
50
- # Install dependencies based on platform (Homebrew on macOS, pkg-config check on Linux)
51
- InstallDeps.run
52
50
  deps_lib_paths = InstallDeps.lib_paths
53
51
 
54
52
  ruby_install_dir = InstallRuby.ruby_install_dir
55
53
  ruby_version = InstallRuby.ruby_version
56
54
  ruby_major_minor = InstallRuby.ruby_major_minor
57
55
  ruby_build_path = InstallRuby.ruby_build_path
58
- ruby_lib = File.join(ruby_install_dir, 'lib')
56
+ ruby_lib = File.join(ruby_install_dir, "lib")
59
57
 
60
58
  kompo_lib = KompoVfsPath.path
61
59
  main_c = MakeMainC.path
62
60
  fs_c = MakeFsC.path
63
61
  exts_dir = BuildNativeGem.exts_dir
64
62
 
65
- puts 'Dependencies collected'
63
+ puts "Dependencies collected"
66
64
 
67
65
  Dependencies.new(
68
66
  ruby_install_dir: ruby_install_dir,
@@ -79,19 +77,29 @@ module Kompo
79
77
  end
80
78
 
81
79
  def get_unique_ext_paths(_work_dir, ruby_build_path, ruby_version, exts_dir)
80
+ ruby_build_dir = File.join(ruby_build_path, "ruby-#{ruby_version}")
81
+
82
82
  # Ruby standard extension .o files
83
- paths = Dir.glob(File.join(ruby_build_path, "ruby-#{ruby_version}", 'ext', '**', '*.o'))
83
+ paths = Dir.glob(File.join(ruby_build_dir, "ext", "**", "*.o"))
84
+
85
+ # Ruby bundled gems .o files (Ruby 4.0+)
86
+ # Skip if --no-stdlib is specified (bundled gems are part of stdlib)
87
+ no_stdlib = Taski.args.fetch(:no_stdlib, false)
88
+ unless no_stdlib
89
+ bundled_gems_paths = Dir.glob(File.join(ruby_build_dir, ".bundle", "gems", "*", "ext", "**", "*.o"))
90
+ paths += bundled_gems_paths
91
+ end
84
92
 
85
93
  # Extract extension path (everything after /ext/) for deduplication
86
94
  # e.g., ".../ext/cgi/escape/escape.o" -> "cgi/escape/escape.o"
87
- ruby_ext_keys = paths.map { |p| p.split('/ext/').last }
95
+ ruby_ext_keys = paths.map { |p| p.split("/ext/").last }
88
96
 
89
97
  # Gem extension .o files (excluding duplicates with Ruby std)
90
98
  if exts_dir && Dir.exist?(exts_dir)
91
99
  gem_ext_paths = Dir.glob("#{exts_dir}/**/*.o")
92
- .to_h { |p| [p.split('/ext/').last, p] }
93
- .except(*ruby_ext_keys)
94
- .values
100
+ .to_h { |p| [p.split("/ext/").last, p] }
101
+ .except(*ruby_ext_keys)
102
+ .values
95
103
  paths += gem_ext_paths
96
104
  end
97
105
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fileutils'
3
+ require "fileutils"
4
4
 
5
5
  module Kompo
6
6
  # Copy Gemfile, Gemfile.lock, and gemspec files to working directory if they exist
@@ -11,25 +11,33 @@ module Kompo
11
11
  work_dir = WorkDir.path
12
12
  project_dir = Taski.args.fetch(:project_dir, Taski.env.working_directory) || Taski.env.working_directory
13
13
 
14
- gemfile_path = File.join(project_dir, 'Gemfile')
15
- gemfile_lock_path = File.join(project_dir, 'Gemfile.lock')
14
+ # Skip Gemfile processing if --no-gemfile is specified
15
+ if Taski.args[:no_gemfile]
16
+ puts "Skipping Gemfile (--no-gemfile specified)"
17
+ @gemfile_exists = false
18
+ @gemspec_paths = []
19
+ return
20
+ end
21
+
22
+ gemfile_path = File.join(project_dir, "Gemfile")
23
+ gemfile_lock_path = File.join(project_dir, "Gemfile.lock")
16
24
 
17
25
  @gemfile_exists = File.exist?(gemfile_path)
18
26
  @gemspec_paths = []
19
27
 
20
28
  if @gemfile_exists
21
29
  FileUtils.cp(gemfile_path, work_dir)
22
- puts 'Copied: Gemfile'
30
+ puts "Copied: Gemfile"
23
31
 
24
32
  if File.exist?(gemfile_lock_path)
25
33
  FileUtils.cp(gemfile_lock_path, work_dir)
26
- puts 'Copied: Gemfile.lock'
34
+ puts "Copied: Gemfile.lock"
27
35
  end
28
36
 
29
37
  # Copy gemspec files if Gemfile references gemspec
30
38
  copy_gemspec_if_needed(gemfile_path, project_dir, work_dir)
31
39
  else
32
- puts 'No Gemfile found, skipping'
40
+ puts "No Gemfile found, skipping"
33
41
  end
34
42
  end
35
43
 
@@ -39,8 +47,8 @@ module Kompo
39
47
  work_dir = WorkDir.path
40
48
  return unless work_dir && Dir.exist?(work_dir)
41
49
 
42
- gemfile = File.join(work_dir, 'Gemfile')
43
- gemfile_lock = File.join(work_dir, 'Gemfile.lock')
50
+ gemfile = File.join(work_dir, "Gemfile")
51
+ gemfile_lock = File.join(work_dir, "Gemfile.lock")
44
52
 
45
53
  FileUtils.rm_f(gemfile)
46
54
  FileUtils.rm_f(gemfile_lock)
@@ -50,7 +58,7 @@ module Kompo
50
58
  FileUtils.rm_f(gemspec)
51
59
  end
52
60
 
53
- puts 'Cleaned up Gemfile'
61
+ puts "Cleaned up Gemfile"
54
62
  end
55
63
 
56
64
  private
@@ -62,7 +70,7 @@ module Kompo
62
70
  return unless gemfile_content.match?(/^\s*gemspec\b/)
63
71
 
64
72
  # Copy all .gemspec files from project directory
65
- gemspec_files = Dir.glob(File.join(project_dir, '*.gemspec'))
73
+ gemspec_files = Dir.glob(File.join(project_dir, "*.gemspec"))
66
74
  gemspec_files.each do |gemspec_path|
67
75
  dest_path = File.join(work_dir, File.basename(gemspec_path))
68
76
  FileUtils.cp(gemspec_path, dest_path)
@@ -71,7 +79,7 @@ module Kompo
71
79
  end
72
80
 
73
81
  if gemspec_files.empty?
74
- warn 'Warning: Gemfile contains gemspec directive but no .gemspec files found'
82
+ warn "Warning: Gemfile contains gemspec directive but no .gemspec files found"
75
83
  end
76
84
  end
77
85
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'fileutils'
3
+ require "fileutils"
4
4
 
5
5
  module Kompo
6
6
  # Copy project files (entrypoint and additional files) to working directory
@@ -10,7 +10,7 @@ module Kompo
10
10
  def run
11
11
  work_dir = WorkDir.path
12
12
  project_dir = Taski.args.fetch(:project_dir, Taski.env.working_directory) || Taski.env.working_directory
13
- entrypoint = Taski.args.fetch(:entrypoint, 'main.rb')
13
+ entrypoint = Taski.args.fetch(:entrypoint, "main.rb")
14
14
  files = Taski.args.fetch(:files, [])
15
15
 
16
16
  # Copy entrypoint (preserve relative path structure)
@@ -54,7 +54,7 @@ module Kompo
54
54
 
55
55
  if File.directory?(src)
56
56
  # Special handling for "." - copy directory contents directly to work_dir
57
- if file == '.' || real_src == real_project_dir
57
+ if file == "." || real_src == real_project_dir
58
58
  copy_directory_contents(src, work_dir)
59
59
  @additional_paths << work_dir
60
60
  else
@@ -90,7 +90,7 @@ module Kompo
90
90
  # Only delete if path is inside work_dir
91
91
  FileUtils.rm_rf(path) if expanded_path.start_with?(real_work_dir + File::SEPARATOR) && File.exist?(path)
92
92
  end
93
- puts 'Cleaned up project files'
93
+ puts "Cleaned up project files"
94
94
  end
95
95
 
96
96
  private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
3
+ require "open3"
4
4
 
5
5
  module Kompo
6
6
  # Find native gem extensions that need to be built
@@ -12,22 +12,32 @@ module Kompo
12
12
  def run
13
13
  @extensions = []
14
14
 
15
- # Skip if no Gemfile
16
- unless CopyGemfile.gemfile_exists
17
- puts 'No Gemfile, no native extensions to find'
18
- return
19
- end
20
-
21
15
  ruby_version = InstallRuby.ruby_version
22
16
  ruby_build_path = InstallRuby.ruby_build_path
23
17
  ruby_install_dir = InstallRuby.ruby_install_dir
24
- bundle_ruby_dir = BundleInstall.bundle_ruby_dir
25
18
 
26
19
  # Get Init functions already defined in libruby-static
27
20
  builtin_init_funcs = get_builtin_init_functions(ruby_install_dir)
28
21
 
22
+ # Find bundled gems with native extensions (Ruby 4.0+)
23
+ # Skip if --no-stdlib is specified (bundled gems are part of stdlib)
24
+ no_stdlib = Taski.args.fetch(:no_stdlib, false)
25
+ unless no_stdlib
26
+ bundled_gems_dir = File.join(ruby_build_path, "ruby-#{ruby_version}", ".bundle", "gems")
27
+ find_bundled_gem_extensions(bundled_gems_dir, builtin_init_funcs)
28
+ end
29
+
30
+ # Skip user gems if no Gemfile
31
+ unless CopyGemfile.gemfile_exists
32
+ puts "No Gemfile, skipping user gem native extensions"
33
+ puts "Found #{@extensions.size} native extensions to build"
34
+ return
35
+ end
36
+
37
+ bundle_ruby_dir = BundleInstall.bundle_ruby_dir
38
+
29
39
  # Find all native extensions in installed gems
30
- extconf_files = Dir.glob(File.join(bundle_ruby_dir, 'gems/**/extconf.rb'))
40
+ extconf_files = Dir.glob(File.join(bundle_ruby_dir, "gems/**/extconf.rb"))
31
41
 
32
42
  extconf_files.each do |extconf_path|
33
43
  dir_name = File.dirname(extconf_path)
@@ -42,21 +52,37 @@ module Kompo
42
52
  end
43
53
 
44
54
  # Skip if this extension is part of Ruby standard library
45
- ruby_std_lib = dir_name.split('/').drop_while { |p| p != 'ext' }.join('/')
46
- ruby_ext_objects = Dir.glob(File.join(ruby_build_path, "ruby-#{ruby_version}", 'ext', '**', '*.o'))
55
+ ruby_std_lib = dir_name.split("/").drop_while { |p| p != "ext" }.join("/")
56
+ ruby_ext_objects = Dir.glob(File.join(ruby_build_path, "ruby-#{ruby_version}", "ext", "**", "*.o"))
47
57
  if ruby_ext_objects.any? { |o| o.include?(ruby_std_lib) }
48
58
  puts "skip: #{gem_ext_name} is included in Ruby standard library"
49
59
  next
50
60
  end
51
61
 
52
- cargo_toml = File.join(dir_name, 'Cargo.toml')
62
+ # Check if already added (e.g., as bundled gem)
63
+ # Prefer Gemfile versions over prebuilt bundled gems
64
+ existing = @extensions.find { |e| e[:gem_ext_name] == gem_ext_name }
65
+ if existing
66
+ if existing[:is_prebuilt]
67
+ # Replace prebuilt bundled gem with Gemfile version
68
+ puts "replacing: #{gem_ext_name} prebuilt bundled gem with Gemfile version"
69
+ @extensions.delete(existing)
70
+ else
71
+ # Already added as non-prebuilt, skip
72
+ puts "skip: #{gem_ext_name} is already added"
73
+ next
74
+ end
75
+ end
76
+
77
+ cargo_toml = File.join(dir_name, "Cargo.toml")
53
78
  is_rust = File.exist?(cargo_toml)
54
79
 
55
80
  @extensions << {
56
81
  dir_name: dir_name,
57
82
  gem_ext_name: gem_ext_name,
58
83
  is_rust: is_rust,
59
- cargo_toml: is_rust ? cargo_toml : nil
84
+ cargo_toml: is_rust ? cargo_toml : nil,
85
+ is_prebuilt: false
60
86
  }
61
87
  end
62
88
 
@@ -65,15 +91,52 @@ module Kompo
65
91
 
66
92
  private
67
93
 
94
+ # Find native extensions in Ruby's bundled gems directory (Ruby 4.0+)
95
+ # These are pre-built during Ruby compilation
96
+ def find_bundled_gem_extensions(bundled_gems_dir, builtin_init_funcs)
97
+ return unless Dir.exist?(bundled_gems_dir)
98
+
99
+ extconf_files = Dir.glob(File.join(bundled_gems_dir, "**/extconf.rb"))
100
+
101
+ extconf_files.each do |extconf_path|
102
+ dir_name = File.dirname(extconf_path)
103
+ gem_ext_name = File.basename(dir_name)
104
+
105
+ # Skip if Init function is already defined in libruby-static
106
+ init_func = "Init_#{gem_ext_name}"
107
+ if builtin_init_funcs.include?(init_func)
108
+ puts "skip: #{gem_ext_name} is already built into Ruby (#{init_func} found in libruby-static)"
109
+ next
110
+ end
111
+
112
+ # Verify that .o files exist (pre-built)
113
+ # Search recursively since object files may be in subdirectories
114
+ o_files = Dir.glob(File.join(dir_name, "**", "*.o"))
115
+ if o_files.empty?
116
+ puts "skip: #{gem_ext_name} has no pre-built .o files"
117
+ next
118
+ end
119
+
120
+ puts "Found bundled gem extension: #{gem_ext_name} (pre-built)"
121
+ @extensions << {
122
+ dir_name: dir_name,
123
+ gem_ext_name: gem_ext_name,
124
+ is_rust: false,
125
+ cargo_toml: nil,
126
+ is_prebuilt: true
127
+ }
128
+ end
129
+ end
130
+
68
131
  # Extract Init_ function names from libruby-static using nm
69
132
  def get_builtin_init_functions(ruby_install_dir)
70
- lib_dir = File.join(ruby_install_dir, 'lib')
71
- static_lib = Dir.glob(File.join(lib_dir, 'libruby*-static.a')).first
133
+ lib_dir = File.join(ruby_install_dir, "lib")
134
+ static_lib = Dir.glob(File.join(lib_dir, "libruby*-static.a")).first
72
135
  return Set.new unless static_lib
73
136
 
74
137
  # Use nm to extract defined Init_ symbols (T = text/code section)
75
138
  # Use Open3.capture2 with array form to avoid shell injection
76
- output, status = Open3.capture2('nm', static_lib, err: File::NULL)
139
+ output, status = Open3.capture2("nm", static_lib, err: File::NULL)
77
140
  return Set.new unless status.success?
78
141
 
79
142
  # Filter lines matching " T _?Init_" pattern and extract the symbol name
@@ -82,7 +145,7 @@ module Kompo
82
145
  symbols.map do |line|
83
146
  # Third whitespace-separated field is the symbol name
84
147
  symbol = line.split[2]
85
- symbol&.delete_prefix('_')
148
+ symbol&.delete_prefix("_")
86
149
  end.compact.to_set
87
150
  end
88
151
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
3
+ require "open3"
4
4
 
5
5
  module Kompo
6
6
  # Section to get the Homebrew path.
@@ -9,7 +9,7 @@ module Kompo
9
9
  interfaces :path
10
10
 
11
11
  # Common Homebrew installation paths
12
- COMMON_BREW_PATHS = ['/opt/homebrew/bin/brew', '/usr/local/bin/brew'].freeze
12
+ COMMON_BREW_PATHS = ["/opt/homebrew/bin/brew", "/usr/local/bin/brew"].freeze
13
13
 
14
14
  def impl
15
15
  homebrew_installed? ? Installed : Install
@@ -19,27 +19,27 @@ module Kompo
19
19
  class Installed < Taski::Task
20
20
  def run
21
21
  # First check PATH
22
- brew_in_path, = Open3.capture2('which', 'brew', err: File::NULL)
22
+ brew_in_path, = Open3.capture2("which", "brew", err: File::NULL)
23
23
  brew_in_path = brew_in_path.chomp
24
24
  @path = if brew_in_path.empty?
25
- # Fallback to common installation paths
26
- HomebrewPath::COMMON_BREW_PATHS.find { |p| File.executable?(p) }
27
- else
28
- brew_in_path
29
- end
25
+ # Fallback to common installation paths
26
+ HomebrewPath::COMMON_BREW_PATHS.find { |p| File.executable?(p) }
27
+ else
28
+ brew_in_path
29
+ end
30
30
  puts "Homebrew path: #{@path}"
31
31
  end
32
32
  end
33
33
 
34
34
  # Install Homebrew
35
35
  class Install < Taski::Task
36
- MARKER_FILE = File.expand_path('~/.kompo_installed_homebrew')
36
+ MARKER_FILE = File.expand_path("~/.kompo_installed_homebrew")
37
37
 
38
38
  def run
39
- puts 'Homebrew not found. Installing...'
39
+ puts "Homebrew not found. Installing..."
40
40
  system('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"')
41
41
 
42
- brew_in_path, = Open3.capture2('which', 'brew', err: File::NULL)
42
+ brew_in_path, = Open3.capture2("which", "brew", err: File::NULL)
43
43
  @path = brew_in_path.chomp
44
44
  if @path.empty?
45
45
  # Check common installation paths
@@ -51,7 +51,7 @@ module Kompo
51
51
  end
52
52
  end
53
53
 
54
- raise 'Failed to install Homebrew' if @path.nil? || @path.empty?
54
+ raise "Failed to install Homebrew" if @path.nil? || @path.empty?
55
55
 
56
56
  # Mark that kompo installed Homebrew
57
57
  File.write(MARKER_FILE, @path)
@@ -62,10 +62,10 @@ module Kompo
62
62
  # Only uninstall if kompo installed it
63
63
  return unless File.exist?(MARKER_FILE)
64
64
 
65
- puts 'Uninstalling Homebrew (installed by kompo)...'
65
+ puts "Uninstalling Homebrew (installed by kompo)..."
66
66
  system('NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)"')
67
67
  File.delete(MARKER_FILE) if File.exist?(MARKER_FILE)
68
- puts 'Homebrew uninstalled'
68
+ puts "Homebrew uninstalled"
69
69
  end
70
70
  end
71
71
 
@@ -73,7 +73,7 @@ module Kompo
73
73
 
74
74
  def homebrew_installed?
75
75
  # Check if brew is in PATH
76
- brew_in_path, = Open3.capture2('which', 'brew', err: File::NULL)
76
+ brew_in_path, = Open3.capture2("which", "brew", err: File::NULL)
77
77
  return true unless brew_in_path.chomp.empty?
78
78
 
79
79
  # Check common installation paths