diff --git a/cmd/brewcask-ci.rb b/cmd/brewcask-ci.rb index 65b42dd787..837c6a3aa4 100755 --- a/cmd/brewcask-ci.rb +++ b/cmd/brewcask-ci.rb @@ -4,7 +4,7 @@ require "utils/github" require "utils/formatter" require_relative "lib/capture" -require_relative "lib/diffable" +require_relative "lib/check" require_relative "lib/travis" module Hbc @@ -48,60 +48,12 @@ module Hbc was_installed = cask.installed? cask_dependencies = CaskDependencies.new(cask).reject(&:installed?) - checks = { - installed_apps: Diffable.new do - ["/Applications", File.expand_path("~/Applications")] - .flat_map { |dir| Dir["#{dir}/**/*.app"] } - end, - installed_kexts: Diffable.new do - system_command!("/usr/sbin/kextstat", args: ["-kl"], print_stderr: false) - .stdout - .lines - .map { |l| l.match(/^.{52}([^\s]+)/)[1] } - .grep_v(/^com\.apple\./) - end, - installed_launchjobs: Diffable.new do - format_launchjob = lambda { |file| - name = file.basename(".plist").to_s - label = Plist.parse_xml(File.read(file))["Label"] - (name == label) ? name : "#{name} (#{label})" - } - - [ - "~/Library/LaunchAgents", - "~/Library/LaunchDaemons", - "/Library/LaunchAgents", - "/Library/LaunchDaemons", - ].map { |p| Pathname(p).expand_path } - .select(&:directory?) - .flat_map(&:children) - .select { |child| child.extname == ".plist" } - .map(&format_launchjob) - end, - loaded_launchjobs: Diffable.new do - launchctl = lambda do |sudo| - system_command!("/bin/launchctl", args: ["list"], print_stderr: false, sudo: sudo) - .stdout - .lines.drop(1) - end - - [false, true] - .flat_map(&launchctl) - .map { |l| l.split(/\s+/)[2] } - .grep_v(/^com\.apple\./) - end, - installed_pkgs: Diffable.new do - Pathname("/var/db/receipts") - .children - .grep(/\.plist$/) - .map(&:basename) - end, - } + check = Check.new overall_success &= step "brew cask install #{cask.token}", "install" do Installer.new(cask, force: true).uninstall if was_installed - checks.values.each(&:before) + check.before Installer.new(cask, verbose: true).install end @@ -120,30 +72,11 @@ module Hbc end end - checks.each do |name, check| - check.after - next unless check.changed? + check.after - success = false + $stderr.puts check.message unless check.success? - message = case name - when :installed_pkgs - "Some packages were not uninstalled." - when :loaded_launchjobs - "Some launch jobs were not unloaded." - when :installed_launchjobs - "Some launch jobs were not uninstalled." - when :installed_kexts - "Some kernel extensions were not uninstalled." - when :installed_apps - "Some applications were not uninstalled." - end - - $stderr.puts Formatter.error(message, label: "Error") - $stderr.puts check.diff_lines.join("\n") - end - - success + success && check.success? end end diff --git a/cmd/lib/check.rb b/cmd/lib/check.rb new file mode 100644 index 0000000000..143414b84e --- /dev/null +++ b/cmd/lib/check.rb @@ -0,0 +1,155 @@ +require "forwardable" + +class Check + CHECKS = { + installed_apps: -> { + ["/Applications", File.expand_path("~/Applications")] + .flat_map { |dir| Dir["#{dir}/**/*.app"] } + }, + installed_kexts: -> { + system_command!("/usr/sbin/kextstat", args: ["-kl"], print_stderr: false) + .stdout + .lines + .map { |l| l.match(/^.{52}([^\s]+)/)[1] } + .grep_v(/^com\.apple\./) + }, + installed_pkgs: -> { + Pathname("/var/db/receipts") + .children + .grep(/\.plist$/) + .map { |path| path.basename.to_s.sub(/\.plist$/, "") } + }, + installed_launchjobs: -> { + format_launchjob = lambda { |file| + name = file.basename(".plist").to_s + label = Plist.parse_xml(File.read(file))["Label"] + (name == label) ? name : "#{name} (#{label})" + } + + [ + "~/Library/LaunchAgents", + "~/Library/LaunchDaemons", + "/Library/LaunchAgents", + "/Library/LaunchDaemons", + ].map { |p| Pathname(p).expand_path } + .select(&:directory?) + .flat_map(&:children) + .select { |child| child.extname == ".plist" } + .map(&format_launchjob) + }, + loaded_launchjobs: -> { + launchctl = lambda do |sudo| + system_command!("/bin/launchctl", args: ["list"], print_stderr: false, sudo: sudo) + .stdout + .lines.drop(1) + end + + [false, true] + .flat_map(&launchctl) + .map { |l| l.split(/\s+/)[2] } + .grep_v(/^com\.apple\./) + }, + } + + class Diff + attr_reader :removed, :added + + def initialize(before, after) + @before = before.sort.uniq + @after = after.sort.uniq + @removed = @before - @after + @added = @after - @before + end + + def changed? + removed.any? || added.any? + end + end + + def before + @before = {} + + CHECKS.each do |name, block| + @before[name] = block.call + end + end + + def after + @after = {} + + CHECKS.each do |name, block| + @after[name] = block.call + end + end + + def diff + return @diff if defined?(@diff) + + @diff = {} + + CHECKS.keys.each do |name| + @diff[name] = Diff.new(@before[name], @after[name]) + end + + @diff + end + private :diff + + def success? + diff.values.map(&:added).all?(&:none?) + end + + def message + return if success? + + lines = [] + + pkg_files = diff[:installed_pkgs] + .added + .flat_map { |id| Hbc::Pkg.new(id).pkgutil_bom_all.map(&:to_s) } + + installed_apps = diff[:installed_apps].added - pkg_files + + if installed_apps.any? + lines << Formatter.error("Some applications are still installed, add them to #{Formatter.identifier("uninstall delete:")}", label: "Error") + lines << installed_apps.join("\n") + end + + if diff[:installed_kexts].added.any? + lines << Formatter.error("Some kernel extensions are still installed, add them to #{Formatter.identifier("uninstall kext:")}", label: "Error") + lines << diff[:installed_kexts].added.join("\n") + end + + if diff[:installed_pkgs].added.any? + lines << Formatter.error("Some packages are still installed, add them to #{Formatter.identifier("uninstall pkgutil:")}", label: "Error") + lines << diff[:installed_pkgs].added.join("\n") + end + + if diff[:installed_launchjobs].added.any? + lines << Formatter.error("Some launch jobs are still installed, add them to #{Formatter.identifier("uninstall launchctl:")}", label: "Error") + lines << diff[:installed_launchjobs].added.join("\n") + end + + running_apps = diff[:loaded_launchjobs] + .added + .select { |id| id.match?(/\.\d+\Z/) } + .map { |id| id.sub(/\.\d+\Z/, "") } + + loaded_launchjobs = diff[:loaded_launchjobs] + .added + .reject { |id| id.match?(/\.\d+\Z/) } + + if running_apps.any? + lines << Formatter.error("Some applications are still running, add them to #{Formatter.identifier("uninstall quit:")}", label: "Error") + lines << running_apps.join("\n") + end + + if loaded_launchjobs.any? + lines << "\n" unless s.empty? + lines << Formatter.error("Some launch jobs were not unloaded, add them to #{Formatter.identifier("uninstall launchctl:")}", label: "Error") + lines << loaded_launchjobs.join("\n") + end + + lines.join("\n") + end +end diff --git a/cmd/lib/diffable.rb b/cmd/lib/diffable.rb deleted file mode 100644 index 66a082119a..0000000000 --- a/cmd/lib/diffable.rb +++ /dev/null @@ -1,51 +0,0 @@ -class Diffable - def initialize(&block) - @before = nil - @after = nil - @gather = block - end - - def gather - @gather.call.sort.uniq - end - - def before - @before ||= gather - end - - def after - @after ||= gather - end - - def diff - removed = before.reject { |e| after.include?(e) } - added = after - before - - [removed, added] - end - - def changed? - removed, added = diff - removed.any? || added.any? - end - - def combined - (before + after).sort.uniq - end - - def diff_lines(skip_unchanged: true) - removed, added = diff - - lines = combined.flat_map do |e| - if removed.include?(e) - "#{Tty.red}- #{e}#{Tty.reset}" - elsif added.include?(e) - "#{Tty.green}+ #{e}#{Tty.reset}" - else - skip_unchanged ? [] : " #{e}" - end - end - - lines - end -end