Merge remote-tracking branch 'origin/master' into deprecate-method-formulae

This commit is contained in:
Mike McQuaid 2020-04-01 13:07:11 +01:00
commit 4121659413
No known key found for this signature in database
GPG Key ID: 48A898132FD8EE70
244 changed files with 974 additions and 129 deletions

View File

@ -3,6 +3,9 @@ on:
push: push:
branches: master branches: master
pull_request: [] pull_request: []
release:
types:
- published
jobs: jobs:
tests: tests:
if: github.repository == 'Homebrew/brew' if: github.repository == 'Homebrew/brew'
@ -78,6 +81,12 @@ jobs:
sudo chmod -R g-w,o-w /home/linuxbrew /home/runner /opt sudo chmod -R g-w,o-w /home/linuxbrew /home/runner /opt
fi fi
- name: Run brew style
run: brew style --display-cop-names
- name: Run brew man
run: brew man --fail-if-changed
- name: Run brew tests - name: Run brew tests
run: | run: |
# brew tests doesn't like world writable directories # brew tests doesn't like world writable directories
@ -107,12 +116,6 @@ jobs:
# These cannot be queried at the macOS level on GitHub Actions. # These cannot be queried at the macOS level on GitHub Actions.
HOMEBREW_LANGUAGES: en-GB HOMEBREW_LANGUAGES: en-GB
- name: Run brew style
run: brew style --display-cop-names
- name: Run brew man
run: brew man --fail-if-changed
- name: Run brew update-tests - name: Run brew update-tests
run: | run: |
git config --global user.name "BrewTestBot" git config --global user.name "BrewTestBot"
@ -140,6 +143,22 @@ jobs:
run: | run: |
if [ "$RUNNER_OS" = "Linux" ]; then if [ "$RUNNER_OS" = "Linux" ]; then
docker-compose -f Dockerfile.yml run --rm -v $GITHUB_WORKSPACE:/tmp/test-bot sut docker-compose -f Dockerfile.yml run --rm -v $GITHUB_WORKSPACE:/tmp/test-bot sut
docker tag homebrew_sut brew
else else
brew test-bot brew test-bot
fi fi
- name: Deploy the latest Docker image
if: matrix.os == 'ubuntu-latest' && github.ref == 'refs/heads/master'
run: |
docker login docker.pkg.github.com -u BrewTestBot -p ${{secrets.GITHUB_TOKEN}}
docker tag brew docker.pkg.github.com/homebrew/brew/brew
docker push docker.pkg.github.com/homebrew/brew/brew
- name: Deploy the tagged Docker image
if: matrix.os == 'ubuntu-latest' && startsWith(github.ref, 'refs/tags/')
run: |
docker login docker.pkg.github.com -u BrewTestBot -p ${{secrets.GITHUB_TOKEN}}
v=${GITHUB_REF:10}
docker tag brew "docker.pkg.github.com/homebrew/brew/brew:$v"
docker push "docker.pkg.github.com/homebrew/brew/brew:$v"

View File

@ -2,7 +2,9 @@ inherit_from: ./.rubocop.yml
AllCops: AllCops:
Include: Include:
- '**/*_spec.rb' - '**/cmd/**/*.rb'
- '**/lib/**/*.rb'
- '**/spec/**/*.rb'
Exclude: Exclude:
- '**/vendor/**/*' - '**/vendor/**/*'

View File

@ -1,7 +1,7 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
activesupport (6.0.2.1) activesupport (6.0.2.2)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
minitest (~> 5.1) minitest (~> 5.1)
@ -51,7 +51,7 @@ GEM
parallel (1.19.1) parallel (1.19.1)
parallel_tests (2.32.0) parallel_tests (2.32.0)
parallel parallel
parser (2.7.0.4) parser (2.7.0.5)
ast (~> 2.4.0) ast (~> 2.4.0)
plist (3.5.0) plist (3.5.0)
rainbow (3.0.0) rainbow (3.0.0)
@ -81,14 +81,14 @@ GEM
rspec-support (3.9.2) rspec-support (3.9.2)
rspec-wait (0.0.9) rspec-wait (0.0.9)
rspec (>= 3, < 4) rspec (>= 3, < 4)
rubocop (0.80.1) rubocop (0.81.0)
jaro_winkler (~> 1.5.1) jaro_winkler (~> 1.5.1)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 2.7.0.1) parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
rexml rexml
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7) unicode-display_width (>= 1.4.0, < 2.0)
rubocop-performance (1.5.2) rubocop-performance (1.5.2)
rubocop (>= 0.71.0) rubocop (>= 0.71.0)
rubocop-rspec (1.38.1) rubocop-rspec (1.38.1)
@ -111,8 +111,8 @@ GEM
thread_safe (~> 0.1) thread_safe (~> 0.1)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.6) unf_ext (0.0.7.7)
unicode-display_width (1.6.1) unicode-display_width (1.7.0)
webrobots (0.1.2) webrobots (0.1.2)
zeitwerk (2.3.0) zeitwerk (2.3.0)

135
Library/Homebrew/bintray.rb Normal file
View File

@ -0,0 +1,135 @@
# frozen_string_literal: true
require "utils/curl"
require "json"
class Bintray
API_URL = "https://api.bintray.com"
class Error < RuntimeError
end
def inspect
"#<Bintray: user=#{@bintray_user} org=#{@bintray_org} key=***>"
end
def initialize(user: ENV["HOMEBREW_BINTRAY_USER"], key: ENV["HOMEBREW_BINTRAY_KEY"], org: "homebrew", clear: true)
@bintray_user = user
@bintray_key = key
@bintray_org = org
if !@bintray_user || !@bintray_key
unless Homebrew.args.dry_run?
raise UsageError, "Missing HOMEBREW_BINTRAY_USER or HOMEBREW_BINTRAY_KEY variables!"
end
end
raise UsageError, "Must set a Bintray organisation!" unless @bintray_org
ENV["HOMEBREW_FORCE_HOMEBREW_ON_LINUX"] = "1" if @bintray_org == "homebrew" && !OS.mac?
ENV.delete "HOMEBREW_BINTRAY_KEY" if clear
end
def open_api(url, *extra_curl_args, auth: true)
args = extra_curl_args
args += ["--user", "#{@bintray_user}:#{@bintray_key}"] if auth
curl(*args, url,
show_output: Homebrew.args.verbose?,
secrets: @bintray_key)
end
def upload(local_file, repo:, package:, version:, remote_file:, sha256: nil)
url = "#{API_URL}/content/#{@bintray_org}/#{repo}/#{package}/#{version}/#{remote_file}"
args = ["--upload-file", local_file]
args += ["--header", "X-Checksum-Sha2: #{sha256}"] unless sha256.blank?
open_api url, *args
end
def publish(repo:, package:, version:)
url = "#{API_URL}/content/#{@bintray_org}/#{repo}/#{package}/#{version}/publish"
open_api url, "--request", "POST"
end
def official_org?(org: @bintray_org)
%w[homebrew linuxbrew].include? org
end
def create_package(repo:, package:, **extra_data_args)
url = "#{API_URL}/packages/#{@bintray_org}/#{repo}/#{package}"
data = { name: package, public_download_numbers: true }
data[:public_stats] = official_org?
data.merge! extra_data_args
open_api url, "--request", "POST", "--data", data.to_json
end
def package_exists?(repo:, package:)
url = "#{API_URL}/packages/#{@bintray_org}/#{repo}/#{package}"
open_api url, "--output", "/dev/null", auth: false
end
def file_published?(repo:, remote_file:)
url = "https://dl.bintray.com/#{@bintray_org}/#{repo}/#{remote_file}"
begin
curl "--silent", "--head", "--output", "/dev/null", url
rescue ErrorDuringExecution => e
stderr = e.output.select { |type,| type == :stderr }
.map { |_, line| line }
.join
raise if e.status.exitstatus != 22 && !stderr.include?("404 Not Found")
false
else
true
end
end
def upload_bottle_json(json_files, publish_package: false)
bottles_hash = json_files.reduce({}) do |hash, json_file|
hash.deep_merge(JSON.parse(IO.read(json_file)))
end
formula_packaged = {}
bottles_hash.each do |formula_name, bottle_hash|
version = bottle_hash["formula"]["pkg_version"]
bintray_package = bottle_hash["bintray"]["package"]
bintray_repo = bottle_hash["bintray"]["repository"]
bottle_hash["bottle"]["tags"].each do |_tag, tag_hash|
filename = tag_hash["filename"]
sha256 = tag_hash["sha256"]
odebug "Checking remote file #{@bintray_org}/#{bintray_repo}/#{filename}"
if file_published? repo: bintray_repo, remote_file: filename
raise Error, <<~EOS
#{filename} is already published.
Please remove it manually from:
https://bintray.com/#{@bintray_org}/#{bintray_repo}/#{bintray_package}/view#files
Or run:
curl -X DELETE -u $HOMEBREW_BINTRAY_USER:$HOMEBREW_BINTRAY_KEY \\
https://api.bintray.com/content/#{@bintray_org}/#{bintray_repo}/#{filename}
EOS
end
if !formula_packaged[formula_name] && !package_exists?(repo: bintray_repo, package: bintray_package)
odebug "Creating package #{@bintray_org}/#{bintray_repo}/#{package}"
create_package repo: bintray_repo, package: bintray_package
formula_packaged[formula_name] = true
end
odebug "Uploading #{@bintray_org}/#{bintray_repo}/#{bintray_package}/#{version}/#{tag_hash["local_filename"]}"
upload(tag_hash["local_filename"],
repo: bintray_repo,
package: bintray_package,
version: version,
remote_file: filename,
sha256: sha256)
end
if publish_package
odebug "Publishing #{@bintray_org}/#{bintray_repo}/#{bintray_package}/#{version}"
publish repo: bintray_repo, package: bintray_package, version: version
end
end
end
end

View File

@ -35,6 +35,13 @@ rescue MissingEnvironmentVariables => e
exec ENV["HOMEBREW_BREW_FILE"], *ARGV exec ENV["HOMEBREW_BREW_FILE"], *ARGV
end end
def head_unsupported_error
$stderr.puts <<~EOS
Please create pull requests instead of asking for help on Homebrew's GitHub,
Discourse, Twitter or IRC.
EOS
end
begin begin
trap("INT", std_trap) # restore default CTRL-C handler trap("INT", std_trap) # restore default CTRL-C handler
@ -141,12 +148,18 @@ rescue Interrupt
rescue BuildError => e rescue BuildError => e
Utils::Analytics.report_build_error(e) Utils::Analytics.report_build_error(e)
e.dump e.dump
head_unsupported_error if Homebrew.args.HEAD?
exit 1 exit 1
rescue RuntimeError, SystemCallError => e rescue RuntimeError, SystemCallError => e
raise if e.message.empty? raise if e.message.empty?
onoe e onoe e
$stderr.puts e.backtrace if ARGV.debug? $stderr.puts e.backtrace if ARGV.debug?
head_unsupported_error if Homebrew.args.HEAD?
exit 1 exit 1
rescue MethodDeprecatedError => e rescue MethodDeprecatedError => e
onoe e onoe e

View File

@ -27,7 +27,7 @@ esac
export HOMEBREW_COMMAND_DEPTH=$((HOMEBREW_COMMAND_DEPTH + 1)) export HOMEBREW_COMMAND_DEPTH=$((HOMEBREW_COMMAND_DEPTH + 1))
ohai() { ohai() {
if [[ -t 1 && -z "$HOMEBREW_NO_COLOR" ]] # check whether stdout is a tty. if [[ -n "$HOMEBREW_COLOR" || (-t 1 && -z "$HOMEBREW_NO_COLOR") ]] # check whether stdout is a tty.
then then
echo -e "\\033[34m==>\\033[0m \\033[1m$*\\033[0m" # blue arrow and bold text echo -e "\\033[34m==>\\033[0m \\033[1m$*\\033[0m" # blue arrow and bold text
else else
@ -36,7 +36,7 @@ ohai() {
} }
onoe() { onoe() {
if [[ -t 2 && -z "$HOMEBREW_NO_COLOR" ]] # check whether stderr is a tty. if [[ -n "$HOMEBREW_COLOR" || (-t 2 && -z "$HOMEBREW_NO_COLOR") ]] # check whether stderr is a tty.
then then
echo -ne "\\033[4;31mError\\033[0m: " >&2 # highlight Error with underline and red color echo -ne "\\033[4;31mError\\033[0m: " >&2 # highlight Error with underline and red color
else else

View File

@ -49,9 +49,11 @@ module Cask
next if MacOS.version < :high_sierra next if MacOS.version < :high_sierra
<<~EOS <<~EOS
To install and/or use #{@cask} you may need to enable its kernel extension in: #{@cask} requires a kernel extension to work.
If the installation fails, retry after you enable it in:
System Preferences Security & Privacy General System Preferences Security & Privacy General
For more information refer to vendor documentation or this Apple Technical Note:
For more information, refer to vendor documentation or this Apple Technical Note:
#{Formatter.url("https://developer.apple.com/library/content/technotes/tn2459/_index.html")} #{Formatter.url("https://developer.apple.com/library/content/technotes/tn2459/_index.html")}
EOS EOS
end end

View File

@ -68,6 +68,19 @@ module Homebrew
named.blank? named.blank?
end end
# If the user passes any flags that trigger building over installing from
# a bottle, they are collected here and returned as an Array for checking.
def collect_build_args
build_flags = []
build_flags << "--HEAD" if head
build_flags << "--universal" if build_universal
build_flags << "--build-bottle" if build_bottle
build_flags << "--build-from-source" if build_from_source
build_flags
end
def formulae def formulae
require "formula" require "formula"
@formulae ||= (downcased_unique_named - casks).map do |name| @formulae ||= (downcased_unique_named - casks).map do |name|
@ -141,6 +154,10 @@ module Homebrew
end end
end end
def build_stable?
!(HEAD? || devel?)
end
private private
def downcased_unique_named def downcased_unique_named
@ -160,11 +177,33 @@ module Homebrew
end end
def head def head
(args_parsed && HEAD?) || cmdline_args.include?("--HEAD") return true if args_parsed && HEAD?
cmdline_args.include?("--HEAD")
end end
def devel def devel
(args_parsed && devel?) || cmdline_args.include?("--devel") return true if args_parsed && devel?
cmdline_args.include?("--devel")
end
def build_universal
return true if args_parsed && universal?
cmdline_args.include?("--universal")
end
def build_bottle
return true if args_parsed && build_bottle?
cmdline_args.include?("--build-bottle")
end
def build_from_source
return true if args_parsed && (build_from_source? || s?)
cmdline_args.include?("--build-from-source") || cmdline_args.include?("-s")
end end
def spec(default = :stable) def spec(default = :stable)

View File

@ -95,6 +95,7 @@ module Homebrew
end end
def comma_array(name, description: nil) def comma_array(name, description: nil)
name = name.chomp "="
description = option_to_description(name) if description.nil? description = option_to_description(name) if description.nil?
process_option(name, description) process_option(name, description)
@parser.on(name, OptionParser::REQUIRED_ARGUMENT, Array, *wrap_option_desc(description)) do |list| @parser.on(name, OptionParser::REQUIRED_ARGUMENT, Array, *wrap_option_desc(description)) do |list|

View File

@ -11,7 +11,7 @@ homebrew-shellenv() {
echo "set -gx HOMEBREW_PREFIX \"$HOMEBREW_PREFIX\";" echo "set -gx HOMEBREW_PREFIX \"$HOMEBREW_PREFIX\";"
echo "set -gx HOMEBREW_CELLAR \"$HOMEBREW_CELLAR\";" echo "set -gx HOMEBREW_CELLAR \"$HOMEBREW_CELLAR\";"
echo "set -gx HOMEBREW_REPOSITORY \"$HOMEBREW_REPOSITORY\";" echo "set -gx HOMEBREW_REPOSITORY \"$HOMEBREW_REPOSITORY\";"
echo "set -g fish_user_paths \"$HOMEBREW_PREFIX/bin\" \"$HOMEBREW_PREFIX/sbin\" \$fish_user_paths;" echo "set -q PATH; or set PATH ''; set -gx PATH \"$HOMEBREW_PREFIX/bin\" \"$HOMEBREW_PREFIX/sbin\" \$PATH;"
echo "set -q MANPATH; or set MANPATH ''; set -gx MANPATH \"$HOMEBREW_PREFIX/share/man\" \$MANPATH;" echo "set -q MANPATH; or set MANPATH ''; set -gx MANPATH \"$HOMEBREW_PREFIX/share/man\" \$MANPATH;"
echo "set -q INFOPATH; or set INFOPATH ''; set -gx INFOPATH \"$HOMEBREW_PREFIX/share/info\" \$INFOPATH;" echo "set -q INFOPATH; or set INFOPATH ''; set -gx INFOPATH \"$HOMEBREW_PREFIX/share/info\" \$INFOPATH;"
;; ;;

View File

@ -73,7 +73,6 @@ module Homebrew
install_core_tap_if_necessary install_core_tap_if_necessary
hub = ReporterHub.new
updated = false updated = false
initial_revision = ENV["HOMEBREW_UPDATE_BEFORE"].to_s initial_revision = ENV["HOMEBREW_UPDATE_BEFORE"].to_s
@ -86,6 +85,11 @@ module Homebrew
updated = true updated = true
end end
Homebrew.failed = true if ENV["HOMEBREW_UPDATE_FAILED"]
return if ENV["HOMEBREW_DISABLE_LOAD_FORMULA"]
hub = ReporterHub.new
updated_taps = [] updated_taps = []
Tap.each do |tap| Tap.each do |tap|
next unless tap.git? next unless tap.git?
@ -127,8 +131,6 @@ module Homebrew
link_completions_manpages_and_docs link_completions_manpages_and_docs
Tap.each(&:link_completions_and_manpages) Tap.each(&:link_completions_and_manpages)
Homebrew.failed = true if ENV["HOMEBREW_UPDATE_FAILED"]
end end
def shorten_revision(revision) def shorten_revision(revision)

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require "compat/cask/dsl/version" require "compat/cask/dsl/version"
require "compat/language/python"
require "compat/requirements/macos_requirement" require "compat/requirements/macos_requirement"
require "compat/formula" require "compat/formula"

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module Language
module Python
class << self
module Compat
def rewrite_python_shebang(python_path)
Pathname.pwd.find do |f|
Utils::Shebang.rewrite_shebang(Shebang.python_shebang_rewrite_info(python_path), f)
end
end
end
prepend Compat
end
end
end

View File

@ -292,7 +292,7 @@ module Homebrew
unversioned_name = unversioned_formula.basename(".rb") unversioned_name = unversioned_formula.basename(".rb")
problem "#{formula} is versioned but no #{unversioned_name} formula exists" problem "#{formula} is versioned but no #{unversioned_name} formula exists"
end end
elsif ARGV.build_stable? && formula.stable? && elsif Homebrew.args.build_stable? && formula.stable? &&
!(versioned_formulae = formula.versioned_formulae).empty? !(versioned_formulae = formula.versioned_formulae).empty?
versioned_aliases = formula.aliases.grep(/.@\d/) versioned_aliases = formula.aliases.grep(/.@\d/)
_, last_alias_version = versioned_formulae.map(&:name).last.split("@") _, last_alias_version = versioned_formulae.map(&:name).last.split("@")
@ -520,6 +520,7 @@ module Homebrew
gnupg@1.4 gnupg@1.4
lua@5.1 lua@5.1
numpy@1.16 numpy@1.16
libsigc++@2
].freeze ].freeze
return if keg_only_whitelist.include?(formula.name) || formula.name.start_with?("gcc@") return if keg_only_whitelist.include?(formula.name) || formula.name.start_with?("gcc@")

View File

@ -40,7 +40,7 @@ module Homebrew
description: "Print the pull request URL instead of opening in a browser." description: "Print the pull request URL instead of opening in a browser."
switch "--no-fork", switch "--no-fork",
description: "Don't try to fork the repository." description: "Don't try to fork the repository."
comma_array "--mirror=", comma_array "--mirror",
description: "Use the specified <URL> as a mirror URL. If <URL> is a comma-separated list "\ description: "Use the specified <URL> as a mirror URL. If <URL> is a comma-separated list "\
"of URLs, multiple mirrors will be added." "of URLs, multiple mirrors will be added."
flag "--version=", flag "--version=",

View File

@ -31,6 +31,8 @@ module Homebrew
description: "Create a basic template for a Perl build." description: "Create a basic template for a Perl build."
switch "--python", switch "--python",
description: "Create a basic template for a Python build." description: "Create a basic template for a Python build."
switch "--ruby",
description: "Create a basic template for a Ruby build."
switch "--rust", switch "--rust",
description: "Create a basic template for a Rust build." description: "Create a basic template for a Rust build."
switch "--no-fetch", switch "--no-fetch",
@ -86,6 +88,8 @@ module Homebrew
:perl :perl
elsif args.python? elsif args.python?
:python :python
elsif args.ruby?
:ruby
elsif args.rust? elsif args.rust?
:rust :rust
end end

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
require "cli/parser"
require "utils/github"
module Homebrew
module_function
def pr_publish_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`pr-publish` <pull_request>
Publishes bottles for a pull request with GitHub Actions.
Requires write access to the repository.
EOS
switch :verbose
end
end
def pr_publish
pr_publish_args.parse
ENV["HOMEBREW_FORCE_HOMEBREW_ON_LINUX"] = "1" unless OS.mac?
odie "You need to specify at least one pull request number!" if Homebrew.args.named.empty?
args.named.each do |arg|
arg = "#{CoreTap.instance.default_remote}/pull/#{arg}" if arg.to_i.positive?
url_match = arg.match HOMEBREW_PULL_OR_COMMIT_URL_REGEX
_, user, repo, issue = *url_match
tap = Tap.fetch(user, repo) if repo.match?(HOMEBREW_OFFICIAL_REPO_PREFIXES_REGEX)
odie "Not a GitHub pull request: #{arg}" unless issue
ohai "Dispatching #{tap} pull request ##{issue}"
GitHub.dispatch_event(user, repo, "Publish ##{issue}", pull_request: issue)
end
end
end

View File

@ -0,0 +1,168 @@
# frozen_string_literal: true
require "cli/parser"
require "utils/github"
require "tmpdir"
require "bintray"
module Homebrew
module_function
def pr_pull_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`pr-pull` <pull_request>
Download and publish bottles, and apply the bottle commit from a
pull request with artifacts generated from GitHub Actions.
Requires write access to the repository.
EOS
switch "--no-publish",
description: "Download the bottles, apply the bottle commit, and "\
"upload the bottles to Bintray, but don't publish them."
switch "--no-upload",
description: "Download the bottles and apply the bottle commit, "\
"but don't upload to Bintray."
switch "--dry-run", "-n",
description: "Print what would be done rather than doing it."
switch "--clean",
description: "Do not amend the commits from pull requests."
switch "--branch-okay",
description: "Do not warn if pulling to a branch besides master (useful for testing)."
switch "--resolve",
description: "When a patch fails to apply, leave in progress and allow user to resolve, instead "\
"of aborting."
flag "--workflow=",
description: "Retrieve artifacts from the specified workflow (default: tests.yml)."
flag "--artifact=",
description: "Download artifacts with the specified name (default: bottles)."
flag "--bintray-org=",
description: "Upload to the specified Bintray organisation (default: homebrew)."
flag "--tap=",
description: "Target repository tap (default: homebrew/core)."
switch :verbose
switch :debug
min_named 1
end
end
def setup_git_environment!
# Passthrough Git environment variables
ENV["GIT_COMMITTER_NAME"] = ENV["HOMEBREW_GIT_NAME"] if ENV["HOMEBREW_GIT_NAME"]
ENV["GIT_COMMITTER_EMAIL"] = ENV["HOMEBREW_GIT_EMAIL"] if ENV["HOMEBREW_GIT_EMAIL"]
# Depending on user configuration, git may try to invoke gpg.
return unless Utils.popen_read("git config --get --bool commit.gpgsign").chomp == "true"
begin
gnupg = Formula["gnupg"]
rescue FormulaUnavailableError
nil
else
if gnupg.installed?
path = PATH.new(ENV.fetch("PATH"))
path.prepend(gnupg.installed_prefix/"bin")
ENV["PATH"] = path
end
end
end
def signoff!(pr, path: ".", dry_run: false)
message = Utils.popen_read "git", "-C", path, "log", "-1", "--pretty=%B"
close_message = "Closes ##{pr}."
message += "\n#{close_message}" unless message.include? close_message
if dry_run
puts "git commit --amend --signoff -m $message"
else
safe_system "git", "-C", path, "commit", "--amend", "--signoff", "--allow-empty", "-q", "-m", message
end
end
def cherry_pick_pr!(pr, path: ".", dry_run: false)
if dry_run
puts <<~EOS
git fetch --force origin +refs/pull/#{pr}/head
git merge-base HEAD FETCH_HEAD
git cherry-pick --ff --allow-empty $merge_base..FETCH_HEAD
EOS
else
safe_system "git", "-C", path, "fetch", "--quiet", "--force", "origin", "+refs/pull/#{pr}/head"
merge_base = Utils.popen_read("git", "-C", path, "merge-base", "HEAD", "FETCH_HEAD").strip
commit_count = Utils.popen_read("git", "-C", path, "rev-list", "#{merge_base}..FETCH_HEAD").lines.count
# git cherry-pick unfortunately has no quiet option
ohai "Cherry-picking #{commit_count} commit#{"s" unless commit_count == 1} from ##{pr}"
cherry_pick_args = "git", "-C", path, "cherry-pick", "--ff", "--allow-empty", "#{merge_base}..FETCH_HEAD"
result = Homebrew.args.verbose? ? system(*cherry_pick_args) : quiet_system(*cherry_pick_args)
unless result
if Homebrew.args.resolve?
odie "Cherry-pick failed: try to resolve it."
else
system "git", "-C", path, "cherry-pick", "--abort"
odie "Cherry-pick failed!"
end
end
end
end
def check_branch(path, ref)
branch = Utils.popen_read("git", "-C", path, "symbolic-ref", "--short", "HEAD").strip
return if branch == ref || args.clean? || args.branch_okay?
opoo "Current branch is #{branch}: do you need to pull inside #{ref}?"
end
def pr_pull
pr_pull_args.parse
bintray_user = ENV["HOMEBREW_BINTRAY_USER"]
bintray_key = ENV["HOMEBREW_BINTRAY_KEY"]
bintray_org = args.bintray_org || "homebrew"
if bintray_user.blank? || bintray_key.blank?
odie "Missing HOMEBREW_BINTRAY_USER or HOMEBREW_BINTRAY_KEY variables!" if !args.dry_run? && !args.no_upload?
else
bintray = Bintray.new(user: bintray_user, key: bintray_key, org: bintray_org)
end
workflow = args.workflow || "tests.yml"
artifact = args.artifact || "bottles"
tap = Tap.fetch(args.tap || "homebrew/core")
setup_git_environment!
args.named.each do |arg|
arg = "#{tap.default_remote}/pull/#{arg}" if arg.to_i.positive?
url_match = arg.match HOMEBREW_PULL_OR_COMMIT_URL_REGEX
_, user, repo, pr = *url_match
odie "Not a GitHub pull request: #{arg}" unless pr
check_branch tap.path, "master"
ohai "Fetching #{tap} pull request ##{pr}"
Dir.mktmpdir pr do |dir|
cd dir do
GitHub.fetch_artifact(user, repo, pr, dir, workflow_id: workflow, artifact_name: artifact)
cherry_pick_pr! pr, path: tap.path, dry_run: args.dry_run?
signoff! pr, path: tap.path, dry_run: args.dry_run? unless args.clean?
if args.dry_run?
puts "brew bottle --merge --write #{Dir["*.json"].join " "}"
else
quiet_system "#{HOMEBREW_PREFIX}/bin/brew", "bottle", "--merge", "--write", *Dir["*.json"]
end
next if args.no_upload?
if args.dry_run?
puts "Upload bottles described by these JSON files to Bintray:\n #{Dir["*.json"].join("\n ")}"
else
bintray.upload_bottle_json Dir["*.json"], publish_package: !args.no_publish?
end
end
end
end
end
end

View File

@ -36,6 +36,7 @@ module Homebrew
test_args.parse test_args.parse
require "formula_assertions" require "formula_assertions"
require "formula_free_port"
args.resolved_formulae.each do |f| args.resolved_formulae.each do |f|
# Cannot test uninstalled formulae # Cannot test uninstalled formulae

View File

@ -43,14 +43,6 @@ module HomebrewArgvExtension
include?("--no-sandbox") || !ENV["HOMEBREW_NO_SANDBOX"].nil? include?("--no-sandbox") || !ENV["HOMEBREW_NO_SANDBOX"].nil?
end end
def build_stable?
!(include?("--HEAD") || include?("--devel"))
end
def build_universal?
include? "--universal"
end
def build_bottle? def build_bottle?
include?("--build-bottle") include?("--build-bottle")
end end
@ -84,19 +76,6 @@ module HomebrewArgvExtension
value "env" value "env"
end end
# If the user passes any flags that trigger building over installing from
# a bottle, they are collected here and returned as an Array for checking.
def collect_build_flags
build_flags = []
build_flags << "--HEAD" if include?("--HEAD")
build_flags << "--universal" if build_universal?
build_flags << "--build-bottle" if build_bottle?
build_flags << "--build-from-source" if build_from_source?
build_flags
end
private private
def options_only def options_only

View File

@ -6,6 +6,7 @@ require "lock_file"
require "formula_pin" require "formula_pin"
require "hardware" require "hardware"
require "utils/bottles" require "utils/bottles"
require "utils/shebang"
require "utils/shell" require "utils/shell"
require "build_environment" require "build_environment"
require "build_options" require "build_options"
@ -50,6 +51,7 @@ require "find"
class Formula class Formula
include FileUtils include FileUtils
include Utils::Inreplace include Utils::Inreplace
include Utils::Shebang
include Utils::Shell include Utils::Shell
include Utils::Deprecate include Utils::Deprecate
extend Enumerable extend Enumerable

View File

@ -112,6 +112,8 @@ module Homebrew
uses_from_macos "perl" uses_from_macos "perl"
<% elsif mode == :python %> <% elsif mode == :python %>
depends_on "python" depends_on "python"
<% elsif mode == :ruby %>
uses_from_macos "ruby"
<% elsif mode == :rust %> <% elsif mode == :rust %>
depends_on "rust" => :build depends_on "rust" => :build
<% elsif mode.nil? %> <% elsif mode.nil? %>
@ -166,6 +168,12 @@ module Homebrew
bin.env_script_all_files(libexec/"bin", :PERL5LIB => ENV["PERL5LIB"]) bin.env_script_all_files(libexec/"bin", :PERL5LIB => ENV["PERL5LIB"])
<% elsif mode == :python %> <% elsif mode == :python %>
virtualenv_install_with_resources virtualenv_install_with_resources
<% elsif mode == :ruby %>
ENV["GEM_HOME"] = libexec
system "gem", "build", "\#{name}.gemspec"
system "gem", "install", "\#{name}-\#{version}.gem"
bin.install libexec/"bin/\#{name}"
bin.env_script_all_files(libexec/"bin", :GEM_HOME => ENV["GEM_HOME"])
<% elsif mode == :rust %> <% elsif mode == :rust %>
system "cargo", "install", "--locked", "--root", prefix, "--path", "." system "cargo", "install", "--locked", "--root", prefix, "--path", "."
<% else %> <% else %>

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
module Homebrew
module FreePort
require "socket"
def free_port
server = TCPServer.new 0
_, port, = server.addr
server.close
port
end
end
end

View File

@ -80,7 +80,7 @@ class FormulaInstaller
# it's necessary to interrupt the user before any sort of installation # it's necessary to interrupt the user before any sort of installation
# can proceed. Only invoked when the user has no developer tools. # can proceed. Only invoked when the user has no developer tools.
def self.prevent_build_flags def self.prevent_build_flags
build_flags = ARGV.collect_build_flags build_flags = Homebrew.args.collect_build_args
return if build_flags.empty? return if build_flags.empty?
all_bottled = ARGV.formulae.all?(&:bottled?) all_bottled = ARGV.formulae.all?(&:bottled?)
@ -598,16 +598,16 @@ class FormulaInstaller
oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}" oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}"
fi.install fi.install
fi.finish fi.finish
rescue FormulaInstallationAlreadyAttemptedError rescue Exception => e # rubocop:disable Lint/RescueException
# We already attempted to install f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on.
nil
rescue Exception # rubocop:disable Lint/RescueException
ignore_interrupts do ignore_interrupts do
tmp_keg.rename(installed_keg) if tmp_keg && !installed_keg.directory? tmp_keg.rename(installed_keg) if tmp_keg && !installed_keg.directory?
linked_keg.link if keg_was_linked linked_keg.link if keg_was_linked
end end
raise raise unless e.is_a? FormulaInstallationAlreadyAttemptedError
# We already attempted to install f as part of another formula's
# dependency tree. In that case, don't generate an error, just move on.
nil
else else
ignore_interrupts { tmp_keg.rmtree if tmp_keg&.directory? } ignore_interrupts { tmp_keg.rmtree if tmp_keg&.directory? }
end end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Language
module Perl
module Shebang
module_function
def detected_perl_shebang(formula = self)
perl_path = if formula.uses_from_macos_elements&.include? "perl"
"/usr/bin/perl"
elsif formula.deps.map(&:name).include? "perl"
Formula["perl"].opt_bin/"perl"
else
raise "Cannot detect Perl shebang: formula does not depend on Perl."
end
Utils::Shebang::RewriteInfo.new(
%r{^#! ?/usr/bin/(env )?perl$},
20, # the length of "#! /usr/bin/env perl"
perl_path,
)
end
end
end
end

View File

@ -87,14 +87,26 @@ module Language
] ]
end end
def self.rewrite_python_shebang(python_path) # Mixin module for {Formula} adding shebang rewrite features.
regex = %r{^#! ?/usr/bin/(env )?python([23](\.\d{1,2})?)?$} module Shebang
maximum_regex_length = 28 # the length of "#! /usr/bin/env pythonx.yyy$" module_function
Pathname(".").find do |f|
next unless f.file?
next unless regex.match?(f.read(maximum_regex_length))
Utils::Inreplace.inreplace f.to_s, regex, "#!#{python_path}" # @private
def python_shebang_rewrite_info(python_path)
Utils::Shebang::RewriteInfo.new(
%r{^#! ?/usr/bin/(env )?python([23](\.\d{1,2})?)?$},
28, # the length of "#! /usr/bin/env pythonx.yyy$"
python_path,
)
end
def detected_python_shebang(formula = self)
python_deps = formula.deps.map(&:name).grep(/^python(@.*)?$/)
raise "Cannot detect Python shebang: formula does not depend on Python." if python_deps.empty?
raise "Cannot detect Python shebang: formula has multiple Python dependencies." if python_deps.length > 1
python_shebang_rewrite_info(Formula[python_deps.first].opt_bin/"python3")
end end
end end

View File

@ -4,6 +4,9 @@ require "pathname"
HOMEBREW_LIBRARY_PATH = Pathname(__dir__).realpath.freeze HOMEBREW_LIBRARY_PATH = Pathname(__dir__).realpath.freeze
$LOAD_PATH.push(HOMEBREW_LIBRARY_PATH.to_s) unless $LOAD_PATH.include?(HOMEBREW_LIBRARY_PATH.to_s) $LOAD_PATH.push HOMEBREW_LIBRARY_PATH.to_s
require "vendor/bundle/bundler/setup" require "vendor/bundle/bundler/setup"
$LOAD_PATH.select! { |d| Pathname(d).directory? }
$LOAD_PATH.uniq!

View File

@ -160,7 +160,7 @@ Note that environment variables must have a value set to be detected. For exampl
`git`(1) remote. If set, instructs Homebrew to instead use the specified URL. `git`(1) remote. If set, instructs Homebrew to instead use the specified URL.
* `HOMEBREW_CURLRC`: * `HOMEBREW_CURLRC`:
If set, Homebrew will not pass `-q` when invoking `curl`(1), which disables If set, Homebrew will not pass `--disable` when invoking `curl`(1), which disables
the use of `curlrc`. the use of `curlrc`.
* `HOMEBREW_CURL_VERBOSE`: * `HOMEBREW_CURL_VERBOSE`:
@ -168,6 +168,7 @@ Note that environment variables must have a value set to be detected. For exampl
* `HOMEBREW_CURL_RETRIES`: * `HOMEBREW_CURL_RETRIES`:
If set, Homebrew will pass the given retry count to `--retry` when invoking `curl`(1). If set, Homebrew will pass the given retry count to `--retry` when invoking `curl`(1).
By default, `curl`(1) is invoked with `--retry 3`.
* `HOMEBREW_DEBUG`: * `HOMEBREW_DEBUG`:
If set, any commands that can emit debugging information will do so. If set, any commands that can emit debugging information will do so.

View File

@ -16,13 +16,13 @@ module OS
when "10.11" then "8.2.1" when "10.11" then "8.2.1"
when "10.12" then "9.2" when "10.12" then "9.2"
when "10.13" then "10.1" when "10.13" then "10.1"
when "10.14" then "11.3" when "10.14" then "11.3.1"
when "10.15" then "11.3" when "10.15" then "11.4"
else else
raise "macOS '#{MacOS.version}' is invalid" unless OS::Mac.prerelease? raise "macOS '#{MacOS.version}' is invalid" unless OS::Mac.prerelease?
# Default to newest known version of Xcode for unreleased macOS versions. # Default to newest known version of Xcode for unreleased macOS versions.
"11.3" "11.4"
end end
end end
@ -175,9 +175,9 @@ module OS
when 81 then "8.3" when 81 then "8.3"
when 90 then "9.2" when 90 then "9.2"
when 91 then "9.4" when 91 then "9.4"
when 100 then "10.2.1" when 100 then "10.3"
when 110 then "11.3" when 110 then "11.4"
else "11.3" else "11.4"
end end
end end
@ -236,8 +236,8 @@ module OS
# on the older supported platform for that Xcode release, i.e there's no # on the older supported platform for that Xcode release, i.e there's no
# CLT package for 10.11 that contains the Clang version from Xcode 8. # CLT package for 10.11 that contains the Clang version from Xcode 8.
case MacOS.version case MacOS.version
when "10.15" then "1100.0.33.16" when "10.15" then "1103.0.32.29"
when "10.14" then "1001.0.46.4" when "10.14" then "1001.0.46.4" # Later versions are available, but not via Software Update
when "10.13" then "1000.10.44.2" when "10.13" then "1000.10.44.2"
when "10.12" then "900.0.39.2" when "10.12" then "900.0.39.2"
when "10.11" then "800.0.42.1" when "10.11" then "800.0.42.1"

View File

@ -7,6 +7,7 @@ require "extend/ENV"
require "timeout" require "timeout"
require "debrew" require "debrew"
require "formula_assertions" require "formula_assertions"
require "formula_free_port"
require "fcntl" require "fcntl"
require "socket" require "socket"
require "cli/parser" require "cli/parser"
@ -26,6 +27,7 @@ begin
formula = Homebrew.args.resolved_formulae.first formula = Homebrew.args.resolved_formulae.first
formula.extend(Homebrew::Assertions) formula.extend(Homebrew::Assertions)
formula.extend(Homebrew::FreePort)
formula.extend(Debrew::Formula) if Homebrew.args.debug? formula.extend(Debrew::Formula) if Homebrew.args.debug?
# tests can also return false to indicate failure # tests can also return false to indicate failure

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require "bintray"
describe Bintray, :needs_network do
bintray = described_class.new(user: "BrewTestBot", key: "deadbeef", org: "homebrew")
describe "::file_published?" do
it "detects a published file" do
results = bintray.file_published?(repo: "bottles", remote_file: "hello-2.10.catalina.bottle.tar.gz")
expect(results).to be true
end
it "fails on a non-existant file" do
results = bintray.file_published?(repo: "bottles", remote_file: "my-fake-bottle-1.0.snow_hyena.tar.gz")
expect(results).to be false
end
end
describe "::package_exists?" do
it "detects a package" do
results = bintray.package_exists?(repo: "bottles", package: "hello")
expect(results.status.exitstatus).to be 0
end
end
end

View File

@ -71,31 +71,6 @@ describe Cask::Cmd::Upgrade, :cask do
expect(local_transmission_path).to be_a_directory expect(local_transmission_path).to be_a_directory
expect(local_transmission.versions).to include("2.60") expect(local_transmission.versions).to include("2.60")
end end
it 'updates "auto_updates" and "latest" Casks when their tokens are provided in the command line' do
local_caffeine = Cask::CaskLoader.load("local-caffeine")
local_caffeine_path = Cask::Config.global.appdir.join("Caffeine.app")
auto_updates = Cask::CaskLoader.load("auto-updates")
auto_updates_path = Cask::Config.global.appdir.join("MyFancyApp.app")
expect(local_caffeine).to be_installed
expect(local_caffeine_path).to be_a_directory
expect(local_caffeine.versions).to include("1.2.2")
expect(auto_updates).to be_installed
expect(auto_updates_path).to be_a_directory
expect(auto_updates.versions).to include("2.57")
described_class.run("local-caffeine", "auto-updates")
expect(local_caffeine).to be_installed
expect(local_caffeine_path).to be_a_directory
expect(local_caffeine.versions).to include("1.2.3")
expect(auto_updates).to be_installed
expect(auto_updates_path).to be_a_directory
expect(auto_updates.versions).to include("2.61")
end
end end
describe "with --greedy it checks additional Casks" do describe "with --greedy it checks additional Casks" do

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require "cmd/shared_examples/args_parse"
describe "Homebrew.pr_publish_args" do
it_behaves_like "parseable arguments"
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require "cmd/shared_examples/args_parse"
describe "Homebrew.pr_pull_args" do
it_behaves_like "parseable arguments"
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require "socket"
require "formula_free_port"
module Homebrew
describe FreePort do
include described_class
describe "#free_port" do
# IANA suggests user port from 1024 to 49151
# and dynamic port for 49152 to 65535
# http://www.iana.org/assignments/port-numbers
MIN_PORT = 1024
MAX_PORT = 65535
it "returns a free TCP/IP port" do
port = free_port
expect(port).to be_between(MIN_PORT, MAX_PORT)
expect { TCPServer.new(port).close }.not_to raise_error
end
end
end
end

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
require "language/perl"
require "utils/shebang"
describe Language::Perl::Shebang do
let(:file) { Tempfile.new("perl-shebang") }
let(:perl_f) do
formula "perl" do
url "https://brew.sh/perl-1.0.tgz"
end
end
let(:f) do
formula "foo" do
url "https://brew.sh/foo-1.0.tgz"
uses_from_macos "perl"
end
end
before do
file.write <<~EOS
#!/usr/bin/env perl
a
b
c
EOS
file.flush
end
after { file.unlink }
describe "#detected_perl_shebang" do
it "can be used to replace Perl shebangs" do
allow(Formulary).to receive(:factory).with(perl_f.name).and_return(perl_f)
Utils::Shebang.rewrite_shebang described_class.detected_perl_shebang(f), file
expected_shebang = if OS.mac?
"/usr/bin/perl"
else
HOMEBREW_PREFIX/"opt/perl/bin/perl"
end
expect(File.read(file)).to eq <<~EOS
#!#{expected_shebang}
a
b
c
EOS
end
end
end

View File

@ -2,6 +2,7 @@
require "language/python" require "language/python"
require "resource" require "resource"
require "utils/shebang"
describe Language::Python, :needs_python do describe Language::Python, :needs_python do
describe "#major_minor_version" do describe "#major_minor_version" do
@ -32,6 +33,48 @@ describe Language::Python, :needs_python do
end end
end end
describe Language::Python::Shebang do
let(:file) { Tempfile.new("python-shebang") }
let(:python_f) do
formula "python" do
url "https://brew.sh/python-1.0.tgz"
end
end
let(:f) do
formula "foo" do
url "https://brew.sh/foo-1.0.tgz"
depends_on "python"
end
end
before do
file.write <<~EOS
#!/usr/bin/env python3
a
b
c
EOS
file.flush
end
after { file.unlink }
describe "#detected_python_shebang" do
it "can be used to replace Python shebangs" do
expect(Formulary).to receive(:factory).with(python_f.name).and_return(python_f)
Utils::Shebang.rewrite_shebang described_class.detected_python_shebang(f), file
expect(File.read(file)).to eq <<~EOS
#!#{HOMEBREW_PREFIX}/opt/python/bin/python3
a
b
c
EOS
end
end
end
describe Language::Python::Virtualenv::Virtualenv do describe Language::Python::Virtualenv::Virtualenv do
subject { described_class.new(formula, dir, "python") } subject { described_class.new(formula, dir, "python") }

View File

@ -157,7 +157,7 @@ describe Resource do
end end
specify "#verify_download_integrity_mismatch" do specify "#verify_download_integrity_mismatch" do
fn = double(file?: true) fn = double(file?: true, basename: "foo")
checksum = subject.sha256(TEST_SHA256) checksum = subject.sha256(TEST_SHA256)
expect(fn).to receive(:verify_checksum).with(checksum) expect(fn).to receive(:verify_checksum).with(checksum)

View File

@ -4,19 +4,23 @@ require "utils/curl"
describe "curl" do describe "curl" do
describe "curl_args" do describe "curl_args" do
it "returns -q as the first argument when HOMEBREW_CURLRC is not set" do it "returns --disable as the first argument when HOMEBREW_CURLRC is not set" do
# -q must be the first argument according to "man curl" # --disable must be the first argument according to "man curl"
expect(curl_args("foo").first).to eq("-q") expect(curl_args("foo").first).to eq("--disable")
end end
it "doesn't return -q as the first argument when HOMEBREW_CURLRC is set" do it "doesn't return --disable as the first argument when HOMEBREW_CURLRC is set" do
ENV["HOMEBREW_CURLRC"] = "1" ENV["HOMEBREW_CURLRC"] = "1"
expect(curl_args("foo").first).not_to eq("-q") expect(curl_args("foo").first).not_to eq("--disable")
end end
it "returns --retry when HOMEBREW_CURL_RETRIES is set" do it "uses `--retry 3` when HOMEBREW_CURL_RETRIES is unset" do
ENV["HOMEBREW_CURL_RETRIES"] = "3"
expect(curl_args("foo").join(" ")).to include("--retry 3") expect(curl_args("foo").join(" ")).to include("--retry 3")
end end
it "uses the given value for `--retry` when HOMEBREW_CURL_RETRIES is set" do
ENV["HOMEBREW_CURL_RETRIES"] = "10"
expect(curl_args("foo").join(" ")).to include("--retry 10")
end
end end
end end

View File

@ -41,4 +41,18 @@ describe GitHub do
expect(results.first["title"]).to eq("Shall we run `brew update` automatically?") expect(results.first["title"]).to eq("Shall we run `brew update` automatically?")
end end
end end
describe "::fetch_artifact", :needs_network do
it "fails to find a nonexistant workflow" do
expect {
subject.fetch_artifact("Homebrew", "homebrew-core", 1, ".")
}.to raise_error(/No matching workflow run found/)
end
it "fails to find artifacts that don't exist" do
expect {
subject.fetch_artifact("Homebrew", "homebrew-core", 51971, ".", artifact_name: "false_bottles")
}.to raise_error(/No artifact .+ was found/)
end
end
end end

View File

@ -12,7 +12,7 @@ module Utils
args = [] args = []
# do not load .curlrc unless requested (must be the first argument) # do not load .curlrc unless requested (must be the first argument)
args << "-q" unless ENV["HOMEBREW_CURLRC"] args << "--disable" unless ENV["HOMEBREW_CURLRC"]
args += %W[ args += %W[
--max-time 3 --max-time 3

View File

@ -17,7 +17,7 @@ def curl_args(*extra_args, show_output: false, user_agent: :default)
args = [] args = []
# do not load .curlrc unless requested (must be the first argument) # do not load .curlrc unless requested (must be the first argument)
args << "-q" unless ENV["HOMEBREW_CURLRC"] args << "--disable" unless ENV["HOMEBREW_CURLRC"]
args << "--globoff" args << "--globoff"
@ -39,7 +39,8 @@ def curl_args(*extra_args, show_output: false, user_agent: :default)
args << "--silent" unless $stdout.tty? args << "--silent" unless $stdout.tty?
end end
args << "--retry" << ENV["HOMEBREW_CURL_RETRIES"] if ENV["HOMEBREW_CURL_RETRIES"] # When changing the default value, the manpage has to be updated.
args << "--retry" << (ENV["HOMEBREW_CURL_RETRIES"] || "3")
args + extra_args args + extra_args
end end

View File

@ -1,7 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require "uri" require "download_strategy"
require "tempfile" require "tempfile"
require "uri"
module GitHub module GitHub
module_function module_function
@ -171,7 +172,7 @@ module GitHub
end end
end end
def open_api(url, data: nil, request_method: nil, scopes: [].freeze) def open_api(url, data: nil, request_method: nil, scopes: [].freeze, parse_json: true)
# This is a no-op if the user is opting out of using the GitHub API. # This is a no-op if the user is opting out of using the GitHub API.
return block_given? ? yield({}) : {} if ENV["HOMEBREW_NO_GITHUB_API"] return block_given? ? yield({}) : {} if ENV["HOMEBREW_NO_GITHUB_API"]
@ -226,11 +227,11 @@ module GitHub
return if http_code == "204" # No Content return if http_code == "204" # No Content
json = JSON.parse output output = JSON.parse output if parse_json
if block_given? if block_given?
yield json yield output
else else
json output
end end
rescue JSON::ParserError => e rescue JSON::ParserError => e
raise Error, "Failed to parse JSON response\n#{e.message}", e.backtrace raise Error, "Failed to parse JSON response\n#{e.message}", e.backtrace
@ -431,6 +432,80 @@ module GitHub
comments.any? { |comment| comment["body"].eql?(body) } comments.any? { |comment| comment["body"].eql?(body) }
end end
def dispatch_event(user, repo, event, **payload)
url = "#{API_URL}/repos/#{user}/#{repo}/dispatches"
open_api(url, data: { event_type: event, client_payload: payload },
request_method: :POST,
scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES)
end
def fetch_artifact(user, repo, pr, dir, workflow_id: "tests.yml", artifact_name: "bottles")
scopes = CREATE_ISSUE_FORK_OR_PR_SCOPES
base_url = "#{API_URL}/repos/#{user}/#{repo}"
pr_payload = open_api("#{base_url}/pulls/#{pr}", scopes: scopes)
pr_sha = pr_payload["head"]["sha"]
pr_branch = pr_payload["head"]["ref"]
workflow = open_api("#{base_url}/actions/workflows/#{workflow_id}/runs?branch=#{pr_branch}", scopes: scopes)
workflow_run = workflow["workflow_runs"].select do |run|
run["head_sha"] == pr_sha
end
if workflow_run.empty?
raise Error, <<~EOS
No matching workflow run found for these criteria!
Commit SHA: #{pr_sha}
Branch ref: #{pr_branch}
Pull request: #{pr}
Workflow: #{workflow_id}
EOS
end
status = workflow_run.first["status"].sub("_", " ")
if status != "completed"
raise Error, <<~EOS
The newest workflow run for ##{pr} is still #{status}!
#{Formatter.url workflow_run.first["html_url"]}
EOS
end
artifacts = open_api(workflow_run.first["artifacts_url"], scopes: scopes)
artifact = artifacts["artifacts"].select do |art|
art["name"] == artifact_name
end
if artifact.empty?
raise Error, <<~EOS
No artifact with the name `#{artifact_name}` was found!
#{Formatter.url workflow_run.first["html_url"]}
EOS
end
artifact_url = artifact.first["archive_download_url"]
token, username = api_credentials
case api_credentials_type
when :env_username_password, :keychain_username_password
curl_args = { user: "#{username}:#{token}" }
when :env_token
curl_args = { header: "Authorization: token #{token}" }
when :none
raise Error, "Credentials must be set to access the Artifacts API"
end
# Download the artifact as a zip file and unpack it into `dir`. This is
# preferred over system `curl` and `tar` as this leverages the Homebrew
# cache to avoid repeated downloads of (possibly large) bottles.
FileUtils.chdir dir do
curl_args[:cache] = Pathname.new(dir)
curl_args[:secrets] = [token]
downloader = CurlDownloadStrategy.new(artifact_url, "artifact", pr, **curl_args)
downloader.fetch
downloader.stage
end
end
def api_errors def api_errors
[GitHub::AuthenticationFailedError, GitHub::HTTPNotFoundError, [GitHub::AuthenticationFailedError, GitHub::HTTPNotFoundError,
GitHub::RateLimitExceededError, GitHub::Error, JSON::ParserError].freeze GitHub::RateLimitExceededError, GitHub::Error, JSON::ParserError].freeze

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
module Utils
module Shebang
module_function
class RewriteInfo
attr_reader :regex, :max_length, :replacement
def initialize(regex, max_length, replacement)
@regex = regex
@max_length = max_length
@replacement = replacement
end
end
def rewrite_shebang(rewrite_info, *paths)
paths.each do |f|
f = Pathname(f)
next unless f.file?
next unless rewrite_info.regex.match?(f.read(rewrite_info.max_length))
Utils::Inreplace.inreplace f.to_s, rewrite_info.regex, "#!#{rewrite_info.replacement}"
end
end
end
end

View File

@ -9,7 +9,7 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/minitest-5.14.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thread_safe-0.3.6/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/thread_safe-0.3.6/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tzinfo-1.2.6/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/tzinfo-1.2.6/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.3.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/zeitwerk-2.3.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/activesupport-6.0.2.1/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/activesupport-6.0.2.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ast-2.4.0/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/ast-2.4.0/lib"
$:.unshift "#{path}/" $:.unshift "#{path}/"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/connection_pool-2.2.2/lib" $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/connection_pool-2.2.2/lib"

Some files were not shown because too many files have changed in this diff Show More