Merge pull request #20025 from abitrolly/new-detect-latest-release

formula_creator: detect GitHub version from latest release
This commit is contained in:
Mike McQuaid 2025-06-20 07:40:44 +00:00 committed by GitHub
commit e6d4db8d6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 102 additions and 73 deletions

View File

@ -23,31 +23,9 @@ module Homebrew
} }
def initialize(url:, name: nil, version: nil, tap: nil, mode: nil, license: nil, fetch: false, head: false) def initialize(url:, name: nil, version: nil, tap: nil, mode: nil, license: nil, fetch: false, head: false)
@url = url @url = url
@mode = mode
if name.blank? @license = license
stem = Pathname.new(url).stem @fetch = fetch
name = if stem.start_with?("index.cgi") && stem.include?("=")
# 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
T.must(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}"
end
@name = T.let(name, String)
version = if version.present?
Version.new(version)
else
Version.detect(url)
end
@version = T.let(version, Version)
tap = if tap.blank? tap = if tap.blank?
CoreTap.instance CoreTap.instance
@ -56,23 +34,58 @@ module Homebrew
end end
@tap = T.let(tap, Tap) @tap = T.let(tap, Tap)
@mode = T.let(mode.presence, T.nilable(Symbol)) if (match_github = url.match %r{github\.com/(?<user>[^/]+)/(?<repo>[^/]+).*})
@license = T.let(license.presence, T.nilable(String)) user = T.must(match_github[:user])
@fetch = fetch repository = T.must(match_github[:repo])
if repository.end_with?(".git")
case url # e.g. https://github.com/Homebrew/brew.git
when %r{github\.com/(\S+)/(\S+)\.git} repository.delete_suffix!(".git")
head = true head = true
user = Regexp.last_match(1) end
repository = Regexp.last_match(2) odebug "github: #{user} #{repository} head:#{head}"
github = GitHub.repository(user, repository) if fetch if name.blank?
when %r{github\.com/(\S+)/(\S+)/(archive|releases)/} name = repository
user = Regexp.last_match(1) odebug "name from github: #{name}"
repository = Regexp.last_match(2) end
github = GitHub.repository(user, repository) if fetch elsif name.blank?
stem = Pathname.new(url).stem
name = if stem.start_with?("index.cgi") && stem.include?("=")
# special cases first
# gitweb URLs e.g. http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary
stem.rpartition("=").last
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}"
end end
@name = T.let(name, String)
@head = head @head = head
if version.present?
version = Version.new(version)
odebug "version from user: #{version}"
else
version = Version.detect(url)
odebug "version from url: #{version}"
end
if fetch && user && repository
github = GitHub.repository(user, repository)
if version.null? && !head
begin
latest_release = GitHub.get_latest_release(user, repository)
version = Version.new(latest_release.fetch("tag_name"))
odebug "github: version from latest_release: #{version}"
rescue GitHub::API::HTTPNotFoundError
odebug "github: latest_release lookup failed: #{url}"
end
end
end
@github = T.let(github, T.untyped) @github = T.let(github, T.untyped)
@version = T.let(version, Version)
@sha256 = T.let(nil, T.nilable(String)) @sha256 = T.let(nil, T.nilable(String))
@desc = T.let(nil, T.nilable(String)) @desc = T.let(nil, T.nilable(String))

View File

@ -5,55 +5,71 @@ require "formula_creator"
RSpec.describe Homebrew::FormulaCreator do RSpec.describe Homebrew::FormulaCreator do
describe ".new" do describe ".new" do
tests = { tests = {
"generic tarball URL": { "generic tarball URL": {
url: "http://digit-labs.org/files/tools/synscan/releases/synscan-5.02.tar.gz", url: "http://digit-labs.org/files/tools/synscan/releases/synscan-5.02.tar.gz",
name: "synscan", expected_name: "synscan",
version: "5.02", expected_version: "5.02",
}, },
"gitweb URL": { "gitweb URL": {
url: "http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary", url: "http://www.codesrc.com/gitweb/index.cgi?p=libzipper.git;a=summary",
name: "libzipper", expected_name: "libzipper",
}, },
"GitHub repo URL with .git": { "GitHub repository URL with .git": {
url: "https://github.com/Homebrew/brew.git", url: "https://github.com/Homebrew/brew.git",
name: "brew", fetch: true,
head: true, github_user_repository: ["Homebrew", "brew"],
fetch: true, expected_name: "brew",
github_user: "Homebrew", expected_head: true,
github_repo: "brew",
}, },
"GitHub archive URL": { "GitHub archive URL": {
url: "https://github.com/Homebrew/brew/archive/4.5.7.tar.gz", url: "https://github.com/Homebrew/brew/archive/4.5.7.tar.gz",
name: "brew", fetch: true,
version: "4.5.7", github_user_repository: ["Homebrew", "brew"],
fetch: true, expected_name: "brew",
github_user: "Homebrew", expected_version: "4.5.7",
github_repo: "brew",
}, },
"GitHub releases URL": { "GitHub releases URL": {
url: "https://github.com/stella-emu/stella/releases/download/6.7/stella-6.7-src.tar.xz", url: "https://github.com/stella-emu/stella/releases/download/6.7/stella-6.7-src.tar.xz",
name: "stella", fetch: true,
version: "6.7", github_user_repository: ["stella-emu", "stella"],
fetch: true, expected_name: "stella",
github_user: "stella-emu", expected_version: "6.7",
github_repo: "stella", },
"GitHub latest release": {
url: "https://github.com/buildpacks/pack",
fetch: true,
github_user_repository: ["buildpacks", "pack"],
latest_release: { "tag_name" => "v0.37.0" },
expected_name: "pack",
expected_version: "v0.37.0",
},
"GitHub URL with name override": {
url: "https://github.com/RooVetGit/Roo-Code",
name: "roo",
expected_name: "roo",
}, },
} }
tests.each do |description, test| tests.each do |description, test|
it "parses #{description}" do it "parses #{description}" do
fetch = test.fetch(:fetch, false) fetch = test.fetch(:fetch, false)
allow(GitHub).to receive(:repository).with(test.fetch(:github_user), test.fetch(:github_repo)) if fetch if fetch
github_user_repository = test.fetch(:github_user_repository)
allow(GitHub).to receive(:repository).with(*github_user_repository)
if (latest_release = test[:latest_release])
expect(GitHub).to receive(:get_latest_release).with(*github_user_repository).and_return(latest_release)
end
end
formula_creator = described_class.new(url: test.fetch(:url), fetch:) formula_creator = described_class.new(url: test.fetch(:url), name: test[:name], fetch:)
expect(formula_creator.name).to eq(test.fetch(:name)) expect(formula_creator.name).to eq(test.fetch(:expected_name))
if (version = test[:version]) if (expected_version = test[:expected_version])
expect(formula_creator.version).to eq(version) expect(formula_creator.version).to eq(expected_version)
else else
expect(formula_creator.version).to be_null expect(formula_creator.version).to be_null
end end
expect(formula_creator.head).to eq(test.fetch(:head, false)) expect(formula_creator.head).to eq(test.fetch(:expected_head, false))
end end
end end
end end