Port Homebrew::DevCmd::PrUpload
This commit is contained in:
		
							parent
							
								
									dc062bea52
								
							
						
					
					
						commit
						6d362ccb3b
					
				@ -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 <URL> 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 <URL> 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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user