Module: Homebrew::Bundle::Commands::Exec Private

Defined in:
bundle/commands/exec.rb

This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.

Constant Summary collapse

HOMEBREW_ENV_CLEANUP =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

Homebrew's global environment variables that we don't want to leak into the brew bundle exec environment.

%w[
  HOMEBREW_HELP_MESSAGE
  HOMEBREW_API_DEFAULT_DOMAIN
  HOMEBREW_BOTTLE_DEFAULT_DOMAIN
  HOMEBREW_BREW_DEFAULT_GIT_REMOTE
  HOMEBREW_CORE_DEFAULT_GIT_REMOTE
  HOMEBREW_DEFAULT_CACHE
  HOMEBREW_DEFAULT_LOGS
  HOMEBREW_DEFAULT_TEMP
  HOMEBREW_REQUIRED_RUBY_VERSION
  HOMEBREW_PRODUCT
  HOMEBREW_SYSTEM
  HOMEBREW_PROCESSOR
  HOMEBREW_PHYSICAL_PROCESSOR
  HOMEBREW_BREWED_CURL_PATH
  HOMEBREW_USER_AGENT_CURL
  HOMEBREW_USER_AGENT
  HOMEBREW_GENERIC_DEFAULT_PREFIX
  HOMEBREW_GENERIC_DEFAULT_REPOSITORY
  HOMEBREW_DEFAULT_PREFIX
  HOMEBREW_DEFAULT_REPOSITORY
  HOMEBREW_AUTO_UPDATE_COMMAND
  HOMEBREW_BREW_GIT_REMOTE
  HOMEBREW_COMMAND_DEPTH
  HOMEBREW_CORE_GIT_REMOTE
  HOMEBREW_MACOS_VERSION_NUMERIC
  HOMEBREW_MINIMUM_GIT_VERSION
  HOMEBREW_MACOS_NEWEST_UNSUPPORTED
  HOMEBREW_MACOS_OLDEST_SUPPORTED
  HOMEBREW_MACOS_OLDEST_ALLOWED
  HOMEBREW_GITHUB_PACKAGES_AUTH
].freeze
PATH_LIKE_ENV_REGEX =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

/.+#{File::PATH_SEPARATOR}/

Class Method Summary collapse

Class Method Details

.run(*args, global: false, file: nil, subcommand: "", services: false) ⇒ Object

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Raises:



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'bundle/commands/exec.rb', line 50

def self.run(*args, global: false, file: nil, subcommand: "", services: false)
  # Cleanup Homebrew's global environment
  HOMEBREW_ENV_CLEANUP.each { |key| ENV.delete(key) }

  # Store the old environment so we can check if things were already set
  # before we start mutating it.
  old_env = ENV.to_h

  # Setup Homebrew's ENV extensions
  ENV.activate_extensions!
  raise UsageError, "No command to execute was specified!" if args.blank?

  command = args.first

  require "bundle/brewfile"
  @dsl = Brewfile.read(global:, file:)

  require "formula"
  require "formulary"

  ENV.deps = @dsl.entries.filter_map do |entry|
    next if entry.type != :brew

    Formulary.factory(entry.name)
  end

  # Allow setting all dependencies to be keg-only
  # (i.e. should be explicitly in HOMEBREW_*PATHs ahead of HOMEBREW_PREFIX)
  ENV.keg_only_deps = if ENV["HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS"].present?
    ENV.delete("HOMEBREW_BUNDLE_EXEC_ALL_KEG_ONLY_DEPS")
    ENV.deps
  else
    ENV.deps.select(&:keg_only?)
  end
  ENV.setup_build_environment

  # Enable compiler flag filtering
  ENV.refurbish_args

  # Set up `nodenv`, `pyenv` and `rbenv` if present.
  env_formulae = %w[nodenv pyenv rbenv]
  ENV.deps.each do |dep|
    dep_name = dep.name
    next unless env_formulae.include?(dep_name)

    dep_root = ENV.fetch("HOMEBREW_#{dep_name.upcase}_ROOT", "#{Dir.home}/.#{dep_name}")
    ENV.prepend_path "PATH", Pathname.new(dep_root)/"shims"
  end

  # Setup pkg-config, if present, to help locate packages
  # Only need this on Linux as Homebrew provides a shim on macOS
  # TODO: use extend/OS here
  # rubocop:todo Homebrew/MoveToExtendOS
  if OS.linux? && (pkgconf = Formulary.factory("pkgconf")) && pkgconf.any_version_installed?
    ENV.prepend_path "PATH", pkgconf.opt_bin.to_s
  end
  # rubocop:enable Homebrew/MoveToExtendOS

  # For commands which aren't either absolute or relative
  # Add the command directory to PATH, since it may get blown away by superenv
  if command.exclude?("/") && (which_command = which(command)).present?
    ENV.prepend_path "PATH", which_command.dirname.to_s
  end

  # Replace the formula versions from the environment variables
  Bundle.formula_versions_from_env.each do |formula_name, formula_version|
    ENV.each do |key, value|
      opt = %r{/opt/#{formula_name}([/:$])}
      next unless value.match(opt)

      cellar = "/Cellar/#{formula_name}/#{formula_version}\\1"

      # Look for PATH-like environment variables
      ENV[key] = if key.include?("PATH") && value.match?(PATH_LIKE_ENV_REGEX)
        rejected_opts = []
        path = PATH.new(ENV.fetch("PATH"))
                   .reject do |path_value|
          rejected_opts << path_value if path_value.match?(opt)
        end
        rejected_opts.each do |path_value|
          path.prepend(path_value.gsub(opt, cellar))
        end
        path.to_s
      else
        value.gsub(opt, cellar)
      end
    end
  end

  # Ensure brew bundle sh/env commands have access to other tools in the PATH
  if ["sh", "env"].include?(subcommand) && (homebrew_path = ENV.fetch("HOMEBREW_PATH", nil))
    ENV.append_path "PATH", homebrew_path
  end

  # For commands which aren't either absolute or relative
  raise "command was not found in your PATH: #{command}" if command.exclude?("/") && which(command).nil?

  if subcommand == "env"
    ENV.sort.each do |key, value|
      # No need to export empty values.
      next if value.blank?

      # Skip exporting non-Homebrew things that were already set in the old environment.
      next if !key.start_with?("HOMEBREW_") && old_env.key?(key) && old_env[key] == value

      puts "export #{key}=\"#{Utils::Shell.sh_quote(value)}\""
    end
    return
  end

  if services
    require "bundle/brew_services"

    exit_code = 0
    run_services(@dsl.entries) do
      Kernel.system(*args)
      exit_code = $CHILD_STATUS.exitstatus
    end
    exit!(exit_code)
  else
    exec(*args)
  end
end