Module: Cask::Caskroom
- Extended by:
- Utils::Output::Mixin
- Defined in:
- cask/caskroom.rb
Overview
This module is part of an internal API. This module may only be used internally in repositories owned by Homebrew, except in casks or formulae. Third parties should avoid using this module if possible, as it may be removed or changed without warning.
Helper functions for interacting with the Caskroom directory.
Constant Summary collapse
- CASKFILE_EXTENSIONS =
This constant is part of an internal API. This constant may only be used internally in repositories owned by Homebrew, except in casks or formulae. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
%w[json internal.json rb].freeze
Class Attribute Summary collapse
Class Method Summary collapse
- .any_casks_installed? ⇒ Boolean private
- .cask_installed?(token) ⇒ Boolean private
- .cask_installed_caskfile(token, old_tokens: []) ⇒ Pathname? private
- .cask_installed_version(token, old_tokens: []) ⇒ String? private
- .caskroom_group_correct?(path) ⇒ Boolean private
-
.casks(config: nil) ⇒ Array<Cask>
internal
Get all installed casks.
- .chgrp_path(path, sudo) ⇒ void private
-
.corrupt_cask_dirs ⇒ Array<String>
private
Return tokens for Caskroom directories missing expected installed metadata.
- .ensure_caskroom_exists ⇒ void private
- .migrate_caskfile_to_json(caskfile) ⇒ void private
- .path ⇒ Pathname private
- .token_from_full_token(token) ⇒ String private
-
.tokens ⇒ Array<String>
private
Return all tokens for installed casks.
Methods included from Utils::Output::Mixin
issue_reporting_message, odebug, odeprecated, odie, odisabled, ofail, oh1, oh1_title, ohai, ohai_title, onoe, opoo, opoo_outside_github_actions, opoo_without_github_actions_annotation, pretty_deprecated, pretty_disabled, pretty_duration, pretty_install_status, pretty_installed, pretty_uninstalled, pretty_unmarked, pretty_upgradable, pretty_warning
Class Attribute Details
.expected_caskroom_group ⇒ String
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.
216 217 218 |
# File 'cask/caskroom.rb', line 216 def self.expected_caskroom_group "admin" end |
Class Method Details
.any_casks_installed? ⇒ Boolean
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.
37 38 39 |
# File 'cask/caskroom.rb', line 37 def self.any_casks_installed? paths.any? end |
.cask_installed?(token) ⇒ Boolean
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.
42 43 44 |
# File 'cask/caskroom.rb', line 42 def self.cask_installed?(token) !cask_installed_version(token).nil? end |
.cask_installed_caskfile(token, old_tokens: []) ⇒ Pathname?
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.
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'cask/caskroom.rb', line 47 def self.cask_installed_caskfile(token, old_tokens: []) # Check if the cask is installed with an old name. [token, *old_tokens].map { |cask_token| token_from_full_token(cask_token) }.uniq.each do |cask_token| caskroom_path = path/cask_token next if !caskroom_path.directory? || caskroom_path.symlink? = Pathname.glob((caskroom_path/".metadata/*/*").to_s).max_by { |p| p.basename.to_s } next unless caskfile = CASKFILE_EXTENSIONS.map { |ext| /"Casks/#{cask_token}.#{ext}" } .find(&:exist?) return caskfile if caskfile end nil end |
.cask_installed_version(token, old_tokens: []) ⇒ String?
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.
65 66 67 68 69 |
# File 'cask/caskroom.rb', line 65 def self.cask_installed_version(token, old_tokens: []) return unless (caskfile = cask_installed_caskfile(token, old_tokens:)) caskfile.dirname.dirname.dirname.basename.to_s end |
.caskroom_group_correct?(path) ⇒ Boolean
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.
201 202 203 204 205 206 |
# File 'cask/caskroom.rb', line 201 def self.caskroom_group_correct?(path) group = Etc.getgrnam(expected_caskroom_group) return false if group.nil? path.stat.gid == group.gid end |
.casks(config: nil) ⇒ Array<Cask>
This method is part of an internal API. This method may only be used internally in repositories owned by Homebrew, except in casks or formulae. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Get all installed casks.
A Caskroom directory for a cask that has been renamed but not yet migrated loads as the cask it was renamed to, so deduplicate to avoid listing it twice.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'cask/caskroom.rb', line 227 def self.casks(config: nil) tokens.sort.filter_map do |token| # This is nested so that the rescue can catch errors from both branches begin CaskLoader.load_prefer_installed(token, config:, warn: false) rescue TapCaskAmbiguityError => e e.loaders.fetch(0).load(config:) end rescue Homebrew::UntrustedTapError # If the tap is untrusted the only place we can load the cask from is the installed cask file, if it exists. begin CaskLoader::FromInstalledPathLoader.try_new(token, warn: false)&.load(config:) rescue nil end rescue # Don't blow up because of a single unavailable cask. nil end.select(&:installed?).uniq(&:full_name) end |
.chgrp_path(path, sudo) ⇒ void
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.
This method returns an undefined value.
196 197 198 |
# File 'cask/caskroom.rb', line 196 def self.chgrp_path(path, sudo) SystemCommand.run("chgrp", args: [expected_caskroom_group, path], sudo:) end |
.corrupt_cask_dirs ⇒ Array<String>
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.
Return tokens for Caskroom directories missing expected installed metadata.
161 162 163 |
# File 'cask/caskroom.rb', line 161 def self.corrupt_cask_dirs paths.filter_map { |p| p.basename.to_s unless (p) } end |
.ensure_caskroom_exists ⇒ void
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.
This method returns an undefined value.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'cask/caskroom.rb', line 178 def self.ensure_caskroom_exists return if path.exist? sudo = !path.parent.writable? if sudo && !ENV.key?("SUDO_ASKPASS") && $stdout.tty? ohai "Creating Caskroom directory: #{path}", "We'll set permissions properly so we won't need sudo in the future." end SystemCommand.run("mkdir", args: ["-p", path], sudo:) SystemCommand.run("chmod", args: ["g+rwx", path], sudo:) SystemCommand.run("chown", args: [User.current.to_s, path], sudo:) chgrp_path(path, sudo) unless caskroom_group_correct?(path) end |
.migrate_caskfile_to_json(caskfile) ⇒ void
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.
This method returns an undefined value.
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 |
# File 'cask/caskroom.rb', line 72 def self.migrate_caskfile_to_json(caskfile) # Parse regular installed JSON so current files can be skipped and useful URL data can survive repairs. token = CaskLoader.token_from_path(caskfile) installed_json_caskfile = CaskLoader.installed_json_caskfile?(caskfile) source_json = CaskLoader.load_installed_json(caskfile) source_artifacts = nil source_url_specs = nil current_json = false if source_json raw_source_artifacts = source_json["artifacts"] raw_source_version = source_json["version"] raw_source_url_specs = source_json["url_specs"] source_artifacts = raw_source_artifacts if raw_source_artifacts.is_a?(Array) source_url_specs = raw_source_url_specs if raw_source_url_specs.is_a?(Hash) # Installed JSON only supplements metadata available from the path or receipt: artifacts and version preserve # otherwise-lost installed values, while url_specs preserves an artifact's staged source path. current_json = (source_json.keys - %w[artifacts url_specs version]).empty? && (raw_source_artifacts.nil? || !source_artifacts.nil?) && (raw_source_version.nil? || raw_source_version.is_a?(String)) && (raw_source_url_specs.nil? || !source_url_specs.nil?) end # Recover missing receipt and legacy caskfile data before deciding what must be stored in the JSON. tab = CaskLoader.load_installed_tab(token) cask = begin if installed_json_caskfile CaskLoader.load_from_installed_caskfile(caskfile) else CaskLoader.load(caskfile, warn: false) end rescue CaskInvalidError, CaskUnavailableError, MethodDeprecatedError, JSON::ParserError, NoMethodError, TypeError nil end return if current_json && cask && (!source_artifacts.nil? || tab.uninstall_artifacts.present?) return if cask&.uninstall_flight_blocks? || tab.uninstall_flight_blocks cask ||= CaskLoader.recover_from_installed_caskfile(caskfile, tab:) return unless cask # Preserve the original version and artifacts whenever the receipt cannot reproduce them. version = cask.version.to_s json_uninstall_artifacts = JSON.parse(JSON.generate(cask.artifacts_list(uninstall_only: true))) # Keep missing artifacts distinguishable from an intentional empty artifact list. return if source_artifacts.nil? && tab.uninstall_artifacts.blank? && json_uninstall_artifacts.empty? installed_json = cask.to_installed_json_hash installed_json["url_specs"] ||= source_url_specs if source_url_specs receipt_artifacts = tab.uninstall_artifacts.presence if receipt_artifacts.nil? || !artifacts_equivalent?(receipt_artifacts, json_uninstall_artifacts) installed_json["artifacts"] = json_uninstall_artifacts end installed_json["version"] = version if caskfile.dirname.dirname.dirname.basename.to_s != version # Replace the old metadata only after the new JSON reloads with the selected version and artifacts. json_caskfile = caskfile.dirname/"#{token}.json" original_contents = caskfile.read if caskfile == json_caskfile json_caskfile.atomic_write(JSON.pretty_generate(installed_json)) begin # Only durable on-disk data may satisfy this check: the API fallback would mask a # migrated caskfile that lost its artifacts for as long as the API definition matches. migrated_cask = CaskLoader.load_from_installed_caskfile(json_caskfile, api_fallback: false) migrated_artifacts = JSON.parse(JSON.generate(migrated_cask.artifacts_list(uninstall_only: true))) if migrated_cask.version.to_s != version || !artifacts_equivalent?(migrated_artifacts, json_uninstall_artifacts) raise "migrated Cask metadata differs from the original after preserving version and artifacts" end rescue if original_contents json_caskfile.atomic_write(original_contents) elsif json_caskfile.exist? json_caskfile.unlink end raise end caskfile.unlink if caskfile != json_caskfile end |
.path ⇒ Pathname
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.
17 18 19 |
# File 'cask/caskroom.rb', line 17 def self.path @path ||= T.let(HOMEBREW_PREFIX/"Caskroom", T.nilable(Pathname)) end |
.token_from_full_token(token) ⇒ String
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.
172 173 174 175 |
# File 'cask/caskroom.rb', line 172 def self.token_from_full_token(token) _, _, cask_token = token.split("/", 3) cask_token || token end |
.tokens ⇒ Array<String>
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.
Return all tokens for installed casks.
32 33 34 |
# File 'cask/caskroom.rb', line 32 def self.tokens paths.map { |path| path.basename.to_s } end |