Merge remote-tracking branch 'origin/master' into deprecate-method-formulae
This commit is contained in:
commit
4121659413
31
.github/workflows/tests.yml
vendored
31
.github/workflows/tests.yml
vendored
@ -3,6 +3,9 @@ on:
|
||||
push:
|
||||
branches: master
|
||||
pull_request: []
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
jobs:
|
||||
tests:
|
||||
if: github.repository == 'Homebrew/brew'
|
||||
@ -78,6 +81,12 @@ jobs:
|
||||
sudo chmod -R g-w,o-w /home/linuxbrew /home/runner /opt
|
||||
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
|
||||
run: |
|
||||
# brew tests doesn't like world writable directories
|
||||
@ -107,12 +116,6 @@ jobs:
|
||||
# These cannot be queried at the macOS level on GitHub Actions.
|
||||
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
|
||||
run: |
|
||||
git config --global user.name "BrewTestBot"
|
||||
@ -140,6 +143,22 @@ jobs:
|
||||
run: |
|
||||
if [ "$RUNNER_OS" = "Linux" ]; then
|
||||
docker-compose -f Dockerfile.yml run --rm -v $GITHUB_WORKSPACE:/tmp/test-bot sut
|
||||
docker tag homebrew_sut brew
|
||||
else
|
||||
brew test-bot
|
||||
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"
|
||||
|
||||
@ -2,7 +2,9 @@ inherit_from: ./.rubocop.yml
|
||||
|
||||
AllCops:
|
||||
Include:
|
||||
- '**/*_spec.rb'
|
||||
- '**/cmd/**/*.rb'
|
||||
- '**/lib/**/*.rb'
|
||||
- '**/spec/**/*.rb'
|
||||
Exclude:
|
||||
- '**/vendor/**/*'
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.2.1)
|
||||
activesupport (6.0.2.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
@ -51,7 +51,7 @@ GEM
|
||||
parallel (1.19.1)
|
||||
parallel_tests (2.32.0)
|
||||
parallel
|
||||
parser (2.7.0.4)
|
||||
parser (2.7.0.5)
|
||||
ast (~> 2.4.0)
|
||||
plist (3.5.0)
|
||||
rainbow (3.0.0)
|
||||
@ -81,14 +81,14 @@ GEM
|
||||
rspec-support (3.9.2)
|
||||
rspec-wait (0.0.9)
|
||||
rspec (>= 3, < 4)
|
||||
rubocop (0.80.1)
|
||||
rubocop (0.81.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.0.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
rexml
|
||||
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 (>= 0.71.0)
|
||||
rubocop-rspec (1.38.1)
|
||||
@ -111,8 +111,8 @@ GEM
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.6)
|
||||
unicode-display_width (1.6.1)
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
webrobots (0.1.2)
|
||||
zeitwerk (2.3.0)
|
||||
|
||||
|
||||
135
Library/Homebrew/bintray.rb
Normal file
135
Library/Homebrew/bintray.rb
Normal 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
|
||||
@ -35,6 +35,13 @@ rescue MissingEnvironmentVariables => e
|
||||
exec ENV["HOMEBREW_BREW_FILE"], *ARGV
|
||||
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
|
||||
trap("INT", std_trap) # restore default CTRL-C handler
|
||||
|
||||
@ -141,12 +148,18 @@ rescue Interrupt
|
||||
rescue BuildError => e
|
||||
Utils::Analytics.report_build_error(e)
|
||||
e.dump
|
||||
|
||||
head_unsupported_error if Homebrew.args.HEAD?
|
||||
|
||||
exit 1
|
||||
rescue RuntimeError, SystemCallError => e
|
||||
raise if e.message.empty?
|
||||
|
||||
onoe e
|
||||
$stderr.puts e.backtrace if ARGV.debug?
|
||||
|
||||
head_unsupported_error if Homebrew.args.HEAD?
|
||||
|
||||
exit 1
|
||||
rescue MethodDeprecatedError => e
|
||||
onoe e
|
||||
|
||||
@ -27,7 +27,7 @@ esac
|
||||
export HOMEBREW_COMMAND_DEPTH=$((HOMEBREW_COMMAND_DEPTH + 1))
|
||||
|
||||
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
|
||||
echo -e "\\033[34m==>\\033[0m \\033[1m$*\\033[0m" # blue arrow and bold text
|
||||
else
|
||||
@ -36,7 +36,7 @@ ohai() {
|
||||
}
|
||||
|
||||
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
|
||||
echo -ne "\\033[4;31mError\\033[0m: " >&2 # highlight Error with underline and red color
|
||||
else
|
||||
|
||||
@ -49,9 +49,11 @@ module Cask
|
||||
next if MacOS.version < :high_sierra
|
||||
|
||||
<<~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
|
||||
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")}
|
||||
EOS
|
||||
end
|
||||
|
||||
@ -68,6 +68,19 @@ module Homebrew
|
||||
named.blank?
|
||||
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
|
||||
require "formula"
|
||||
@formulae ||= (downcased_unique_named - casks).map do |name|
|
||||
@ -141,6 +154,10 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def build_stable?
|
||||
!(HEAD? || devel?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def downcased_unique_named
|
||||
@ -160,11 +177,33 @@ module Homebrew
|
||||
end
|
||||
|
||||
def head
|
||||
(args_parsed && HEAD?) || cmdline_args.include?("--HEAD")
|
||||
return true if args_parsed && HEAD?
|
||||
|
||||
cmdline_args.include?("--HEAD")
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def spec(default = :stable)
|
||||
|
||||
@ -95,6 +95,7 @@ module Homebrew
|
||||
end
|
||||
|
||||
def comma_array(name, description: nil)
|
||||
name = name.chomp "="
|
||||
description = option_to_description(name) if description.nil?
|
||||
process_option(name, description)
|
||||
@parser.on(name, OptionParser::REQUIRED_ARGUMENT, Array, *wrap_option_desc(description)) do |list|
|
||||
|
||||
@ -11,7 +11,7 @@ homebrew-shellenv() {
|
||||
echo "set -gx HOMEBREW_PREFIX \"$HOMEBREW_PREFIX\";"
|
||||
echo "set -gx HOMEBREW_CELLAR \"$HOMEBREW_CELLAR\";"
|
||||
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 INFOPATH; or set INFOPATH ''; set -gx INFOPATH \"$HOMEBREW_PREFIX/share/info\" \$INFOPATH;"
|
||||
;;
|
||||
|
||||
@ -73,7 +73,6 @@ module Homebrew
|
||||
|
||||
install_core_tap_if_necessary
|
||||
|
||||
hub = ReporterHub.new
|
||||
updated = false
|
||||
|
||||
initial_revision = ENV["HOMEBREW_UPDATE_BEFORE"].to_s
|
||||
@ -86,6 +85,11 @@ module Homebrew
|
||||
updated = true
|
||||
end
|
||||
|
||||
Homebrew.failed = true if ENV["HOMEBREW_UPDATE_FAILED"]
|
||||
return if ENV["HOMEBREW_DISABLE_LOAD_FORMULA"]
|
||||
|
||||
hub = ReporterHub.new
|
||||
|
||||
updated_taps = []
|
||||
Tap.each do |tap|
|
||||
next unless tap.git?
|
||||
@ -127,8 +131,6 @@ module Homebrew
|
||||
|
||||
link_completions_manpages_and_docs
|
||||
Tap.each(&:link_completions_and_manpages)
|
||||
|
||||
Homebrew.failed = true if ENV["HOMEBREW_UPDATE_FAILED"]
|
||||
end
|
||||
|
||||
def shorten_revision(revision)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "compat/cask/dsl/version"
|
||||
require "compat/language/python"
|
||||
require "compat/requirements/macos_requirement"
|
||||
require "compat/formula"
|
||||
|
||||
17
Library/Homebrew/compat/language/python.rb
Normal file
17
Library/Homebrew/compat/language/python.rb
Normal 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
|
||||
@ -292,7 +292,7 @@ module Homebrew
|
||||
unversioned_name = unversioned_formula.basename(".rb")
|
||||
problem "#{formula} is versioned but no #{unversioned_name} formula exists"
|
||||
end
|
||||
elsif ARGV.build_stable? && formula.stable? &&
|
||||
elsif Homebrew.args.build_stable? && formula.stable? &&
|
||||
!(versioned_formulae = formula.versioned_formulae).empty?
|
||||
versioned_aliases = formula.aliases.grep(/.@\d/)
|
||||
_, last_alias_version = versioned_formulae.map(&:name).last.split("@")
|
||||
@ -520,6 +520,7 @@ module Homebrew
|
||||
gnupg@1.4
|
||||
lua@5.1
|
||||
numpy@1.16
|
||||
libsigc++@2
|
||||
].freeze
|
||||
|
||||
return if keg_only_whitelist.include?(formula.name) || formula.name.start_with?("gcc@")
|
||||
|
||||
@ -40,7 +40,7 @@ module Homebrew
|
||||
description: "Print the pull request URL instead of opening in a browser."
|
||||
switch "--no-fork",
|
||||
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 "\
|
||||
"of URLs, multiple mirrors will be added."
|
||||
flag "--version=",
|
||||
|
||||
@ -31,6 +31,8 @@ module Homebrew
|
||||
description: "Create a basic template for a Perl build."
|
||||
switch "--python",
|
||||
description: "Create a basic template for a Python build."
|
||||
switch "--ruby",
|
||||
description: "Create a basic template for a Ruby build."
|
||||
switch "--rust",
|
||||
description: "Create a basic template for a Rust build."
|
||||
switch "--no-fetch",
|
||||
@ -86,6 +88,8 @@ module Homebrew
|
||||
:perl
|
||||
elsif args.python?
|
||||
:python
|
||||
elsif args.ruby?
|
||||
:ruby
|
||||
elsif args.rust?
|
||||
:rust
|
||||
end
|
||||
|
||||
38
Library/Homebrew/dev-cmd/pr-publish.rb
Normal file
38
Library/Homebrew/dev-cmd/pr-publish.rb
Normal 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
|
||||
168
Library/Homebrew/dev-cmd/pr-pull.rb
Normal file
168
Library/Homebrew/dev-cmd/pr-pull.rb
Normal 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
|
||||
@ -36,6 +36,7 @@ module Homebrew
|
||||
test_args.parse
|
||||
|
||||
require "formula_assertions"
|
||||
require "formula_free_port"
|
||||
|
||||
args.resolved_formulae.each do |f|
|
||||
# Cannot test uninstalled formulae
|
||||
|
||||
@ -43,14 +43,6 @@ module HomebrewArgvExtension
|
||||
include?("--no-sandbox") || !ENV["HOMEBREW_NO_SANDBOX"].nil?
|
||||
end
|
||||
|
||||
def build_stable?
|
||||
!(include?("--HEAD") || include?("--devel"))
|
||||
end
|
||||
|
||||
def build_universal?
|
||||
include? "--universal"
|
||||
end
|
||||
|
||||
def build_bottle?
|
||||
include?("--build-bottle")
|
||||
end
|
||||
@ -84,19 +76,6 @@ module HomebrewArgvExtension
|
||||
value "env"
|
||||
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
|
||||
|
||||
def options_only
|
||||
|
||||
@ -6,6 +6,7 @@ require "lock_file"
|
||||
require "formula_pin"
|
||||
require "hardware"
|
||||
require "utils/bottles"
|
||||
require "utils/shebang"
|
||||
require "utils/shell"
|
||||
require "build_environment"
|
||||
require "build_options"
|
||||
@ -50,6 +51,7 @@ require "find"
|
||||
class Formula
|
||||
include FileUtils
|
||||
include Utils::Inreplace
|
||||
include Utils::Shebang
|
||||
include Utils::Shell
|
||||
include Utils::Deprecate
|
||||
extend Enumerable
|
||||
|
||||
@ -112,6 +112,8 @@ module Homebrew
|
||||
uses_from_macos "perl"
|
||||
<% elsif mode == :python %>
|
||||
depends_on "python"
|
||||
<% elsif mode == :ruby %>
|
||||
uses_from_macos "ruby"
|
||||
<% elsif mode == :rust %>
|
||||
depends_on "rust" => :build
|
||||
<% elsif mode.nil? %>
|
||||
@ -166,6 +168,12 @@ module Homebrew
|
||||
bin.env_script_all_files(libexec/"bin", :PERL5LIB => ENV["PERL5LIB"])
|
||||
<% elsif mode == :python %>
|
||||
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 %>
|
||||
system "cargo", "install", "--locked", "--root", prefix, "--path", "."
|
||||
<% else %>
|
||||
|
||||
15
Library/Homebrew/formula_free_port.rb
Normal file
15
Library/Homebrew/formula_free_port.rb
Normal 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
|
||||
@ -80,7 +80,7 @@ class FormulaInstaller
|
||||
# it's necessary to interrupt the user before any sort of installation
|
||||
# can proceed. Only invoked when the user has no developer tools.
|
||||
def self.prevent_build_flags
|
||||
build_flags = ARGV.collect_build_flags
|
||||
build_flags = Homebrew.args.collect_build_args
|
||||
return if build_flags.empty?
|
||||
|
||||
all_bottled = ARGV.formulae.all?(&:bottled?)
|
||||
@ -598,16 +598,16 @@ class FormulaInstaller
|
||||
oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}"
|
||||
fi.install
|
||||
fi.finish
|
||||
rescue FormulaInstallationAlreadyAttemptedError
|
||||
# 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
|
||||
rescue Exception => e # rubocop:disable Lint/RescueException
|
||||
ignore_interrupts do
|
||||
tmp_keg.rename(installed_keg) if tmp_keg && !installed_keg.directory?
|
||||
linked_keg.link if keg_was_linked
|
||||
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
|
||||
ignore_interrupts { tmp_keg.rmtree if tmp_keg&.directory? }
|
||||
end
|
||||
|
||||
25
Library/Homebrew/language/perl.rb
Normal file
25
Library/Homebrew/language/perl.rb
Normal 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
|
||||
@ -87,14 +87,26 @@ module Language
|
||||
]
|
||||
end
|
||||
|
||||
def self.rewrite_python_shebang(python_path)
|
||||
regex = %r{^#! ?/usr/bin/(env )?python([23](\.\d{1,2})?)?$}
|
||||
maximum_regex_length = 28 # the length of "#! /usr/bin/env pythonx.yyy$"
|
||||
Pathname(".").find do |f|
|
||||
next unless f.file?
|
||||
next unless regex.match?(f.read(maximum_regex_length))
|
||||
# Mixin module for {Formula} adding shebang rewrite features.
|
||||
module Shebang
|
||||
module_function
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -4,6 +4,9 @@ require "pathname"
|
||||
|
||||
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"
|
||||
|
||||
$LOAD_PATH.select! { |d| Pathname(d).directory? }
|
||||
$LOAD_PATH.uniq!
|
||||
|
||||
@ -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.
|
||||
|
||||
* `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`.
|
||||
|
||||
* `HOMEBREW_CURL_VERBOSE`:
|
||||
@ -168,6 +168,7 @@ Note that environment variables must have a value set to be detected. For exampl
|
||||
|
||||
* `HOMEBREW_CURL_RETRIES`:
|
||||
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`:
|
||||
If set, any commands that can emit debugging information will do so.
|
||||
|
||||
@ -16,13 +16,13 @@ module OS
|
||||
when "10.11" then "8.2.1"
|
||||
when "10.12" then "9.2"
|
||||
when "10.13" then "10.1"
|
||||
when "10.14" then "11.3"
|
||||
when "10.15" then "11.3"
|
||||
when "10.14" then "11.3.1"
|
||||
when "10.15" then "11.4"
|
||||
else
|
||||
raise "macOS '#{MacOS.version}' is invalid" unless OS::Mac.prerelease?
|
||||
|
||||
# Default to newest known version of Xcode for unreleased macOS versions.
|
||||
"11.3"
|
||||
"11.4"
|
||||
end
|
||||
end
|
||||
|
||||
@ -175,9 +175,9 @@ module OS
|
||||
when 81 then "8.3"
|
||||
when 90 then "9.2"
|
||||
when 91 then "9.4"
|
||||
when 100 then "10.2.1"
|
||||
when 110 then "11.3"
|
||||
else "11.3"
|
||||
when 100 then "10.3"
|
||||
when 110 then "11.4"
|
||||
else "11.4"
|
||||
end
|
||||
end
|
||||
|
||||
@ -236,8 +236,8 @@ module OS
|
||||
# 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.
|
||||
case MacOS.version
|
||||
when "10.15" then "1100.0.33.16"
|
||||
when "10.14" then "1001.0.46.4"
|
||||
when "10.15" then "1103.0.32.29"
|
||||
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.12" then "900.0.39.2"
|
||||
when "10.11" then "800.0.42.1"
|
||||
|
||||
@ -7,6 +7,7 @@ require "extend/ENV"
|
||||
require "timeout"
|
||||
require "debrew"
|
||||
require "formula_assertions"
|
||||
require "formula_free_port"
|
||||
require "fcntl"
|
||||
require "socket"
|
||||
require "cli/parser"
|
||||
@ -26,6 +27,7 @@ begin
|
||||
|
||||
formula = Homebrew.args.resolved_formulae.first
|
||||
formula.extend(Homebrew::Assertions)
|
||||
formula.extend(Homebrew::FreePort)
|
||||
formula.extend(Debrew::Formula) if Homebrew.args.debug?
|
||||
|
||||
# tests can also return false to indicate failure
|
||||
|
||||
25
Library/Homebrew/test/bintray_spec.rb
Normal file
25
Library/Homebrew/test/bintray_spec.rb
Normal 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
|
||||
@ -71,31 +71,6 @@ describe Cask::Cmd::Upgrade, :cask do
|
||||
expect(local_transmission_path).to be_a_directory
|
||||
expect(local_transmission.versions).to include("2.60")
|
||||
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
|
||||
|
||||
describe "with --greedy it checks additional Casks" do
|
||||
|
||||
7
Library/Homebrew/test/dev-cmd/pr-publish_spec.rb
Normal file
7
Library/Homebrew/test/dev-cmd/pr-publish_spec.rb
Normal 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
|
||||
7
Library/Homebrew/test/dev-cmd/pr-pull_spec.rb
Normal file
7
Library/Homebrew/test/dev-cmd/pr-pull_spec.rb
Normal 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
|
||||
25
Library/Homebrew/test/formula_free_port_spec.rb
Normal file
25
Library/Homebrew/test/formula_free_port_spec.rb
Normal 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
|
||||
52
Library/Homebrew/test/language/perl/shebang_spec.rb
Normal file
52
Library/Homebrew/test/language/perl/shebang_spec.rb
Normal 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
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
require "language/python"
|
||||
require "resource"
|
||||
require "utils/shebang"
|
||||
|
||||
describe Language::Python, :needs_python do
|
||||
describe "#major_minor_version" do
|
||||
@ -32,6 +33,48 @@ describe Language::Python, :needs_python do
|
||||
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
|
||||
subject { described_class.new(formula, dir, "python") }
|
||||
|
||||
|
||||
@ -157,7 +157,7 @@ describe Resource do
|
||||
end
|
||||
|
||||
specify "#verify_download_integrity_mismatch" do
|
||||
fn = double(file?: true)
|
||||
fn = double(file?: true, basename: "foo")
|
||||
checksum = subject.sha256(TEST_SHA256)
|
||||
|
||||
expect(fn).to receive(:verify_checksum).with(checksum)
|
||||
|
||||
@ -4,19 +4,23 @@ require "utils/curl"
|
||||
|
||||
describe "curl" do
|
||||
describe "curl_args" do
|
||||
it "returns -q as the first argument when HOMEBREW_CURLRC is not set" do
|
||||
# -q must be the first argument according to "man curl"
|
||||
expect(curl_args("foo").first).to eq("-q")
|
||||
it "returns --disable as the first argument when HOMEBREW_CURLRC is not set" do
|
||||
# --disable must be the first argument according to "man curl"
|
||||
expect(curl_args("foo").first).to eq("--disable")
|
||||
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"
|
||||
expect(curl_args("foo").first).not_to eq("-q")
|
||||
expect(curl_args("foo").first).not_to eq("--disable")
|
||||
end
|
||||
|
||||
it "returns --retry when HOMEBREW_CURL_RETRIES is set" do
|
||||
ENV["HOMEBREW_CURL_RETRIES"] = "3"
|
||||
it "uses `--retry 3` when HOMEBREW_CURL_RETRIES is unset" do
|
||||
expect(curl_args("foo").join(" ")).to include("--retry 3")
|
||||
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
|
||||
|
||||
@ -41,4 +41,18 @@ describe GitHub do
|
||||
expect(results.first["title"]).to eq("Shall we run `brew update` automatically?")
|
||||
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
|
||||
|
||||
@ -12,7 +12,7 @@ module Utils
|
||||
args = []
|
||||
|
||||
# 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[
|
||||
--max-time 3
|
||||
|
||||
@ -17,7 +17,7 @@ def curl_args(*extra_args, show_output: false, user_agent: :default)
|
||||
args = []
|
||||
|
||||
# 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"
|
||||
|
||||
@ -39,7 +39,8 @@ def curl_args(*extra_args, show_output: false, user_agent: :default)
|
||||
args << "--silent" unless $stdout.tty?
|
||||
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
|
||||
end
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "uri"
|
||||
require "download_strategy"
|
||||
require "tempfile"
|
||||
require "uri"
|
||||
|
||||
module GitHub
|
||||
module_function
|
||||
@ -171,7 +172,7 @@ module GitHub
|
||||
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.
|
||||
return block_given? ? yield({}) : {} if ENV["HOMEBREW_NO_GITHUB_API"]
|
||||
|
||||
@ -226,11 +227,11 @@ module GitHub
|
||||
|
||||
return if http_code == "204" # No Content
|
||||
|
||||
json = JSON.parse output
|
||||
output = JSON.parse output if parse_json
|
||||
if block_given?
|
||||
yield json
|
||||
yield output
|
||||
else
|
||||
json
|
||||
output
|
||||
end
|
||||
rescue JSON::ParserError => e
|
||||
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) }
|
||||
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
|
||||
[GitHub::AuthenticationFailedError, GitHub::HTTPNotFoundError,
|
||||
GitHub::RateLimitExceededError, GitHub::Error, JSON::ParserError].freeze
|
||||
|
||||
27
Library/Homebrew/utils/shebang.rb
Normal file
27
Library/Homebrew/utils/shebang.rb
Normal 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
|
||||
@ -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/tzinfo-1.2.6/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}/"
|
||||
$:.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
Loading…
x
Reference in New Issue
Block a user