| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  | # typed: true | 
					
						
							|  |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module Homebrew | 
					
						
							| 
									
										
										
										
											2020-11-18 10:25:12 +01:00
										 |  |  |   # Auditor for checking common violations in {Tap}s. | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  |   # | 
					
						
							|  |  |  |   # @api private | 
					
						
							|  |  |  |   class TapAuditor | 
					
						
							|  |  |  |     extend T::Sig | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 15:27:23 +08:00
										 |  |  |     attr_reader :name, :path, :formula_names, :formula_aliases, :cask_tokens, | 
					
						
							|  |  |  |                 :tap_audit_exceptions, :tap_style_exceptions, :tap_pypi_formula_mappings, :problems | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 22:20:34 +11:00
										 |  |  |     sig { params(tap: Tap, strict: T.nilable(T::Boolean)).void } | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  |     def initialize(tap, strict:) | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |       @name                      = tap.name | 
					
						
							|  |  |  |       @path                      = tap.path | 
					
						
							| 
									
										
										
										
											2020-12-21 12:53:12 -05:00
										 |  |  |       @cask_tokens               = tap.cask_tokens | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |       @tap_audit_exceptions      = tap.audit_exceptions | 
					
						
							| 
									
										
										
										
											2020-11-27 01:23:07 -05:00
										 |  |  |       @tap_style_exceptions      = tap.style_exceptions | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |       @tap_pypi_formula_mappings = tap.pypi_formula_mappings | 
					
						
							|  |  |  |       @problems                  = [] | 
					
						
							| 
									
										
										
										
											2021-08-20 02:35:17 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-18 15:27:23 +08:00
										 |  |  |       @formula_aliases = tap.aliases | 
					
						
							| 
									
										
										
										
											2021-08-20 02:35:17 -04:00
										 |  |  |       @formula_names = tap.formula_names.map do |formula_name| | 
					
						
							|  |  |  |         formula_name.split("/").last | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sig { void } | 
					
						
							|  |  |  |     def audit | 
					
						
							|  |  |  |       audit_json_files | 
					
						
							| 
									
										
										
										
											2020-11-20 00:55:21 -05:00
										 |  |  |       audit_tap_formula_lists | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sig { void } | 
					
						
							|  |  |  |     def audit_json_files | 
					
						
							|  |  |  |       json_patterns = Tap::HOMEBREW_TAP_JSON_FILES.map { |pattern| @path/pattern } | 
					
						
							|  |  |  |       Pathname.glob(json_patterns).each do |file| | 
					
						
							|  |  |  |         JSON.parse file.read | 
					
						
							|  |  |  |       rescue JSON::ParserError | 
					
						
							|  |  |  |         problem "#{file.to_s.delete_prefix("#{@path}/")} contains invalid JSON" | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sig { void } | 
					
						
							| 
									
										
										
										
											2020-11-20 00:55:21 -05:00
										 |  |  |     def audit_tap_formula_lists | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |       check_formula_list_directory "audit_exceptions", @tap_audit_exceptions | 
					
						
							| 
									
										
										
										
											2020-11-27 01:23:07 -05:00
										 |  |  |       check_formula_list_directory "style_exceptions", @tap_style_exceptions | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |       check_formula_list "pypi_formula_mappings", @tap_pypi_formula_mappings | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |     sig { params(message: String).void } | 
					
						
							|  |  |  |     def problem(message) | 
					
						
							|  |  |  |       @problems << ({ message: message, location: nil }) | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |     private | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |     sig { params(list_file: String, list: T.untyped).void } | 
					
						
							|  |  |  |     def check_formula_list(list_file, list) | 
					
						
							|  |  |  |       unless [Hash, Array].include? list.class | 
					
						
							|  |  |  |         problem <<~EOS | 
					
						
							|  |  |  |           #{list_file}.json should contain a JSON array | 
					
						
							|  |  |  |           of formula names or a JSON object mapping formula names to values | 
					
						
							|  |  |  |         EOS | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-21 12:53:12 -05:00
										 |  |  |       list = list.keys if list.is_a? Hash | 
					
						
							| 
									
										
										
										
											2020-12-22 10:51:29 -05:00
										 |  |  |       invalid_formulae_casks = list.select do |formula_or_cask_name| | 
					
						
							| 
									
										
										
										
											2022-08-18 15:27:23 +08:00
										 |  |  |         formula_names.exclude?(formula_or_cask_name) && | 
					
						
							|  |  |  |           formula_aliases.exclude?(formula_or_cask_name) && | 
					
						
							|  |  |  |           cask_tokens.exclude?("#{@name}/#{formula_or_cask_name}") | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 10:51:29 -05:00
										 |  |  |       return if invalid_formulae_casks.empty? | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |       problem <<~EOS | 
					
						
							|  |  |  |         #{list_file}.json references | 
					
						
							| 
									
										
										
										
											2020-12-21 12:53:12 -05:00
										 |  |  |         formulae or casks that are not found in the #{@name} tap. | 
					
						
							| 
									
										
										
										
											2020-12-22 10:51:29 -05:00
										 |  |  |         Invalid formulae or casks: #{invalid_formulae_casks.join(", ")} | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |       EOS | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 18:14:45 -05:00
										 |  |  |     sig { params(directory_name: String, lists: Hash).void } | 
					
						
							|  |  |  |     def check_formula_list_directory(directory_name, lists) | 
					
						
							|  |  |  |       lists.each do |list_name, list| | 
					
						
							|  |  |  |         check_formula_list "#{directory_name}/#{list_name}", list | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-11-18 10:05:23 +01:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |