Skip to content
Snippets Groups Projects
Unverified Commit 16107a88 authored by Mike McQuaid's avatar Mike McQuaid Committed by GitHub
Browse files

Merge pull request #7834 from MLH-Fellowship/mlh-outdated-packages

compare and display package versions
parents d2f10b0c e64af7c0
No related branches found
No related tags found
No related merge requests found
# 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
# 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
# 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
# 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" ...@@ -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"
......
# 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
# 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 ...@@ -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. ...@@ -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 ...@@ -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\.
. .
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment