Merge remote-tracking branch 'origin/master' into deprecate-cask-commands

This commit is contained in:
Mike McQuaid 2020-11-25 09:39:00 +00:00
commit f364f0c841
No known key found for this signature in database
GPG Key ID: 48A898132FD8EE70
94 changed files with 1249 additions and 1172 deletions

View File

@ -19,7 +19,7 @@ jobs:
steps:
- name: Re-run this workflow
if: github.event_name == 'schedule' || github.event.action == 'closed'
uses: reitermarkus/rerun-workflow@cf91bee6964dfde64eccbf5600c3ea206af11359
uses: reitermarkus/rerun-workflow@e2647e8885422412d5acbd61f9add5b1e77ad3ab
with:
token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
continuous-label: waiting for feedback
@ -101,6 +101,30 @@ jobs:
}
}
function offsetDate(start, offsetHours, skippedDays) {
let end = new Date(start)
end.setUTCHours(end.getUTCHours() + (offsetHours % 24))
while (skippedDays.includes(end.getUTCDay()) || offsetHours >= 24) {
if (!skippedDays.includes(end.getUTCDay())) {
offsetHours -= 24
}
end.setUTCDate(end.getUTCDate() + 1)
}
if (skippedDays.includes(start.getUTCDay())) {
end.setUTCHours(offsetHours, 0, 0)
}
return end
}
function formatDate(date) {
return date.toISOString().replace(/\.\d+Z$/, ' UTC').replace('T', ' at ')
}
async function reviewPullRequest(pullRequestNumber) {
const { data: pullRequest } = await github.pulls.get({
owner: context.repo.owner,
@ -108,6 +132,12 @@ jobs:
pull_number: pullRequestNumber,
})
const { data: user } = await github.users.getAuthenticated()
if (pullRequest.user.login == user) {
core.warning('Pull request author is a bot.')
return
}
if (pullRequest.author_association != 'MEMBER') {
core.warning('Pull request author is not a member.')
return
@ -120,38 +150,21 @@ jobs:
const hasReviewLabel = labels.includes(reviewLabel)
const hasCriticalLabel = labels.includes(criticalLabel)
const reviewStartDate = new Date(pullRequest.created_at)
const reviewEndDate = new Date(reviewStartDate)
switch (reviewStartDate.getDay()) {
// Skip from Friday to Monday and from Saturday to Tuesday.
case 5:
case 6:
reviewEndDate.setDate(reviewStartDate.getDate() + 3)
break
// Skip from Sunday to Tuesday.
case 0:
reviewEndDate.setDate(reviewStartDate.getDate() + 2)
break
default:
reviewEndDate.setDate(reviewStartDate.getDate() + 1)
break
}
const offsetHours = 24
const skippedDays = [
6, // Saturday
0, // Sunday
]
const currentDate = new Date()
const reviewStartDate = new Date(pullRequest.created_at)
const reviewEndDate = offsetDate(reviewStartDate, offsetHours, skippedDays)
const reviewEnded = currentDate > reviewEndDate
function formatDate(date) {
return date.toISOString().replace(/\.\d+Z$/, ' UTC').replace('T', ' at ')
}
if (reviewEnded || hasCriticalLabel) {
let message
if (hasCriticalLabel && !reviewEnded) {
if (hasReviewLabel) {
message = `Review period cancelled due to \`${criticalLabel}\` label.`
} else {
message = `Review period skipped due to \`${criticalLabel}\` label.`
}
message = `Review period skipped due to \`${criticalLabel}\` label.`
} else {
message = 'Review period ended.'
}
@ -169,7 +182,7 @@ jobs:
await approvePullRequest(pullRequestNumber)
} else {
const message = `Review period will end on ${formatDate(reviewEndDate)}.`
core.warning(message)
core.info(message)
await dismissApprovals(pullRequestNumber, 'Review period has not ended yet.')
await createOrUpdateComment(pullRequestNumber, 'review-period-begin', message)

View File

@ -54,8 +54,8 @@ GEM
nokogiri (1.10.10)
mini_portile2 (~> 2.4.0)
ntlm-http (0.1.1)
parallel (1.20.0)
parallel_tests (3.3.0)
parallel (1.20.1)
parallel_tests (3.4.0)
parallel
parlour (4.0.1)
commander (~> 4.5)
@ -126,11 +126,11 @@ GEM
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov-html (0.12.3)
sorbet (0.5.6100)
sorbet-static (= 0.5.6100)
sorbet-runtime (0.5.6100)
sorbet (0.5.6101)
sorbet-static (= 0.5.6101)
sorbet-runtime (0.5.6101)
sorbet-runtime-stub (0.2.0)
sorbet-static (0.5.6100-universal-darwin-14)
sorbet-static (0.5.6101-universal-darwin-14)
spoom (1.0.6)
colorize
sorbet (~> 0.5.5)

View File

@ -160,7 +160,7 @@ update-preinstall() {
fi
if [[ "$HOMEBREW_COMMAND" = "install" || "$HOMEBREW_COMMAND" = "upgrade" ||
"$HOMEBREW_COMMAND" = "bump-formula-pr" ||
"$HOMEBREW_COMMAND" = "bump-formula-pr" || "$HOMEBREW_COMMAND" = "bump-cask-pr" ||
"$HOMEBREW_COMMAND" = "bundle" ||
"$HOMEBREW_COMMAND" = "tap" && $HOMEBREW_ARG_COUNT -gt 1 ||
"$HOMEBREW_CASK_COMMAND" = "install" || "$HOMEBREW_CASK_COMMAND" = "upgrade" ]]
@ -345,7 +345,7 @@ else
: "${HOMEBREW_OS_VERSION:=$(uname -r)}"
HOMEBREW_OS_USER_AGENT_VERSION="$HOMEBREW_OS_VERSION"
if [[ -n $HOMEBREW_FORCE_HOMEBREW_ON_LINUX && -n $HOMEBREW_DEVELOPER && -n $HOMEBREW_ON_DEBIAN7 ]]
if [[ -n "$HOMEBREW_FORCE_HOMEBREW_ON_LINUX" && -n "$HOMEBREW_ON_DEBIAN7" ]]
then
# Special version for our debian 7 docker container used to build patchelf and binutils
HOMEBREW_MINIMUM_CURL_VERSION="7.25.0"

View File

@ -13,9 +13,8 @@ class BuildOptions
# TODO: rename private_include? when include? is removed.
# @deprecated
def include?(name)
odeprecated "BuildOptions#include?"
private_include?("--#{name}")
def include?(_)
odisabled "BuildOptions#include?"
end
# True if a {Formula} is being built with a specific option.
@ -65,11 +64,6 @@ class BuildOptions
private_include? "HEAD"
end
# @private
def devel?
odisabled "BuildOptions#devel?"
end
# True if a {Formula} is being built with {Formula.stable} instead of {Formula.head}.
# This is the default.
# <pre>args << "--some-beta" if build.head?</pre>
@ -81,14 +75,12 @@ class BuildOptions
# e.g. on newer Intel Macs this means a combined x86_64/x86 binary/library.
# <pre>args << "--universal-binary" if build.universal?</pre>
def universal?
odeprecated "BuildOptions#universal?"
private_include?("universal") && option_defined?("universal")
odisabled "BuildOptions#universal?"
end
# True if a {Formula} is being built in C++11 mode.
def cxx11?
odeprecated "BuildOptions#cxx11?"
private_include?("c++11") && option_defined?("c++11")
odisabled "BuildOptions#cxx11?"
end
# True if the build has any arguments or options specified.

View File

@ -63,48 +63,39 @@ module Homebrew
end
def formulae
odeprecated "args.formulae", "args.named.to_formulae"
named.to_formulae
odisabled "args.formulae", "args.named.to_formulae"
end
def formulae_and_casks
odeprecated "args.formulae_and_casks", "args.named.to_formulae_and_casks"
named.to_formulae_and_casks
odisabled "args.formulae_and_casks", "args.named.to_formulae_and_casks"
end
def resolved_formulae
odeprecated "args.resolved_formulae", "args.named.to_resolved_formulae"
named.to_resolved_formulae
odisabled "args.resolved_formulae", "args.named.to_resolved_formulae"
end
def resolved_formulae_casks
odeprecated "args.resolved_formulae_casks", "args.named.to_resolved_formulae_to_casks"
named.to_resolved_formulae_to_casks
odisabled "args.resolved_formulae_casks", "args.named.to_resolved_formulae_to_casks"
end
def formulae_paths
odeprecated "args.formulae_paths", "args.named.to_formulae_paths"
named.to_formulae_paths
odisabled "args.formulae_paths", "args.named.to_formulae_paths"
end
def casks
odeprecated "args.casks", "args.named.homebrew_tap_cask_names"
named.homebrew_tap_cask_names
odisabled "args.casks", "args.named.homebrew_tap_cask_names"
end
def loaded_casks
odeprecated "args.loaded_casks", "args.named.to_cask"
named.to_casks
odisabled "args.loaded_casks", "args.named.to_cask"
end
def kegs
odeprecated "args.kegs", "args.named.to_kegs"
named.to_kegs
odisabled "args.kegs", "args.named.to_kegs"
end
def kegs_casks
odeprecated "args.kegs", "args.named.to_kegs_to_casks"
named.to_kegs_to_casks
odisabled "args.kegs", "args.named.to_kegs_to_casks"
end
def build_stable?

View File

@ -57,16 +57,7 @@ module Homebrew
files["00.tap.out"] = { content: tap }
end
if GitHub.api_credentials_type == :none
puts <<~EOS
You can create a new personal access token:
#{GitHub::ALL_SCOPES_URL}
#{Utils::Shell.set_variable_in_profile("HOMEBREW_GITHUB_API_TOKEN", "your_token_here")}
EOS
odeprecated "`brew gist-logs` with a password", "HOMEBREW_GITHUB_API_TOKEN"
login!
end
odisabled "`brew gist-logs` with a password", "HOMEBREW_GITHUB_API_TOKEN" if GitHub.api_credentials_type == :none
# Description formatted to work well as page title when viewing gist
descr = if f.core_formula?

View File

@ -103,8 +103,7 @@ module Homebrew
ls_args << "-t" if args.t?
if !$stdout.tty? && !args.formula?
odeprecated "`brew list` to only list formulae", "`brew list --formula`"
safe_system "ls", *ls_args, HOMEBREW_CELLAR
odisabled "`brew list` to only list formulae", "`brew list --formula`"
else
safe_system "ls", *ls_args, HOMEBREW_CELLAR
list_casks(args: args) unless args.formula?

View File

@ -50,16 +50,7 @@ module Homebrew
case (j = json_version(args.json))
when :v1, :default
odeprecated "brew outdated --json#{j == :v1 ? "=v1" : ""}", "brew outdated --json=v2"
outdated = if args.formula? || !args.cask?
outdated_formulae args: args
else
outdated_casks args: args
end
puts JSON.generate(json_info(outdated, args: args))
odisabled "brew outdated --json#{j == :v1 ? "=v1" : ""}", "brew outdated --json=v2"
when :v2
formulae, casks = if args.formula?
[outdated_formulae(args: args), []]

View File

@ -32,7 +32,7 @@ module Homebrew
odie "#{name} not found in the Cellar." unless rack.directory?
# odeprecated "`brew switch`", "`brew link` @-versioned formulae"
odeprecated "`brew switch`", "`brew link` @-versioned formulae"
versions = rack.subdirs
.map { |d| Keg.new(d).version }

View File

@ -1,12 +1,6 @@
# typed: strict
# frozen_string_literal: true
require "compat/dependencies_helpers"
require "compat/cli/parser"
require "compat/extend/nil"
require "compat/extend/string"
require "compat/formula"
require "compat/global"
require "compat/language/java"
require "compat/language/python"
require "compat/os/mac" if OS.mac?

View File

@ -1,18 +0,0 @@
# typed: true
# frozen_string_literal: true
require "cli/args"
module DependenciesHelpers
module Compat
def argv_includes_ignores(argv = nil)
unless @printed_includes_ignores_warning
odisabled "Homebrew.argv_includes_ignores", "Homebrew.args_includes_ignores"
@printed_includes_ignores_warning = true
end
args_includes_ignores(argv ? Homebrew::CLI::Args.new : Homebrew.args)
end
end
prepend Compat
end

View File

@ -1,9 +0,0 @@
# typed: strict
module DependenciesHelpers
module Compat
include Kernel
def args_includes_ignores(args); end
end
end

View File

@ -1,12 +0,0 @@
# typed: true
# frozen_string_literal: true
class NilClass
module Compat
def chuzzle
odisabled ".chuzzle", "&.chomp.presence"
end
end
prepend Compat
end

View File

@ -1,7 +0,0 @@
# typed: strict
class NilClass
module Compat
include Kernel
end
end

View File

@ -1,12 +0,0 @@
# typed: true
# frozen_string_literal: true
class String
module Compat
def chuzzle
odisabled ".chuzzle", "&.chomp.presence"
end
end
prepend Compat
end

View File

@ -1,7 +0,0 @@
# typed: strict
class String
module Compat
include Kernel
end
end

View File

@ -3,33 +3,20 @@
class Formula
module Compat
def installed?
odisabled "Formula#installed?",
"Formula#latest_version_installed? (or Formula#any_version_installed? )"
end
def prepare_patches
odisabled "patches", "patch do" if respond_to?(:patches)
super
end
def installed_prefix
odeprecated "Formula#installed_prefix",
"Formula#latest_installed_prefix (or Formula#any_installed_prefix)"
latest_installed_prefix
odisabled "Formula#installed_prefix",
"Formula#latest_installed_prefix (or Formula#any_installed_prefix)"
end
# The currently installed version for this formula. Will raise an exception
# if the formula is not installed.
# @private
def installed_version
odeprecated "Formula#installed_version"
Keg.new(latest_installed_prefix).version
odisabled "Formula#installed_version"
end
def opt_or_installed_prefix_keg
odeprecated "Formula#opt_or_installed_prefix_keg", "Formula#any_installed_keg"
any_installed_keg
odisabled "Formula#opt_or_installed_prefix_keg", "Formula#any_installed_keg"
end
end

View File

@ -7,8 +7,7 @@ module Homebrew
def args
unless @printed_args_warning
odeprecated "Homebrew.args", "`args = <command>_args.parse` and pass `args` along the call chain"
@printed_args_warning = true
odisabled "Homebrew.args", "`args = <command>_args.parse` and pass `args` along the call chain"
end
@args ||= CLI::Args.new

View File

@ -4,35 +4,9 @@
module Language
module Haskell
module Cabal
module Compat
def cabal_sandbox(_options = {})
odisabled "Language::Haskell::Cabal.cabal_sandbox"
end
def cabal_sandbox_add_source(*_args)
odisabled "Language::Haskell::Cabal.cabal_sandbox_add_source"
end
def cabal_install(*_args)
odisabled "Language::Haskell::Cabal.cabal_install",
"cabal v2-install directly with std_cabal_v2_args"
end
def cabal_configure(_flags)
odisabled "Language::Haskell::Cabal.cabal_configure"
end
def cabal_install_tools(*_tools)
odisabled "Language::Haskell::Cabal.cabal_install_tools"
end
def install_cabal_package(*_args, **_options)
odisabled "Language::Haskell::Cabal.install_cabal_package",
"cabal v2-update directly followed by v2-install with std_cabal_v2_args"
end
def self.included(_)
odisabled "include Language::Haskell::Cabal"
end
prepend Compat
end
end
end

View File

@ -1,17 +0,0 @@
# typed: false
# frozen_string_literal: true
module Language
module Java
class << self
module Compat
def java_home_cmd(_version = nil)
odisabled "Language::Java.java_home_cmd",
"Language::Java.java_home or Language::Java.overridable_java_home_env"
end
end
prepend Compat
end
end
end

View File

@ -1,17 +0,0 @@
# typed: false
# frozen_string_literal: true
module Language
module Python
class << self
module Compat
def rewrite_python_shebang(_python_path)
odisabled "Language::Python.rewrite_python_shebang",
"Utils::Shebang.rewrite_shebang and Shebang.python_shebang_rewrite_info(python_path)"
end
end
prepend Compat
end
end
end

View File

@ -1,24 +0,0 @@
# typed: true
# frozen_string_literal: true
module OS
module Mac
class << self
module Compat
def preferred_arch
odisabled "MacOS.preferred_arch", "Hardware::CPU.arch (or ideally let the compiler handle it)"
end
def tcc_db
odisabled "MacOS.tcc_db"
end
def pre_mavericks_accessibility_dotfile
odisabled "MacOS.pre_mavericks_accessibility_dotfile"
end
end
prepend Compat
end
end
end

View File

@ -1,11 +0,0 @@
# typed: strict
module OS
module Mac
class << self
module Compat
include Kernel
end
end
end
end

View File

@ -34,15 +34,15 @@ module Context
end
def debug?
@debug
@debug == true
end
def quiet?
@quiet
@quiet == true
end
def verbose?
@verbose
@verbose == true
end
end

View File

@ -180,7 +180,7 @@ module Homebrew
except: args.except,
spdx_license_data: spdx_license_data,
spdx_exception_data: spdx_exception_data,
tap_audit_exceptions: f.tap.audit_exceptions,
tap_audit_exceptions: f.tap&.audit_exceptions,
style_offenses: style_offenses ? style_offenses.for_path(f.path) : nil,
display_cop_names: args.display_cop_names?,
build_stable: args.build_stable?,

View File

@ -74,7 +74,7 @@ module Homebrew
new_hash = args.sha256
old_version = cask.version
old_hash = cask.sha256
old_hash = cask.sha256.to_s
tap_full_name = cask.tap&.full_name
origin_branch = Utils::Git.origin_branch(cask.tap.path) if cask.tap
@ -159,7 +159,7 @@ module Homebrew
lang_cask = Cask::CaskLoader.load(tmp_contents)
lang_cask.config = lang_config
lang_url = lang_cask.url.to_s
lang_old_hash = lang_cask.sha256
lang_old_hash = lang_cask.sha256.to_s
resource_path = fetch_resource(cask, new_version, lang_url)
Utils::Tar.validate_file(resource_path)

View File

@ -336,7 +336,7 @@ module Homebrew
end
unless args.dry_run?
resources_checked = PyPI.update_python_resources! formula, new_formula_version,
resources_checked = PyPI.update_python_resources! formula, version: new_formula_version,
silent: args.quiet?, ignore_non_pypi_packages: true
end

View File

@ -6,6 +6,7 @@ require "formula_creator"
require "missing_formula"
require "cli/parser"
require "utils/pypi"
require "cask/cask_loader"
module Homebrew
extend T::Sig
@ -18,14 +19,16 @@ module Homebrew
usage_banner <<~EOS
`create` [<options>] <URL>
Generate a formula for the downloadable file at <URL> and open it in the editor.
Homebrew will attempt to automatically derive the formula name and version, but
if it fails, you'll have to make your own template. The `wget` formula serves as
a simple example. For the complete API, see:
Generate a formula or, with `--cask`, a cask for the downloadable file at <URL>
and open it in the editor. Homebrew will attempt to automatically derive the
formula name and version, but if it fails, you'll have to make your own template.
The `wget` formula serves as a simple example. For the complete API, see:
<https://rubydoc.brew.sh/Formula>
EOS
switch "--autotools",
description: "Create a basic template for an Autotools-style build."
switch "--cask",
description: "Create a basic template for a cask."
switch "--cmake",
description: "Create a basic template for a CMake-style build."
switch "--crystal",
@ -51,9 +54,10 @@ module Homebrew
switch "--HEAD",
description: "Indicate that <URL> points to the package's repository rather than a file."
flag "--set-name=",
description: "Explicitly set the <name> of the new formula."
description: "Explicitly set the <name> of the new formula or cask.",
required_for: "--cask"
flag "--set-version=",
description: "Explicitly set the <version> of the new formula."
description: "Explicitly set the <version> of the new formula or cask."
flag "--set-license=",
description: "Explicitly set the <license> of the new formula."
flag "--tap=",
@ -61,7 +65,12 @@ module Homebrew
switch "-f", "--force",
description: "Ignore errors for disallowed formula names and names that shadow aliases."
conflicts "--autotools", "--cmake", "--crystal", "--go", "--meson", "--node", "--perl", "--python", "--rust"
conflicts "--autotools", "--cmake", "--crystal", "--go", "--meson", "--node",
"--perl", "--python", "--ruby", "--rust", "--cask"
conflicts "--cask", "--HEAD"
conflicts "--cask", "--set-license"
conflicts "--cask", "--tap"
named 1
end
end
@ -70,24 +79,73 @@ module Homebrew
def create
args = create_args.parse
# Ensure that the cache exists so we can fetch the tarball
HOMEBREW_CACHE.mkpath
path = if args.cask?
create_cask(args: args)
else
create_formula(args: args)
end
url = args.named.first # Pull the first (and only) url from ARGV
exec_editor path
end
version = args.set_version
name = args.set_name
license = args.set_license
tap = args.tap
def create_cask(args:)
url = args.named.first
if (token = args.set_name).nil?
raise UsageError, "The `--set-name` flag is required for creating casks."
end
cask_path = Cask::CaskLoader.path(token)
raise Cask::CaskAlreadyCreatedError, token if cask_path.exist?
version = if args.set_version
Version.create(args.set_version)
else
Version.detect(url.gsub(token, ""))
end
interpolated_url, sha256 = if version.null?
[url, ""]
else
sha256 = if args.no_fetch?
""
else
strategy = DownloadStrategyDetector.detect(url)
downloader = strategy.new(url, token, version.to_s, cache: Cask::Cache.path)
downloader.fetch
downloader.cached_location.sha256
end
[url.gsub(version.to_s, "\#{version}"), sha256]
end
cask_path.atomic_write <<~RUBY
cask "#{token}" do
version "#{version}"
sha256 "#{sha256}"
url "#{interpolated_url}"
name ""
desc ""
homepage ""
app ""
end
RUBY
puts "Please run `brew audit --cask --new #{token}` before submitting, thanks."
cask_path
end
def create_formula(args:)
fc = FormulaCreator.new(args)
fc.name = name
fc.version = version
fc.license = license
fc.tap = Tap.fetch(tap || "homebrew/core")
fc.name = args.name
fc.version = args.version
fc.license = args.license
fc.tap = Tap.fetch(args.tap || "homebrew/core")
raise TapUnavailableError, tap unless fc.tap.installed?
fc.url = url
fc.url = args.named.first # Pull the first (and only) url from ARGV
fc.mode = if args.cmake?
:cmake
@ -143,8 +201,8 @@ module Homebrew
PyPI.update_python_resources! Formula[fc.name], ignore_non_pypi_packages: true if args.python?
puts "Please run `brew audit --new-formula #{fc.name}` before submitting, thanks."
exec_editor fc.path
puts "Please run `brew audit --new #{fc.name}` before submitting, thanks."
fc.path
end
def __gets

View File

@ -25,13 +25,14 @@ module Homebrew
description: "Explicitly set the <version> of the package being installed."
max_named 0
hide_from_man_page!
end
end
def diy
args = diy_args.parse
# odeprecated "`brew diy`"
odeprecated "`brew diy`"
path = Pathname.getwd

View File

@ -25,7 +25,6 @@ module Homebrew
description: "Upload to Bintray, but don't publish."
min_named :formula
hide_from_man_page!
end
end

View File

@ -50,7 +50,8 @@ module Homebrew
depends_on: "--autosquash",
description: "Message to include when autosquashing revision bumps, deletions, and rebuilds."
flag "--workflow=",
description: "Retrieve artifacts from the specified workflow (default: `tests.yml`)."
description: "Retrieve artifacts from the specified workflow (default: `tests.yml`). "\
"Legacy: use --workflows instead"
flag "--artifact=",
description: "Download artifacts with the specified name (default: `bottles`)."
flag "--bintray-org=",
@ -62,6 +63,11 @@ module Homebrew
flag "--bintray-mirror=",
description: "Use the specified Bintray repository to automatically mirror stable URLs "\
"defined in the formulae (default: `mirror`)."
comma_array "--workflows=",
description: "Retrieve artifacts from the specified workflow (default: `tests.yml`) "\
"Comma-separated list to include multiple workflows."
comma_array "--ignore-missing-artifacts=",
description: "Comma-separated list of workflows which can be ignored if they have not been run."
conflicts "--clean", "--autosquash"
min_named 1
@ -357,7 +363,13 @@ module Homebrew
def pr_pull
args = pr_pull_args.parse
workflow = args.workflow || "tests.yml"
odeprecated "`brew pr-pull --workflow`", "`brew pr-pull --workflows=`" if args.workflow.presence
workflows = if args.workflow.blank?
args.workflows.presence || ["tests.yml"]
else
[args.workflow].compact.presence || ["tests.yml"]
end
artifact = args.artifact || "bottles"
bintray_org = args.bintray_org || "homebrew"
mirror_repo = args.bintray_mirror || "mirror"
@ -401,8 +413,22 @@ module Homebrew
next
end
url = GitHub.get_artifact_url(user, repo, pr, workflow_id: workflow, artifact_name: artifact)
download_artifact(url, dir, pr)
workflows.each do |workflow|
workflow_run = GitHub.get_workflow_run(
user, repo, pr, workflow_id: workflow, artifact_name: artifact
)
if args.ignore_missing_artifacts.present? &&
args.ignore_missing_artifacts.include?(workflow) &&
workflow_run.empty?
# Ignore that workflow as it was not executed and we specified
# that we could skip it.
next
end
ohai "Downloading bottles for workflow: #{workflow}"
url = GitHub.get_artifact_url(workflow_run)
download_artifact(url, dir, pr)
end
next if args.no_upload?

View File

@ -1,52 +0,0 @@
# typed: false
# frozen_string_literal: true
require "net/http"
require "net/https"
require "json"
require "cli/parser"
require "formula"
require "formulary"
require "version"
require "pkg_version"
require "formula_info"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def pull_args
Homebrew::CLI::Parser.new do
usage_banner <<~EOS
`pull` [<options>] <patch>
Get a patch from a GitHub commit or pull request and apply it to Homebrew.
Each <patch> may be the number of a pull request in `homebrew/core`
or the URL of any pull request or commit on GitHub.
EOS
switch "--bump",
description: "For one-formula PRs, automatically reword commit message to our preferred format."
switch "--clean",
description: "Do not rewrite or otherwise modify the commits found in the pulled PR."
switch "--ignore-whitespace",
description: "Silently ignore whitespace discrepancies when applying diffs."
switch "--resolve",
description: "When a patch fails to apply, leave in progress and allow user to resolve, instead "\
"of aborting."
switch "--branch-okay",
description: "Do not warn if pulling to a branch besides master (useful for testing)."
switch "--no-pbcopy",
description: "Do not copy anything to the system clipboard."
min_named 1
hide_from_man_page!
end
end
def pull
odisabled "brew pull", "gh pr checkout"
end
end

View File

@ -54,7 +54,9 @@ module Homebrew
safe_system(ENV["SHELL"], args.named.first)
else
subshell = if ENV["SHELL"].include?("zsh")
"PS1='brew %B%F{green}%~%f%b$ ' #{ENV["SHELL"]} -d"
"PS1='brew %B%F{green}%~%f%b$ ' #{ENV["SHELL"]} -d -f"
elsif ENV["SHELL"].include?("bash")
"PS1=\"brew \\[\\033[1;32m\\]\\w\\[\\033[0m\\]$ \" #{ENV["SHELL"]} --noprofile --norc"
else
"PS1=\"brew \\[\\033[1;32m\\]\\w\\[\\033[0m\\]$ \" #{ENV["SHELL"]}"
end

View File

@ -26,6 +26,13 @@ module Homebrew
flag "--version=",
description: "Use the specified <version> when finding resources for <formula>. "\
"If no version is specified, the current version for <formula> will be used."
flag "--package-name=",
description: "Use the specified <package-name> when finding resources for <formula>. "\
"If no package name is specified, it will be inferred from the formula's stable URL."
comma_array "--extra-packages=",
description: "Include these additional packages when finding resources."
comma_array "--exclude-packages=",
description: "Exclude these packages when finding resources."
min_named :formula
end
@ -35,7 +42,13 @@ module Homebrew
args = update_python_resources_args.parse
args.named.to_formulae.each do |formula|
PyPI.update_python_resources! formula, args.version, print_only: args.print_only?, silent: args.silent?,
PyPI.update_python_resources! formula,
version: args.version,
package_name: args.package_name,
extra_packages: args.extra_packages,
exclude_packages: args.exclude_packages,
print_only: args.print_only?,
silent: args.silent?,
ignore_non_pypi_packages: args.ignore_non_pypi_packages?
end
end

View File

@ -351,6 +351,7 @@ module Homebrew
end
# Needs a custom implementation.
sig { returns(String) }
def make_jobs
jobs = ENV["HOMEBREW_MAKE_JOBS"].to_i
return jobs.to_s if jobs.positive?

View File

@ -1,4 +1,4 @@
# typed: false
# typed: strict
# frozen_string_literal: true
require "hardware"
@ -7,11 +7,22 @@ require "extend/ENV/shared"
require "extend/ENV/std"
require "extend/ENV/super"
def superenv?(env)
env != "std" && Superenv.bin
module Kernel
extend T::Sig
sig { params(env: T.nilable(String)).returns(T::Boolean) }
def superenv?(env)
return false if env == "std"
!Superenv.bin.nil?
end
private :superenv?
end
module EnvActivation
extend T::Sig
sig { params(env: T.nilable(String)).void }
def activate_extensions!(env: nil)
if superenv?(env)
extend(Superenv)
@ -20,25 +31,41 @@ module EnvActivation
end
end
def with_build_environment(env: nil, cc: nil, build_bottle: false, bottle_arch: nil)
sig do
params(
env: T.nilable(String),
cc: T.nilable(String),
build_bottle: T.nilable(T::Boolean),
bottle_arch: T.nilable(String),
_block: T.proc.returns(T.untyped),
).returns(T.untyped)
end
def with_build_environment(env: nil, cc: nil, build_bottle: false, bottle_arch: nil, &_block)
old_env = to_hash.dup
tmp_env = to_hash.dup.extend(EnvActivation)
tmp_env.activate_extensions!(env: env)
tmp_env.setup_build_environment(cc: cc, build_bottle: build_bottle, bottle_arch: bottle_arch)
T.cast(tmp_env, EnvActivation).activate_extensions!(env: env)
T.cast(tmp_env, T.any(Superenv, Stdenv))
.setup_build_environment(cc: cc, build_bottle: build_bottle, bottle_arch: bottle_arch)
replace(tmp_env)
yield
ensure
replace(old_env)
begin
yield
ensure
replace(old_env)
end
end
sig { params(key: T.any(String, Symbol)).returns(T::Boolean) }
def sensitive?(key)
/(cookie|key|token|password)/i =~ key
key.match?(/(cookie|key|token|password)/i)
end
sig { returns(T::Hash[String, String]) }
def sensitive_environment
select { |key, _| sensitive?(key) }
end
sig { void }
def clear_sensitive_environment!
each_key { |key| delete key if sensitive?(key) }
end

View File

@ -0,0 +1,49 @@
# typed: strict
module EnvMethods
include Kernel
sig { params(key: String).returns(T::Boolean) }
def key?(key); end
sig { params(key: String).returns(T.nilable(String)) }
def [](key); end
sig { params(key: String).returns(String) }
def fetch(key); end
sig { params(key: String, value: T.nilable(T.any(String, PATH))).returns(T.nilable(String)) }
def []=(key, value); end
sig { params(block: T.proc.params(arg0: [String, String]).returns(T::Boolean)).returns(T::Hash[String, String]) }
def select(&block); end
sig { params(block: T.proc.params(arg0: String).void).void }
def each_key(&block); end
sig { params(key: String).returns(T.nilable(String)) }
def delete(key); end
sig do
params(other: T.any(T::Hash[String, String], Sorbet::Private::Static::ENVClass))
.returns(Sorbet::Private::Static::ENVClass)
end
def replace(other); end
sig { returns(T::Hash[String, String]) }
def to_hash; end
end
module EnvActivation
include EnvMethods
end
class Sorbet
module Private
module Static
class ENVClass
include EnvActivation
end
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "compilers"
@ -11,13 +11,16 @@ require "development_tools"
# @see Stdenv
# @see https://www.rubydoc.info/stdlib/Env Ruby's ENV API
module SharedEnvExtension
extend T::Sig
include CompilerConstants
# @private
CC_FLAG_VARS = %w[CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS].freeze
# @private
private_constant :CC_FLAG_VARS
FC_FLAG_VARS = %w[FCFLAGS FFLAGS].freeze
# @private
private_constant :FC_FLAG_VARS
SANITIZED_VARS = %w[
CDPATH CLICOLOR_FORCE
CPATH C_INCLUDE_PATH CPLUS_INCLUDE_PATH OBJC_INCLUDE_PATH
@ -28,8 +31,16 @@ module SharedEnvExtension
GOBIN GOPATH GOROOT PERL_MB_OPT PERL_MM_OPT
LIBRARY_PATH LD_LIBRARY_PATH LD_PRELOAD LD_RUN_PATH
].freeze
private_constant :SANITIZED_VARS
# @private
sig do
params(
formula: T.nilable(Formula),
cc: T.nilable(String),
build_bottle: T.nilable(T::Boolean),
bottle_arch: T.nilable(T::Boolean),
).void
end
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil)
@formula = formula
@cc = cc
@ -37,57 +48,62 @@ module SharedEnvExtension
@bottle_arch = bottle_arch
reset
end
private :setup_build_environment
# @private
sig { void }
def reset
SANITIZED_VARS.each { |k| delete(k) }
end
private :reset
sig { returns(T::Hash[String, String]) }
def remove_cc_etc
keys = %w[CC CXX OBJC OBJCXX LD CPP CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS LDFLAGS CPPFLAGS]
removed = Hash[*keys.flat_map { |key| [key, self[key]] }]
keys.each do |key|
delete(key)
end
removed
keys.map { |key| [key, delete(key)] }.to_h
end
sig { params(newflags: String).void }
def append_to_cflags(newflags)
append(CC_FLAG_VARS, newflags)
end
sig { params(val: T.any(Regexp, String)).void }
def remove_from_cflags(val)
remove CC_FLAG_VARS, val
end
sig { params(value: String).void }
def append_to_cccfg(value)
append("HOMEBREW_CCCFG", value, "")
end
sig { params(keys: T.any(String, T::Array[String]), value: T.untyped, separator: String).void }
def append(keys, value, separator = " ")
value = value.to_s
Array(keys).each do |key|
old = self[key]
if old.nil? || old.empty?
self[key] = value
old_value = self[key]
self[key] = if old_value.nil? || old_value.empty?
value
else
self[key] += separator + value
old_value + separator + value
end
end
end
sig { params(keys: T.any(String, T::Array[String]), value: T.untyped, separator: String).void }
def prepend(keys, value, separator = " ")
value = value.to_s
Array(keys).each do |key|
old = self[key]
self[key] = if old.nil? || old.empty?
old_value = self[key]
self[key] = if old_value.nil? || old_value.empty?
value
else
value + separator + old
value + separator + old_value
end
end
end
sig { params(key: String, path: String).void }
def append_path(key, path)
self[key] = PATH.new(self[key]).append(path)
end
@ -99,61 +115,78 @@ module SharedEnvExtension
# Prepending a system path such as /usr/bin is a no-op so that requirements
# don't accidentally override superenv shims or formulae's `bin` directories.
# <pre>ENV.prepend_path "PATH", which("emacs").dirname</pre>
sig { params(key: String, path: String).void }
def prepend_path(key, path)
return if %w[/usr/bin /bin /usr/sbin /sbin].include? path.to_s
self[key] = PATH.new(self[key]).prepend(path)
end
sig { params(key: String, path: T.any(String, Pathname)).void }
def prepend_create_path(key, path)
path = Pathname.new(path) unless path.is_a? Pathname
path = Pathname(path)
path.mkpath
prepend_path key, path
end
sig { params(keys: T.any(String, T::Array[String]), value: T.untyped).void }
def remove(keys, value)
return if value.nil?
Array(keys).each do |key|
next unless self[key]
old_value = self[key]
next if old_value.nil?
self[key] = self[key].sub(value, "")
delete(key) if self[key].empty?
new_value = old_value.sub(value, "")
if new_value.empty?
delete(key)
else
self[key] = new_value
end
end
end
sig { returns(T.nilable(String)) }
def cc
self["CC"]
end
sig { returns(T.nilable(String)) }
def cxx
self["CXX"]
end
sig { returns(T.nilable(String)) }
def cflags
self["CFLAGS"]
end
sig { returns(T.nilable(String)) }
def cxxflags
self["CXXFLAGS"]
end
sig { returns(T.nilable(String)) }
def cppflags
self["CPPFLAGS"]
end
sig { returns(T.nilable(String)) }
def ldflags
self["LDFLAGS"]
end
sig { returns(T.nilable(String)) }
def fc
self["FC"]
end
sig { returns(T.nilable(String)) }
def fflags
self["FFLAGS"]
end
sig { returns(T.nilable(String)) }
def fcflags
self["FCFLAGS"]
end
@ -164,8 +197,7 @@ module SharedEnvExtension
# # modify CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS in one go:
# ENV.append_to_cflags "-I ./missing/includes"
# end</pre>
#
# @return [Symbol]
sig { returns(T.any(Symbol, String)) }
def compiler
@compiler ||= if (cc = @cc)
warn_about_non_apple_gcc(cc) if cc.match?(GNU_GCC_REGEXP)
@ -189,27 +221,31 @@ module SharedEnvExtension
end
end
# @private
sig { returns(T.any(String, Pathname)) }
def determine_cc
COMPILER_SYMBOL_MAP.invert.fetch(compiler, compiler)
end
private :determine_cc
COMPILERS.each do |compiler|
define_method(compiler) do
@compiler = compiler
self.cc = determine_cc
self.cxx = determine_cxx
send(:cc=, send(:determine_cc))
send(:cxx=, send(:determine_cxx))
end
end
# Snow Leopard defines an NCURSES value the opposite of most distros.
# @see https://bugs.python.org/issue6848
# Currently only used by aalib in core.
sig { void }
def ncurses_define
append "CPPFLAGS", "-DNCURSES_OPAQUE=0"
end
# @private
sig { void }
def userpaths!
path = PATH.new(self["PATH"]).select do |p|
# put Superenv.bin and opt path at the first
@ -228,6 +264,7 @@ module SharedEnvExtension
self["PATH"] = path
end
sig { void }
def fortran
# Ignore repeated calls to this function as it will misleadingly warn about
# building with an alternative Fortran compiler without optimization flags,
@ -260,6 +297,7 @@ module SharedEnvExtension
end
# @private
sig { returns(Symbol) }
def effective_arch
if @build_bottle && @bottle_arch
@bottle_arch.to_sym
@ -269,6 +307,7 @@ module SharedEnvExtension
end
# @private
sig { params(name: String).returns(Formula) }
def gcc_version_formula(name)
version = name[GNU_GCC_REGEXP, 1]
gcc_version_name = "gcc@#{version}"
@ -282,6 +321,7 @@ module SharedEnvExtension
end
# @private
sig { params(name: String).void }
def warn_about_non_apple_gcc(name)
begin
gcc_formula = gcc_version_formula(name)
@ -299,30 +339,40 @@ module SharedEnvExtension
EOS
end
sig { void }
def permit_arch_flags; end
# A no-op until we enable this by default again (which we may never do).
sig { void }
def permit_weak_imports; end
# @private
sig { params(cc: T.any(Symbol, String)).returns(T::Boolean) }
def compiler_any_clang?(cc = compiler)
%w[clang llvm_clang].include?(cc.to_s)
end
private
sig { params(_flags: T::Array[String], _map: T::Hash[Symbol, String]).void }
def set_cpu_flags(_flags, _map = {}); end
sig { params(val: T.any(String, Pathname)).returns(String) }
def cc=(val)
self["CC"] = self["OBJC"] = val.to_s
end
sig { params(val: T.any(String, Pathname)).returns(String) }
def cxx=(val)
self["CXX"] = self["OBJCXX"] = val.to_s
end
sig { returns(T.nilable(String)) }
def homebrew_cc
self["HOMEBREW_CC"]
end
sig { params(value: String, source: String).returns(Symbol) }
def fetch_compiler(value, source)
COMPILER_SYMBOL_MAP.fetch(value) do |other|
case other
@ -334,10 +384,9 @@ module SharedEnvExtension
end
end
sig { void }
def check_for_compiler_universal_support
return unless homebrew_cc.match?(GNU_GCC_REGEXP)
raise "Non-Apple GCC can't build universal binaries"
raise "Non-Apple GCC can't build universal binaries" if homebrew_cc&.match?(GNU_GCC_REGEXP)
end
end

View File

@ -0,0 +1,15 @@
# typed: strict
module SharedEnvExtension
include EnvMethods
end
class Sorbet
module Private
module Static
class ENVClass
include SharedEnvExtension
end
end
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: strict
# frozen_string_literal: true
require "hardware"
@ -14,8 +14,16 @@ module Stdenv
SAFE_CFLAGS_FLAGS = "-w -pipe"
# @private
def setup_build_environment(**options)
super(**options)
sig do
params(
formula: T.nilable(Formula),
cc: T.nilable(String),
build_bottle: T.nilable(T::Boolean),
bottle_arch: T.nilable(T::Boolean),
).void
end
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil)
super
self["HOMEBREW_ENV"] = "std"
@ -49,13 +57,14 @@ module Stdenv
send(compiler)
return unless cc.match?(GNU_GCC_REGEXP)
return unless cc&.match?(GNU_GCC_REGEXP)
gcc_formula = gcc_version_formula(cc)
append_path "PATH", gcc_formula.opt_bin.to_s
end
alias generic_setup_build_environment setup_build_environment
sig { returns(T::Array[Pathname]) }
def homebrew_extra_pkg_config_paths
[]
end
@ -73,10 +82,11 @@ module Stdenv
# Removes the MAKEFLAGS environment variable, causing make to use a single job.
# This is useful for makefiles with race conditions.
# When passed a block, MAKEFLAGS is removed only for the duration of the block and is restored after its completion.
def deparallelize
sig { params(block: T.proc.returns(T.untyped)).returns(T.untyped) }
def deparallelize(&block)
old = self["MAKEFLAGS"]
remove "MAKEFLAGS", /-j\d+/
if block_given?
if block
begin
yield
ensure
@ -89,30 +99,33 @@ module Stdenv
%w[O3 O2 O1 O0 Os].each do |opt|
define_method opt do
remove_from_cflags(/-O./)
append_to_cflags "-#{opt}"
send(:remove_from_cflags, /-O./)
send(:append_to_cflags, "-#{opt}")
end
end
# @private
sig { returns(T.any(String, Pathname)) }
def determine_cc
s = super
DevelopmentTools.locate(s) || Pathname.new(s)
DevelopmentTools.locate(s) || Pathname(s)
end
private :determine_cc
# @private
sig { returns(Pathname) }
def determine_cxx
dir, base = determine_cc.split
dir, base = Pathname(determine_cc).split
dir/base.to_s.sub("gcc", "g++").sub("clang", "clang++")
end
private :determine_cxx
GNU_GCC_VERSIONS.each do |n|
define_method(:"gcc-#{n}") do
super()
set_cpu_cflags
send(:set_cpu_cflags)
end
end
sig { void }
def clang
super()
replace_in_cflags(/-Xarch_#{Hardware::CPU.arch_32_bit} (-march=\S*)/, '\1')
@ -124,16 +137,19 @@ module Stdenv
set_cpu_cflags(map)
end
sig { void }
def m64
append_to_cflags "-m64"
append "LDFLAGS", "-arch #{Hardware::CPU.arch_64_bit}"
end
sig { void }
def m32
append_to_cflags "-m32"
append "LDFLAGS", "-arch #{Hardware::CPU.arch_32_bit}"
end
sig { void }
def universal_binary
check_for_compiler_universal_support
@ -141,33 +157,38 @@ module Stdenv
append "LDFLAGS", Hardware::CPU.universal_archs.as_arch_flags
return if compiler_any_clang?
return unless Hardware.is_32_bit?
return unless Hardware::CPU.is_32_bit?
# Can't mix "-march" for a 32-bit CPU with "-arch x86_64"
replace_in_cflags(/-march=\S*/, "-Xarch_#{Hardware::CPU.arch_32_bit} \\0")
end
sig { void }
def cxx11
append "CXX", "-std=c++11"
libcxx
end
sig { void }
def libcxx
append "CXX", "-stdlib=libc++" if compiler == :clang
end
sig { void }
def libstdcxx
append "CXX", "-stdlib=libstdc++" if compiler == :clang
end
# @private
sig { params(before: Regexp, after: String).void }
def replace_in_cflags(before, after)
CC_FLAG_VARS.each do |key|
self[key] = self[key].sub(before, after) if key?(key)
self[key] = fetch(key).sub(before, after) if key?(key)
end
end
# Convenience method to set all C compiler flags in one shot.
sig { params(val: String).void }
def define_cflags(val)
CC_FLAG_VARS.each { |key| self[key] = val }
end
@ -175,6 +196,7 @@ module Stdenv
# Sets architecture-specific flags for every environment variable
# given in the list `flags`.
# @private
sig { params(flags: T::Array[String], map: T::Hash[Symbol, String]).void }
def set_cpu_flags(flags, map = Hardware::CPU.optimization_flags)
cflags =~ /(-Xarch_#{Hardware::CPU.arch_32_bit} )-march=/
xarch = Regexp.last_match(1).to_s
@ -186,19 +208,23 @@ module Stdenv
append flags, map.fetch(effective_arch)
end
sig { void }
def x11; end
# @private
sig { params(map: T::Hash[Symbol, String]).void }
def set_cpu_cflags(map = Hardware::CPU.optimization_flags) # rubocop:disable Naming/AccessorMethodName
set_cpu_flags(CC_FLAG_VARS, map)
end
sig { returns(Integer) }
def make_jobs
Homebrew::EnvConfig.make_jobs.to_i
end
# This method does nothing in stdenv since there's no arg refurbishment
# @private
sig { void }
def refurbish_args; end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "extend/ENV/shared"
@ -22,6 +22,7 @@ module Superenv
# @private
attr_accessor :keg_only_deps, :deps, :run_time_deps, :x11
sig { params(base: Superenv).void }
def self.extended(base)
base.keg_only_deps = []
base.deps = []
@ -29,8 +30,10 @@ module Superenv
end
# @private
sig { returns(T.nilable(Pathname)) }
def self.bin; end
sig { void }
def reset
super
# Configure scripts generated by autoconf 2.61 or later export as_nl, which
@ -39,8 +42,16 @@ module Superenv
end
# @private
def setup_build_environment(**options)
super(**options)
sig do
params(
formula: T.nilable(Formula),
cc: T.nilable(String),
build_bottle: T.nilable(T::Boolean),
bottle_arch: T.nilable(T::Boolean),
).void
end
def setup_build_environment(formula: nil, cc: nil, build_bottle: false, bottle_arch: nil)
super
send(compiler)
self["HOMEBREW_ENV"] = "super"
@ -89,18 +100,22 @@ module Superenv
private
sig { params(val: T.any(String, Pathname)).returns(String) }
def cc=(val)
self["HOMEBREW_CC"] = super
end
sig { params(val: T.any(String, Pathname)).returns(String) }
def cxx=(val)
self["HOMEBREW_CXX"] = super
end
sig { returns(String) }
def determine_cxx
determine_cc.to_s.gsub("gcc", "g++").gsub("clang", "clang++")
end
sig { returns(T::Array[Pathname]) }
def homebrew_extra_paths
[]
end
@ -115,7 +130,7 @@ module Superenv
path.append("/usr/bin", "/bin", "/usr/sbin", "/sbin")
begin
path.append(gcc_version_formula(homebrew_cc).opt_bin) if homebrew_cc.match?(GNU_GCC_REGEXP)
path.append(gcc_version_formula(T.must(homebrew_cc)).opt_bin) if homebrew_cc&.match?(GNU_GCC_REGEXP)
rescue FormulaUnavailableError
# Don't fail and don't add these formulae to the path if they don't exist.
nil
@ -124,6 +139,7 @@ module Superenv
path.existing
end
sig { returns(T::Array[Pathname]) }
def homebrew_extra_pkg_config_paths
[]
end
@ -143,6 +159,7 @@ module Superenv
).existing
end
sig { returns(T::Array[Pathname]) }
def homebrew_extra_aclocal_paths
[]
end
@ -156,6 +173,7 @@ module Superenv
).existing
end
sig { returns(T::Array[Pathname]) }
def homebrew_extra_isystem_paths
[]
end
@ -173,6 +191,7 @@ module Superenv
PATH.new(keg_only_deps.map(&:opt_include)).existing
end
sig { returns(T::Array[Pathname]) }
def homebrew_extra_library_paths
[]
end
@ -187,6 +206,7 @@ module Superenv
PATH.new(paths).existing
end
sig { returns(String) }
def determine_dependencies
deps.map(&:name).join(",")
end
@ -199,6 +219,7 @@ module Superenv
).existing
end
sig { returns(T::Array[Pathname]) }
def homebrew_extra_cmake_include_paths
[]
end
@ -208,6 +229,7 @@ module Superenv
PATH.new(homebrew_extra_cmake_include_paths).existing
end
sig { returns(T::Array[Pathname]) }
def homebrew_extra_cmake_library_paths
[]
end
@ -217,6 +239,7 @@ module Superenv
PATH.new(homebrew_extra_cmake_library_paths).existing
end
sig { returns(T::Array[Pathname]) }
def homebrew_extra_cmake_frameworks_paths
[]
end
@ -229,14 +252,17 @@ module Superenv
).existing
end
sig { returns(String) }
def determine_make_jobs
Homebrew::EnvConfig.make_jobs
end
sig { returns(String) }
def determine_optflags
Hardware::CPU.optimization_flags.fetch(effective_arch)
end
sig { returns(String) }
def determine_cccfg
""
end
@ -246,9 +272,10 @@ module Superenv
# Removes the MAKEFLAGS environment variable, causing make to use a single job.
# This is useful for makefiles with race conditions.
# When passed a block, MAKEFLAGS is removed only for the duration of the block and is restored after its completion.
def deparallelize
sig { params(block: T.proc.returns(T.untyped)).returns(T.untyped) }
def deparallelize(&block)
old = delete("MAKEFLAGS")
if block_given?
if block
begin
yield
ensure
@ -259,56 +286,64 @@ module Superenv
old
end
sig { returns(Integer) }
def make_jobs
self["MAKEFLAGS"] =~ /-\w*j(\d+)/
[Regexp.last_match(1).to_i, 1].max
end
sig { void }
def universal_binary
check_for_compiler_universal_support
self["HOMEBREW_ARCHFLAGS"] = Hardware::CPU.universal_archs.as_arch_flags
end
sig { void }
def permit_arch_flags
append_to_cccfg "K"
end
sig { void }
def m32
append "HOMEBREW_ARCHFLAGS", "-m32"
end
sig { void }
def m64
append "HOMEBREW_ARCHFLAGS", "-m64"
end
sig { void }
def cxx11
append_to_cccfg "x"
append_to_cccfg "g" if homebrew_cc == "clang"
end
sig { void }
def libcxx
append_to_cccfg "g" if compiler == :clang
end
sig { void }
def libstdcxx
append_to_cccfg "h" if compiler == :clang
end
# @private
sig { void }
def refurbish_args
append_to_cccfg "O"
end
%w[O3 O2 O1 O0 Os].each do |opt|
define_method opt do
self["HOMEBREW_OPTIMIZATION_LEVEL"] = opt
send(:[]=, "HOMEBREW_OPTIMIZATION_LEVEL", opt)
end
end
sig { void }
def set_x11_env_if_installed; end
def set_cpu_flags(_arg0, _arg1 = "", _arg2 = {}); end
end
require "extend/os/extend/ENV/super"

View File

@ -27,7 +27,7 @@ module Superenv
def homebrew_extra_paths
paths = []
paths << MacOS::XQuartz.bin.to_s if x11?
paths << MacOS::XQuartz.bin if x11?
paths
end

View File

@ -69,7 +69,7 @@ module Hardware
# conflict between what `uname` reports and the underlying `sysctl` flags,
# since the `sysctl` flags don't change behaviour under Rosetta 2.
def in_rosetta2?
intel? && physical_cpu_arm64?
sysctl_bool("sysctl.proc_translated")
end
def features

View File

@ -26,14 +26,14 @@ class JavaRequirement < Requirement
end
def java_home_cmd
# TODO: enable for all macOS versions and Linux on next minor release
# TODO: disable for all macOS versions and Linux on next minor release
# but --version with ranges is broken on Big Sur today.
if MacOS.version >= :big_sur && @version&.end_with?("+")
odisabled %Q(depends_on java: "#{@version}"),
'depends_on "openjdk@11", depends_on "openjdk@8" or depends_on "openjdk"'
end
# odeprecated "depends_on :java",
# 'depends_on "openjdk@11", depends_on "openjdk@8" or depends_on "openjdk"'
odeprecated "depends_on :java",
'depends_on "openjdk@11", depends_on "openjdk@8" or depends_on "openjdk"'
return unless File.executable?("/usr/libexec/java_home")

View File

@ -113,11 +113,6 @@ class Formula
# @private
attr_reader :stable
# @private
def devel
odisabled "Formula#devel"
end
# The HEAD {SoftwareSpec} for this {Formula}.
# Installed when using `brew install --HEAD`.
# This is always installed with the version `HEAD` and taken from the latest
@ -328,11 +323,6 @@ class Formula
active_spec == stable
end
# @private
def devel?
odisabled "Formula#devel?"
end
# Is the currently active {SoftwareSpec} a {#head} build?
# @private
def head?
@ -2227,10 +2217,6 @@ class Formula
raise "You cannot override Formula#brew in class #{name}"
when :test
define_method(:test_defined?) { true }
when :patches
odisabled "a Formula#patches definition", "'patch do' block calls"
when :options
odisabled "a Formula#options definition", "'option do' block calls"
end
end
@ -2274,10 +2260,7 @@ class Formula
if args.nil?
@licenses
else
if args.is_a? Array
odeprecated "`license [...]`", "`license any_of: [...]`"
args = { any_of: args }
end
odisabled "`license [...]`", "`license any_of: [...]`" if args.is_a? Array
@licenses = args
end
end
@ -2463,11 +2446,6 @@ class Formula
@stable.instance_eval(&block)
end
# @private
def devel
odisabled "'devel' blocks in formulae", "'head' blocks or @-versioned formulae"
end
# @!attribute [w] head
# Adds a {.head} {SoftwareSpec}.
# This can be installed by passing the `--HEAD` option to allow

View File

@ -1,6 +1,7 @@
# typed: false
# frozen_string_literal: true
require "deprecate_disable"
require "formula_text_auditor"
require "resource_auditor"
@ -325,6 +326,11 @@ module Homebrew
return if version_conflicts.empty?
return if formula.disabled?
return if formula.deprecated? &&
formula.deprecation_reason != DeprecateDisable::DEPRECATE_DISABLE_REASONS[:versioned_formula]
problem <<~EOS
#{formula.full_name} contains conflicting version recursive dependencies:
#{version_conflicts.to_a.join ", "}

View File

@ -225,12 +225,12 @@ module Formulary
def load_file(flags:)
if %r{githubusercontent.com/[\w-]+/[\w-]+/[a-f0-9]{40}(?:/Formula)?/(?<formula_name>[\w+-.@]+).rb} =~ url # rubocop:disable Style/CaseLikeIf
odisabled "Installation of #{formula_name} from a GitHub commit URL",
"'brew extract #{formula_name}' to stable tap on GitHub"
raise UsageError, "Installation of #{formula_name} from a GitHub commit URL is unsupported! " \
"'brew extract #{formula_name}' to stable tap on GitHub instead."
elsif url.match?(%r{^(https?|ftp)://})
odisabled "Non-checksummed download of #{name} formula file from an arbitrary URL",
"'brew extract' or 'brew create' and 'brew tap-new' to create a "\
"formula file in a tap on GitHub"
raise UsageError, "Non-checksummed download of #{name} formula file from an arbitrary URL is unsupported! ",
"'brew extract' or 'brew create' and 'brew tap-new' to create a "\
"formula file in a tap on GitHub instead."
end
HOMEBREW_CACHE_FORMULA.mkpath
FileUtils.rm_f(path)

View File

@ -7,7 +7,7 @@ module Language
# @api public
module Python
def self.major_minor_version(python)
version = /\d\.\d/.match `#{python} --version 2>&1`
version = /\d\.\d+/.match `#{python} --version 2>&1`
return unless version
Version.create(version.to_s)

View File

@ -44,13 +44,6 @@ module OS
Version.new "11.0"
end
def latest_stable_version
# TODO: bump version when new macOS is released and also update
# references in docs/Installation.md and
# https://github.com/Homebrew/install/blob/HEAD/install.sh
Version.new "11.0"
end
def outdated_release?
# TODO: bump version when new macOS is released and also update
# references in docs/Installation.md and
@ -59,7 +52,10 @@ module OS
end
def prerelease?
version > latest_stable_version
# TODO: bump version when new macOS is released or announced
# and also update references in docs/Installation.md and
# https://github.com/Homebrew/install/blob/HEAD/install.sh
version >= "12.0"
end
def languages

View File

@ -22,7 +22,7 @@ module OS
def latest_version
latest_stable = "12.2"
case MacOS.version
when "11.0" then latest_stable
when /^11\./ then latest_stable
when "10.15" then "12.2"
when "10.14" then "11.3.1"
when "10.13" then "10.1"
@ -45,7 +45,7 @@ module OS
sig { returns(String) }
def minimum_version
case MacOS.version
when "11.0" then "12.2"
when /^11\./ then "12.2"
when "10.15" then "11.0"
when "10.14" then "10.2"
when "10.13" then "9.0"
@ -275,7 +275,7 @@ module OS
sig { returns(String) }
def latest_clang_version
case MacOS.version
when "11.0", "10.15" then "1200.0.32.27"
when /^11\./, "10.15" then "1200.0.32.27"
when "10.14" then "1100.0.33.17"
when "10.13" then "1000.10.44.2"
when "10.12" then "900.0.39.2"
@ -291,7 +291,7 @@ module OS
sig { returns(String) }
def minimum_version
case MacOS.version
when "11.0" then "12.0.0"
when /^11\./ then "12.0.0"
when "10.15" then "11.0.0"
when "10.14" then "10.0.0"
when "10.13" then "9.0.0"

View File

@ -41,14 +41,8 @@ module Patch
else
{}
end.each_pair do |strip, urls|
Array(urls).each do |url|
patch = case url
when :DATA
DATAPatch.new(strip)
else
LegacyPatch.new(strip, url)
end
patches << patch
Array(urls).each do
patches << DATAPatch.new(strip)
end
end
@ -196,15 +190,3 @@ class ExternalPatch
"#<#{self.class.name}: #{strip.inspect} #{url.inspect}>"
end
end
# A legacy patch.
#
# Legacy patches have no checksum and are not cached.
#
# @api private
class LegacyPatch < ExternalPatch
def initialize(strip, _url)
odisabled "legacy patches", "'patch do' blocks"
super(strip)
end
end

View File

@ -34,6 +34,9 @@ class JavaRequirement < Requirement
end
def initialize(tags = [])
odeprecated "depends_on :java",
'"depends_on "openjdk@11", "depends_on "openjdk@8" or "depends_on "openjdk"'
@version = tags.shift if tags.first&.match?(/^\d/)
super(tags)
@cask = suggestion.token

View File

@ -14,16 +14,10 @@ class MacOSRequirement < Requirement
attr_reader :comparator, :version
def initialize(tags = [], comparator: ">=")
begin
@version = if comparator == "==" && tags.first.respond_to?(:map)
tags.shift.map { |s| MacOS::Version.from_symbol(s) }
else
MacOS::Version.from_symbol(tags.shift) unless tags.empty?
end
rescue MacOSVersionError => e
raise if e.version != :mavericks
odisabled "depends_on :macos => :mavericks"
@version = if comparator == "==" && tags.first.respond_to?(:map)
tags.shift.map { |s| MacOS::Version.from_symbol(s) }
else
MacOS::Version.from_symbol(tags.shift) unless tags.empty?
end
@comparator = comparator

View File

@ -150,6 +150,8 @@ module RuboCop
valid_node ||= child.method_name.to_s == "patch"
valid_node ||= child.method_name.to_s == "resource"
valid_node ||= child.method_name.to_s == "deprecate!"
valid_node ||= child.method_name.to_s == "disable!"
@offensive_node = on_os_block
@offense_source_range = on_os_block.source_range

View File

@ -95,21 +95,6 @@ module RuboCop
end
end
find_strings(body_node).each do |n|
next unless regex_match_group(n, /JAVA_HOME/i)
next if @formula_name.match?(/^openjdk(@|$)/)
next if find_every_method_call_by_name(body_node, :depends_on).any? do |dependency|
dependency.each_descendant(:str).count.zero? ||
regex_match_group(dependency.each_descendant(:str).first, /^openjdk(@|$)/) ||
depends_on?(:java)
end
offending_node(n)
problem "Use `depends_on :java` to set JAVA_HOME"
end
prefix_path(body_node) do |prefix_node, path|
next unless match = path.match(%r{^(bin|include|libexec|lib|sbin|share|Frameworks)(?:/| |$)})

View File

@ -0,0 +1,21 @@
# typed: strict
# frozen_string_literal: true
source = ARGV[5]
methods = if (single = source[/delegate\s+([^:]+):\s+/, 1])
[single]
else
multiple = source[/delegate\s+\[(.*?)\]\s+=>\s+/m, 1]
non_comments = multiple.gsub(/\#.*$/, "")
non_comments.scan(/:([^:,\s]+)/).flatten
end
methods.each do |method|
puts <<~RUBY
# typed: strict
sig {params(arg0: T.untyped).returns(T.untyped)}
def #{method}(*arg0); end
RUBY
end

View File

@ -3,7 +3,8 @@
source = ARGV[5]
/\busing +Magic\b/.match(source) do |_|
case source[/\Ausing\s+(.*)\Z/, 1]
when "Magic"
puts <<-RUBY
# typed: strict
@ -18,4 +19,13 @@ source = ARGV[5]
def zipinfo; end
end
RUBY
when "HashValidator"
puts <<-RUBY
# typed: strict
class ::Hash
sig { params(valid_keys: T.untyped).void }
def assert_valid_keys!(*valid_keys); end
end
RUBY
end

View File

@ -5841,22 +5841,6 @@ module Cask::Utils
extend ::T::Private::Methods::SingletonMethodHooks
end
class Caveats
def empty?(*args, &block); end
def to_s(*args, &block); end
end
class Checksum
def [](*args, &block); end
def empty?(*args, &block); end
def length(*args, &block); end
def to_s(*args, &block); end
end
class Class
def any_instance(); end
@ -11897,14 +11881,6 @@ module MachO
def self.open(filename); end
end
module MachOShim
def delete_rpath(*args, &block); end
def dylib_id(*args, &block); end
def rpaths(*args, &block); end
end
Markdown = RDiscount
module Marshal
@ -15369,18 +15345,6 @@ class Pathname
extend ::T::Private::Methods::SingletonMethodHooks
end
class PkgVersion
def major(*args, &block); end
def major_minor(*args, &block); end
def major_minor_patch(*args, &block); end
def minor(*args, &block); end
def patch(*args, &block); end
end
class Proc
include ::MethodSource::SourceLocation::ProcExtensions
include ::MethodSource::MethodExtensions
@ -28595,6 +28559,7 @@ class Socket
IPV6_PATHMTU = ::T.let(nil, ::T.untyped)
IPV6_RECVPATHMTU = ::T.let(nil, ::T.untyped)
IPV6_USE_MIN_MTU = ::T.let(nil, ::T.untyped)
IP_DONTFRAG = ::T.let(nil, ::T.untyped)
IP_PORTRANGE = ::T.let(nil, ::T.untyped)
IP_RECVDSTADDR = ::T.let(nil, ::T.untyped)
IP_RECVIF = ::T.let(nil, ::T.untyped)
@ -28686,6 +28651,7 @@ module Socket::Constants
IPV6_PATHMTU = ::T.let(nil, ::T.untyped)
IPV6_RECVPATHMTU = ::T.let(nil, ::T.untyped)
IPV6_USE_MIN_MTU = ::T.let(nil, ::T.untyped)
IP_DONTFRAG = ::T.let(nil, ::T.untyped)
IP_PORTRANGE = ::T.let(nil, ::T.untyped)
IP_RECVDSTADDR = ::T.let(nil, ::T.untyped)
IP_RECVIF = ::T.let(nil, ::T.untyped)
@ -29246,6 +29212,11 @@ end
class SynchronizedDelegator
end
class SystemCommand::Result
extend ::T::Private::Methods::MethodHooks
extend ::T::Private::Methods::SingletonMethodHooks
end
class SystemCommand
extend ::T::Private::Methods::MethodHooks
extend ::T::Private::Methods::SingletonMethodHooks

View File

@ -2,5 +2,6 @@ ruby_extra_args:
- --disable-gems
triggers:
using: sorbet/plugins/unpack_strategy_magic.rb
using: sorbet/plugins/using.rb
attr_predicate: sorbet/plugins/attr_predicate.rb
delegate: sorbet/plugins/delegate.rb

View File

@ -9,7 +9,6 @@ require "shellwords"
require "extend/io"
require "extend/predicable"
require "extend/hash_validator"
using HashValidator
# Class for running sub-processes and capturing their output and exit status.
#
@ -17,14 +16,16 @@ using HashValidator
class SystemCommand
extend T::Sig
using HashValidator
# Helper functions for calling {SystemCommand.run}.
module Mixin
def system_command(*args)
SystemCommand.run(*args)
T.unsafe(SystemCommand).run(*args)
end
def system_command!(*args)
SystemCommand.run!(*args)
T.unsafe(SystemCommand).run!(*args)
end
end
@ -34,11 +35,11 @@ class SystemCommand
attr_reader :pid
def self.run(executable, **options)
new(executable, **options).run!
T.unsafe(self).new(executable, **options).run!
end
def self.run!(command, **options)
run(command, **options, must_succeed: true)
T.unsafe(self).run(command, **options, must_succeed: true)
end
sig { returns(SystemCommand::Result) }
@ -63,45 +64,61 @@ class SystemCommand
result
end
sig do
params(
executable: T.any(String, Pathname),
args: T::Array[T.any(String, Integer, Float, URI::Generic)],
sudo: T::Boolean,
env: T::Hash[String, String],
input: T.any(String, T::Array[String]),
must_succeed: T::Boolean,
print_stdout: T::Boolean,
print_stderr: T::Boolean,
verbose: T::Boolean,
secrets: T::Array[String],
chdir: T.any(String, Pathname),
).void
end
def initialize(executable, args: [], sudo: false, env: {}, input: [], must_succeed: false,
print_stdout: false, print_stderr: true, verbose: false, secrets: [], **options)
print_stdout: false, print_stderr: true, verbose: false, secrets: [], chdir: T.unsafe(nil))
require "extend/ENV"
@executable = executable
@args = args
@sudo = sudo
@input = Array(input)
@print_stdout = print_stdout
@print_stderr = print_stderr
@verbose = verbose
@secrets = (Array(secrets) + ENV.sensitive_environment.values).uniq
@must_succeed = must_succeed
options.assert_valid_keys!(:chdir)
@options = options
@env = env
@env.each_key do |name|
env.each_key do |name|
next if /^[\w&&\D]\w*$/.match?(name)
raise ArgumentError, "Invalid variable name: '#{name}'"
end
@env = env
@input = Array(input)
@must_succeed = must_succeed
@print_stdout = print_stdout
@print_stderr = print_stderr
@verbose = verbose
@secrets = (Array(secrets) + ENV.sensitive_environment.values).uniq
@chdir = chdir
end
sig { returns(T::Array[String]) }
def command
[*sudo_prefix, *env_args, executable.to_s, *expanded_args]
end
private
attr_reader :executable, :args, :input, :options, :env
attr_reader :executable, :args, :input, :chdir, :env
attr_predicate :sudo?, :print_stdout?, :print_stderr?, :must_succeed?
sig { returns(T::Boolean) }
def verbose?
return super if @verbose.nil?
@verbose
end
sig { returns(T::Array[String]) }
def env_args
set_variables = env.compact.map do |name, value|
sanitized_name = Shellwords.escape(name)
@ -114,6 +131,7 @@ class SystemCommand
["/usr/bin/env", *set_variables]
end
sig { returns(T::Array[String]) }
def sudo_prefix
return [] unless sudo?
@ -121,11 +139,12 @@ class SystemCommand
["/usr/bin/sudo", *askpass_flags, "-E", "--"]
end
sig { returns(T::Array[String]) }
def expanded_args
@expanded_args ||= args.map do |arg|
if arg.respond_to?(:to_path)
File.absolute_path(arg)
elsif arg.is_a?(Integer) || arg.is_a?(Float) || arg.is_a?(URI)
elsif arg.is_a?(Integer) || arg.is_a?(Float) || arg.is_a?(URI::Generic)
arg.to_s
else
arg.to_str
@ -137,7 +156,7 @@ class SystemCommand
executable, *args = command
raw_stdin, raw_stdout, raw_stderr, raw_wait_thr =
Open3.popen3(env, [executable, executable], *args, **options)
T.unsafe(Open3).popen3(env, [executable, executable], *args, **{ chdir: chdir }.compact)
@pid = raw_wait_thr.pid
write_input_to(raw_stdin)
@ -158,7 +177,7 @@ class SystemCommand
loop do
readable_sources, = IO.select(sources)
readable_sources = readable_sources.reject(&:eof?)
readable_sources = T.must(readable_sources).reject(&:eof?)
break if readable_sources.empty?
@ -176,10 +195,20 @@ class SystemCommand
# Result containing the output and exit status of a finished sub-process.
class Result
extend T::Sig
include Context
attr_accessor :command, :status, :exit_status
sig do
params(
command: T::Array[String],
output: T::Array[[Symbol, String]],
status: Process::Status,
secrets: T::Array[String],
).void
end
def initialize(command, output, status, secrets:)
@command = command
@output = output
@ -188,57 +217,65 @@ class SystemCommand
@secrets = secrets
end
sig { void }
def assert_success!
return if @status.success?
raise ErrorDuringExecution.new(command, status: @status, output: @output, secrets: @secrets)
end
sig { returns(String) }
def stdout
@stdout ||= @output.select { |type,| type == :stdout }
.map { |_, line| line }
.join
end
sig { returns(String) }
def stderr
@stderr ||= @output.select { |type,| type == :stderr }
.map { |_, line| line }
.join
end
sig { returns(String) }
def merged_output
@merged_output ||= @output.map { |_, line| line }
.join
end
sig { returns(T::Boolean) }
def success?
return false if @exit_status.nil?
@exit_status.zero?
end
sig { returns([String, String, Process::Status]) }
def to_ary
[stdout, stderr, status]
end
sig { returns(T.nilable(T.any(Array, Hash))) }
def plist
@plist ||= begin
output = stdout
if /\A(?<garbage>.*?)<\?\s*xml/m =~ output
output = output.sub(/\A#{Regexp.escape(garbage)}/m, "")
warn_plist_garbage(garbage)
output = output.sub(/\A(.*?)(\s*<\?\s*xml)/m) do
warn_plist_garbage(T.must(Regexp.last_match(1)))
Regexp.last_match(2)
end
if %r{<\s*/\s*plist\s*>(?<garbage>.*?)\Z}m =~ output
output = output.sub(/#{Regexp.escape(garbage)}\Z/, "")
warn_plist_garbage(garbage)
output = output.sub(%r{(<\s*/\s*plist\s*>\s*)(.*?)\Z}m) do
warn_plist_garbage(T.must(Regexp.last_match(2)))
Regexp.last_match(1)
end
Plist.parse_xml(output)
end
end
sig { params(garbage: String).void }
def warn_plist_garbage(garbage)
return unless verbose?
return unless garbage.match?(/\S/)

View File

@ -233,23 +233,17 @@ class Tab < OpenStruct
end
def universal?
odeprecated "Tab#universal?"
include?("universal")
odisabled "Tab#universal?"
end
def cxx11?
odeprecated "Tab#cxx11?"
include?("c++11")
odisabled "Tab#cxx11?"
end
def head?
spec == :head
end
def devel?
odisabled "Tab#devel?"
end
def stable?
spec == :stable
end
@ -314,10 +308,6 @@ class Tab < OpenStruct
Version.create(versions["stable"]) if versions["stable"]
end
def devel_version
odisabled "Tab#devel_version"
end
def head_version
Version.create(versions["head"]) if versions["head"]
end

View File

@ -21,11 +21,13 @@ class Tap
HOMEBREW_TAP_FORMULA_RENAMES_FILE = "formula_renames.json"
HOMEBREW_TAP_MIGRATIONS_FILE = "tap_migrations.json"
HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR = "audit_exceptions"
HOMEBREW_TAP_PYPI_FORMULA_MAPPINGS = "pypi_formula_mappings.json"
HOMEBREW_TAP_JSON_FILES = %W[
#{HOMEBREW_TAP_FORMULA_RENAMES_FILE}
#{HOMEBREW_TAP_MIGRATIONS_FILE}
#{HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR}/*.json
#{HOMEBREW_TAP_PYPI_FORMULA_MAPPINGS}
].freeze
def self.fetch(*args)
@ -112,6 +114,7 @@ class Tap
@formula_renames = nil
@tap_migrations = nil
@audit_exceptions = nil
@pypi_formula_mappings = nil
@config = nil
remove_instance_variable(:@private) if instance_variable_defined?(:@private)
end
@ -559,23 +562,15 @@ class Tap
end
# Hash with audit exceptions
sig { returns(Hash) }
def audit_exceptions
@audit_exceptions = {}
@audit_exceptions = read_formula_list_directory "#{HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR}/*"
end
Pathname.glob(path/HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR/"*").each do |exception_file|
list_name = exception_file.basename.to_s.chomp(".json").to_sym
list_contents = begin
JSON.parse exception_file.read
rescue JSON::ParserError
opoo "#{exception_file} contains invalid JSON"
end
next if list_contents.nil?
@audit_exceptions[list_name] = list_contents
end
@audit_exceptions
# Hash with pypi formula mappings
sig { returns(Hash) }
def pypi_formula_mappings
@pypi_formula_mappings = read_formula_list path/HOMEBREW_TAP_PYPI_FORMULA_MAPPINGS
end
def ==(other)
@ -636,6 +631,32 @@ class Tap
end
end
end
sig { params(file: Pathname).returns(Hash) }
def read_formula_list(file)
JSON.parse file.read
rescue JSON::ParserError
opoo "#{file} contains invalid JSON"
{}
rescue Errno::ENOENT
{}
end
sig { params(directory: String).returns(Hash) }
def read_formula_list_directory(directory)
list = {}
Pathname.glob(path/directory).each do |exception_file|
list_name = exception_file.basename.to_s.chomp(".json").to_sym
list_contents = read_formula_list exception_file
next if list_contents.blank?
list[list_name] = list_contents
end
list
end
end
# A specialized {Tap} class for the core formulae.
@ -739,6 +760,13 @@ class CoreTap < Tap
end
end
def pypi_formula_mappings
@pypi_formula_mappings ||= begin
self.class.ensure_installed!
super
end
end
# @private
def formula_file_to_name(file)
file.basename(".rb").to_s

View File

@ -8,20 +8,21 @@ module Homebrew
class TapAuditor
extend T::Sig
attr_reader :name, :path, :tap_audit_exceptions, :problems
attr_reader :name, :path, :tap_audit_exceptions, :tap_pypi_formula_mappings, :problems
sig { params(tap: Tap, strict: T.nilable(T::Boolean)).void }
def initialize(tap, strict:)
@name = tap.name
@path = tap.path
@tap_audit_exceptions = tap.audit_exceptions
@problems = []
@name = tap.name
@path = tap.path
@tap_audit_exceptions = tap.audit_exceptions
@tap_pypi_formula_mappings = tap.pypi_formula_mappings
@problems = []
end
sig { void }
def audit
audit_json_files
audit_tap_audit_exceptions
audit_tap_formula_lists
end
sig { void }
@ -35,38 +36,49 @@ module Homebrew
end
sig { void }
def audit_tap_audit_exceptions
@tap_audit_exceptions.each do |list_name, formula_names|
unless [Hash, Array].include? formula_names.class
problem <<~EOS
audit_exceptions/#{list_name}.json should contain a JSON array
of formula names or a JSON object mapping formula names to values
EOS
next
end
formula_names = formula_names.keys if formula_names.is_a? Hash
invalid_formulae = []
formula_names.each do |name|
invalid_formulae << name if Formula[name].tap != @name
rescue FormulaUnavailableError
invalid_formulae << name
end
next if invalid_formulae.empty?
problem <<~EOS
audit_exceptions/#{list_name}.json references
formulae that are not found in the #{@name} tap.
Invalid formulae: #{invalid_formulae.join(", ")}
EOS
end
def audit_tap_formula_lists
check_formula_list_directory "audit_exceptions", @tap_audit_exceptions
check_formula_list "pypi_formula_mappings", @tap_pypi_formula_mappings
end
sig { params(message: String).void }
def problem(message)
@problems << ({ message: message, location: nil })
end
private
sig { params(list_file: String, list: T.untyped).void }
def check_formula_list(list_file, list)
unless [Hash, Array].include? list.class
problem <<~EOS
#{list_file}.json should contain a JSON array
of formula names or a JSON object mapping formula names to values
EOS
return
end
invalid_formulae = []
list.each do |name, _|
invalid_formulae << name if Formula[name].tap != @name
rescue FormulaUnavailableError
invalid_formulae << name
end
return if invalid_formulae.empty?
problem <<~EOS
#{list_file}.json references
formulae that are not found in the #{@name} tap.
Invalid formulae: #{invalid_formulae.join(", ")}
EOS
end
sig { params(directory_name: String, lists: Hash).void }
def check_formula_list_directory(directory_name, lists)
lists.each do |list_name, list|
check_formula_list "#{directory_name}/#{list_name}", list
end
end
end
end

View File

@ -8,7 +8,7 @@ shared_examples "#uninstall_phase or #zap_phase" do
let(:artifact_dsl_key) { described_class.dsl_key }
let(:artifact) { cask.artifacts.find { |a| a.is_a?(described_class) } }
let(:fake_system_command) { FakeSystemCommand }
let(:fake_system_command) { class_double(SystemCommand) }
context "using :launchctl" do
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-launchctl")) }
@ -31,41 +31,37 @@ shared_examples "#uninstall_phase or #zap_phase" do
end
it "works when job is owned by user" do
FakeSystemCommand.stubs_command(
launchctl_list_cmd,
service_info,
)
allow(fake_system_command).to receive(:run)
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: false)
.and_return(instance_double(SystemCommand::Result, stdout: service_info))
allow(fake_system_command).to receive(:run)
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: true)
.and_return(instance_double(SystemCommand::Result, stdout: unknown_response))
FakeSystemCommand.stubs_command(
sudo(launchctl_list_cmd),
unknown_response,
)
FakeSystemCommand.expects_command(launchctl_remove_cmd)
expect(fake_system_command).to receive(:run!)
.with("/bin/launchctl", args: ["remove", "my.fancy.package.service"], sudo: false)
.and_return(instance_double(SystemCommand::Result))
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
end
it "works when job is owned by system" do
FakeSystemCommand.stubs_command(
launchctl_list_cmd,
unknown_response,
)
allow(fake_system_command).to receive(:run)
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: false)
.and_return(instance_double(SystemCommand::Result, stdout: unknown_response))
allow(fake_system_command).to receive(:run)
.with("/bin/launchctl", args: ["list", "my.fancy.package.service"], print_stderr: false, sudo: true)
.and_return(instance_double(SystemCommand::Result, stdout: service_info))
FakeSystemCommand.stubs_command(
sudo(launchctl_list_cmd),
service_info,
)
FakeSystemCommand.expects_command(sudo(launchctl_remove_cmd))
expect(fake_system_command).to receive(:run!)
.with("/bin/launchctl", args: ["remove", "my.fancy.package.service"], sudo: true)
.and_return(instance_double(SystemCommand::Result))
subject.public_send(:"#{artifact_dsl_key}_phase", command: fake_system_command)
end
end
context "using :pkgutil" do
let(:fake_system_command) { class_double(SystemCommand) }
let(:cask) { Cask::CaskLoader.load(cask_path("with-#{artifact_dsl_key}-pkgutil")) }
let(:main_pkg_id) { "my.fancy.package.main" }

View File

@ -6,7 +6,8 @@ require "test/cask/dsl/shared_examples/staged"
describe Cask::DSL::Postflight, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) }
let(:dsl) { described_class.new(cask, FakeSystemCommand) }
let(:fake_system_command) { class_double(SystemCommand) }
let(:dsl) { described_class.new(cask, fake_system_command) }
it_behaves_like Cask::DSL::Base

View File

@ -6,7 +6,8 @@ require "test/cask/dsl/shared_examples/staged"
describe Cask::DSL::Preflight, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) }
let(:dsl) { described_class.new(cask, FakeSystemCommand) }
let(:fake_system_command) { class_double(SystemCommand) }
let(:dsl) { described_class.new(cask, fake_system_command) }
it_behaves_like Cask::DSL::Base

View File

@ -4,8 +4,8 @@
require "cask/staged"
shared_examples Cask::Staged do
let(:existing_path) { Pathname.new("/path/to/file/that/exists") }
let(:non_existent_path) { Pathname.new("/path/to/file/that/does/not/exist") }
let(:existing_path) { Pathname("/path/to/file/that/exists") }
let(:non_existent_path) { Pathname("/path/to/file/that/does/not/exist") }
before do
allow(existing_path).to receive(:exist?).and_return(true)
@ -17,9 +17,8 @@ shared_examples Cask::Staged do
end
it "can run system commands with list-form arguments" do
FakeSystemCommand.expects_command(
["echo", "homebrew-cask", "rocks!"],
)
expect(fake_system_command).to receive(:run!)
.with("echo", args: ["homebrew-cask", "rocks!"])
staged.system_command("echo", args: ["homebrew-cask", "rocks!"])
end
@ -28,9 +27,8 @@ shared_examples Cask::Staged do
fake_pathname = existing_path
allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command(
["/bin/chmod", "-R", "--", "777", fake_pathname],
)
expect(fake_system_command).to receive(:run!)
.with("/bin/chmod", args: ["-R", "--", "777", fake_pathname], sudo: false)
staged.set_permissions(fake_pathname.to_s, "777")
end
@ -39,9 +37,8 @@ shared_examples Cask::Staged do
fake_pathname = existing_path
allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command(
["/bin/chmod", "-R", "--", "777", fake_pathname, fake_pathname],
)
expect(fake_system_command).to receive(:run!)
.with("/bin/chmod", args: ["-R", "--", "777", fake_pathname, fake_pathname], sudo: false)
staged.set_permissions([fake_pathname.to_s, fake_pathname.to_s], "777")
end
@ -58,9 +55,8 @@ shared_examples Cask::Staged do
allow(User).to receive(:current).and_return(User.new("fake_user"))
allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command(
sudo("/usr/sbin/chown", "-R", "--", "fake_user:staff", fake_pathname),
)
expect(fake_system_command).to receive(:run!)
.with("/usr/sbin/chown", args: ["-R", "--", "fake_user:staff", fake_pathname], sudo: true)
staged.set_ownership(fake_pathname.to_s)
end
@ -71,9 +67,8 @@ shared_examples Cask::Staged do
allow(User).to receive(:current).and_return(User.new("fake_user"))
allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command(
sudo("/usr/sbin/chown", "-R", "--", "fake_user:staff", fake_pathname, fake_pathname),
)
expect(fake_system_command).to receive(:run!)
.with("/usr/sbin/chown", args: ["-R", "--", "fake_user:staff", fake_pathname, fake_pathname], sudo: true)
staged.set_ownership([fake_pathname.to_s, fake_pathname.to_s])
end
@ -83,9 +78,8 @@ shared_examples Cask::Staged do
allow(staged).to receive(:Pathname).and_return(fake_pathname)
FakeSystemCommand.expects_command(
sudo("/usr/sbin/chown", "-R", "--", "other_user:other_group", fake_pathname),
)
expect(fake_system_command).to receive(:run!)
.with("/usr/sbin/chown", args: ["-R", "--", "other_user:other_group", fake_pathname], sudo: true)
staged.set_ownership(fake_pathname.to_s, user: "other_user", group: "other_group")
end

View File

@ -5,7 +5,7 @@ require "test/cask/dsl/shared_examples/base"
describe Cask::DSL::UninstallPostflight, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) }
let(:dsl) { described_class.new(cask, FakeSystemCommand) }
let(:dsl) { described_class.new(cask, class_double(SystemCommand)) }
it_behaves_like Cask::DSL::Base
end

View File

@ -6,7 +6,8 @@ require "test/cask/dsl/shared_examples/staged"
describe Cask::DSL::UninstallPreflight, :cask do
let(:cask) { Cask::CaskLoader.load(cask_path("basic-cask")) }
let(:dsl) { described_class.new(cask, FakeSystemCommand) }
let(:fake_system_command) { class_double(SystemCommand) }
let(:dsl) { described_class.new(cask, fake_system_command) }
it_behaves_like Cask::DSL::Base

View File

@ -153,8 +153,12 @@ describe Cask::Pkg, :cask do
"/usr/sbin/pkgutil",
args: ["--pkg-info-plist", pkg_id],
).and_return(
SystemCommand::Result.new(nil, [[:stdout, pkg_info_plist]], instance_double(Process::Status, exitstatus: 0),
secrets: []),
SystemCommand::Result.new(
["/usr/sbin/pkgutil", "--pkg-info-plist", pkg_id],
[[:stdout, pkg_info_plist]],
instance_double(Process::Status, exitstatus: 0),
secrets: [],
),
)
info = pkg.info

View File

@ -6,17 +6,3 @@ require "cmd/shared_examples/args_parse"
describe "Homebrew.switch_args" do
it_behaves_like "parseable arguments"
end
describe "brew switch", :integration_test do
it "allows switching between Formula versions" do
install_test_formula "testball"
testball_rack = HOMEBREW_CELLAR/"testball"
FileUtils.cp_r testball_rack/"0.1", testball_rack/"0.2"
expect { brew "switch", "testball", "0.2" }
.to output(/links created/).to_stdout
.and not_to_output.to_stderr
.and be_a_success
end
end

View File

@ -794,21 +794,19 @@ describe Formula do
f1 = formula "f1" do
url "f1-1"
depends_on :java
depends_on x11: :recommended
depends_on xcode: ["1.0", :optional]
end
stub_formula_loader(f1)
java = JavaRequirement.new
x11 = X11Requirement.new([:recommended])
xcode = XcodeRequirement.new(["1.0", :optional])
expect(Set.new(f1.recursive_requirements)).to eq(Set[java, x11])
expect(Set.new(f1.recursive_requirements)).to eq(Set[x11])
f1.build = BuildOptions.new(["--with-xcode", "--without-x11"], f1.options)
expect(Set.new(f1.recursive_requirements)).to eq(Set[java, xcode])
expect(Set.new(f1.recursive_requirements)).to eq(Set[xcode])
f1.build = f1.stable.build
f2 = formula "f2" do
@ -817,14 +815,14 @@ describe Formula do
depends_on "f1"
end
expect(Set.new(f2.recursive_requirements)).to eq(Set[java, x11])
expect(Set.new(f2.recursive_requirements {})).to eq(Set[java, x11, xcode])
expect(Set.new(f2.recursive_requirements)).to eq(Set[x11])
expect(Set.new(f2.recursive_requirements {})).to eq(Set[x11, xcode])
requirements = f2.recursive_requirements do |_dependent, requirement|
Requirement.prune if requirement.is_a?(JavaRequirement)
Requirement.prune if requirement.is_a?(X11Requirement)
end
expect(Set.new(requirements)).to eq(Set[x11, xcode])
expect(Set.new(requirements)).to eq(Set[xcode])
end
specify "#to_hash" do

View File

@ -6,13 +6,13 @@ require "language/java"
describe Language::Java do
describe "::java_home" do
if !OS.mac? || MacOS.version < :big_sur
it "returns valid JAVA_HOME if version is specified", :needs_java do
it "returns valid JAVA_HOME if version is specified", :needs_macos do
java_home = described_class.java_home("1.6+")
expect(java_home/"bin/java").to be_an_executable
end
end
it "returns valid JAVA_HOME if version is not specified", :needs_java do
it "returns valid JAVA_HOME if version is not specified", :needs_macos do
java_home = described_class.java_home
expect(java_home/"bin/java").to be_an_executable
end
@ -24,7 +24,7 @@ describe Language::Java do
expect(java_home[:JAVA_HOME]).to include("--version blah")
end
it "returns java_home path without version if version is not specified", :needs_java do
it "returns java_home path without version if version is not specified", :needs_macos do
java_home = described_class.java_home_env
expect(java_home[:JAVA_HOME]).not_to include("--version")
end
@ -36,7 +36,7 @@ describe Language::Java do
expect(java_home[:JAVA_HOME]).to include("--version blah")
end
it "returns java_home path without version if version is not specified", :needs_java do
it "returns java_home path without version if version is not specified", :needs_macos do
java_home = described_class.overridable_java_home_env
expect(java_home[:JAVA_HOME]).not_to include("--version")
end

View File

@ -1,38 +0,0 @@
# typed: false
# frozen_string_literal: true
require "requirements/java_requirement"
require "fileutils"
describe JavaRequirement do
subject { described_class.new(%w[1.8]) }
let(:java_home) { mktmpdir }
before do
FileUtils.mkdir java_home/"bin"
FileUtils.touch java_home/"bin/java"
allow(subject).to receive(:preferred_java).and_return(java_home/"bin/java")
end
specify "Apple Java environment" do
expect(subject).to be_satisfied
expect(ENV).to receive(:prepend_path)
expect(ENV).to receive(:append_to_cflags)
subject.modify_build_environment
expect(ENV["JAVA_HOME"]).to eq(java_home.to_s)
end
specify "Oracle Java environment" do
expect(subject).to be_satisfied
FileUtils.mkdir java_home/"include"
expect(ENV).to receive(:prepend_path)
expect(ENV).to receive(:append_to_cflags).twice
subject.modify_build_environment
expect(ENV["JAVA_HOME"]).to eq(java_home.to_s)
end
end

View File

@ -1,159 +0,0 @@
# typed: false
# frozen_string_literal: true
require "requirements/java_requirement"
describe JavaRequirement do
subject { described_class.new([]) }
before do
ENV["JAVA_HOME"] = nil
end
describe "#initialize" do
it "parses '1.8' tag correctly" do
req = described_class.new(["1.8"])
expect(req.display_s).to eq("Java = 1.8")
end
it "parses '9' tag correctly" do
req = described_class.new(["9"])
expect(req.display_s).to eq("Java = 9")
end
it "parses '9+' tag correctly" do
req = described_class.new(["9+"])
expect(req.display_s).to eq("Java >= 9")
end
it "parses '11' tag correctly" do
req = described_class.new(["11"])
expect(req.display_s).to eq("Java = 11")
end
it "parses bogus tag correctly" do
req = described_class.new(["bogus1.8"])
expect(req.display_s).to eq("Java")
end
end
describe "#message" do
its(:message) { is_expected.to match(/Java is required for this software./) }
end
describe "#inspect" do
subject { described_class.new(%w[1.7+]) }
its(:inspect) { is_expected.to eq('#<JavaRequirement: version="1.7+" []>') }
end
describe "#display_s" do
context "without specific version" do
its(:display_s) { is_expected.to eq("Java") }
end
context "with version 1.8" do
subject { described_class.new(%w[1.8]) }
its(:display_s) { is_expected.to eq("Java = 1.8") }
end
context "with version 1.8+" do
subject { described_class.new(%w[1.8+]) }
its(:display_s) { is_expected.to eq("Java >= 1.8") }
end
end
describe "#satisfied?" do
subject(:requirement) { described_class.new(%w[1.8]) }
if !OS.mac? || MacOS.version < :big_sur
it "returns false if no `java` executable can be found" do
allow(File).to receive(:executable?).and_return(false)
expect(requirement).not_to be_satisfied
end
end
it "returns true if #preferred_java returns a path" do
allow(requirement).to receive(:preferred_java).and_return(Pathname.new("/usr/bin/java"))
expect(requirement).to be_satisfied
end
context "when #possible_javas contains paths" do
let(:path) { mktmpdir }
let(:java) { path/"java" }
def setup_java_with_version(version)
IO.write java, <<~SH
#!/bin/sh
echo 'java version "#{version}"' 1>&2
SH
FileUtils.chmod "+x", java
end
before do
allow(requirement).to receive(:possible_javas).and_return([java])
end
context "and 1.7 is required" do
subject(:requirement) { described_class.new(%w[1.7]) }
it "returns false if all are lower" do
setup_java_with_version "1.6.0_5"
expect(requirement).not_to be_satisfied
end
it "returns true if one is equal" do
setup_java_with_version "1.7.0_5"
expect(requirement).to be_satisfied
end
it "returns false if all are higher" do
setup_java_with_version "1.8.0_5"
expect(requirement).not_to be_satisfied
end
end
context "and 1.7+ is required" do
subject(:requirement) { described_class.new(%w[1.7+]) }
it "returns false if all are lower" do
setup_java_with_version "1.6.0_5"
expect(requirement).not_to be_satisfied
end
it "returns true if one is equal" do
setup_java_with_version "1.7.0_5"
expect(requirement).to be_satisfied
end
it "returns true if one is higher" do
setup_java_with_version "1.8.0_5"
expect(requirement).to be_satisfied
end
end
end
end
describe "#suggestion" do
context "without specific version" do
its(:suggestion) { is_expected.to match(/brew install --cask adoptopenjdk/) }
its(:cask) { is_expected.to eq("adoptopenjdk") }
end
context "with version 1.8" do
subject { described_class.new(%w[1.8]) }
its(:suggestion) { is_expected.to match(%r{brew install --cask homebrew/cask-versions/adoptopenjdk8}) }
its(:cask) { is_expected.to eq("homebrew/cask-versions/adoptopenjdk8") }
end
context "with version 1.8+" do
subject { described_class.new(%w[1.8+]) }
its(:suggestion) { is_expected.to match(/brew install --cask adoptopenjdk/) }
its(:cask) { is_expected.to eq("adoptopenjdk") }
end
end
end

View File

@ -324,6 +324,7 @@ describe RuboCop::Cop::FormulaAudit::ComponentsOrder do
homepage "https://brew.sh"
on_macos do
disable! because: :does_not_build
depends_on "readline"
end
@ -341,6 +342,7 @@ describe RuboCop::Cop::FormulaAudit::ComponentsOrder do
homepage "https://brew.sh"
on_linux do
deprecate! because: "it's deprecated"
depends_on "readline"
end

View File

@ -250,61 +250,6 @@ describe RuboCop::Cop::FormulaAudit::Text do
RUBY
end
it "When using JAVA_HOME without a java dependency" do
expect_offense(<<~RUBY)
class Foo < Formula
def install
ohai "JAVA_HOME"
^^^^^^^^^^^ Use `depends_on :java` to set JAVA_HOME
end
end
RUBY
end
it "When using JAVA_HOME with an openjdk dependency" do
expect_no_offenses(<<~RUBY)
class Foo < Formula
depends_on "openjdk"
def install
ohai "JAVA_HOME"
end
end
RUBY
end
it "When using JAVA_HOME with an openjdk build dependency" do
expect_no_offenses(<<~RUBY)
class Foo < Formula
depends_on "openjdk" => :build
def install
ohai "JAVA_HOME"
end
end
RUBY
end
it "When using JAVA_HOME with a java dependency" do
expect_no_offenses(<<~RUBY)
class Foo < Formula
depends_on :java
def install
ohai "JAVA_HOME"
end
end
RUBY
end
it "When using JAVA_HOME with a java build dependency" do
expect_no_offenses(<<~RUBY)
class Foo < Formula
depends_on :java => :build
def install
ohai "JAVA_HOME"
end
end
RUBY
end
it "When using `prefix + \"bin\"` instead of `bin`" do
expect_offense(<<~RUBY)
class Foo < Formula

View File

@ -1,74 +0,0 @@
# typed: true
# frozen_string_literal: true
def sudo(*args)
["/usr/bin/sudo", "-E", "--"] + args.flatten
end
class FakeSystemCommand
def self.responses
@responses ||= {}
end
def self.expectations
@expectations ||= {}
end
def self.system_calls
@system_calls ||= Hash.new(0)
end
def self.clear
@responses = nil
@expectations = nil
@system_calls = nil
end
def self.stubs_command(command, response = "")
command = command.map(&:to_s)
responses[command] = response
end
def self.expects_command(command, response = "", times = 1)
command = command.map(&:to_s)
stubs_command(command, response)
expectations[command] = times
end
def self.verify_expectations!
expectations.each do |command, times|
unless system_calls[command] == times
raise("expected #{command.inspect} to be run #{times} times, but got #{system_calls[command]}")
end
end
end
def self.run(command_string, options = {})
command = SystemCommand.new(command_string, options).command
puts command
unless responses.key?(command)
raise("no response faked for #{command.inspect}, faked responses are: #{responses.inspect}")
end
system_calls[command] += 1
response = responses[command]
if response.respond_to?(:call)
response.call(command_string, options)
else
SystemCommand::Result.new(command, [[:stdout, response]], OpenStruct.new(exitstatus: 0), secrets: [])
end
end
def self.run!(command, options = {})
run(command, options.merge(must_succeed: true))
end
end
RSpec.configure do |config|
config.after do
FakeSystemCommand.verify_expectations!
ensure
FakeSystemCommand.clear
end
end

View File

@ -4,7 +4,6 @@
require "cask/config"
require "cask/cache"
require "test/support/helper/cask/fake_system_command"
require "test/support/helper/cask/install_helper"
require "test/support/helper/cask/never_sudo_system_command"

View File

@ -61,18 +61,24 @@ describe GitHub do
describe "::get_artifact_url", :needs_network do
it "fails to find a nonexistant workflow" do
expect {
subject.get_artifact_url("Homebrew", "homebrew-core", 1)
subject.get_artifact_url(
subject.get_workflow_run("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.get_artifact_url("Homebrew", "homebrew-core", 51971, artifact_name: "false_bottles")
subject.get_artifact_url(
subject.get_workflow_run("Homebrew", "homebrew-core", 51971, artifact_name: "false_bottles"),
)
}.to raise_error(/No artifact .+ was found/)
end
it "gets an artifact link" do
url = subject.get_artifact_url("Homebrew", "homebrew-core", 51971, artifact_name: "bottles")
url = subject.get_artifact_url(
subject.get_workflow_run("Homebrew", "homebrew-core", 51971, artifact_name: "bottles"),
)
expect(url).to eq("https://api.github.com/repos/Homebrew/homebrew-core/actions/artifacts/3557392/zip")
end
end

View File

@ -0,0 +1,171 @@
# typed: false
# frozen_string_literal: true
require "utils/pypi"
describe PyPI do
let(:package_url) do
"https://files.pythonhosted.org/packages/b0/3f/2e1dad67eb172b6443b5eb37eb885a054a55cfd733393071499514140282/"\
"snakemake-5.29.0.tar.gz"
end
let(:old_package_url) do
"https://files.pythonhosted.org/packages/6f/c4/da52bfdd6168ea46a0fe2b7c983b6c34c377a8733ec177cc00b197a96a9f/"\
"snakemake-5.28.0.tar.gz"
end
describe PyPI::Package do
let(:package_checksum) { "47417307d08ecb0707b3b29effc933bd63d8c8e3ab15509c62b685b7614c6568" }
let(:old_package_checksum) { "2367ce91baf7f8fa7738d33aff9670ffdf5410bbac49aeb209f73b45a3425046" }
let(:package) { described_class.new("snakemake") }
let(:package_with_version) { described_class.new("snakemake==5.28.0") }
let(:package_with_different_version) { described_class.new("snakemake==5.29.0") }
let(:package_with_extra) { described_class.new("snakemake[foo]") }
let(:package_with_extra_and_version) { described_class.new("snakemake[foo]==5.28.0") }
let(:package_from_url) { described_class.new(package_url, is_url: true) }
let(:other_package) { described_class.new("virtualenv==20.2.0") }
describe "initialize" do
it "initializes name" do
expect(described_class.new("foo").name).to eq "foo"
end
it "initializes name with extra" do
expect(described_class.new("foo[bar]").name).to eq "foo"
end
it "initializes extra" do
expect(described_class.new("foo[bar]").extras).to eq ["bar"]
end
it "initializes multiple extras" do
expect(described_class.new("foo[bar,baz]").extras).to eq ["bar", "baz"]
end
it "initializes name with version" do
expect(described_class.new("foo==1.2.3").name).to eq "foo"
end
it "initializes version" do
expect(described_class.new("foo==1.2.3").version).to eq "1.2.3"
end
it "initializes extra with version" do
expect(described_class.new("foo[bar]==1.2.3").extras).to eq ["bar"]
end
it "initializes multiple extras with version" do
expect(described_class.new("foo[bar,baz]==1.2.3").extras).to eq ["bar", "baz"]
end
it "initializes version with extra" do
expect(described_class.new("foo[bar]==1.2.3").version).to eq "1.2.3"
end
it "initializes version with multiple extras" do
expect(described_class.new("foo[bar,baz]==1.2.3").version).to eq "1.2.3"
end
it "initializes name from url" do
expect(described_class.new(package_url, is_url: true).name).to eq "snakemake"
end
it "initializes version from url" do
expect(described_class.new(package_url, is_url: true).version).to eq "5.29.0"
end
end
describe ".pypi_info", :needs_network do
it "gets pypi info from a package name" do
expect(package.pypi_info.first).to eq "snakemake"
end
it "gets pypi info from a package name and specified version" do
expect(package.pypi_info(version: "5.29.0")).to eq ["snakemake", package_url, package_checksum, "5.29.0"]
end
it "gets pypi info from a package name with extra" do
expect(package_with_extra.pypi_info.first).to eq "snakemake"
end
it "gets pypi info from a package name and version" do
expect(package_with_version.pypi_info).to eq ["snakemake", old_package_url, old_package_checksum, "5.28.0"]
end
it "gets pypi info from a package name with overriden version" do
expected_result = ["snakemake", package_url, package_checksum, "5.29.0"]
expect(package_with_version.pypi_info(version: "5.29.0")).to eq expected_result
end
it "gets pypi info from a package name, extras, and version" do
expected_result = ["snakemake", old_package_url, old_package_checksum, "5.28.0"]
expect(package_with_extra_and_version.pypi_info).to eq expected_result
end
it "gets pypi info from a url" do
expect(package_from_url.pypi_info).to eq ["snakemake", package_url, package_checksum, "5.29.0"]
end
it "gets pypi info from a url with overriden version" do
expected_result = ["snakemake", old_package_url, old_package_checksum, "5.28.0"]
expect(package_from_url.pypi_info(version: "5.28.0")).to eq expected_result
end
end
describe ".to_s" do
it "returns string representation of package name" do
expect(package.to_s).to eq "snakemake"
end
it "returns string representation of package with version" do
expect(package_with_version.to_s).to eq "snakemake==5.28.0"
end
it "returns string representation of package with extra" do
expect(package_with_extra.to_s).to eq "snakemake[foo]"
end
it "returns string representation of package with extra and version" do
expect(package_with_extra_and_version.to_s).to eq "snakemake[foo]==5.28.0"
end
it "returns string representation of package from url" do
expect(package_from_url.to_s).to eq "snakemake==5.29.0"
end
end
describe ".same_package?" do
it "returns false for different packages" do
expect(package.same_package?(other_package)).to eq false
end
it "returns true for the same package" do
expect(package.same_package?(package_with_version)).to eq true
end
it "returns true for the same package with different versions" do
expect(package_with_version.same_package?(package_with_different_version)).to eq true
end
end
describe "<=>" do
it "returns -1" do
expect(package <=> other_package).to eq((-1))
end
it "returns 0" do
expect(package <=> package_with_version).to eq 0
end
it "returns 1" do
expect(other_package <=> package_with_extra_and_version).to eq 1
end
end
end
describe "update_pypi_url", :needs_network do
it "updates url to new version" do
expect(described_class.update_pypi_url(old_package_url, "5.29.0")).to eq package_url
end
end
end

View File

@ -91,8 +91,10 @@ module UnpackStrategy
Tempfile.open(["", ".bom"]) do |bomfile|
bomfile.close
bom = path.bom
Tempfile.open(["", ".list"]) do |filelist|
filelist.puts(path.bom)
filelist.puts(bom)
filelist.close
system_command! "mkbom",
@ -100,9 +102,23 @@ module UnpackStrategy
verbose: verbose
end
system_command! "ditto",
args: ["--bom", bomfile.path, "--", path, unpack_dir],
verbose: verbose
result = system_command! "ditto",
args: ["--bom", bomfile.path, "--", path, unpack_dir],
verbose: verbose
if result.stderr.include?("contains no files, nothing copied")
all_paths_find = system_command("find", args: [".", "-print0"], chdir: path, print_stderr: false)
.stdout
.split("\0")
all_paths_ruby = Pathname.glob(path/"**/*", File::FNM_DOTMATCH)
.map { |p| p.relative_path_from(path).to_s }
odebug "BOM contents:", bom
odebug "BOM contents (retry):", path.bom
odebug "Directory contents (find):", all_paths_find.join("\n")
odebug "Directory contents (Ruby):", all_paths_ruby.join("\n")
end
FileUtils.chmod "u+w", Pathname.glob(unpack_dir/"**/*", File::FNM_DOTMATCH).reject(&:symlink?)
end

View File

@ -96,9 +96,7 @@ module GitHub
return unless Homebrew::EnvConfig.github_api_username
return unless Homebrew::EnvConfig.github_api_password
odeprecated "the GitHub API with HOMEBREW_GITHUB_API_PASSWORD", "HOMEBREW_GITHUB_API_TOKEN"
[Homebrew::EnvConfig.github_api_password, Homebrew::EnvConfig.github_api_username]
odisabled "the GitHub API with HOMEBREW_GITHUB_API_PASSWORD", "HOMEBREW_GITHUB_API_TOKEN"
end
def keychain_username_password
@ -510,7 +508,7 @@ module GitHub
open_api(url, data_binary_path: local_file, request_method: :POST, scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES)
end
def get_artifact_url(user, repo, pr, workflow_id: "tests.yml", artifact_name: "bottles")
def get_workflow_run(user, repo, pr, 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)
@ -523,6 +521,11 @@ module GitHub
run["head_sha"] == pr_sha
end
[workflow_run, pr_sha, pr_branch, pr, workflow_id, scopes, artifact_name]
end
def get_artifact_url(workflow_array)
workflow_run, pr_sha, pr_branch, pr, workflow_id, scopes, artifact_name = *workflow_array
if workflow_run.empty?
raise Error, <<~EOS
No matching workflow run found for these criteria!

View File

@ -34,7 +34,7 @@ module Utils
# @api public
sig do
params(
paths: T.any(T::Array[T.untyped], String),
paths: T.any(T::Array[T.untyped], String, Pathname),
before: T.nilable(T.any(Regexp, String)),
after: T.nilable(T.any(String, Symbol)),
audit_result: T::Boolean,

View File

@ -5,70 +5,161 @@
#
# @api private
module PyPI
extend T::Sig
module_function
PYTHONHOSTED_URL_PREFIX = "https://files.pythonhosted.org/packages/"
private_constant :PYTHONHOSTED_URL_PREFIX
AUTOMATIC_RESOURCE_UPDATE_BLOCKLIST = %w[
ansible
ansible@2.8
cloudformation-cli
diffoscope
dxpy
ipython
molecule
salt
].freeze
private_constant :AUTOMATIC_RESOURCE_UPDATE_BLOCKLIST
@pipgrip_installed = nil
def url_to_pypi_package_name(url)
return unless url.start_with? PYTHONHOSTED_URL_PREFIX
# PyPI Package
#
# @api private
class Package
extend T::Sig
File.basename(url).match(/^(.+)-[a-z\d.]+$/)[1]
attr_accessor :name
attr_accessor :extras
attr_accessor :version
sig { params(package_string: String, is_url: T::Boolean).void }
def initialize(package_string, is_url: false)
@pypi_info = nil
if is_url
unless package_string.start_with?(PYTHONHOSTED_URL_PREFIX) &&
match = File.basename(package_string).match(/^(.+)-([a-z\d.]+?)(?:.tar.gz|.zip)$/)
raise ArgumentError, "package should be a valid PyPI url"
end
@name = match[1]
@version = match[2]
return
end
@name = package_string
@name, @version = @name.split("==") if @name.include? "=="
return unless match = @name.match(/^(.*?)\[(.+)\]$/)
@name = match[1]
@extras = match[2].split ","
end
# Get name, URL, SHA-256 checksum, and latest version for a given PyPI package.
sig { params(version: T.nilable(T.any(String, Version))).returns(T.nilable(T::Array[String])) }
def pypi_info(version: nil)
return @pypi_info if @pypi_info.present? && version.blank?
version ||= @version
metadata_url = if version.present?
"https://pypi.org/pypi/#{@name}/#{version}/json"
else
"https://pypi.org/pypi/#{@name}/json"
end
out, _, status = curl_output metadata_url, "--location"
return unless status.success?
begin
json = JSON.parse out
rescue JSON::ParserError
return
end
sdist = json["urls"].find { |url| url["packagetype"] == "sdist" }
return json["info"]["name"] if sdist.nil?
@pypi_info = [json["info"]["name"], sdist["url"], sdist["digests"]["sha256"], json["info"]["version"]]
end
sig { returns(T::Boolean) }
def valid_pypi_package?
info = pypi_info
info.present? && info.is_a?(Array)
end
sig { returns(String) }
def to_s
out = @name
out += "[#{@extras.join(",")}]" if @extras.present?
out += "==#{@version}" if @version.present?
out
end
sig { params(other: Package).returns(T::Boolean) }
def same_package?(other)
@name.tr("_", "-") == other.name.tr("_", "-")
end
# Compare only names so we can use .include? on a Package array
sig { params(other: Package).returns(T::Boolean) }
def ==(other)
same_package?(other)
end
sig { params(other: Package).returns(T.nilable(Integer)) }
def <=>(other)
@name <=> other.name
end
end
sig { params(url: String, version: T.any(String, Version)).returns(T.nilable(String)) }
def update_pypi_url(url, version)
package = url_to_pypi_package_name url
return if package.nil?
package = Package.new url, is_url: true
_, url = get_pypi_info(package, version)
_, url = package.pypi_info(version: version)
url
end
# Get name, URL and SHA-256 checksum for a given PyPI package.
def get_pypi_info(package, version)
metadata_url = "https://pypi.org/pypi/#{package}/#{version}/json"
out, _, status = curl_output metadata_url, "--location"
return unless status.success?
begin
json = JSON.parse out
rescue JSON::ParserError
return
end
sdist = json["urls"].find { |url| url["packagetype"] == "sdist" }
return json["info"]["name"] if sdist.nil?
[json["info"]["name"], sdist["url"], sdist["digests"]["sha256"]]
end
# Return true if resources were checked (even if no change).
def update_python_resources!(formula, version = nil, print_only: false, silent: false,
ignore_non_pypi_packages: false)
sig do
params(
formula: Formula,
version: T.nilable(String),
package_name: T.nilable(String),
extra_packages: T.nilable(T::Array[String]),
exclude_packages: T.nilable(T::Array[String]),
print_only: T::Boolean,
silent: T::Boolean,
ignore_non_pypi_packages: T::Boolean,
).returns(T.nilable(T::Boolean))
end
def update_python_resources!(formula, version: nil, package_name: nil, extra_packages: nil, exclude_packages: nil,
print_only: false, silent: false, ignore_non_pypi_packages: false)
if !print_only && AUTOMATIC_RESOURCE_UPDATE_BLOCKLIST.include?(formula.full_name)
odie "The resources for \"#{formula.name}\" need special attention. Please update them manually."
return
auto_update_list = formula.tap&.pypi_formula_mappings
if auto_update_list.present? && auto_update_list.key?(formula.full_name) &&
package_name.blank? && extra_packages.blank? && exclude_packages.blank?
list_entry = auto_update_list[formula.full_name]
case list_entry
when false
unless print_only
odie "The resources for \"#{formula.name}\" need special attention. Please update them manually."
end
when String
package_name = list_entry
when Hash
package_name = list_entry["package_name"]
extra_packages = list_entry["extra_packages"]
exclude_packages = list_entry["exclude_packages"]
end
end
pypi_name = url_to_pypi_package_name formula.stable.url
main_package = if package_name.present?
Package.new(package_name)
else
begin
Package.new(formula.stable.url, is_url: true)
rescue ArgumentError
nil
end
end
if pypi_name.nil?
if main_package.blank?
return if ignore_non_pypi_packages
odie <<~EOS
@ -77,47 +168,81 @@ module PyPI
EOS
end
version ||= formula.version
unless main_package.valid_pypi_package?
return if ignore_non_pypi_packages
if get_pypi_info(pypi_name, version).blank?
odie "\"#{pypi_name}\" at version #{version} is not available on PyPI." unless ignore_non_pypi_packages
return
odie "\"#{main_package}\" is not available on PyPI."
end
non_pypi_resources = formula.resources.reject do |resource|
resource.url.start_with? PYTHONHOSTED_URL_PREFIX
main_package.version = version if version.present?
extra_packages = (extra_packages || []).map { |p| Package.new p }
exclude_packages = (exclude_packages || []).map { |p| Package.new p }
input_packages = [main_package]
extra_packages.each do |extra_package|
if !extra_package.valid_pypi_package? && !ignore_non_pypi_packages
odie "\"#{extra_package}\" is not available on PyPI."
end
input_packages.each do |existing_package|
if existing_package.same_package?(extra_package) && existing_package.version != extra_package.version
odie "Conflicting versions specified for the `#{extra_package.name}` package: "\
"#{existing_package.version}, #{extra_package.version}"
end
end
input_packages << extra_package unless input_packages.include? extra_package
end
if non_pypi_resources.present? && !print_only
odie "\"#{formula.name}\" contains non-PyPI resources. Please update the resources manually."
formula.resources.each do |resource|
if !print_only && !resource.url.start_with?(PYTHONHOSTED_URL_PREFIX)
odie "\"#{formula.name}\" contains non-PyPI resources. Please update the resources manually."
end
end
@pipgrip_installed ||= Formula["pipgrip"].any_version_installed?
odie '"pipgrip" must be installed (`brew install pipgrip`)' unless @pipgrip_installed
ohai "Retrieving PyPI dependencies for \"#{pypi_name}==#{version}\"..." if !print_only && !silent
pipgrip_output = Utils.popen_read Formula["pipgrip"].bin/"pipgrip", "--json", "--no-cache-dir",
"#{pypi_name}==#{version}"
unless $CHILD_STATUS.success?
odie <<~EOS
Unable to determine dependencies for \"#{pypi_name}\" because of a failure when running
`pipgrip --json --no-cache-dir #{pypi_name}==#{version}`.
Please update the resources for \"#{formula.name}\" manually.
EOS
end
found_packages = []
input_packages.each do |package|
ohai "Retrieving PyPI dependencies for \"#{package}\"..." if !print_only && !silent
pipgrip_output = Utils.popen_read Formula["pipgrip"].bin/"pipgrip", "--json", "--no-cache-dir", package.to_s
unless $CHILD_STATUS.success?
odie <<~EOS
Unable to determine dependencies for \"#{package}\" because of a failure when running
`pipgrip --json --no-cache-dir #{package}`.
Please update the resources for \"#{formula.name}\" manually.
EOS
end
packages = JSON.parse(pipgrip_output).sort.to_h
JSON.parse(pipgrip_output).to_h.each do |new_name, new_version|
new_package = Package.new("#{new_name}==#{new_version}")
found_packages.each do |existing_package|
if existing_package.same_package?(new_package) && existing_package.version != new_package.version
odie "Conflicting versions found for the `#{new_package.name}` resource: "\
"#{existing_package.version}, #{new_package.version}"
end
end
found_packages << new_package unless found_packages.include? new_package
end
end
# Remove extra packages that may be included in pipgrip output
exclude_list = %W[#{pypi_name.downcase} argparse pip setuptools wheel wsgiref]
packages.delete_if do |package|
exclude_list.include? package
end
exclude_list = %W[#{main_package.name} argparse pip setuptools wheel wsgiref].map { |p| Package.new p }
found_packages.delete_if { |package| exclude_list.include? package }
new_resource_blocks = ""
packages.each do |package, package_version|
ohai "Getting PyPI info for \"#{package}==#{package_version}\"" if !print_only && !silent
name, url, checksum = get_pypi_info package, package_version
found_packages.sort.each do |package|
if exclude_packages.include? package
ohai "Excluding \"#{package}\"" if !print_only && !silent
next
end
ohai "Getting PyPI info for \"#{package}\"" if !print_only && !silent
name, url, checksum = package.pypi_info
# Fail if unable to find name, url or checksum for any resource
if name.blank?
odie "Unable to resolve some dependencies. Please update the resources for \"#{formula.name}\" manually."

View File

@ -32,7 +32,6 @@ module SharedAudits
end
GITHUB_PRERELEASE_ALLOWLIST = {
"amd-power-gadget" => :all,
"elm-format" => "0.8.3",
"extraterm" => :all,
"freetube" => :all,

View File

@ -47,11 +47,11 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/webrobots-0.1.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mechanize-2.7.6/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/method_source-1.0.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/mustache-1.1.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel-1.20.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel_tests-3.3.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel-1.20.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parallel_tests-3.4.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parser-2.7.2.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rainbow-3.0.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.6100/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-runtime-0.5.6101/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/parlour-4.0.1/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/patchelf-1.3.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/plist-3.5.0/lib"
@ -68,8 +68,8 @@ $:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec-mocks-3.10.0/li
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec-3.10.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec-its-1.3.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec-retry-0.6.2/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.6100-universal-darwin-19/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.6100/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-static-0.5.6101-universal-darwin-19/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/sorbet-0.5.6101/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec-sorbet-1.7.0/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rspec-wait-0.0.9/lib"
$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/gems/rubocop-ast-1.1.1/lib"

View File

@ -68,7 +68,6 @@ pr-publish
pr-pull
pr-upload
prof
pull
readall
reinstall
release-notes

View File

@ -84,7 +84,7 @@ GEM
html-pipeline (2.14.0)
activesupport (>= 2)
nokogiri (>= 1.4)
html-proofer (3.17.0)
html-proofer (3.17.1)
addressable (~> 2.3)
mercenary (~> 0.3)
nokogumbo (~> 2.0)
@ -219,12 +219,12 @@ GEM
multipart-post (2.1.1)
nokogiri (1.10.10)
mini_portile2 (~> 2.4.0)
nokogumbo (2.0.2)
nokogumbo (2.0.3)
nokogiri (~> 1.8, >= 1.8.4)
octokit (4.18.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
parallel (1.20.0)
parallel (1.20.1)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (3.1.1)

View File

@ -11,7 +11,7 @@ it does it too. You have to confirm everything it will do before it starts.
## macOS Requirements
* A 64-bit Intel CPU <sup>[1](#1)</sup>
* macOS High Sierra (10.13) (or higher) <sup>[2](#2)</sup>
* macOS Mojave (10.14) (or higher) <sup>[2](#2)</sup>
* Command Line Tools (CLT) for Xcode: `xcode-select --install`,
[developer.apple.com/downloads](https://developer.apple.com/downloads) or
[Xcode](https://itunes.apple.com/us/app/xcode/id497799835) <sup>[3](#3)</sup>
@ -52,7 +52,7 @@ Uninstallation is documented in the [FAQ](FAQ.md).
<a name="1"><sup>1</sup></a> For 32-bit or PPC support see
[Tigerbrew](https://github.com/mistydemeo/tigerbrew).
<a name="2"><sup>2</sup></a> 10.13 or higher is recommended. 10.910.12 are
<a name="2"><sup>2</sup></a> 10.14 or higher is recommended. 10.910.13 are
supported on a best-effort basis. For 10.4-10.6 see
[Tigerbrew](https://github.com/mistydemeo/tigerbrew).

View File

@ -905,14 +905,16 @@ Display the path to the file being used when invoking `brew` *`cmd`*.
### `create` [*`options`*] *`URL`*
Generate a formula for the downloadable file at *`URL`* and open it in the editor.
Homebrew will attempt to automatically derive the formula name and version, but
if it fails, you'll have to make your own template. The `wget` formula serves as
a simple example. For the complete API, see:
Generate a formula or, with `--cask`, a cask for the downloadable file at *`URL`*
and open it in the editor. Homebrew will attempt to automatically derive the
formula name and version, but if it fails, you'll have to make your own template.
The `wget` formula serves as a simple example. For the complete API, see:
<https://rubydoc.brew.sh/Formula>
* `--autotools`:
Create a basic template for an Autotools-style build.
* `--cask`:
Create a basic template for a cask.
* `--cmake`:
Create a basic template for a CMake-style build.
* `--crystal`:
@ -936,9 +938,9 @@ a simple example. For the complete API, see:
* `--HEAD`:
Indicate that *`URL`* points to the package's repository rather than a file.
* `--set-name`:
Explicitly set the *`name`* of the new formula.
Explicitly set the *`name`* of the new formula or cask.
* `--set-version`:
Explicitly set the *`version`* of the new formula.
Explicitly set the *`version`* of the new formula or cask.
* `--set-license`:
Explicitly set the *`license`* of the new formula.
* `--tap`:
@ -961,17 +963,6 @@ Build bottles for these formulae with GitHub Actions.
* `--upload`:
Upload built bottles to Bintray.
### `diy` [*`options`*]
Automatically determine the installation prefix for non-Homebrew software.
Using the output from this command, you can install your own software into
the Cellar and then link it into Homebrew's prefix with `brew link`.
* `--name`:
Explicitly set the *`name`* of the package being installed.
* `--version`:
Explicitly set the *`version`* of the package being installed.
### `edit` [*`formula`*|*`cask`*]
Open a *`formula`* or *`cask`* in the editor set by `EDITOR` or `HOMEBREW_EDITOR`,
@ -1055,6 +1046,17 @@ Generate Homebrew's manpages.
* `--link`:
This is now done automatically by `brew update`.
### `mirror` *`formula`*
Reupload the stable URL of a formula to Bintray for use as a mirror.
* `--bintray-org`:
Upload to the specified Bintray organisation (default: `homebrew`).
* `--bintray-repo`:
Upload to the specified Bintray repository (default: `mirror`).
* `--no-publish`:
Upload to Bintray, but don't publish.
### `pr-automerge` [*`options`*]
Find pull requests that can be automatically merged using `brew pr-publish`.
@ -1115,7 +1117,7 @@ Requires write access to the repository.
* `--message`:
Message to include when autosquashing revision bumps, deletions, and rebuilds.
* `--workflow`:
Retrieve artifacts from the specified workflow (default: `tests.yml`).
Retrieve artifacts from the specified workflow (default: `tests.yml`). Legacy: use --workflows instead
* `--artifact`:
Download artifacts with the specified name (default: `bottles`).
* `--bintray-org`:
@ -1126,6 +1128,10 @@ Requires write access to the repository.
Use the specified *`URL`* as the root of the bottle's URL instead of Homebrew's default.
* `--bintray-mirror`:
Use the specified Bintray repository to automatically mirror stable URLs defined in the formulae (default: `mirror`).
* `--workflows`:
Retrieve artifacts from the specified workflow (default: `tests.yml`) Comma-separated list to include multiple workflows.
* `--ignore-missing-artifacts`:
Comma-separated list of workflows which can be ignored if they have not been run.
### `pr-upload` [*`options`*]
@ -1305,6 +1311,12 @@ Update versions for PyPI resource blocks in *`formula`*.
Don't fail if *`formula`* is not a PyPI package.
* `--version`:
Use the specified *`version`* when finding resources for *`formula`*. If no version is specified, the current version for *`formula`* will be used.
* `--package-name`:
Use the specified *`package-name`* when finding resources for *`formula`*. If no package name is specified, it will be inferred from the formula's stable URL.
* `--extra-packages`:
Include these additional packages when finding resources.
* `--exclude-packages`:
Exclude these packages when finding resources.
### `update-test` [*`options`*]

View File

@ -1241,13 +1241,17 @@ Treat all named arguments as casks\.
Display the path to the file being used when invoking \fBbrew\fR \fIcmd\fR\.
.
.SS "\fBcreate\fR [\fIoptions\fR] \fIURL\fR"
Generate a formula for the downloadable file at \fIURL\fR and open it in the editor\. Homebrew will attempt to automatically derive the formula name and version, but if it fails, you\'ll have to make your own template\. The \fBwget\fR formula serves as a simple example\. For the complete API, see: \fIhttps://rubydoc\.brew\.sh/Formula\fR
Generate a formula or, with \fB\-\-cask\fR, a cask for the downloadable file at \fIURL\fR and open it in the editor\. Homebrew will attempt to automatically derive the formula name and version, but if it fails, you\'ll have to make your own template\. The \fBwget\fR formula serves as a simple example\. For the complete API, see: \fIhttps://rubydoc\.brew\.sh/Formula\fR
.
.TP
\fB\-\-autotools\fR
Create a basic template for an Autotools\-style build\.
.
.TP
\fB\-\-cask\fR
Create a basic template for a cask\.
.
.TP
\fB\-\-cmake\fR
Create a basic template for a CMake\-style build\.
.
@ -1293,11 +1297,11 @@ Indicate that \fIURL\fR points to the package\'s repository rather than a file\.
.
.TP
\fB\-\-set\-name\fR
Explicitly set the \fIname\fR of the new formula\.
Explicitly set the \fIname\fR of the new formula or cask\.
.
.TP
\fB\-\-set\-version\fR
Explicitly set the \fIversion\fR of the new formula\.
Explicitly set the \fIversion\fR of the new formula or cask\.
.
.TP
\fB\-\-set\-license\fR
@ -1334,17 +1338,6 @@ Dispatch specified workflow (default: \fBdispatch\-build\-bottle\.yml\fR)\.
\fB\-\-upload\fR
Upload built bottles to Bintray\.
.
.SS "\fBdiy\fR [\fIoptions\fR]"
Automatically determine the installation prefix for non\-Homebrew software\. Using the output from this command, you can install your own software into the Cellar and then link it into Homebrew\'s prefix with \fBbrew link\fR\.
.
.TP
\fB\-\-name\fR
Explicitly set the \fIname\fR of the package being installed\.
.
.TP
\fB\-\-version\fR
Explicitly set the \fIversion\fR of the package being installed\.
.
.SS "\fBedit\fR [\fIformula\fR|\fIcask\fR]"
Open a \fIformula\fR or \fIcask\fR in the editor set by \fBEDITOR\fR or \fBHOMEBREW_EDITOR\fR, or open the Homebrew repository for editing if no formula is provided\.
.
@ -1444,6 +1437,21 @@ Return a failing status code if changes are detected in the manpage outputs\. Th
\fB\-\-link\fR
This is now done automatically by \fBbrew update\fR\.
.
.SS "\fBmirror\fR \fIformula\fR"
Reupload the stable URL of a formula to Bintray for use as a mirror\.
.
.TP
\fB\-\-bintray\-org\fR
Upload to the specified Bintray organisation (default: \fBhomebrew\fR)\.
.
.TP
\fB\-\-bintray\-repo\fR
Upload to the specified Bintray repository (default: \fBmirror\fR)\.
.
.TP
\fB\-\-no\-publish\fR
Upload to Bintray, but don\'t publish\.
.
.SS "\fBpr\-automerge\fR [\fIoptions\fR]"
Find pull requests that can be automatically merged using \fBbrew pr\-publish\fR\.
.
@ -1539,7 +1547,7 @@ Message to include when autosquashing revision bumps, deletions, and rebuilds\.
.
.TP
\fB\-\-workflow\fR
Retrieve artifacts from the specified workflow (default: \fBtests\.yml\fR)\.
Retrieve artifacts from the specified workflow (default: \fBtests\.yml\fR)\. Legacy: use \-\-workflows instead
.
.TP
\fB\-\-artifact\fR
@ -1561,6 +1569,14 @@ Use the specified \fIURL\fR as the root of the bottle\'s URL instead of Homebrew
\fB\-\-bintray\-mirror\fR
Use the specified Bintray repository to automatically mirror stable URLs defined in the formulae (default: \fBmirror\fR)\.
.
.TP
\fB\-\-workflows\fR
Retrieve artifacts from the specified workflow (default: \fBtests\.yml\fR) Comma\-separated list to include multiple workflows\.
.
.TP
\fB\-\-ignore\-missing\-artifacts\fR
Comma\-separated list of workflows which can be ignored if they have not been run\.
.
.SS "\fBpr\-upload\fR [\fIoptions\fR]"
Apply the bottle commit and publish bottles to Bintray or GitHub Releases\.
.
@ -1797,6 +1813,18 @@ Don\'t fail if \fIformula\fR is not a PyPI package\.
\fB\-\-version\fR
Use the specified \fIversion\fR when finding resources for \fIformula\fR\. If no version is specified, the current version for \fIformula\fR will be used\.
.
.TP
\fB\-\-package\-name\fR
Use the specified \fIpackage\-name\fR when finding resources for \fIformula\fR\. If no package name is specified, it will be inferred from the formula\'s stable URL\.
.
.TP
\fB\-\-extra\-packages\fR
Include these additional packages when finding resources\.
.
.TP
\fB\-\-exclude\-packages\fR
Exclude these packages when finding resources\.
.
.SS "\fBupdate\-test\fR [\fIoptions\fR]"
Run a test of \fBbrew update\fR with a new repository clone\. If no options are passed, use \fBorigin/master\fR as the start commit\.
.