Module: Cask::CI::Check Private

Extended by:
SystemCommand::Mixin
Defined in:
cask/ci/check.rb

Overview

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.

Captures and compares system state around Cask installation tests.

Constant Summary collapse

Snapshot =

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.

T.type_alias { T::Hash[Symbol, T::Array[String]] }

Class Method Summary collapse

Methods included from SystemCommand::Mixin

system_command, system_command!

Class Method Details

.allSnapshot

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.

Returns:



112
113
114
# File 'cask/ci/check.rb', line 112

def self.all
  CHECKS.transform_values(&:call)
end

.errors(before, after, cask:) ⇒ 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.

Parameters:

Returns:



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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'cask/ci/check.rb', line 117

def self.errors(before, after, cask:)
  uninstall_artifact = cask.artifacts.find { |artifact| artifact.is_a?(::Cask::Artifact::Uninstall) }
  uninstall_directives = if uninstall_artifact.is_a?(::Cask::Artifact::Uninstall)
    uninstall_artifact.directives
  else
    {}
  end

  diff = T.let({}, T::Hash[Symbol, Diff])
  CHECKS.each_key do |name|
    diff[name] = Diff.new(before.fetch(name), after.fetch(name))
  end

  errors = T.let([], T::Array[String])

  pkg_files = diff.fetch(:installed_pkgs)
                  .added
                  .flat_map { |id| ::Cask::Pkg.new(id).pkgutil_bom_all.map(&:to_s) }
  installed_apps = diff.fetch(:installed_apps).added - pkg_files
  if installed_apps.any?
    message = "Some applications are still installed, add them to " \
              "#{Formatter.identifier("uninstall delete:")}\n"
    message += installed_apps.join("\n")
    errors << message
  end

  installed_kexts = diff.fetch(:installed_kexts)
                        .added
                        .grep_v(/^com\.(softraid\.driver\.SoftRAID|highpoint-tech\.kext\.*)/)
  if installed_kexts.any?
    message = "Some kernel extensions are still installed, add them to " \
              "#{Formatter.identifier("uninstall kext:")}\n"
    message += installed_kexts.join("\n")
    errors << message
  end

  installed_packages = diff.fetch(:installed_pkgs)
                           .added
                           .grep_v(/^com\.logi\.installer\.pluginservice\.package/i)
  if installed_packages.any?
    message = "Some packages are still installed, add them to #{Formatter.identifier("uninstall pkgutil:")}\n"
    message += installed_packages.join("\n")
    errors << message
  end

  installed_launchjobs = diff.fetch(:installed_launchjobs).added
  if installed_launchjobs.any?
    message = "Some launch jobs are still installed, add them to " \
              "#{Formatter.identifier("uninstall launchctl:")}\n"
    message += installed_launchjobs.join("\n")
    errors << message
  end

  running_apps = diff.fetch(:loaded_launchjobs)
                     .added
                     .grep(/\.\d+\z/)
                     .grep_v(APPLE_LAUNCHJOBS_REGEX)
                     .grep_v(GOOGLE_LAUNCHJOBS_REGEX)
                     .map { |id| id.sub(/\A(?:application\.)?(.*?)(?:\.\d+){0,2}\z/, '\\1') }

  loaded_launchjobs = diff.fetch(:loaded_launchjobs)
                          .added
                          .grep_v(/\.\d+\z/)

  missing_running_apps = reject_matching(running_apps, uninstall_directives[:quit])

  # Some applications may launch a browser session after install.
  # Skip Firefox, unless the cask is a Firefox cask.
  missing_running_apps.delete("org.mozilla.firefox") unless cask.token.include?("firefox")

  if missing_running_apps.any?
    message = "Some applications are still running, add them to #{Formatter.identifier("uninstall quit:")}\n"
    message += missing_running_apps.join("\n")
    errors << message
  end

  missing_loaded_launchjobs = reject_matching(
    loaded_launchjobs,
    uninstall_directives[:launchctl],
    anchored:    false,
    ignore_case: false,
  )
  if missing_loaded_launchjobs.any?
    message = "Some launch jobs were not unloaded, add them to " \
              "#{Formatter.identifier("uninstall launchctl:")}\n"
    message += missing_loaded_launchjobs.join("\n")
    errors << message
  end

  errors
end

.reject_matching(ids, directives, anchored: true, ignore_case: true) ⇒ 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.

Match * wildcards as Cask::Artifact::AbstractUninstall resolves them: quit: anchors and ignores case, launchctl: does neither.

Parameters:

  • ids (Array<String>)
  • directives (Object)
  • anchored (Boolean) (defaults to: true)
  • ignore_case (Boolean) (defaults to: true)

Returns:



219
220
221
222
223
224
225
226
227
# File 'cask/ci/check.rb', line 219

def self.reject_matching(ids, directives, anchored: true, ignore_case: true)
  patterns = Array(directives).map do |directive|
    directive = directive.to_s
    source = Regexp.escape(directive).gsub("\\*", ".*")
    source = "\\A#{source}\\z" if anchored || directive.exclude?("*")
    Regexp.new(source, ignore_case ? Regexp::IGNORECASE : nil)
  end
  ids.reject { |id| patterns.any? { |pattern| pattern.match?(id) } }
end