Merge pull request #7834 from MLH-Fellowship/mlh-outdated-packages
compare and display package versions
This commit is contained in:
commit
16107a884e
86
Library/Homebrew/dev-cmd/bump.rb
Normal file
86
Library/Homebrew/dev-cmd/bump.rb
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cli/parser"
|
||||||
|
|
||||||
|
module Homebrew
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def bump_args
|
||||||
|
Homebrew::CLI::Parser.new do
|
||||||
|
usage_banner <<~EOS
|
||||||
|
`bump` [<options>] [<formula>]
|
||||||
|
|
||||||
|
Display out-of-date brew formulae and the latest version available.
|
||||||
|
Also displays whether a pull request has been opened with the URL.
|
||||||
|
EOS
|
||||||
|
flag "--limit=",
|
||||||
|
description: "Limit number of package results returned."
|
||||||
|
switch :verbose
|
||||||
|
switch :debug
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def bump
|
||||||
|
args = bump_args.parse
|
||||||
|
|
||||||
|
requested_formulae = args.formulae.map(&:name) if args.formulae.present?
|
||||||
|
|
||||||
|
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]
|
||||||
|
end
|
||||||
|
|
||||||
|
formulae = {}
|
||||||
|
repology_excluded_formulae.each do |formula|
|
||||||
|
formulae[formula] = Repology.format_package(formula, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
formulae.each { |formula, data| validated_formulae[formula] = data }
|
||||||
|
end
|
||||||
|
|
||||||
|
display(validated_formulae)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_formula(formula_name)
|
||||||
|
Formula[formula_name]
|
||||||
|
rescue
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def up_to_date?(package)
|
||||||
|
package &&
|
||||||
|
package[:current_formula_version] == package[:repology_latest_version] &&
|
||||||
|
package[:current_formula_version] == package[:livecheck_latest_version]
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
30
Library/Homebrew/test/dev-cmd/bump_spec.rb
Normal file
30
Library/Homebrew/test/dev-cmd/bump_spec.rb
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "cmd/shared_examples/args_parse"
|
||||||
|
|
||||||
|
describe "brew bump" do
|
||||||
|
describe "Homebrew.bump_args" do
|
||||||
|
it_behaves_like "parseable arguments"
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "formula", :integration_test do
|
||||||
|
it "returns data for single valid specified formula" do
|
||||||
|
install_test_formula "testball"
|
||||||
|
|
||||||
|
expect { brew "bump", "testball" }
|
||||||
|
.to output.to_stdout
|
||||||
|
.and not_to_output.to_stderr
|
||||||
|
.and be_a_success
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns data for multiple valid specified formula" do
|
||||||
|
install_test_formula "testball"
|
||||||
|
install_test_formula "testball2"
|
||||||
|
|
||||||
|
expect { brew "bump", "testball", "testball2" }
|
||||||
|
.to output.to_stdout
|
||||||
|
.and not_to_output.to_stderr
|
||||||
|
.and be_a_success
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
38
Library/Homebrew/test/utils/livecheck_formula_spec.rb
Normal file
38
Library/Homebrew/test/utils/livecheck_formula_spec.rb
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 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
|
||||||
36
Library/Homebrew/test/utils/repology_spec.rb
Normal file
36
Library/Homebrew/test/utils/repology_spec.rb
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
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" do
|
||||||
|
it "returns nil for non-existent package" do
|
||||||
|
response = described_class.single_package_query("invalidName")
|
||||||
|
|
||||||
|
expect(response).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a hash for existing package" do
|
||||||
|
response = described_class.single_package_query("openclonk")
|
||||||
|
|
||||||
|
expect(response).not_to be_nil
|
||||||
|
expect(response).to be_a(Hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "parse_api_response" do
|
||||||
|
limit = 1
|
||||||
|
response = described_class.parse_api_response(limit)
|
||||||
|
|
||||||
|
it "returns a hash of data" do
|
||||||
|
expect(response).not_to be_nil
|
||||||
|
expect(response).to be_a(Hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -9,7 +9,9 @@ require "utils/git"
|
|||||||
require "utils/github"
|
require "utils/github"
|
||||||
require "utils/inreplace"
|
require "utils/inreplace"
|
||||||
require "utils/link"
|
require "utils/link"
|
||||||
|
require "utils/livecheck_formula"
|
||||||
require "utils/popen"
|
require "utils/popen"
|
||||||
|
require "utils/repology"
|
||||||
require "utils/svn"
|
require "utils/svn"
|
||||||
require "utils/tty"
|
require "utils/tty"
|
||||||
require "tap_constants"
|
require "tap_constants"
|
||||||
|
|||||||
27
Library/Homebrew/utils/livecheck_formula.rb
Normal file
27
Library/Homebrew/utils/livecheck_formula.rb
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module LivecheckFormula
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def init(formula)
|
||||||
|
ohai "Checking livecheck formula: #{formula}" if Homebrew.args.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
|
||||||
115
Library/Homebrew/utils/repology.rb
Normal file
115
Library/Homebrew/utils/repology.rb
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "utils/curl"
|
||||||
|
|
||||||
|
module Repology
|
||||||
|
module_function
|
||||||
|
|
||||||
|
MAX_PAGINATION = 15
|
||||||
|
|
||||||
|
def query_api(last_package_in_response = "")
|
||||||
|
last_package_in_response += "/" if last_package_in_response.present?
|
||||||
|
url = "https://repology.org/api/v1/projects/#{last_package_in_response}?inrepo=homebrew&outdated=1"
|
||||||
|
|
||||||
|
output, _errors, _status = curl_output(url.to_s)
|
||||||
|
JSON.parse(output)
|
||||||
|
end
|
||||||
|
|
||||||
|
def single_package_query(name)
|
||||||
|
url = "https://repology.org/api/v1/project/#{name}"
|
||||||
|
|
||||||
|
output, _errors, _status = curl_output(url.to_s)
|
||||||
|
data = JSON.parse(output)
|
||||||
|
|
||||||
|
homebrew = data.select do |repo|
|
||||||
|
repo["repo"] == "homebrew"
|
||||||
|
end
|
||||||
|
|
||||||
|
homebrew.empty? ? nil : { name => data }
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_api_response(limit = nil)
|
||||||
|
ohai "Querying outdated packages from Repology"
|
||||||
|
|
||||||
|
page_no = 1
|
||||||
|
outdated_packages = {}
|
||||||
|
last_package_index = ""
|
||||||
|
|
||||||
|
while page_no <= MAX_PAGINATION
|
||||||
|
odebug "Paginating Repology API page: #{page_no}"
|
||||||
|
|
||||||
|
response = query_api(last_package_index)
|
||||||
|
response_size = response.size
|
||||||
|
outdated_packages.merge!(response)
|
||||||
|
last_package_index = outdated_packages.size - 1
|
||||||
|
|
||||||
|
page_no += 1
|
||||||
|
break if limit && outdated_packages.size >= limit || response_size <= 1
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "#{outdated_packages.size} outdated #{"package".pluralize(outdated_packages.size)} found"
|
||||||
|
puts
|
||||||
|
|
||||||
|
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"
|
||||||
|
end
|
||||||
|
|
||||||
|
packages = {}
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -13,6 +13,7 @@ abv
|
|||||||
analytics
|
analytics
|
||||||
audit
|
audit
|
||||||
bottle
|
bottle
|
||||||
|
bump
|
||||||
bump-formula-pr
|
bump-formula-pr
|
||||||
bump-revision
|
bump-revision
|
||||||
cask
|
cask
|
||||||
|
|||||||
@ -817,6 +817,14 @@ value, while `--no-rebuild` will remove it.
|
|||||||
* `--root-url`:
|
* `--root-url`:
|
||||||
Use the specified *`URL`* as the root of the bottle's URL instead of Homebrew's default.
|
Use the specified *`URL`* as the root of the bottle's URL instead of Homebrew's default.
|
||||||
|
|
||||||
|
### `bump` [*`options`*] [*`formula`*]
|
||||||
|
|
||||||
|
Display out-of-date brew formulae and the latest version available.
|
||||||
|
Also displays whether a pull request has been opened with the URL.
|
||||||
|
|
||||||
|
* `--limit`:
|
||||||
|
Limit number of package results returned.
|
||||||
|
|
||||||
### `bump-formula-pr` [*`options`*] [*`formula`*]
|
### `bump-formula-pr` [*`options`*] [*`formula`*]
|
||||||
|
|
||||||
Create a pull request to update *`formula`* with a new URL or a new tag.
|
Create a pull request to update *`formula`* with a new URL or a new tag.
|
||||||
|
|||||||
@ -1139,6 +1139,13 @@ When passed with \fB\-\-write\fR, a new commit will not generated after writing
|
|||||||
\fB\-\-root\-url\fR
|
\fB\-\-root\-url\fR
|
||||||
Use the specified \fIURL\fR as the root of the bottle\'s URL instead of Homebrew\'s default\.
|
Use the specified \fIURL\fR as the root of the bottle\'s URL instead of Homebrew\'s default\.
|
||||||
.
|
.
|
||||||
|
.SS "\fBbump\fR [\fIoptions\fR] [\fIformula\fR]"
|
||||||
|
Display out\-of\-date brew formulae and the latest version available\. Also displays whether a pull request has been opened with the URL\.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
\fB\-\-limit\fR
|
||||||
|
Limit number of package results returned\.
|
||||||
|
.
|
||||||
.SS "\fBbump\-formula\-pr\fR [\fIoptions\fR] [\fIformula\fR]"
|
.SS "\fBbump\-formula\-pr\fR [\fIoptions\fR] [\fIformula\fR]"
|
||||||
Create a pull request to update \fIformula\fR with a new URL or a new tag\.
|
Create a pull request to update \fIformula\fR with a new URL or a new tag\.
|
||||||
.
|
.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user