Merge pull request #10285 from nandahkrishna/refactor-utils-bump
Refactor `brew bump`
This commit is contained in:
commit
97d56d122e
@ -2,6 +2,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cli/parser"
|
||||
require "livecheck/livecheck"
|
||||
|
||||
module Homebrew
|
||||
extend T::Sig
|
||||
@ -17,8 +18,6 @@ module Homebrew
|
||||
EOS
|
||||
flag "--limit=",
|
||||
description: "Limit number of package results returned."
|
||||
switch :verbose
|
||||
switch :debug
|
||||
|
||||
named_args :formula
|
||||
end
|
||||
@ -27,64 +26,107 @@ module Homebrew
|
||||
def bump
|
||||
args = bump_args.parse
|
||||
|
||||
requested_formulae = args.named.to_formulae.map(&:name) if args.named.to_formulae.present?
|
||||
|
||||
requested_formulae = args.named.to_formulae.presence
|
||||
requested_limit = args.limit.to_i if args.limit.present?
|
||||
|
||||
repology_data = if requested_formulae
|
||||
response = {}
|
||||
requested_formulae.each do |formula|
|
||||
raise FormulaUnavailableError, formula unless validate_formula(formula)
|
||||
|
||||
package_data = Repology.single_package_query(formula)
|
||||
response[package_data.keys.first] = package_data.values.first if package_data
|
||||
end
|
||||
|
||||
response
|
||||
else
|
||||
Repology.parse_api_response(requested_limit)
|
||||
end
|
||||
|
||||
validated_formulae = {}
|
||||
|
||||
validated_formulae = Repology.validate_and_format_packages(repology_data, requested_limit) if repology_data
|
||||
|
||||
if requested_formulae
|
||||
repology_excluded_formulae = requested_formulae.reject do |formula|
|
||||
repology_data[formula]
|
||||
Livecheck.load_other_tap_strategies(requested_formulae)
|
||||
|
||||
requested_formulae.each_with_index do |formula, i|
|
||||
puts if i.positive?
|
||||
|
||||
if formula.head_only?
|
||||
ohai formula.name
|
||||
puts "Formula is HEAD-only."
|
||||
next
|
||||
end
|
||||
|
||||
formulae = {}
|
||||
repology_excluded_formulae.each do |formula|
|
||||
formulae[formula] = Repology.format_package(formula, nil)
|
||||
current_version = formula.stable.version.to_s
|
||||
|
||||
package_data = Repology.single_package_query(formula.name)
|
||||
repology_latest = if package_data.present?
|
||||
Repology.latest_version(package_data.values.first)
|
||||
else
|
||||
"not found"
|
||||
end
|
||||
|
||||
formulae.each { |formula, data| validated_formulae[formula] = data }
|
||||
livecheck_latest = livecheck_result(formula)
|
||||
pull_requests = retrieve_pull_requests(formula)
|
||||
display(formula, current_version, repology_latest, livecheck_latest, pull_requests)
|
||||
end
|
||||
else
|
||||
outdated_packages = Repology.parse_api_response(requested_limit)
|
||||
outdated_packages.each_with_index do |(_name, repositories), i|
|
||||
puts if i.positive?
|
||||
|
||||
homebrew_repo = repositories.find do |repo|
|
||||
repo["repo"] == "homebrew"
|
||||
end
|
||||
|
||||
display(validated_formulae)
|
||||
end
|
||||
next if homebrew_repo.blank?
|
||||
|
||||
def validate_formula(formula_name)
|
||||
Formula[formula_name]
|
||||
formula = begin
|
||||
Formula[homebrew_repo["srcname"]]
|
||||
rescue
|
||||
nil
|
||||
next
|
||||
end
|
||||
|
||||
def up_to_date?(package)
|
||||
package &&
|
||||
package[:current_formula_version] == package[:repology_latest_version] &&
|
||||
package[:current_formula_version] == package[:livecheck_latest_version]
|
||||
current_version = formula.stable.version.to_s
|
||||
repology_latest = Repology.latest_version(repositories)
|
||||
livecheck_latest = livecheck_result(formula)
|
||||
pull_requests = retrieve_pull_requests(formula)
|
||||
display(formula, current_version, repology_latest, livecheck_latest, pull_requests)
|
||||
|
||||
break if requested_limit && i >= requested_limit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def livecheck_result(formula)
|
||||
skip_result = Livecheck::SkipConditions.skip_information(formula)
|
||||
if skip_result.present?
|
||||
return "#{skip_result[:status]}#{" - #{skip_result[:messages].join(", ")}" if skip_result[:messages].present?}"
|
||||
end
|
||||
|
||||
version_info = Livecheck.latest_version(
|
||||
formula,
|
||||
json: true, full_name: false, verbose: false, debug: false,
|
||||
)
|
||||
latest = version_info[:latest] if version_info.present?
|
||||
|
||||
return "unable to get versions" if latest.blank?
|
||||
|
||||
latest.to_s
|
||||
end
|
||||
|
||||
def retrieve_pull_requests(formula)
|
||||
pull_requests = GitHub.fetch_pull_requests(formula.name, formula.tap&.full_name, state: "open")
|
||||
if pull_requests.try(:any?)
|
||||
pull_requests = pull_requests.map { |pr| "#{pr["title"]} (#{Formatter.url(pr["html_url"])})" }.join(", ")
|
||||
end
|
||||
|
||||
return "none" if pull_requests.blank?
|
||||
|
||||
pull_requests
|
||||
end
|
||||
|
||||
def up_to_date?(current_version, repology_latest, livecheck_latest)
|
||||
current_version == repology_latest &&
|
||||
current_version == livecheck_latest
|
||||
end
|
||||
|
||||
def display(formula, current_version, repology_latest, livecheck_latest, pull_requests)
|
||||
title = if current_version == repology_latest &&
|
||||
current_version == livecheck_latest
|
||||
"#{formula} is up to date!"
|
||||
else
|
||||
formula.name
|
||||
end
|
||||
|
||||
def display(formulae)
|
||||
formulae.each do |formula, package_details|
|
||||
title = (up_to_date?(package_details) ? "#{formula} is up to date!" : formula).to_s
|
||||
ohai title
|
||||
puts "Current formula version: #{package_details[:current_formula_version]}"
|
||||
puts "Latest Repology version: #{package_details[:repology_latest_version]}"
|
||||
puts "Latest livecheck version: #{package_details[:livecheck_latest_version]}"
|
||||
puts "Open pull requests: #{package_details[:open_pull_requests]}"
|
||||
end
|
||||
puts "Current formula version: #{current_version}"
|
||||
puts "Latest Repology version: #{repology_latest}"
|
||||
puts "Latest livecheck version: #{livecheck_latest}"
|
||||
puts "Open pull requests: #{pull_requests}"
|
||||
end
|
||||
end
|
||||
|
@ -60,6 +60,27 @@ module Homebrew
|
||||
@livecheck_strategy_names.freeze
|
||||
end
|
||||
|
||||
# Uses `formulae_and_casks_to_check` to identify taps in use other than
|
||||
# homebrew/core and homebrew/cask and loads strategies from them.
|
||||
sig { params(formulae_and_casks_to_check: T::Enumerable[T.any(Formula, Cask::Cask)]).void }
|
||||
def load_other_tap_strategies(formulae_and_casks_to_check)
|
||||
other_taps = {}
|
||||
formulae_and_casks_to_check.each do |formula_or_cask|
|
||||
next if formula_or_cask.tap.blank?
|
||||
next if formula_or_cask.tap.name == CoreTap.instance.name
|
||||
next if formula_or_cask.tap.name == "homebrew/cask"
|
||||
next if other_taps[formula_or_cask.tap.name]
|
||||
|
||||
other_taps[formula_or_cask.tap.name] = formula_or_cask.tap
|
||||
end
|
||||
other_taps = other_taps.sort.to_h
|
||||
|
||||
other_taps.each_value do |tap|
|
||||
tap_strategy_path = "#{tap.path}/livecheck/strategy"
|
||||
Dir["#{tap_strategy_path}/*.rb"].sort.each(&method(:require)) if Dir.exist?(tap_strategy_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Executes the livecheck logic for each formula/cask in the
|
||||
# `formulae_and_casks_to_check` array and prints the results.
|
||||
sig {
|
||||
@ -77,22 +98,7 @@ module Homebrew
|
||||
formulae_and_casks_to_check,
|
||||
full_name: false, json: false, newer_only: false, debug: false, quiet: false, verbose: false
|
||||
)
|
||||
# Identify any non-homebrew/core taps in use for current formulae
|
||||
non_core_taps = {}
|
||||
formulae_and_casks_to_check.each do |formula_or_cask|
|
||||
next if formula_or_cask.tap.blank?
|
||||
next if formula_or_cask.tap.name == CoreTap.instance.name
|
||||
next if non_core_taps[formula_or_cask.tap.name]
|
||||
|
||||
non_core_taps[formula_or_cask.tap.name] = formula_or_cask.tap
|
||||
end
|
||||
non_core_taps = non_core_taps.sort.to_h
|
||||
|
||||
# Load additional Strategy files from taps
|
||||
non_core_taps.each_value do |tap|
|
||||
tap_strategy_path = "#{tap.path}/livecheck/strategy"
|
||||
Dir["#{tap_strategy_path}/*.rb"].sort.each(&method(:require)) if Dir.exist?(tap_strategy_path)
|
||||
end
|
||||
load_other_tap_strategies(formulae_and_casks_to_check)
|
||||
|
||||
has_a_newer_upstream_version = T.let(false, T::Boolean)
|
||||
|
||||
@ -452,12 +458,6 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
# Skip Gists until/unless we create a method of identifying revisions
|
||||
if original_url.include?("gist.github.com")
|
||||
odebug "Skipping: GitHub Gists are not supported"
|
||||
next
|
||||
end
|
||||
|
||||
# Only preprocess the URL when it's appropriate
|
||||
url = if STRATEGY_SYMBOLS_TO_SKIP_PREPROCESS_URL.include?(livecheck_strategy)
|
||||
original_url
|
||||
|
@ -1,39 +0,0 @@
|
||||
# typed: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "utils/livecheck_formula"
|
||||
require "formula_installer"
|
||||
|
||||
describe LivecheckFormula do
|
||||
describe "init" do
|
||||
let(:f) { formula { url "foo-1.0" } }
|
||||
let(:options) { FormulaInstaller.new(f).display_options(f) }
|
||||
let(:action) { "#{f.full_name} #{options}".strip }
|
||||
|
||||
it "runs livecheck command for Formula" do
|
||||
formatted_response = described_class.init(action)
|
||||
|
||||
expect(formatted_response).not_to be_nil
|
||||
expect(formatted_response).to be_a(Hash)
|
||||
expect(formatted_response.size).not_to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse_livecheck_response" do
|
||||
it "returns a hash of Formula version data" do
|
||||
example_raw_command_response = "aacgain : 7834 ==> 1.8"
|
||||
formatted_response = described_class.parse_livecheck_response(example_raw_command_response)
|
||||
|
||||
expect(formatted_response).not_to be_nil
|
||||
expect(formatted_response).to be_a(Hash)
|
||||
|
||||
expect(formatted_response).to include(:name)
|
||||
expect(formatted_response).to include(:formula_version)
|
||||
expect(formatted_response).to include(:livecheck_version)
|
||||
|
||||
expect(formatted_response[:name]).to eq("aacgain")
|
||||
expect(formatted_response[:formula_version]).to eq("7834")
|
||||
expect(formatted_response[:livecheck_version]).to eq("1.8")
|
||||
end
|
||||
end
|
||||
end
|
@ -4,12 +4,6 @@
|
||||
require "utils/repology"
|
||||
|
||||
describe Repology do
|
||||
describe "formula_data" do
|
||||
it "returns nil for invalid Homebrew Formula" do
|
||||
expect(described_class.formula_data("invalidName")).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "single_package_query", :needs_network do
|
||||
it "returns nil for non-existent package" do
|
||||
response = described_class.single_package_query("invalidName")
|
||||
|
@ -11,7 +11,6 @@ require "utils/git_repository"
|
||||
require "utils/github"
|
||||
require "utils/inreplace"
|
||||
require "utils/link"
|
||||
require "utils/livecheck_formula"
|
||||
require "utils/popen"
|
||||
require "utils/repology"
|
||||
require "utils/svn"
|
||||
|
@ -1,35 +0,0 @@
|
||||
# typed: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "context"
|
||||
|
||||
# Helper module for parsing output of `brew livecheck`.
|
||||
#
|
||||
# @api private
|
||||
module LivecheckFormula
|
||||
extend Context
|
||||
|
||||
module_function
|
||||
|
||||
def init(formula)
|
||||
ohai "Checking livecheck formula: #{formula}" if verbose?
|
||||
|
||||
response = Utils.popen_read(HOMEBREW_BREW_FILE, "livecheck", formula, "--quiet").chomp
|
||||
|
||||
parse_livecheck_response(response)
|
||||
end
|
||||
|
||||
def parse_livecheck_response(response)
|
||||
# e.g response => aacgain : 7834 ==> 1.8
|
||||
output = response.delete(" ").split(/:|==>/)
|
||||
|
||||
# e.g. ["openclonk", "7.0", "8.1"]
|
||||
package_name, brew_version, latest_version = output
|
||||
|
||||
{
|
||||
name: package_name,
|
||||
formula_version: brew_version,
|
||||
livecheck_version: latest_version,
|
||||
}
|
||||
end
|
||||
end
|
@ -21,16 +21,17 @@ module Repology
|
||||
end
|
||||
|
||||
def single_package_query(name)
|
||||
url = "https://repology.org/api/v1/project/#{name}"
|
||||
url = "https://repology.org/tools/project-by?repo=homebrew&" \
|
||||
"name_type=srcname&target_page=api_v1_project&name=#{name}"
|
||||
|
||||
output, _errors, _status = curl_output(url.to_s)
|
||||
output, _errors, _status = curl_output("--location", url.to_s)
|
||||
|
||||
begin
|
||||
data = JSON.parse(output)
|
||||
|
||||
homebrew = data.select do |repo|
|
||||
repo["repo"] == "homebrew"
|
||||
{ name => data }
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
homebrew.empty? ? nil : { name => data }
|
||||
end
|
||||
|
||||
def parse_api_response(limit = nil)
|
||||
@ -58,63 +59,23 @@ module Repology
|
||||
outdated_packages
|
||||
end
|
||||
|
||||
def validate_and_format_packages(outdated_repology_packages, limit)
|
||||
if outdated_repology_packages.size > 10 && (limit.blank? || limit > 10)
|
||||
ohai "Verifying outdated repology packages"
|
||||
def latest_version(repositories)
|
||||
# The status is "unique" when the package is present only in Homebrew, so
|
||||
# Repology has no way of knowing if the package is up-to-date.
|
||||
is_unique = repositories.find do |repo|
|
||||
repo["status"] == "unique"
|
||||
end.present?
|
||||
|
||||
return "present only in Homebrew" if is_unique
|
||||
|
||||
latest_version = repositories.find do |repo|
|
||||
repo["status"] == "newest"
|
||||
end
|
||||
|
||||
packages = {}
|
||||
# Repology cannot identify "newest" versions for packages without a version
|
||||
# scheme
|
||||
return "no latest version" if latest_version.blank?
|
||||
|
||||
outdated_repology_packages.each do |_name, repositories|
|
||||
repology_homebrew_repo = repositories.find do |repo|
|
||||
repo["repo"] == "homebrew"
|
||||
end
|
||||
|
||||
next if repology_homebrew_repo.blank?
|
||||
|
||||
latest_version = repositories.find { |repo| repo["status"] == "newest" }
|
||||
|
||||
next if latest_version.blank?
|
||||
|
||||
latest_version = latest_version["version"]
|
||||
srcname = repology_homebrew_repo["srcname"]
|
||||
package_details = format_package(srcname, latest_version)
|
||||
packages[srcname] = package_details unless package_details.nil?
|
||||
|
||||
break if limit && packages.size >= limit
|
||||
end
|
||||
|
||||
packages
|
||||
end
|
||||
|
||||
def format_package(package_name, latest_version)
|
||||
formula = formula_data(package_name)
|
||||
|
||||
return if formula.blank?
|
||||
|
||||
formula_name = formula.to_s
|
||||
tap_full_name = formula.tap&.full_name
|
||||
current_version = formula.version.to_s
|
||||
livecheck_response = LivecheckFormula.init(package_name)
|
||||
pull_requests = GitHub.fetch_pull_requests(formula_name, tap_full_name, state: "open")
|
||||
|
||||
if pull_requests.try(:any?)
|
||||
pull_requests = pull_requests.map { |pr| "#{pr["title"]} (#{Formatter.url(pr["html_url"])})" }.join(", ")
|
||||
end
|
||||
|
||||
pull_requests = "none" if pull_requests.blank?
|
||||
|
||||
{
|
||||
repology_latest_version: latest_version || "not found",
|
||||
current_formula_version: current_version.to_s,
|
||||
livecheck_latest_version: livecheck_response[:livecheck_version] || "not found",
|
||||
open_pull_requests: pull_requests,
|
||||
}
|
||||
end
|
||||
|
||||
def formula_data(package_name)
|
||||
Formula[package_name]
|
||||
rescue
|
||||
nil
|
||||
latest_version["version"]
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user