diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml index 841ecfabee..dc51cd15ec 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -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) diff --git a/Library/Homebrew/Gemfile.lock b/Library/Homebrew/Gemfile.lock index 224c2cad66..f013bc21ef 100644 --- a/Library/Homebrew/Gemfile.lock +++ b/Library/Homebrew/Gemfile.lock @@ -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) diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index 4e4a55c8a7..c9d64f1c04 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -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" diff --git a/Library/Homebrew/build_options.rb b/Library/Homebrew/build_options.rb index af06250a32..36ae1d613e 100644 --- a/Library/Homebrew/build_options.rb +++ b/Library/Homebrew/build_options.rb @@ -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. #
args << "--some-beta" if build.head?
@@ -81,14 +75,12 @@ class BuildOptions # e.g. on newer Intel Macs this means a combined x86_64/x86 binary/library. #
args << "--universal-binary" if build.universal?
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. diff --git a/Library/Homebrew/cli/args.rb b/Library/Homebrew/cli/args.rb index c9bdd72ccf..2e878ceed8 100644 --- a/Library/Homebrew/cli/args.rb +++ b/Library/Homebrew/cli/args.rb @@ -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? diff --git a/Library/Homebrew/cmd/gist-logs.rb b/Library/Homebrew/cmd/gist-logs.rb index d590fb3526..a42d2457f9 100644 --- a/Library/Homebrew/cmd/gist-logs.rb +++ b/Library/Homebrew/cmd/gist-logs.rb @@ -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? diff --git a/Library/Homebrew/cmd/list.rb b/Library/Homebrew/cmd/list.rb index e7e5a97536..c67ed6e223 100644 --- a/Library/Homebrew/cmd/list.rb +++ b/Library/Homebrew/cmd/list.rb @@ -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? diff --git a/Library/Homebrew/cmd/outdated.rb b/Library/Homebrew/cmd/outdated.rb index 6cb52621cb..ea40aad78f 100644 --- a/Library/Homebrew/cmd/outdated.rb +++ b/Library/Homebrew/cmd/outdated.rb @@ -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), []] diff --git a/Library/Homebrew/cmd/switch.rb b/Library/Homebrew/cmd/switch.rb index a78ef0a907..3f3e4baaf3 100644 --- a/Library/Homebrew/cmd/switch.rb +++ b/Library/Homebrew/cmd/switch.rb @@ -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 } diff --git a/Library/Homebrew/compat.rb b/Library/Homebrew/compat.rb index c9ad444268..583726728f 100644 --- a/Library/Homebrew/compat.rb +++ b/Library/Homebrew/compat.rb @@ -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? diff --git a/Library/Homebrew/compat/dependencies_helpers.rb b/Library/Homebrew/compat/dependencies_helpers.rb deleted file mode 100644 index 21122e4c35..0000000000 --- a/Library/Homebrew/compat/dependencies_helpers.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/compat/dependencies_helpers.rbi b/Library/Homebrew/compat/dependencies_helpers.rbi deleted file mode 100644 index 3ea83b62b9..0000000000 --- a/Library/Homebrew/compat/dependencies_helpers.rbi +++ /dev/null @@ -1,9 +0,0 @@ -# typed: strict - -module DependenciesHelpers - module Compat - include Kernel - - def args_includes_ignores(args); end - end -end diff --git a/Library/Homebrew/compat/extend/nil.rb b/Library/Homebrew/compat/extend/nil.rb deleted file mode 100644 index 5e521a4367..0000000000 --- a/Library/Homebrew/compat/extend/nil.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/compat/extend/nil.rbi b/Library/Homebrew/compat/extend/nil.rbi deleted file mode 100644 index 0a0adb9756..0000000000 --- a/Library/Homebrew/compat/extend/nil.rbi +++ /dev/null @@ -1,7 +0,0 @@ -# typed: strict - -class NilClass - module Compat - include Kernel - end -end diff --git a/Library/Homebrew/compat/extend/string.rb b/Library/Homebrew/compat/extend/string.rb deleted file mode 100644 index bdbed56112..0000000000 --- a/Library/Homebrew/compat/extend/string.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/compat/extend/string.rbi b/Library/Homebrew/compat/extend/string.rbi deleted file mode 100644 index 4851c5b127..0000000000 --- a/Library/Homebrew/compat/extend/string.rbi +++ /dev/null @@ -1,7 +0,0 @@ -# typed: strict - -class String - module Compat - include Kernel - end -end diff --git a/Library/Homebrew/compat/formula.rb b/Library/Homebrew/compat/formula.rb index 0f08e86d9f..fd5f98f979 100644 --- a/Library/Homebrew/compat/formula.rb +++ b/Library/Homebrew/compat/formula.rb @@ -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 diff --git a/Library/Homebrew/compat/global.rb b/Library/Homebrew/compat/global.rb index 3ab5383e60..e229b7cc6e 100644 --- a/Library/Homebrew/compat/global.rb +++ b/Library/Homebrew/compat/global.rb @@ -7,8 +7,7 @@ module Homebrew def args unless @printed_args_warning - odeprecated "Homebrew.args", "`args = _args.parse` and pass `args` along the call chain" - @printed_args_warning = true + odisabled "Homebrew.args", "`args = _args.parse` and pass `args` along the call chain" end @args ||= CLI::Args.new diff --git a/Library/Homebrew/compat/language/haskell.rb b/Library/Homebrew/compat/language/haskell.rb index 3619e88c02..00f6466807 100644 --- a/Library/Homebrew/compat/language/haskell.rb +++ b/Library/Homebrew/compat/language/haskell.rb @@ -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 diff --git a/Library/Homebrew/compat/language/java.rb b/Library/Homebrew/compat/language/java.rb deleted file mode 100644 index 4a23f7a6c2..0000000000 --- a/Library/Homebrew/compat/language/java.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/compat/language/python.rb b/Library/Homebrew/compat/language/python.rb deleted file mode 100644 index abb4673f6b..0000000000 --- a/Library/Homebrew/compat/language/python.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/compat/os/mac.rb b/Library/Homebrew/compat/os/mac.rb deleted file mode 100644 index ab64aeac96..0000000000 --- a/Library/Homebrew/compat/os/mac.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/compat/os/mac.rbi b/Library/Homebrew/compat/os/mac.rbi deleted file mode 100644 index b6156fa2e8..0000000000 --- a/Library/Homebrew/compat/os/mac.rbi +++ /dev/null @@ -1,11 +0,0 @@ -# typed: strict - -module OS - module Mac - class << self - module Compat - include Kernel - end - end - end -end diff --git a/Library/Homebrew/context.rb b/Library/Homebrew/context.rb index 60c354af1e..7655c8eaa7 100644 --- a/Library/Homebrew/context.rb +++ b/Library/Homebrew/context.rb @@ -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 diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index c7082b7f42..8072739efe 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -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?, diff --git a/Library/Homebrew/dev-cmd/bump-cask-pr.rb b/Library/Homebrew/dev-cmd/bump-cask-pr.rb index f34f0c762b..1a70fed47f 100644 --- a/Library/Homebrew/dev-cmd/bump-cask-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-cask-pr.rb @@ -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) diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index 4e648874f2..dffc4dcdf3 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -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 diff --git a/Library/Homebrew/dev-cmd/create.rb b/Library/Homebrew/dev-cmd/create.rb index ea4b8a38a1..3575710c4d 100644 --- a/Library/Homebrew/dev-cmd/create.rb +++ b/Library/Homebrew/dev-cmd/create.rb @@ -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` [] - Generate a formula for the downloadable file at 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 + 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: 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 points to the package's repository rather than a file." flag "--set-name=", - description: "Explicitly set the of the new formula." + description: "Explicitly set the of the new formula or cask.", + required_for: "--cask" flag "--set-version=", - description: "Explicitly set the of the new formula." + description: "Explicitly set the of the new formula or cask." flag "--set-license=", description: "Explicitly set the 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 diff --git a/Library/Homebrew/dev-cmd/diy.rb b/Library/Homebrew/dev-cmd/diy.rb index 55696b2352..aba0a000ff 100644 --- a/Library/Homebrew/dev-cmd/diy.rb +++ b/Library/Homebrew/dev-cmd/diy.rb @@ -25,13 +25,14 @@ module Homebrew description: "Explicitly set the 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 diff --git a/Library/Homebrew/dev-cmd/mirror.rb b/Library/Homebrew/dev-cmd/mirror.rb index a6c2b45b1a..29c238f6a9 100644 --- a/Library/Homebrew/dev-cmd/mirror.rb +++ b/Library/Homebrew/dev-cmd/mirror.rb @@ -25,7 +25,6 @@ module Homebrew description: "Upload to Bintray, but don't publish." min_named :formula - hide_from_man_page! end end diff --git a/Library/Homebrew/dev-cmd/pr-pull.rb b/Library/Homebrew/dev-cmd/pr-pull.rb index 010d3e6acc..7e059fc478 100644 --- a/Library/Homebrew/dev-cmd/pr-pull.rb +++ b/Library/Homebrew/dev-cmd/pr-pull.rb @@ -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? diff --git a/Library/Homebrew/dev-cmd/pull.rb b/Library/Homebrew/dev-cmd/pull.rb deleted file mode 100644 index ce6f69c358..0000000000 --- a/Library/Homebrew/dev-cmd/pull.rb +++ /dev/null @@ -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` [] - - Get a patch from a GitHub commit or pull request and apply it to Homebrew. - - Each 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 diff --git a/Library/Homebrew/dev-cmd/sh.rb b/Library/Homebrew/dev-cmd/sh.rb index 3c56c13dd0..b97e2a5a48 100644 --- a/Library/Homebrew/dev-cmd/sh.rb +++ b/Library/Homebrew/dev-cmd/sh.rb @@ -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 diff --git a/Library/Homebrew/dev-cmd/update-python-resources.rb b/Library/Homebrew/dev-cmd/update-python-resources.rb index ee3cd4be19..936dad5aba 100644 --- a/Library/Homebrew/dev-cmd/update-python-resources.rb +++ b/Library/Homebrew/dev-cmd/update-python-resources.rb @@ -26,6 +26,13 @@ module Homebrew flag "--version=", description: "Use the specified when finding resources for . "\ "If no version is specified, the current version for will be used." + flag "--package-name=", + description: "Use the specified when finding resources for . "\ + "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 diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 713c460222..e6675f14f0 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -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? diff --git a/Library/Homebrew/extend/ENV.rb b/Library/Homebrew/extend/ENV.rb index 17a6fd1227..d83beea7dc 100644 --- a/Library/Homebrew/extend/ENV.rb +++ b/Library/Homebrew/extend/ENV.rb @@ -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 diff --git a/Library/Homebrew/extend/ENV.rbi b/Library/Homebrew/extend/ENV.rbi new file mode 100644 index 0000000000..edee664dba --- /dev/null +++ b/Library/Homebrew/extend/ENV.rbi @@ -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 diff --git a/Library/Homebrew/extend/ENV/shared.rb b/Library/Homebrew/extend/ENV/shared.rb index ae49480479..eac0a305d4 100644 --- a/Library/Homebrew/extend/ENV/shared.rb +++ b/Library/Homebrew/extend/ENV/shared.rb @@ -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. #
ENV.prepend_path "PATH", which("emacs").dirname
+ 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 - # - # @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 diff --git a/Library/Homebrew/extend/ENV/shared.rbi b/Library/Homebrew/extend/ENV/shared.rbi new file mode 100644 index 0000000000..b1eac4cd3e --- /dev/null +++ b/Library/Homebrew/extend/ENV/shared.rbi @@ -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 diff --git a/Library/Homebrew/extend/ENV/std.rb b/Library/Homebrew/extend/ENV/std.rb index bea1efc156..8a8a648d81 100644 --- a/Library/Homebrew/extend/ENV/std.rb +++ b/Library/Homebrew/extend/ENV/std.rb @@ -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 diff --git a/Library/Homebrew/extend/ENV/super.rb b/Library/Homebrew/extend/ENV/super.rb index 4b1ba40b84..5e2c1da21f 100644 --- a/Library/Homebrew/extend/ENV/super.rb +++ b/Library/Homebrew/extend/ENV/super.rb @@ -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" diff --git a/Library/Homebrew/extend/os/mac/extend/ENV/super.rb b/Library/Homebrew/extend/os/mac/extend/ENV/super.rb index 4e541a2be9..d797ff7484 100644 --- a/Library/Homebrew/extend/os/mac/extend/ENV/super.rb +++ b/Library/Homebrew/extend/os/mac/extend/ENV/super.rb @@ -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 diff --git a/Library/Homebrew/extend/os/mac/hardware/cpu.rb b/Library/Homebrew/extend/os/mac/hardware/cpu.rb index 7b10da535a..ad84a72420 100644 --- a/Library/Homebrew/extend/os/mac/hardware/cpu.rb +++ b/Library/Homebrew/extend/os/mac/hardware/cpu.rb @@ -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 diff --git a/Library/Homebrew/extend/os/mac/requirements/java_requirement.rb b/Library/Homebrew/extend/os/mac/requirements/java_requirement.rb index 5619850d06..dfcb9ea655 100644 --- a/Library/Homebrew/extend/os/mac/requirements/java_requirement.rb +++ b/Library/Homebrew/extend/os/mac/requirements/java_requirement.rb @@ -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") diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index eaed557009..b7db312947 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -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 diff --git a/Library/Homebrew/formula_auditor.rb b/Library/Homebrew/formula_auditor.rb index 0392b11e7e..e86f600efd 100644 --- a/Library/Homebrew/formula_auditor.rb +++ b/Library/Homebrew/formula_auditor.rb @@ -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 ", "} diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index efe4ef24b4..23a20fbfbe 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -225,12 +225,12 @@ module Formulary def load_file(flags:) if %r{githubusercontent.com/[\w-]+/[\w-]+/[a-f0-9]{40}(?:/Formula)?/(?[\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) diff --git a/Library/Homebrew/language/python.rb b/Library/Homebrew/language/python.rb index 165ee1da28..5fa55214cc 100644 --- a/Library/Homebrew/language/python.rb +++ b/Library/Homebrew/language/python.rb @@ -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) diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 6c20bad598..2be9dfc439 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -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 diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb index 3ad279dc0d..daf3706fb4 100644 --- a/Library/Homebrew/os/mac/xcode.rb +++ b/Library/Homebrew/os/mac/xcode.rb @@ -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" diff --git a/Library/Homebrew/patch.rb b/Library/Homebrew/patch.rb index 1157c61005..ebdbe3a19d 100644 --- a/Library/Homebrew/patch.rb +++ b/Library/Homebrew/patch.rb @@ -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 diff --git a/Library/Homebrew/requirements/java_requirement.rb b/Library/Homebrew/requirements/java_requirement.rb index cf6b944d42..c99e82f7a5 100644 --- a/Library/Homebrew/requirements/java_requirement.rb +++ b/Library/Homebrew/requirements/java_requirement.rb @@ -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 diff --git a/Library/Homebrew/requirements/macos_requirement.rb b/Library/Homebrew/requirements/macos_requirement.rb index 52b1e84b20..5284acb114 100644 --- a/Library/Homebrew/requirements/macos_requirement.rb +++ b/Library/Homebrew/requirements/macos_requirement.rb @@ -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 diff --git a/Library/Homebrew/rubocops/components_order.rb b/Library/Homebrew/rubocops/components_order.rb index 7f6613799c..84dba9a945 100644 --- a/Library/Homebrew/rubocops/components_order.rb +++ b/Library/Homebrew/rubocops/components_order.rb @@ -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 diff --git a/Library/Homebrew/rubocops/text.rb b/Library/Homebrew/rubocops/text.rb index 5ace971a75..443eab7a58 100644 --- a/Library/Homebrew/rubocops/text.rb +++ b/Library/Homebrew/rubocops/text.rb @@ -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)(?:/| |$)}) diff --git a/Library/Homebrew/sorbet/plugins/delegate.rb b/Library/Homebrew/sorbet/plugins/delegate.rb new file mode 100644 index 0000000000..2f308cc45d --- /dev/null +++ b/Library/Homebrew/sorbet/plugins/delegate.rb @@ -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 diff --git a/Library/Homebrew/sorbet/plugins/unpack_strategy_magic.rb b/Library/Homebrew/sorbet/plugins/using.rb similarity index 57% rename from Library/Homebrew/sorbet/plugins/unpack_strategy_magic.rb rename to Library/Homebrew/sorbet/plugins/using.rb index d5c83fa8b5..e1bf761b29 100644 --- a/Library/Homebrew/sorbet/plugins/unpack_strategy_magic.rb +++ b/Library/Homebrew/sorbet/plugins/using.rb @@ -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 diff --git a/Library/Homebrew/sorbet/rbi/gems/parallel@1.20.0.rbi b/Library/Homebrew/sorbet/rbi/gems/parallel@1.20.1.rbi similarity index 100% rename from Library/Homebrew/sorbet/rbi/gems/parallel@1.20.0.rbi rename to Library/Homebrew/sorbet/rbi/gems/parallel@1.20.1.rbi diff --git a/Library/Homebrew/sorbet/rbi/gems/parallel_tests@3.3.0.rbi b/Library/Homebrew/sorbet/rbi/gems/parallel_tests@3.4.0.rbi similarity index 100% rename from Library/Homebrew/sorbet/rbi/gems/parallel_tests@3.3.0.rbi rename to Library/Homebrew/sorbet/rbi/gems/parallel_tests@3.4.0.rbi diff --git a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi index c49adbb35a..a9699d5831 100644 --- a/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi +++ b/Library/Homebrew/sorbet/rbi/hidden-definitions/hidden.rbi @@ -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 diff --git a/Library/Homebrew/sorbet/triggers.yml b/Library/Homebrew/sorbet/triggers.yml index 48a007d979..747e297c2e 100644 --- a/Library/Homebrew/sorbet/triggers.yml +++ b/Library/Homebrew/sorbet/triggers.yml @@ -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 diff --git a/Library/Homebrew/system_command.rb b/Library/Homebrew/system_command.rb index a9ef6cf6b5..33ce5069bc 100644 --- a/Library/Homebrew/system_command.rb +++ b/Library/Homebrew/system_command.rb @@ -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(?.*?)<\?\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*>(?.*?)\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/) diff --git a/Library/Homebrew/tab.rb b/Library/Homebrew/tab.rb index 8b732cae14..5cdbc92261 100644 --- a/Library/Homebrew/tab.rb +++ b/Library/Homebrew/tab.rb @@ -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 diff --git a/Library/Homebrew/tap.rb b/Library/Homebrew/tap.rb index d4a33d8ab7..0022617249 100644 --- a/Library/Homebrew/tap.rb +++ b/Library/Homebrew/tap.rb @@ -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 diff --git a/Library/Homebrew/tap_auditor.rb b/Library/Homebrew/tap_auditor.rb index d8294815f2..5936980919 100644 --- a/Library/Homebrew/tap_auditor.rb +++ b/Library/Homebrew/tap_auditor.rb @@ -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 diff --git a/Library/Homebrew/test/cask/artifact/shared_examples/uninstall_zap.rb b/Library/Homebrew/test/cask/artifact/shared_examples/uninstall_zap.rb index fc91356cfb..3d97672cc4 100644 --- a/Library/Homebrew/test/cask/artifact/shared_examples/uninstall_zap.rb +++ b/Library/Homebrew/test/cask/artifact/shared_examples/uninstall_zap.rb @@ -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" } diff --git a/Library/Homebrew/test/cask/dsl/postflight_spec.rb b/Library/Homebrew/test/cask/dsl/postflight_spec.rb index 259921fceb..bc36ff4367 100644 --- a/Library/Homebrew/test/cask/dsl/postflight_spec.rb +++ b/Library/Homebrew/test/cask/dsl/postflight_spec.rb @@ -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 diff --git a/Library/Homebrew/test/cask/dsl/preflight_spec.rb b/Library/Homebrew/test/cask/dsl/preflight_spec.rb index 93f1a60174..12cbf50b17 100644 --- a/Library/Homebrew/test/cask/dsl/preflight_spec.rb +++ b/Library/Homebrew/test/cask/dsl/preflight_spec.rb @@ -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 diff --git a/Library/Homebrew/test/cask/dsl/shared_examples/staged.rb b/Library/Homebrew/test/cask/dsl/shared_examples/staged.rb index b6490e825d..0036751501 100644 --- a/Library/Homebrew/test/cask/dsl/shared_examples/staged.rb +++ b/Library/Homebrew/test/cask/dsl/shared_examples/staged.rb @@ -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 diff --git a/Library/Homebrew/test/cask/dsl/uninstall_postflight_spec.rb b/Library/Homebrew/test/cask/dsl/uninstall_postflight_spec.rb index 48c7653d5f..c188142015 100644 --- a/Library/Homebrew/test/cask/dsl/uninstall_postflight_spec.rb +++ b/Library/Homebrew/test/cask/dsl/uninstall_postflight_spec.rb @@ -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 diff --git a/Library/Homebrew/test/cask/dsl/uninstall_preflight_spec.rb b/Library/Homebrew/test/cask/dsl/uninstall_preflight_spec.rb index 635c35c030..e8d7d5c4ff 100644 --- a/Library/Homebrew/test/cask/dsl/uninstall_preflight_spec.rb +++ b/Library/Homebrew/test/cask/dsl/uninstall_preflight_spec.rb @@ -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 diff --git a/Library/Homebrew/test/cask/pkg_spec.rb b/Library/Homebrew/test/cask/pkg_spec.rb index e98face38a..cd45d57cf7 100644 --- a/Library/Homebrew/test/cask/pkg_spec.rb +++ b/Library/Homebrew/test/cask/pkg_spec.rb @@ -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 diff --git a/Library/Homebrew/test/cmd/switch_spec.rb b/Library/Homebrew/test/cmd/switch_spec.rb index b568e53000..8ad0ceb4c7 100644 --- a/Library/Homebrew/test/cmd/switch_spec.rb +++ b/Library/Homebrew/test/cmd/switch_spec.rb @@ -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 diff --git a/Library/Homebrew/test/formula_spec.rb b/Library/Homebrew/test/formula_spec.rb index c91b27d04f..e09790157b 100644 --- a/Library/Homebrew/test/formula_spec.rb +++ b/Library/Homebrew/test/formula_spec.rb @@ -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 diff --git a/Library/Homebrew/test/language/java_spec.rb b/Library/Homebrew/test/language/java_spec.rb index 52613697e0..ccabd4a0f4 100644 --- a/Library/Homebrew/test/language/java_spec.rb +++ b/Library/Homebrew/test/language/java_spec.rb @@ -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 diff --git a/Library/Homebrew/test/os/mac/java_requirement_spec.rb b/Library/Homebrew/test/os/mac/java_requirement_spec.rb deleted file mode 100644 index 20ea5687b6..0000000000 --- a/Library/Homebrew/test/os/mac/java_requirement_spec.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/test/requirements/java_requirement_spec.rb b/Library/Homebrew/test/requirements/java_requirement_spec.rb deleted file mode 100644 index 95835ce2f7..0000000000 --- a/Library/Homebrew/test/requirements/java_requirement_spec.rb +++ /dev/null @@ -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('#') } - 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 diff --git a/Library/Homebrew/test/rubocops/components_order_spec.rb b/Library/Homebrew/test/rubocops/components_order_spec.rb index 8aa6bfc867..0a925d52ca 100644 --- a/Library/Homebrew/test/rubocops/components_order_spec.rb +++ b/Library/Homebrew/test/rubocops/components_order_spec.rb @@ -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 diff --git a/Library/Homebrew/test/rubocops/text_spec.rb b/Library/Homebrew/test/rubocops/text_spec.rb index c308451b19..63c49df4f8 100644 --- a/Library/Homebrew/test/rubocops/text_spec.rb +++ b/Library/Homebrew/test/rubocops/text_spec.rb @@ -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 diff --git a/Library/Homebrew/test/support/helper/cask/fake_system_command.rb b/Library/Homebrew/test/support/helper/cask/fake_system_command.rb deleted file mode 100644 index 2587e7bc52..0000000000 --- a/Library/Homebrew/test/support/helper/cask/fake_system_command.rb +++ /dev/null @@ -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 diff --git a/Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb b/Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb index 760bd80147..1682091b97 100644 --- a/Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb +++ b/Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb @@ -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" diff --git a/Library/Homebrew/test/utils/github_spec.rb b/Library/Homebrew/test/utils/github_spec.rb index 1949123d28..8704b8f6a5 100644 --- a/Library/Homebrew/test/utils/github_spec.rb +++ b/Library/Homebrew/test/utils/github_spec.rb @@ -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 diff --git a/Library/Homebrew/test/utils/pypi_spec.rb b/Library/Homebrew/test/utils/pypi_spec.rb new file mode 100644 index 0000000000..021a2215c8 --- /dev/null +++ b/Library/Homebrew/test/utils/pypi_spec.rb @@ -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 diff --git a/Library/Homebrew/unpack_strategy/dmg.rb b/Library/Homebrew/unpack_strategy/dmg.rb index db6f3285d5..b8395ddef5 100644 --- a/Library/Homebrew/unpack_strategy/dmg.rb +++ b/Library/Homebrew/unpack_strategy/dmg.rb @@ -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 diff --git a/Library/Homebrew/utils/github.rb b/Library/Homebrew/utils/github.rb index a235b77d74..dfd585a567 100644 --- a/Library/Homebrew/utils/github.rb +++ b/Library/Homebrew/utils/github.rb @@ -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! diff --git a/Library/Homebrew/utils/inreplace.rb b/Library/Homebrew/utils/inreplace.rb index b414647aea..951b55148c 100644 --- a/Library/Homebrew/utils/inreplace.rb +++ b/Library/Homebrew/utils/inreplace.rb @@ -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, diff --git a/Library/Homebrew/utils/pypi.rb b/Library/Homebrew/utils/pypi.rb index 6f91f31b57..d1cffdfd36 100644 --- a/Library/Homebrew/utils/pypi.rb +++ b/Library/Homebrew/utils/pypi.rb @@ -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." diff --git a/Library/Homebrew/utils/shared_audits.rb b/Library/Homebrew/utils/shared_audits.rb index b8fdb8c0f6..6587672c8e 100644 --- a/Library/Homebrew/utils/shared_audits.rb +++ b/Library/Homebrew/utils/shared_audits.rb @@ -32,7 +32,6 @@ module SharedAudits end GITHUB_PRERELEASE_ALLOWLIST = { - "amd-power-gadget" => :all, "elm-format" => "0.8.3", "extraterm" => :all, "freetube" => :all, diff --git a/Library/Homebrew/vendor/bundle/bundler/setup.rb b/Library/Homebrew/vendor/bundle/bundler/setup.rb index fbfbedd4b5..ba4a5500af 100644 --- a/Library/Homebrew/vendor/bundle/bundler/setup.rb +++ b/Library/Homebrew/vendor/bundle/bundler/setup.rb @@ -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" diff --git a/completions/internal_commands_list.txt b/completions/internal_commands_list.txt index 9c91e9f1f3..13cf54e93a 100644 --- a/completions/internal_commands_list.txt +++ b/completions/internal_commands_list.txt @@ -68,7 +68,6 @@ pr-publish pr-pull pr-upload prof -pull readall reinstall release-notes diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 584884075e..69694766ee 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -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) diff --git a/docs/Installation.md b/docs/Installation.md index f9ebcad45b..6bc84a6563 100644 --- a/docs/Installation.md +++ b/docs/Installation.md @@ -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 [1](#1) -* macOS High Sierra (10.13) (or higher) [2](#2) +* macOS Mojave (10.14) (or higher) [2](#2) * 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) [3](#3) @@ -52,7 +52,7 @@ Uninstallation is documented in the [FAQ](FAQ.md). 1 For 32-bit or PPC support see [Tigerbrew](https://github.com/mistydemeo/tigerbrew). -2 10.13 or higher is recommended. 10.9–10.12 are +2 10.14 or higher is recommended. 10.9–10.13 are supported on a best-effort basis. For 10.4-10.6 see [Tigerbrew](https://github.com/mistydemeo/tigerbrew). diff --git a/docs/Manpage.md b/docs/Manpage.md index 280e07ca95..8c2c809f51 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -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: * `--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`*] diff --git a/manpages/brew.1 b/manpages/brew.1 index e7287d93ed..0d2eeaf63b 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -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\. .