| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  | # typed: true # rubocop:todo Sorbet/StrictSigil | 
					
						
							|  |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module Homebrew | 
					
						
							|  |  |  |   module Bundle | 
					
						
							|  |  |  |     class Dsl | 
					
						
							|  |  |  |       class Entry | 
					
						
							|  |  |  |         attr_reader :type, :name, :options | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def initialize(type, name, options = {}) | 
					
						
							|  |  |  |           @type = type | 
					
						
							|  |  |  |           @name = name | 
					
						
							|  |  |  |           @options = options | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def to_s | 
					
						
							|  |  |  |           name | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       attr_reader :entries, :cask_arguments, :input | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def initialize(path) | 
					
						
							|  |  |  |         @path = path | 
					
						
							|  |  |  |         @input = path.read | 
					
						
							|  |  |  |         @entries = [] | 
					
						
							|  |  |  |         @cask_arguments = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         begin | 
					
						
							|  |  |  |           process | 
					
						
							|  |  |  |         # Want to catch all exceptions for e.g. syntax errors. | 
					
						
							|  |  |  |         rescue Exception => e # rubocop:disable Lint/RescueException | 
					
						
							|  |  |  |           error_msg = "Invalid Brewfile: #{e.message}" | 
					
						
							|  |  |  |           raise RuntimeError, error_msg, e.backtrace | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def process | 
					
						
							|  |  |  |         instance_eval(@input, @path.to_s) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def cask_args(args) | 
					
						
							|  |  |  |         raise "cask_args(#{args.inspect}) should be a Hash object" unless args.is_a? Hash | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-09 07:40:37 +01:00
										 |  |  |         @cask_arguments.merge!(args) | 
					
						
							| 
									
										
										
										
											2025-03-18 17:38:37 +00:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def brew(name, options = {}) | 
					
						
							|  |  |  |         raise "name(#{name.inspect}) should be a String object" unless name.is_a? String | 
					
						
							|  |  |  |         raise "options(#{options.inspect}) should be a Hash object" unless options.is_a? Hash | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         name = Homebrew::Bundle::Dsl.sanitize_brew_name(name) | 
					
						
							|  |  |  |         @entries << Entry.new(:brew, name, options) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def cask(name, options = {}) | 
					
						
							|  |  |  |         raise "name(#{name.inspect}) should be a String object" unless name.is_a? String | 
					
						
							|  |  |  |         raise "options(#{options.inspect}) should be a Hash object" unless options.is_a? Hash | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         options[:full_name] = name | 
					
						
							|  |  |  |         name = Homebrew::Bundle::Dsl.sanitize_cask_name(name) | 
					
						
							|  |  |  |         options[:args] = @cask_arguments.merge options.fetch(:args, {}) | 
					
						
							|  |  |  |         @entries << Entry.new(:cask, name, options) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def mas(name, options = {}) | 
					
						
							|  |  |  |         id = options[:id] | 
					
						
							|  |  |  |         raise "name(#{name.inspect}) should be a String object" unless name.is_a? String | 
					
						
							|  |  |  |         raise "options[:id](#{id}) should be an Integer object" unless id.is_a? Integer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @entries << Entry.new(:mas, name, id:) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def whalebrew(name) | 
					
						
							|  |  |  |         raise "name(#{name.inspect}) should be a String object" unless name.is_a? String | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @entries << Entry.new(:whalebrew, name) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def vscode(name) | 
					
						
							|  |  |  |         raise "name(#{name.inspect}) should be a String object" unless name.is_a? String | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @entries << Entry.new(:vscode, name) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def tap(name, clone_target = nil, options = {}) | 
					
						
							|  |  |  |         raise "name(#{name.inspect}) should be a String object" unless name.is_a? String | 
					
						
							|  |  |  |         if clone_target && !clone_target.is_a?(String) | 
					
						
							|  |  |  |           raise "clone_target(#{clone_target.inspect}) should be nil or a String object" | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         options[:clone_target] = clone_target | 
					
						
							|  |  |  |         name = Homebrew::Bundle::Dsl.sanitize_tap_name(name) | 
					
						
							|  |  |  |         @entries << Entry.new(:tap, name, options) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       HOMEBREW_TAP_ARGS_REGEX = %r{^([\w-]+)/(homebrew-)?([\w-]+)$} | 
					
						
							|  |  |  |       HOMEBREW_CORE_FORMULA_REGEX = %r{^homebrew/homebrew/([\w+-.@]+)$}i | 
					
						
							|  |  |  |       HOMEBREW_TAP_FORMULA_REGEX = %r{^([\w-]+)/([\w-]+)/([\w+-.@]+)$} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def self.sanitize_brew_name(name) | 
					
						
							|  |  |  |         name = name.downcase | 
					
						
							|  |  |  |         if name =~ HOMEBREW_CORE_FORMULA_REGEX | 
					
						
							|  |  |  |           Regexp.last_match(1) | 
					
						
							|  |  |  |         elsif name =~ HOMEBREW_TAP_FORMULA_REGEX | 
					
						
							|  |  |  |           user = Regexp.last_match(1) | 
					
						
							|  |  |  |           repo = T.must(Regexp.last_match(2)) | 
					
						
							|  |  |  |           name = Regexp.last_match(3) | 
					
						
							|  |  |  |           "#{user}/#{repo.sub("homebrew-", "")}/#{name}" | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           name | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def self.sanitize_tap_name(name) | 
					
						
							|  |  |  |         name = name.downcase | 
					
						
							|  |  |  |         if name =~ HOMEBREW_TAP_ARGS_REGEX | 
					
						
							|  |  |  |           "#{Regexp.last_match(1)}/#{Regexp.last_match(3)}" | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           name | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def self.sanitize_cask_name(name) | 
					
						
							|  |  |  |         name = name.split("/").last if name.include?("/") | 
					
						
							|  |  |  |         name.downcase | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       def self.pluralize_dependency(installed_count) | 
					
						
							|  |  |  |         (installed_count == 1) ? "dependency" : "dependencies" | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |