From 6d362ccb3b74834185ee5307c3bd79cc5cfc4a28 Mon Sep 17 00:00:00 2001 From: Douglas Eichelberger Date: Thu, 21 Mar 2024 21:33:58 -0700 Subject: [PATCH] Port Homebrew::DevCmd::PrUpload --- Library/Homebrew/dev-cmd/pr-upload.rb | 265 +++++++++--------- .../Homebrew/test/dev-cmd/pr-upload_spec.rb | 3 +- 2 files changed, 135 insertions(+), 133 deletions(-) diff --git a/Library/Homebrew/dev-cmd/pr-upload.rb b/Library/Homebrew/dev-cmd/pr-upload.rb index 5f6fa25633..540580aae8 100644 --- a/Library/Homebrew/dev-cmd/pr-upload.rb +++ b/Library/Homebrew/dev-cmd/pr-upload.rb @@ -1,6 +1,7 @@ # typed: true # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "formula" require "github_packages" @@ -8,159 +9,159 @@ require "github_releases" require "extend/hash/deep_merge" module Homebrew - module_function + module DevCmd + class PrUpload < AbstractCommand + cmd_args do + description <<~EOS + Apply the bottle commit and publish bottles to a host. + EOS + switch "--keep-old", + description: "If the formula specifies a rebuild version, " \ + "attempt to preserve its value in the generated DSL." + switch "-n", "--dry-run", + description: "Print what would be done rather than doing it." + switch "--no-commit", + description: "Do not generate a new commit before uploading." + switch "--warn-on-upload-failure", + description: "Warn instead of raising an error if the bottle upload fails. " \ + "Useful for repairing bottle uploads that previously failed." + switch "--upload-only", + description: "Skip running `brew bottle` before uploading." + flag "--committer=", + description: "Specify a committer name and email in `git`'s standard author format." + flag "--root-url=", + description: "Use the specified as the root of the bottle's URL instead of Homebrew's default." + flag "--root-url-using=", + description: "Use the specified download strategy class for downloading the bottle's URL instead of " \ + "Homebrew's default." - sig { returns(CLI::Parser) } - def pr_upload_args - Homebrew::CLI::Parser.new do - description <<~EOS - Apply the bottle commit and publish bottles to a host. - EOS - switch "--keep-old", - description: "If the formula specifies a rebuild version, " \ - "attempt to preserve its value in the generated DSL." - switch "-n", "--dry-run", - description: "Print what would be done rather than doing it." - switch "--no-commit", - description: "Do not generate a new commit before uploading." - switch "--warn-on-upload-failure", - description: "Warn instead of raising an error if the bottle upload fails. " \ - "Useful for repairing bottle uploads that previously failed." - switch "--upload-only", - description: "Skip running `brew bottle` before uploading." - flag "--committer=", - description: "Specify a committer name and email in `git`'s standard author format." - flag "--root-url=", - description: "Use the specified as the root of the bottle's URL instead of Homebrew's default." - flag "--root-url-using=", - description: "Use the specified download strategy class for downloading the bottle's URL instead of " \ - "Homebrew's default." + conflicts "--upload-only", "--keep-old" + conflicts "--upload-only", "--no-commit" - conflicts "--upload-only", "--keep-old" - conflicts "--upload-only", "--no-commit" - - named_args :none - end - end - - def check_bottled_formulae!(bottles_hash) - bottles_hash.each do |name, bottle_hash| - formula_path = HOMEBREW_REPOSITORY/bottle_hash["formula"]["path"] - formula_version = Formulary.factory(formula_path).pkg_version - bottle_version = PkgVersion.parse bottle_hash["formula"]["pkg_version"] - next if formula_version == bottle_version - - odie "Bottles are for #{name} #{bottle_version} but formula is version #{formula_version}!" - end - end - - def github_releases?(bottles_hash) - @github_releases ||= bottles_hash.values.all? do |bottle_hash| - root_url = bottle_hash["bottle"]["root_url"] - url_match = root_url.match GitHubReleases::URL_REGEX - _, _, _, tag = *url_match - - tag - end - end - - def github_packages?(bottles_hash) - @github_packages ||= bottles_hash.values.all? do |bottle_hash| - bottle_hash["bottle"]["root_url"].match? GitHubPackages::URL_REGEX - end - end - - def bottles_hash_from_json_files(json_files, args) - puts "Reading JSON files: #{json_files.join(", ")}" if args.verbose? - - bottles_hash = json_files.reduce({}) do |hash, json_file| - hash.deep_merge(JSON.parse(File.read(json_file))) - end - - if args.root_url - bottles_hash.each_value do |bottle_hash| - bottle_hash["bottle"]["root_url"] = args.root_url + named_args :none end - end - bottles_hash - end + sig { override.void } + def run + json_files = Dir["*.bottle.json"] + odie "No bottle JSON files found in the current working directory" if json_files.blank? + bottles_hash = bottles_hash_from_json_files(json_files, args) - def pr_upload - args = pr_upload_args.parse + Homebrew.install_bundler_gems!(groups: ["pr_upload"]) - json_files = Dir["*.bottle.json"] - odie "No bottle JSON files found in the current working directory" if json_files.blank? - bottles_hash = bottles_hash_from_json_files(json_files, args) + unless args.upload_only? + bottle_args = ["bottle", "--merge", "--write"] + bottle_args << "--verbose" if args.verbose? + bottle_args << "--debug" if args.debug? + bottle_args << "--keep-old" if args.keep_old? + bottle_args << "--root-url=#{args.root_url}" if args.root_url + bottle_args << "--committer=#{args.committer}" if args.committer + bottle_args << "--no-commit" if args.no_commit? + bottle_args << "--root-url-using=#{args.root_url_using}" if args.root_url_using + bottle_args += json_files - Homebrew.install_bundler_gems!(groups: ["pr_upload"]) + if args.dry_run? + dry_run_service = if github_packages?(bottles_hash) + # GitHub Packages has its own --dry-run handling. + nil + elsif github_releases?(bottles_hash) + "GitHub Releases" + else + odie "Service specified by root_url is not recognized" + end - unless args.upload_only? - bottle_args = ["bottle", "--merge", "--write"] - bottle_args << "--verbose" if args.verbose? - bottle_args << "--debug" if args.debug? - bottle_args << "--keep-old" if args.keep_old? - bottle_args << "--root-url=#{args.root_url}" if args.root_url - bottle_args << "--committer=#{args.committer}" if args.committer - bottle_args << "--no-commit" if args.no_commit? - bottle_args << "--root-url-using=#{args.root_url_using}" if args.root_url_using - bottle_args += json_files + if dry_run_service + puts <<~EOS + brew #{bottle_args.join " "} + Upload bottles described by these JSON files to #{dry_run_service}: + #{json_files.join("\n ")} + EOS + return + end + end - if args.dry_run? - dry_run_service = if github_packages?(bottles_hash) - # GitHub Packages has its own --dry-run handling. - nil - elsif github_releases?(bottles_hash) - "GitHub Releases" + check_bottled_formulae!(bottles_hash) + + safe_system HOMEBREW_BREW_FILE, *bottle_args + + json_files = Dir["*.bottle.json"] + if json_files.blank? + puts "No bottle JSON files after merge, no upload needed!" + return + end + + # Reload the JSON files (in case `brew bottle --merge` generated + # `all: $SHA256` bottles) + bottles_hash = bottles_hash_from_json_files(json_files, args) + + # Check the bottle commits did not break `brew audit` + unless args.no_commit? + audit_args = ["audit", "--skip-style"] + audit_args << "--verbose" if args.verbose? + audit_args << "--debug" if args.debug? + audit_args += bottles_hash.keys + safe_system HOMEBREW_BREW_FILE, *audit_args + end + end + + if github_releases?(bottles_hash) + github_releases = GitHubReleases.new + github_releases.upload_bottles(bottles_hash) + elsif github_packages?(bottles_hash) + github_packages = GitHubPackages.new + github_packages.upload_bottles(bottles_hash, + keep_old: args.keep_old?, + dry_run: args.dry_run?, + warn_on_error: args.warn_on_upload_failure?) else odie "Service specified by root_url is not recognized" end + end - if dry_run_service - puts <<~EOS - brew #{bottle_args.join " "} - Upload bottles described by these JSON files to #{dry_run_service}: - #{json_files.join("\n ")} - EOS - return + private + + def check_bottled_formulae!(bottles_hash) + bottles_hash.each do |name, bottle_hash| + formula_path = HOMEBREW_REPOSITORY/bottle_hash["formula"]["path"] + formula_version = Formulary.factory(formula_path).pkg_version + bottle_version = PkgVersion.parse bottle_hash["formula"]["pkg_version"] + next if formula_version == bottle_version + + odie "Bottles are for #{name} #{bottle_version} but formula is version #{formula_version}!" end end - check_bottled_formulae!(bottles_hash) + def github_releases?(bottles_hash) + @github_releases ||= bottles_hash.values.all? do |bottle_hash| + root_url = bottle_hash["bottle"]["root_url"] + url_match = root_url.match GitHubReleases::URL_REGEX + _, _, _, tag = *url_match - safe_system HOMEBREW_BREW_FILE, *bottle_args - - json_files = Dir["*.bottle.json"] - if json_files.blank? - puts "No bottle JSON files after merge, no upload needed!" - return + tag + end end - # Reload the JSON files (in case `brew bottle --merge` generated - # `all: $SHA256` bottles) - bottles_hash = bottles_hash_from_json_files(json_files, args) - - # Check the bottle commits did not break `brew audit` - unless args.no_commit? - audit_args = ["audit", "--skip-style"] - audit_args << "--verbose" if args.verbose? - audit_args << "--debug" if args.debug? - audit_args += bottles_hash.keys - safe_system HOMEBREW_BREW_FILE, *audit_args + def github_packages?(bottles_hash) + @github_packages ||= bottles_hash.values.all? do |bottle_hash| + bottle_hash["bottle"]["root_url"].match? GitHubPackages::URL_REGEX + end end - end - if github_releases?(bottles_hash) - github_releases = GitHubReleases.new - github_releases.upload_bottles(bottles_hash) - elsif github_packages?(bottles_hash) - github_packages = GitHubPackages.new - github_packages.upload_bottles(bottles_hash, - keep_old: args.keep_old?, - dry_run: args.dry_run?, - warn_on_error: args.warn_on_upload_failure?) - else - odie "Service specified by root_url is not recognized" + def bottles_hash_from_json_files(json_files, args) + puts "Reading JSON files: #{json_files.join(", ")}" if args.verbose? + + bottles_hash = json_files.reduce({}) do |hash, json_file| + hash.deep_merge(JSON.parse(File.read(json_file))) + end + + if args.root_url + bottles_hash.each_value do |bottle_hash| + bottle_hash["bottle"]["root_url"] = args.root_url + end + end + + bottles_hash + end end end end diff --git a/Library/Homebrew/test/dev-cmd/pr-upload_spec.rb b/Library/Homebrew/test/dev-cmd/pr-upload_spec.rb index dfac35a539..2f4774f4d1 100644 --- a/Library/Homebrew/test/dev-cmd/pr-upload_spec.rb +++ b/Library/Homebrew/test/dev-cmd/pr-upload_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/pr-upload" -RSpec.describe "brew pr-upload" do +RSpec.describe Homebrew::DevCmd::PrUpload do it_behaves_like "parseable arguments" end