Refactor version.rb and add output to PRs. — tk (#12388)

* Refactor version.rb and add output to PRs.

Signed-off-by: Satadru Pramanik <satadru@gmail.com>

* Update tk homepage.

Signed-off-by: Satadru Pramanik <satadru@gmail.com>

* Adjust PR creation text logic.

Signed-off-by: Satadru Pramanik <satadru@gmail.com>

---------

Signed-off-by: Satadru Pramanik <satadru@gmail.com>
This commit is contained in:
Satadru Pramanik, DO, MPH, MEng
2025-08-08 15:15:50 -04:00
committed by GitHub
parent 404b6817ef
commit ef26c23a47
5 changed files with 185 additions and 91 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env ruby
# version.rb version 1.3 (for Chromebrew)
# version.rb version 1.4 (for Chromebrew)
OPTIONS = %w[-h --help -u --update-package-files -v --verbose]
OPTIONS = %w[-h --help -j --json -u --update-package-files -v --verbose]
if ARGV.include?('-h') || ARGV.include?('--help')
abort <<~EOM
@@ -11,6 +11,7 @@ if ARGV.include?('-h') || ARGV.include?('--help')
If <package> is omitted, all packages will be processed.
Passing --update-package-files or -u will try to update the version
field in the package file.
Passing --json or -j will only give json output.
EOM
end
@@ -20,30 +21,25 @@ require 'net/http'
require 'ruby_libversion'
require 'uri/http'
crew_local_repo_root = `git rev-parse --show-toplevel 2> /dev/null`.chomp
# When invoked from crew, pwd is CREW_DEST_DIR, so crew_local_repo_root
# is empty.
crew_local_repo_root = '../' if crew_local_repo_root.to_s.empty?
require File.join(crew_local_repo_root, 'lib/color')
require File.join(crew_local_repo_root, 'lib/const')
require File.join(crew_local_repo_root, 'lib/package')
require File.join(crew_local_repo_root, 'lib/package_utils')
# Add >LOCAL< lib to LOAD_PATH
$LOAD_PATH.unshift '../lib'
require_relative '../lib/color'
require_relative '../lib/package'
require_relative '../lib/package_utils'
$LOAD_PATH.unshift File.join(crew_local_repo_root, 'lib')
OUTPUT_JSON = ARGV.include?('-j') || ARGV.include?('--json')
UPDATE_PACKAGE_FILES = ARGV.include?('-u') || ARGV.include?('--update-package-files')
VERBOSE = ARGV.include?('-v') || ARGV.include?('--verbose')
bc_updated = {}
pkg_names = {}
version_line_string = {}
versions_updated = {}
# Some packges need manual adjustments of URLS for different versions.
automatic_version_update_excluded_packages = Set[
{ pkg_name: 'cf', comments: 'Uses a dynamic source package URL.' },
{ pkg_name: 'cursor', comments: 'Uses a dynamic source package URL.' }
]
excluded_pkgs = automatic_version_update_excluded_packages.map { |h| h[:pkg_name] }
exclusion_regex = "(#{excluded_pkgs.join('|')})"
# Some packages have different names in anitya.
@anitya_package_name_mappings = Set[
{ pkg_name: 'cvs', anitya_pkg: 'cvs-stable', comments: '' }
].to_h { |h| [h[:pkg_name], h[:anitya_pkg]] }
versions = []
def get_version(name, homepage, source)
# Determine if the source is a GitHub repository.
@@ -58,8 +54,8 @@ def get_version(name, homepage, source)
end
end
end
anitya_name_mapping_idx = @anitya_package_name_mappings.keys.find_index { |i| i == name }
anitya_name = anitya_name_mapping_idx.nil? ? name : @anitya_package_name_mappings.values[anitya_name_mapping_idx]
anitya_name_mapping_idx = CREW_ANITYA_PACKAGE_NAME_MAPPINGS.keys.find_index { |i| i == name }
anitya_name = anitya_name_mapping_idx.nil? ? name : CREW_ANITYA_PACKAGE_NAME_MAPPINGS.values[anitya_name_mapping_idx]
anitya_id = get_anitya_id(anitya_name, homepage)
# If we weren't able to get an Anitya ID, return early here to save time and headaches
return if anitya_id.nil?
@@ -127,84 +123,128 @@ if ARGV.length.positive? && !(ARGV.length == 1 && OPTIONS.include?(ARGV[0]))
arg = arg.gsub('.rb', '')
next unless arg =~ /^[0-9a-zA-Z\_\*]+$/
if arg.include?('*')
Dir["../packages/#{arg}.rb"].each do |filename|
Dir[File.join(crew_local_repo_root, "packages/#{arg}.rb")].each do |filename|
filelist.push filename
end
else
filename = "../packages/#{arg}.rb"
filename = File.join(crew_local_repo_root, "packages/#{arg}.rb")
filelist.push filename if File.exist?(filename)
end
end
else
Dir['../packages/*.rb'].each do |filename|
Dir[File.join(crew_local_repo_root, 'packages/*.rb')].each do |filename|
filelist.push filename
end
end
if filelist.length.positive?
puts "#{'Package'.ljust(35)}#{'Status'.ljust(20)}#{'Current'.ljust(20)}Upstream"
puts "#{'-------'.ljust(35)}#{'------'.ljust(20)}#{'-------'.ljust(20)}--------"
max_pkg_name_length = File.basename(filelist.max_by(&:length)).length - 3
package_field_length = [max_pkg_name_length, 7].max + 4
status_field_length = 20
version_field_length = 16
puts "#{'Package'.ljust(package_field_length)}#{'Status'.ljust(status_field_length)}#{'Current'.ljust(version_field_length)}Upstream" unless OUTPUT_JSON
puts "#{'-------'.ljust(package_field_length)}#{'------'.ljust(status_field_length)}#{'-------'.ljust(version_field_length)}--------" unless OUTPUT_JSON
filelist.each do |filename|
pkg = Package.load_package(filename)
# Instead of typing out the name of every python package, we just use a regex here.
# Also, we annotate some packages to let us know that they won't work here.
if pkg.name.match?(/py3\S+/) || pkg.no_upstream_update?
puts pkg.name.ljust(35) + 'noupdate'.lightred if VERBOSE
next
end
# We skip fake packages.
pkg_names[pkg.name.to_sym] = pkg.name
version_line_string[pkg.name.to_sym] = ''
# We annotate some packages to let us know that they won't work here.
versions_updated[pkg.name.to_sym] = 'Up to date.' if pkg.no_upstream_update?
if pkg.is_fake?
puts pkg.name.ljust(35) + 'fake'.lightred if VERBOSE
versions_updated[pkg.name.to_sym] = 'Fake'
next
end
# Skip ruby and pip buildsystem packages.
if %w[RUBY Pip].include?(pkg.superclass.to_s)
puts pkg.name.ljust(35) + 'skipped'.lightred if VERBOSE
next
if %w[RUBY].include?(pkg.superclass.to_s)
gem_name = pkg.name.sub('ruby_', '')
# We replace all dashes with underscores in our initial package names, but some gems actually use underscores, so we need special cases.
# This list was created by looking at what packages were listed as not having updates in rubygems, and then looking up the upstream name for them.
if %w[
connection_pool error_highlight jaro_winkler
lint_roller method_source mini_mime multi_xml mutex_m
power_assert regexp_parser repl_type_completor ruby2_keywords
syntax_suggest
].include?(gem_name)
# These gems used underscores originally, so don't replace anything
elsif gem_name == 'language_server_protocol'
# These gems have an underscore then a dash, but there's only one, so we hardcode the logic for now.
gem_name = 'language_server-protocol'
elsif gem_name == 'unicode_display_width'
# These gems have a dash then an underscore, but there's only one, so we hardcode the logic for now.
gem_name = 'unicode-display_width'
else
# In the common case, the gem name used only dashes, which we all replaced with underscores.
gem_name.gsub!('_', '-')
end
upstream_version = JSON.parse(Net::HTTP.get(URI("https://rubygems.org/api/v1/versions/#{gem_name}/latest.json")))['version']
elsif %w[Pip].include?(pkg.superclass.to_s)
versions_updated[pkg.name.to_sym] = 'Not Found.' if pkg.name[/#{CREW_AUTOMATIC_VERSION_UPDATE_EXCLUSION_REGEX}/]
pip_name = pkg.name.sub('py3_', '').gsub('_', '-')
begin
upstream_version = `pip index versions #{'--pre' if pkg.prerelease?} #{pip_name} 2>/dev/null`.match(/#{Regexp.escape(pip_name)} \(([^)]+)\)/)[1]
rescue NoMethodError
versions_updated[pkg.name.to_sym] = 'Not Found.'
end
else
# Get the upstream version.
upstream_version = get_version(pkg.name.tr('_', '-'), pkg.homepage, pkg.source_url)
end
# Get the upstream version.
upstream_version = get_version(pkg.name.tr('_', '-'), pkg.homepage, pkg.source_url)
# Some packages don't work with this yet, so gracefully exit now rather than throwing false positives.
if upstream_version.nil? || upstream_version.to_s.chomp == 'null'
puts pkg.name.ljust(35) + 'notfound'.lightred if VERBOSE
next
end
versions_updated[pkg.name.to_sym] = 'Not Found.' if upstream_version.nil? || upstream_version.to_s.chomp == 'null'
# Bail out if we arent verbose and so dont want to print packages that are up to date.
next if Libversion.version_compare2(PackageUtils.get_clean_version(pkg.version), upstream_version) >= 0 && !VERBOSE
# Print the package name.
print pkg.name.ljust(35)
# Print the package update status.
if Libversion.version_compare2(PackageUtils.get_clean_version(pkg.version), upstream_version) >= 0
print 'uptodate'.ljust(20).lightgreen
elsif Libversion.version_compare2(PackageUtils.get_clean_version(pkg.version), upstream_version) == -1
if UPDATE_PACKAGE_FILES && !pkg.name[/#{exclusion_regex}/]
sed_cmd = <<~SED
grep "^ version '#{PackageUtils.get_clean_version(pkg.version)}'" #{filename} && sed "s,^ version '#{PackageUtils.get_clean_version(pkg.version)}', version '#{upstream_version.chomp}'," #{filename} > #{filename}.tmp && mv #{filename}.tmp #{filename}
SED
`#{sed_cmd}`
versions_updated[pkg.name.to_sym] = $CHILD_STATUS.success?
unless upstream_version.nil?
versions_updated[pkg.name.to_sym] = 'Up to date.' if (Libversion.version_compare2(PackageUtils.get_clean_version(pkg.version), upstream_version) >= 0) && versions_updated[pkg.name.to_sym] != 'Not Found.'
if Libversion.version_compare2(PackageUtils.get_clean_version(pkg.version), upstream_version) == -1
if UPDATE_PACKAGE_FILES && !pkg.name[/#{CREW_AUTOMATIC_VERSION_UPDATE_EXCLUSION_REGEX}/]
sed_cmd = <<~SED
grep "^ version '#{PackageUtils.get_clean_version(pkg.version)}'" #{filename} && sed "s,^ version '#{PackageUtils.get_clean_version(pkg.version)}', version '#{upstream_version.chomp}'," #{filename} > #{filename}.tmp && mv #{filename}.tmp #{filename}
SED
`#{sed_cmd}`
versions_updated[pkg.name.to_sym] = $CHILD_STATUS.success?
binary_compression_sed_cmd = <<~BC_SED
sed "s,^ binary_compression 'tar.xz', binary_compression 'tar.zst'," #{filename} > #{filename}.tmp && mv #{filename}.tmp #{filename}
BC_SED
if pkg.binary_compression == 'tar.xz' && !pkg.no_zstd?
`#{binary_compression_sed_cmd}`
bc_updated[pkg.name.to_sym] = $CHILD_STATUS.success?
binary_compression_sed_cmd = <<~BC_SED
sed "s,^ binary_compression 'tar.xz', binary_compression 'tar.zst'," #{filename} > #{filename}.tmp && mv #{filename}.tmp #{filename}
BC_SED
if pkg.binary_compression == 'tar.xz' && !pkg.no_zstd?
`#{binary_compression_sed_cmd}`
bc_updated[pkg.name.to_sym] = $CHILD_STATUS.success?
end
end
if UPDATE_PACKAGE_FILES && versions_updated[pkg.name.to_sym]
versions_updated[pkg.name.to_sym] = 'Updated.'
else
versions_updated[pkg.name.to_sym] = pkg.name[/#{CREW_AUTOMATIC_VERSION_UPDATE_EXCLUSION_REGEX}/] ? 'Update manually.' : 'Outdated.'
FileUtils.rm_f "#{filename}.tmp"
end
end
if UPDATE_PACKAGE_FILES && versions_updated[pkg.name.to_sym]
print 'updated for build'.ljust(20).blue
else
print pkg.name[/#{exclusion_regex}/] ? 'Update MANUALLY.'.ljust(20).red : 'outdated'.ljust(20).yellow
FileUtils.rm_f "#{filename}.tmp"
end
end
# Print the package versions.
puts PackageUtils.get_clean_version(pkg.version).ljust(20) + upstream_version
print "failed sed cmd: #{sed_cmd}".ljust(20).yellow if versions_updated[pkg.name.to_sym].to_s == 'false'
print "failed sed cmd: #{binary_compression_sed_cmd}".ljust(20).yellow if bc_updated[pkg.name.to_sym].to_s == 'false'
# puts PackageUtils.get_clean_version(pkg.version).ljust(status_field_length) + upstream_version unless OUTPUT_JSON
version_status_string = ''.ljust(status_field_length)
case versions_updated[pkg.name.to_sym]
when 'Fake'
version_status_string = 'Fake'.ljust(status_field_length).lightred
upstream_version = ''
when 'Not Found.'
version_status_string = 'Not Found.'.ljust(status_field_length).lightred
upstream_version = ''
when 'Outdated.'
version_status_string = 'Outdated.'.ljust(status_field_length).yellow
when 'Update manually.'
version_status_string = 'Update manually.'.ljust(status_field_length).red
when 'Updated.'
version_status_string = 'Updated.'.ljust(status_field_length).blue
when 'Up to date.'
version_status_string = 'Up to date.'.ljust(status_field_length).lightgreen
end
cleaned_pkg_version = PackageUtils.get_clean_version(pkg.version)
versions.push(package: pkg.name, update_status: versions_updated[pkg.name.to_sym], version: cleaned_pkg_version, upstream_version: upstream_version.chomp)
version_line_string[pkg.name.to_sym] = "#{pkg.name.ljust(package_field_length)}#{version_status_string}#{cleaned_pkg_version.ljust(version_field_length)}#{upstream_version}\n"
print version_line_string[pkg.name.to_sym] unless OUTPUT_JSON
print "failed sed cmd: #{sed_cmd}".ljust(sed_cmd.length).yellow if !OUTPUT_JSON && (versions_updated[pkg.name.to_sym].to_s == 'false')
print "failed sed cmd: #{binary_compression_sed_cmd}".ljust(binary_compression_sed_cmd.length).yellow if !OUTPUT_JSON && (bc_updated[pkg.name.to_sym].to_s == 'false')
end
puts versions.to_json if OUTPUT_JSON
end