2020-10-10 14:16:11 +02:00
|
|
|
# typed: false
|
2020-09-04 16:58:31 -07:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require "cask"
|
|
|
|
require "cli/parser"
|
|
|
|
require "utils/tar"
|
|
|
|
|
|
|
|
module Homebrew
|
2020-10-20 12:03:48 +02:00
|
|
|
extend T::Sig
|
|
|
|
|
2020-09-04 16:58:31 -07:00
|
|
|
module_function
|
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(CLI::Parser) }
|
2020-09-04 16:58:31 -07:00
|
|
|
def bump_cask_pr_args
|
|
|
|
Homebrew::CLI::Parser.new do
|
|
|
|
usage_banner <<~EOS
|
2020-11-12 10:40:41 -05:00
|
|
|
`bump-cask-pr` [<options>] <cask>
|
2020-09-04 16:58:31 -07:00
|
|
|
|
|
|
|
Create a pull request to update <cask> with a new version.
|
|
|
|
|
|
|
|
A best effort to determine the <SHA-256> will be made if the value is not
|
|
|
|
supplied by the user.
|
|
|
|
EOS
|
|
|
|
switch "-n", "--dry-run",
|
|
|
|
description: "Print what would be done rather than doing it."
|
|
|
|
switch "--write",
|
|
|
|
description: "Make the expected file modifications without taking any Git actions."
|
|
|
|
switch "--commit",
|
|
|
|
depends_on: "--write",
|
|
|
|
description: "When passed with `--write`, generate a new commit after writing changes "\
|
|
|
|
"to the cask file."
|
|
|
|
switch "--no-audit",
|
2020-11-18 08:10:21 +01:00
|
|
|
description: "Don't run `brew audit` before opening the PR."
|
2020-09-16 10:56:54 -07:00
|
|
|
switch "--online",
|
2020-11-18 08:10:21 +01:00
|
|
|
description: "Run `brew audit --online` before opening the PR."
|
2020-09-04 16:58:31 -07:00
|
|
|
switch "--no-style",
|
2020-11-18 08:10:21 +01:00
|
|
|
description: "Don't run `brew style --fix` before opening the PR."
|
2020-09-04 16:58:31 -07:00
|
|
|
switch "--no-browse",
|
|
|
|
description: "Print the pull request URL instead of opening in a browser."
|
|
|
|
switch "--no-fork",
|
|
|
|
description: "Don't try to fork the repository."
|
|
|
|
flag "--version=",
|
|
|
|
description: "Specify the new <version> for the cask."
|
|
|
|
flag "--message=",
|
|
|
|
description: "Append <message> to the default pull request message."
|
|
|
|
flag "--url=",
|
|
|
|
description: "Specify the <URL> for the new download."
|
|
|
|
flag "--sha256=",
|
|
|
|
description: "Specify the <SHA-256> checksum of the new download."
|
|
|
|
switch "-f", "--force",
|
|
|
|
description: "Ignore duplicate open PRs."
|
|
|
|
|
|
|
|
conflicts "--dry-run", "--write"
|
2020-09-16 10:56:54 -07:00
|
|
|
conflicts "--no-audit", "--online"
|
2020-09-04 16:58:31 -07:00
|
|
|
named 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def bump_cask_pr
|
|
|
|
args = bump_cask_pr_args.parse
|
|
|
|
|
|
|
|
# As this command is simplifying user-run commands then let's just use a
|
|
|
|
# user path, too.
|
|
|
|
ENV["PATH"] = ENV["HOMEBREW_PATH"]
|
|
|
|
|
|
|
|
# Use the user's browser, too.
|
|
|
|
ENV["BROWSER"] = Homebrew::EnvConfig.browser
|
|
|
|
|
|
|
|
cask = args.named.to_casks.first
|
|
|
|
new_version = args.version
|
2020-09-10 20:02:26 -07:00
|
|
|
new_version = "latest" if new_version == ":latest"
|
|
|
|
new_version = Cask::DSL::Version.new(new_version)
|
2020-09-04 16:58:31 -07:00
|
|
|
new_base_url = args.url
|
|
|
|
new_hash = args.sha256
|
2020-12-04 00:07:02 +01:00
|
|
|
new_hash = :no_check if new_hash == ":no_check"
|
2020-09-04 16:58:31 -07:00
|
|
|
|
|
|
|
old_version = cask.version
|
2020-12-04 00:07:02 +01:00
|
|
|
old_hash = cask.sha256
|
2020-09-04 16:58:31 -07:00
|
|
|
|
|
|
|
tap_full_name = cask.tap&.full_name
|
2020-11-27 18:45:13 +11:00
|
|
|
default_remote_branch = cask.tap.path.git_origin_branch if cask.tap
|
|
|
|
default_remote_branch ||= "master"
|
2020-09-04 16:58:31 -07:00
|
|
|
previous_branch = "-"
|
|
|
|
|
|
|
|
check_open_pull_requests(cask, tap_full_name, args: args)
|
|
|
|
|
2020-09-10 20:02:26 -07:00
|
|
|
odie "#{cask}: no --version= argument specified!" if new_version.empty?
|
2020-09-04 16:58:31 -07:00
|
|
|
|
2020-09-10 20:02:26 -07:00
|
|
|
check_closed_pull_requests(cask, tap_full_name, version: new_version, args: args) unless new_version.latest?
|
2020-09-04 16:58:31 -07:00
|
|
|
|
2020-09-10 20:02:26 -07:00
|
|
|
if new_version == old_version
|
2020-09-04 16:58:31 -07:00
|
|
|
odie <<~EOS
|
|
|
|
You need to bump this cask manually since the new version
|
|
|
|
and old version are both #{new_version}.
|
|
|
|
EOS
|
2020-09-10 20:02:26 -07:00
|
|
|
elsif old_version.latest?
|
|
|
|
opoo "No --url= argument specified!" unless new_base_url
|
|
|
|
elsif new_version.latest?
|
2020-12-04 00:07:02 +01:00
|
|
|
opoo "Ignoring specified --sha256= argument." if new_hash && new_check != :no_check
|
2020-09-10 20:02:26 -07:00
|
|
|
elsif Version.new(new_version) < Version.new(old_version)
|
|
|
|
odie <<~EOS
|
|
|
|
You need to bump this cask manually since changing the
|
|
|
|
version from #{old_version} to #{new_version} would be a downgrade.
|
|
|
|
EOS
|
2020-09-04 16:58:31 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
old_contents = File.read(cask.sourcefile_path)
|
|
|
|
|
2020-09-10 20:02:26 -07:00
|
|
|
replacement_pairs = []
|
|
|
|
|
|
|
|
replacement_pairs << if old_version.latest?
|
|
|
|
[
|
|
|
|
"version :latest",
|
|
|
|
"version \"#{new_version}\"",
|
|
|
|
]
|
|
|
|
elsif new_version.latest?
|
|
|
|
[
|
|
|
|
"version \"#{old_version}\"",
|
|
|
|
"version :latest",
|
|
|
|
]
|
|
|
|
else
|
2020-09-04 16:58:31 -07:00
|
|
|
[
|
|
|
|
old_version,
|
|
|
|
new_version,
|
2020-09-10 20:02:26 -07:00
|
|
|
]
|
|
|
|
end
|
2020-09-04 16:58:31 -07:00
|
|
|
|
|
|
|
if new_base_url.present?
|
|
|
|
m = /^ +url "(.+?)"\n/m.match(old_contents)
|
|
|
|
odie "Could not find old URL in cask!" if m.nil?
|
|
|
|
|
|
|
|
old_base_url = m.captures.first
|
|
|
|
|
|
|
|
replacement_pairs << [
|
|
|
|
/#{Regexp.escape(old_base_url)}/,
|
|
|
|
new_base_url,
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
2020-12-04 00:07:02 +01:00
|
|
|
if new_version.latest?
|
|
|
|
new_hash = :no_check
|
|
|
|
elsif new_hash.nil? || cask.languages.present?
|
2020-09-04 16:58:31 -07:00
|
|
|
tmp_contents = Utils::Inreplace.inreplace_pairs(cask.sourcefile_path,
|
|
|
|
replacement_pairs.uniq.compact,
|
|
|
|
read_only_run: true,
|
|
|
|
silent: true)
|
|
|
|
|
|
|
|
tmp_cask = Cask::CaskLoader.load(tmp_contents)
|
2020-11-12 13:49:54 -08:00
|
|
|
tmp_config = cask.config
|
2020-09-04 16:58:31 -07:00
|
|
|
tmp_url = tmp_cask.url.to_s
|
|
|
|
|
|
|
|
if new_hash.nil?
|
|
|
|
resource_path = fetch_resource(cask, new_version, tmp_url)
|
|
|
|
Utils::Tar.validate_file(resource_path)
|
|
|
|
new_hash = resource_path.sha256
|
|
|
|
end
|
|
|
|
|
|
|
|
cask.languages.each do |language|
|
|
|
|
next if language == cask.language
|
|
|
|
|
2020-11-12 13:49:54 -08:00
|
|
|
lang_config = tmp_config.merge(Cask::Config.new(explicit: { languages: [language] }))
|
2020-09-04 16:58:31 -07:00
|
|
|
lang_cask = Cask::CaskLoader.load(tmp_contents)
|
2020-11-12 13:49:54 -08:00
|
|
|
lang_cask.config = lang_config
|
2020-09-04 16:58:31 -07:00
|
|
|
lang_url = lang_cask.url.to_s
|
2020-11-21 19:08:06 +01:00
|
|
|
lang_old_hash = lang_cask.sha256.to_s
|
2020-09-04 16:58:31 -07:00
|
|
|
|
|
|
|
resource_path = fetch_resource(cask, new_version, lang_url)
|
|
|
|
Utils::Tar.validate_file(resource_path)
|
|
|
|
lang_new_hash = resource_path.sha256
|
|
|
|
|
|
|
|
replacement_pairs << [
|
|
|
|
lang_old_hash,
|
|
|
|
lang_new_hash,
|
|
|
|
]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-12-04 00:07:02 +01:00
|
|
|
p old_hash
|
|
|
|
|
|
|
|
replacement_pairs << if old_version.latest? || new_version.latest? || new_hash == :no_check
|
|
|
|
hash_regex = old_hash == :no_check ? ":no_check" : "[\"']#{Regexp.escape(old_hash.to_s)}[\"']"
|
|
|
|
|
2020-09-10 20:02:26 -07:00
|
|
|
[
|
2020-12-04 00:07:02 +01:00
|
|
|
/sha256\s+#{hash_regex}/m,
|
|
|
|
"sha256 #{new_hash == :no_check ? ":no_check" : "\"#{new_hash}\""}",
|
2020-09-10 20:02:26 -07:00
|
|
|
]
|
|
|
|
else
|
|
|
|
[
|
|
|
|
old_hash,
|
|
|
|
new_hash,
|
|
|
|
]
|
|
|
|
end
|
2020-09-04 16:58:31 -07:00
|
|
|
|
|
|
|
Utils::Inreplace.inreplace_pairs(cask.sourcefile_path,
|
|
|
|
replacement_pairs.uniq.compact,
|
|
|
|
read_only_run: args.dry_run?,
|
|
|
|
silent: args.quiet?)
|
|
|
|
|
|
|
|
run_cask_audit(cask, old_contents, args: args)
|
|
|
|
run_cask_style(cask, old_contents, args: args)
|
|
|
|
|
|
|
|
pr_info = {
|
|
|
|
sourcefile_path: cask.sourcefile_path,
|
|
|
|
old_contents: old_contents,
|
2020-11-27 18:45:13 +11:00
|
|
|
remote_branch: default_remote_branch,
|
2020-09-15 10:31:40 -07:00
|
|
|
branch_name: "bump-#{cask.token}-#{new_version.tr(",:", "-")}",
|
2020-09-04 16:58:31 -07:00
|
|
|
commit_message: "Update #{cask.token} from #{old_version} to #{new_version}",
|
|
|
|
previous_branch: previous_branch,
|
|
|
|
tap: cask.tap,
|
|
|
|
tap_full_name: tap_full_name,
|
|
|
|
pr_message: "Created with `brew bump-cask-pr`.",
|
|
|
|
}
|
|
|
|
GitHub.create_bump_pr(pr_info, args: args)
|
|
|
|
end
|
|
|
|
|
|
|
|
def fetch_resource(cask, new_version, url, **specs)
|
|
|
|
resource = Resource.new
|
|
|
|
resource.url(url, specs)
|
|
|
|
resource.owner = Resource.new(cask.token)
|
|
|
|
resource.version = new_version
|
|
|
|
resource.fetch
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_open_pull_requests(cask, tap_full_name, args:)
|
|
|
|
GitHub.check_for_duplicate_pull_requests(cask.token, tap_full_name, state: "open", args: args)
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_closed_pull_requests(cask, tap_full_name, version:, args:)
|
|
|
|
# if we haven't already found open requests, try for an exact match across closed requests
|
|
|
|
pr_title = "Update #{cask.token} from #{cask.version} to #{version}"
|
|
|
|
GitHub.check_for_duplicate_pull_requests(pr_title, tap_full_name, state: "closed", args: args)
|
|
|
|
end
|
|
|
|
|
|
|
|
def run_cask_audit(cask, old_contents, args:)
|
|
|
|
if args.dry_run?
|
|
|
|
if args.no_audit?
|
2020-11-18 08:10:21 +01:00
|
|
|
ohai "Skipping `brew audit`"
|
2020-09-16 10:56:54 -07:00
|
|
|
elsif args.online?
|
2020-11-18 08:10:21 +01:00
|
|
|
ohai "brew audit --cask --online #{cask.sourcefile_path.basename}"
|
2020-09-04 16:58:31 -07:00
|
|
|
else
|
2020-11-18 08:10:21 +01:00
|
|
|
ohai "brew audit --cask #{cask.sourcefile_path.basename}"
|
2020-09-04 16:58:31 -07:00
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
failed_audit = false
|
|
|
|
if args.no_audit?
|
2020-11-18 08:10:21 +01:00
|
|
|
ohai "Skipping `brew audit`"
|
2020-09-16 10:56:54 -07:00
|
|
|
elsif args.online?
|
2020-11-18 08:10:21 +01:00
|
|
|
system HOMEBREW_BREW_FILE, "audit", "--cask", "--online", cask.sourcefile_path
|
2020-09-16 10:56:54 -07:00
|
|
|
failed_audit = !$CHILD_STATUS.success?
|
2020-09-04 16:58:31 -07:00
|
|
|
else
|
2020-11-18 08:10:21 +01:00
|
|
|
system HOMEBREW_BREW_FILE, "audit", "--cask", cask.sourcefile_path
|
2020-09-04 16:58:31 -07:00
|
|
|
failed_audit = !$CHILD_STATUS.success?
|
|
|
|
end
|
|
|
|
return unless failed_audit
|
|
|
|
|
|
|
|
cask.sourcefile_path.atomic_write(old_contents)
|
2020-11-18 08:10:21 +01:00
|
|
|
odie "`brew audit` failed!"
|
2020-09-04 16:58:31 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
def run_cask_style(cask, old_contents, args:)
|
|
|
|
if args.dry_run?
|
|
|
|
if args.no_style?
|
2020-11-18 08:10:21 +01:00
|
|
|
ohai "Skipping `brew style --fix`"
|
2020-09-04 16:58:31 -07:00
|
|
|
else
|
2020-11-18 08:10:21 +01:00
|
|
|
ohai "brew style --fix #{cask.sourcefile_path.basename}"
|
2020-09-04 16:58:31 -07:00
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
failed_style = false
|
|
|
|
if args.no_style?
|
2020-11-18 08:10:21 +01:00
|
|
|
ohai "Skipping `brew style --fix`"
|
2020-09-04 16:58:31 -07:00
|
|
|
else
|
2020-11-18 08:10:21 +01:00
|
|
|
system HOMEBREW_BREW_FILE, "style", "--fix", cask.sourcefile_path
|
2020-09-04 16:58:31 -07:00
|
|
|
failed_style = !$CHILD_STATUS.success?
|
|
|
|
end
|
|
|
|
return unless failed_style
|
|
|
|
|
|
|
|
cask.sourcefile_path.atomic_write(old_contents)
|
2020-11-18 08:10:21 +01:00
|
|
|
odie "`brew style --fix` failed!"
|
2020-09-04 16:58:31 -07:00
|
|
|
end
|
|
|
|
end
|