diff --git a/Library/Homebrew/dev-cmd/create.rb b/Library/Homebrew/dev-cmd/create.rb index e3c4f660f8..b74be43d40 100644 --- a/Library/Homebrew/dev-cmd/create.rb +++ b/Library/Homebrew/dev-cmd/create.rb @@ -184,16 +184,15 @@ module Homebrew end fc = FormulaCreator.new( - args.set_name, - args.set_version, - tap: args.tap, url: args.named.fetch(0), + name: args.set_name, + version: args.set_version, + tap: args.tap, mode:, license: args.set_license, fetch: !args.no_fetch?, head: args.HEAD?, ) - fc.parse_url # ask for confirmation if name wasn't passed explicitly if args.set_name.blank? print "Formula name [#{fc.name}]: " @@ -205,7 +204,7 @@ module Homebrew # Check for disallowed formula, or names that shadow aliases, # unless --force is specified. unless args.force? - if (reason = MissingFormula.disallowed_reason(fc.name)) + if (reason = MissingFormula.disallowed_reason(T.must(fc.name))) odie <<~EOS The formula '#{fc.name}' is not allowed to be created. #{reason} @@ -229,7 +228,7 @@ module Homebrew formula = Homebrew.with_no_api_env do CoreTap.instance.clear_cache - Formula[fc.name] + Formula[T.must(fc.name)] end PyPI.update_python_resources! formula, ignore_non_pypi_packages: true if args.python? diff --git a/Library/Homebrew/formula_creator.rb b/Library/Homebrew/formula_creator.rb index d5c66228bd..399ebaaec5 100644 --- a/Library/Homebrew/formula_creator.rb +++ b/Library/Homebrew/formula_creator.rb @@ -7,21 +7,30 @@ require "erb" module Homebrew # Class for generating a formula from a template. class FormulaCreator + sig { returns(T.nilable(String)) } attr_accessor :name + sig { returns(T.nilable(Version)) } + attr_reader :version + + sig { returns(T::Boolean) } + attr_reader :head + sig { - params(name: T.nilable(String), version: T.nilable(String), tap: T.nilable(String), url: String, + params(url: String, name: T.nilable(String), version: T.nilable(String), tap: T.nilable(String), mode: T.nilable(Symbol), license: T.nilable(String), fetch: T::Boolean, head: T::Boolean).void } - def initialize(name, version, tap:, url:, mode:, license:, fetch:, head:) - @name = name - @version = Version.new(version) if version - @tap = Tap.fetch(tap || "homebrew/core") + def initialize(url:, name: nil, version: nil, tap: nil, mode: nil, license: nil, fetch: false, head: false) @url = url - @mode = mode - @license = license + @name = name.presence + @version = T.let(Version.new(version), T.nilable(Version)) if version.present? + @tap = T.let(Tap.fetch(tap.presence || "homebrew/core"), Tap) + @mode = T.let(mode.presence, T.nilable(Symbol)) + @license = T.let(license.presence, T.nilable(String)) @fetch = fetch @head = head + + parse_url end sig { void } @@ -29,28 +38,27 @@ module Homebrew raise TapUnavailableError, @tap.name unless @tap.installed? end - sig { params(url: String).returns(T.nilable(String)) } - def self.name_from_url(url) - stem = Pathname.new(url).stem - # special cases first - if stem.start_with? "index.cgi" - # gitweb URLs e.g. http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary - stem.rpartition("=").last - elsif url =~ %r{github\.com/\S+/(\S+)/(archive|releases)/} - # e.g. https://github.com/stella-emu/stella/releases/download/6.7/stella-6.7-src.tar.xz - Regexp.last_match(1) - else - # e.g. http://digit-labs.org/files/tools/synscan/releases/synscan-5.02.tar.gz - pathver = Version.parse(stem).to_s - stem.sub(/[-_.]?#{Regexp.escape(pathver)}$/, "") - end - end - sig { void } def parse_url - @name = FormulaCreator.name_from_url(@url) if @name.blank? - odebug "name_from_url: #{@name}" - @version = Version.detect(@url) if @version.nil? + @name ||= begin + stem = Pathname.new(@url).stem + name = if stem.start_with? "index.cgi" + # special cases first + # gitweb URLs e.g. http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary + stem.rpartition("=").last + elsif @url =~ %r{github\.com/\S+/(\S+)/(archive|releases)/} + # e.g. https://github.com/stella-emu/stella/releases/download/6.7/stella-6.7-src.tar.xz + Regexp.last_match(1) + else + # e.g. http://digit-labs.org/files/tools/synscan/releases/synscan-5.02.tar.gz + pathver = Version.parse(stem).to_s + stem.sub(/[-_.]?#{Regexp.escape(pathver)}$/, "") + end + odebug "name from url: #{name}" + name + end + + @version ||= Version.detect(@url) case @url when %r{github\.com/(\S+)/(\S+)\.git} @@ -91,7 +99,7 @@ module Homebrew raise "Downloaded URL is not archive" end - @sha256 = filepath.sha256 + @sha256 = T.let(filepath.sha256, T.nilable(String)) end if @github diff --git a/Library/Homebrew/test/formula_creator_spec.rb b/Library/Homebrew/test/formula_creator_spec.rb index a708686406..772a123d05 100644 --- a/Library/Homebrew/test/formula_creator_spec.rb +++ b/Library/Homebrew/test/formula_creator_spec.rb @@ -3,28 +3,52 @@ require "formula_creator" RSpec.describe Homebrew::FormulaCreator do - it "gets name from GitHub archive URL" do - t = described_class.name_from_url("https://github.com/abitrolly/lapce/archive/v0.3.0.tar.gz") - expect(t).to eq("lapce") - end + describe ".new" do + tests = { + "generic tarball URL": { + url: "http://digit-labs.org/files/tools/synscan/releases/synscan-5.02.tar.gz", + expected: { + name: "synscan", + version: "5.02", + }, + }, + "gitweb URL": { + url: "http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary", + expected: { + name: "libzipper", + }, + }, + "GitHub repo URL": { + url: "https://github.com/abitrolly/lapce.git", + expected: { + name: "lapce", + head: true, + }, + }, + "GitHub archive URL": { + url: "https://github.com/abitrolly/lapce/archive/v0.3.0.tar.gz", + expected: { + name: "lapce", + version: "0.3.0", + }, + }, + "GitHub download URL": { + url: "https://github.com/stella-emu/stella/releases/download/6.7/stella-6.7-src.tar.xz", + expected: { + name: "stella", + version: "6.7", + }, + }, + } - it "gets name from gitweb URL" do - t = described_class.name_from_url("http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary") - expect(t).to eq("libzipper") - end - - it "gets name from GitHub repo URL" do - t = described_class.name_from_url("https://github.com/abitrolly/lapce.git") - expect(t).to eq("lapce") - end - - it "gets name from GitHub download URL" do - t = described_class.name_from_url("https://github.com/stella-emu/stella/releases/download/6.7/stella-6.7-src.tar.xz") - expect(t).to eq("stella") - end - - it "gets name from generic tarball URL" do - t = described_class.name_from_url("http://digit-labs.org/files/tools/synscan/releases/synscan-5.02.tar.gz") - expect(t).to eq("synscan") + tests.each do |description, test| + it "parses #{description}" do + fc = described_class.new(url: test.fetch(:url)) + ex = test.fetch(:expected) + expect(fc.name).to eq(ex[:name]) if ex.key?(:name) + expect(fc.version).to eq(ex[:version]) if ex.key?(:version) + expect(fc.head).to eq(ex[:head]) if ex.key?(:head) + end + end end end