homebrew-cask/cmd/brewcask-ci.rb

255 lines
7.4 KiB
Ruby
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: false
require "utils/github"
require "utils/formatter"
require_relative "lib/capture"
require_relative "lib/check"
require_relative "lib/travis"
module Cask
class Cmd
class Ci < AbstractCommand
def self.escape(s)
s.gsub(/\r/, '%0D')
.gsub(/\n/, '%0A')
.gsub(/]/, '%5D')
.gsub(/;/, '%3B');
end
def run
unless ENV.key?("CI")
raise CaskError, "This command isnt meant to be run locally."
end
$stdout.sync = true
$stderr.sync = true
unless tap
raise CaskError, "This command must be run from inside a tap directory."
end
@commit_range = begin
commit_range_start = system_command!("git", args: ["rev-parse", "origin/master"]).stdout.chomp
commit_range_end = system_command!("git", args: ["rev-parse", "HEAD"]).stdout.chomp
"#{commit_range_start}...#{commit_range_end}"
end
ruby_files_in_wrong_directory = modified_ruby_files - (modified_cask_files + modified_command_files + modified_github_files)
unless ruby_files_in_wrong_directory.empty?
raise CaskError, "Casks are in the wrong directory:\n" +
ruby_files_in_wrong_directory.join("\n")
end
if modified_cask_files.count > 1 && tap.name != "homebrew/cask-fonts"
raise CaskError, "More than one cask modified; please submit a pull request for each cask separately."
end
overall_success = true
style_offenses = []
modified_cask_files.each do |path|
cask = CaskLoader.load(path)
overall_success &= step "brew cask style #{cask.token}", "style" do
begin
Style.run(path)
true
rescue => e
json = Style.rubocop(path, json: true)
json.fetch("files").each do |file|
file.fetch("offenses").each do |o|
path = Pathname(file.fetch("path")).relative_path_from(tap.path).to_s
line = o.fetch("location").fetch("start_line")
column = o.fetch("location").fetch("start_column")
message = o.fetch("message")
puts "::error file=#{self.class.escape(path)},line=#{line},col=#{column}::#{self.class.escape(message)}"
end
end
false
end
end
overall_success &= step "brew cask audit #{cask.token}", "audit" do
Auditor.audit(cask, audit_download: true,
audit_appcast: true,
audit_token_conflicts: added_cask_files.include?(path),
commit_range: @commit_range)
end
if (macos_requirement = cask.depends_on.macos) && !macos_requirement.satisfied?
opoo "Skipping installation: #{macos_requirement.message}"
next
end
was_installed = cask.installed?
installer = Installer.new(cask, verbose: true)
cask_and_formula_dependencies = installer.missing_cask_and_formula_dependencies
check = Check.new
overall_success &= step "brew cask install #{cask.token}", "install" do
if was_installed
old_cask = CaskLoader.load(cask.installed_caskfile)
Installer.new(old_cask, verbose: true).zap
end
check.before
installer.install
end
overall_success &= step "brew cask uninstall #{cask.token}", "uninstall" do
success = begin
if manual_installer?(cask)
puts 'Cask has a manual installer, skipping...'
else
installer.uninstall
end
true
rescue => e
$stderr.puts e.message
$stderr.puts e.backtrace
false
ensure
cask_and_formula_dependencies.reverse.each do |cask_or_formula|
next unless cask_or_formula.is_a?(Cask)
Installer.new(cask_or_formula, verbose: true).uninstall if cask_or_formula.installed?
end
end
check.after
next success if check.success?
$stderr.puts check.message
false
end
if check.success? && !check.success?(ignore_exceptions: false)
overall_success &= step "brew cask zap #{cask.token}", "zap" do
success = begin
Installer.new(cask, verbose: true).zap
true
rescue => e
$stderr.puts e.message
$stderr.puts e.backtrace
false
end
check.after
next success if check.success?(ignore_exceptions: false)
$stderr.puts check.message(stanza: "zap")
false
end
end
end
if overall_success
puts Formatter.success("Build finished successfully.", label: "Success")
return
end
raise CaskError, "Build failed."
end
private
def step(name, travis_id)
unless ENV.key?("TRAVIS_COMMIT_RANGE")
puts Formatter.headline(name, color: :yellow)
return yield != false
end
success = false
output = nil
Travis.fold travis_id do
print Formatter.headline("#{name} ", color: :yellow)
real_stdout = $stdout.dup
travis_wait = Thread.new do
loop do
sleep 595
real_stdout.print "\u200b"
end
end
success, output = capture do
begin
yield != false
rescue => e
$stderr.puts e.message
$stderr.puts e.backtrace
false
end
end
travis_wait.kill
travis_wait.join
if success
puts Formatter.success("")
puts output unless output.empty?
else
puts Formatter.error("")
end
end
puts output unless success
success
end
def tap
@tap ||= Tap.from_path(Dir.pwd)
end
def modified_files
@modified_files ||= system_command!(
"git", args: ["diff", "--name-only", "--diff-filter=AMR", @commit_range]
).stdout.split("\n").map { |path| Pathname(path) }
end
def added_files
@added_files ||= system_command!(
"git", args: ["diff", "--name-only", "--diff-filter=A", @commit_range]
).stdout.split("\n").map { |path| Pathname(path) }
end
def modified_ruby_files
@modified_ruby_files ||= modified_files.select { |path| path.extname == ".rb" }
end
def modified_command_files
@modified_command_files ||= modified_files.select { |path| path.ascend.to_a.last.to_s == "cmd" }
end
def modified_github_files
@modified_github_files ||= modified_files.select { |path| path.to_s.start_with?(".github/") }
end
def modified_cask_files
@modified_cask_files ||= modified_files.select { |path| tap.cask_file?(path) }
end
def added_cask_files
@added_cask_files ||= added_files.select { |path| tap.cask_file?(path) }
end
def manual_installer?(cask)
cask.artifacts.any? { |artifact| artifact.is_a?(Artifact::Installer::ManualInstaller) }
end
end
end
end