2014-09-20 14:24:52 +01:00
|
|
|
# Comprehensively test a formula or pull request.
|
|
|
|
#
|
|
|
|
# Usage: brew test-bot [options...] <pull-request|formula>
|
|
|
|
#
|
|
|
|
# Options:
|
2016-04-05 17:23:24 -04:00
|
|
|
# --keep-logs: Write and keep log files under ./brewbot/.
|
2015-03-20 21:31:52 +00:00
|
|
|
# --cleanup: Clean the Homebrew directory. Very dangerous. Use with care.
|
|
|
|
# --clean-cache: Remove all cached downloads. Use with care.
|
|
|
|
# --skip-setup: Don't check the local system is setup correctly.
|
2015-03-20 21:32:01 +00:00
|
|
|
# --skip-homebrew: Don't check Homebrew's files and tests are all valid.
|
2015-03-20 21:31:52 +00:00
|
|
|
# --junit: Generate a JUnit XML test results file.
|
2016-04-05 17:23:24 -04:00
|
|
|
# --no-bottle: Run brew install without --build-bottle.
|
2015-09-11 10:15:21 +01:00
|
|
|
# --keep-old: Run brew bottle --keep-old to build new bottles for a single platform.
|
2016-05-01 14:55:45 +02:00
|
|
|
# --legacy Build formula from legacy Homebrew/legacy-homebrew repo.
|
2016-04-02 23:30:11 +08:00
|
|
|
# (TODO remove it when it's not longer necessary)
|
2016-04-05 17:23:24 -04:00
|
|
|
# --HEAD: Run brew install with --HEAD.
|
|
|
|
# --local: Ask Homebrew to write verbose logs under ./logs/ and set HOME to ./home/.
|
|
|
|
# --tap=<tap>: Use the git repository of the given tap.
|
2015-03-20 21:31:52 +00:00
|
|
|
# --dry-run: Just print commands, don't run them.
|
|
|
|
# --fail-fast: Immediately exit on a failing step.
|
2016-04-05 17:23:24 -04:00
|
|
|
# --verbose: Print test step output in realtime. Has the side effect of passing output
|
|
|
|
# as raw bytes instead of re-encoding in UTF-8.
|
|
|
|
# --fast: Don't install any packages, but run e.g. audit anyway.
|
2016-04-10 22:53:56 -04:00
|
|
|
# --keep-tmp: Keep temporary files written by main installs and tests that are run.
|
2014-09-20 14:24:52 +01:00
|
|
|
#
|
2015-02-19 11:37:07 +00:00
|
|
|
# --ci-master: Shortcut for Homebrew master branch CI options.
|
|
|
|
# --ci-pr: Shortcut for Homebrew pull request CI options.
|
|
|
|
# --ci-testing: Shortcut for Homebrew testing CI options.
|
|
|
|
# --ci-upload: Homebrew CI bottle upload.
|
2014-09-20 14:24:52 +01:00
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
require "formula"
|
|
|
|
require "utils"
|
|
|
|
require "date"
|
|
|
|
require "rexml/document"
|
|
|
|
require "rexml/xmldecl"
|
|
|
|
require "rexml/cdata"
|
2015-12-19 19:10:22 +08:00
|
|
|
require "tap"
|
2014-09-20 14:24:52 +01:00
|
|
|
|
|
|
|
module Homebrew
|
2014-10-16 10:28:51 +01:00
|
|
|
BYTES_IN_1_MEGABYTE = 1024*1024
|
2016-04-06 16:48:07 -04:00
|
|
|
MAX_STEP_OUTPUT_SIZE = BYTES_IN_1_MEGABYTE - (200*1024) # margin of safety
|
|
|
|
|
2016-03-07 18:18:36 +08:00
|
|
|
HOMEBREW_TAP_REGEX = %r{^([\w-]+)/homebrew-([\w-]+)$}
|
2014-09-20 14:24:52 +01:00
|
|
|
|
2016-04-05 17:23:24 -04:00
|
|
|
def ruby_has_encoding?
|
|
|
|
String.method_defined?(:force_encoding)
|
|
|
|
end
|
|
|
|
|
2016-04-06 06:22:22 +02:00
|
|
|
if ruby_has_encoding?
|
|
|
|
def fix_encoding!(str)
|
|
|
|
# Assume we are starting from a "mostly" UTF-8 string
|
|
|
|
str.force_encoding(Encoding::UTF_8)
|
|
|
|
return str if str.valid_encoding?
|
|
|
|
str.encode!(Encoding::UTF_16, :invalid => :replace)
|
|
|
|
str.encode!(Encoding::UTF_8)
|
|
|
|
end
|
|
|
|
elsif require "iconv"
|
|
|
|
def fix_encoding!(str)
|
|
|
|
Iconv.conv("UTF-8//IGNORE", "UTF-8", str)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
def fix_encoding!(str)
|
|
|
|
str
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-09-18 20:18:37 +08:00
|
|
|
def resolve_test_tap
|
2016-01-14 15:52:10 +08:00
|
|
|
if tap = ARGV.value("tap")
|
|
|
|
return Tap.fetch(tap)
|
|
|
|
end
|
|
|
|
|
2016-03-07 18:18:36 +08:00
|
|
|
if (tap = ENV["TRAVIS_REPO_SLUG"]) && (tap =~ HOMEBREW_TAP_REGEX)
|
2016-01-14 15:52:10 +08:00
|
|
|
return Tap.fetch(tap)
|
2015-12-07 14:11:04 +08:00
|
|
|
end
|
2015-09-18 20:18:37 +08:00
|
|
|
|
|
|
|
if ENV["UPSTREAM_BOT_PARAMS"]
|
|
|
|
bot_argv = ENV["UPSTREAM_BOT_PARAMS"].split " "
|
|
|
|
bot_argv.extend HomebrewArgvExtension
|
2016-01-14 15:52:10 +08:00
|
|
|
if tap = bot_argv.value("tap")
|
2016-03-07 19:58:49 +08:00
|
|
|
return Tap.fetch(tap)
|
2015-12-07 14:11:04 +08:00
|
|
|
end
|
2015-09-18 20:18:37 +08:00
|
|
|
end
|
|
|
|
|
2015-12-11 08:08:51 +00:00
|
|
|
if git_url = ENV["UPSTREAM_GIT_URL"] || ENV["GIT_URL"]
|
|
|
|
# Also can get tap from Jenkins GIT_URL.
|
2015-12-11 21:46:02 +08:00
|
|
|
url_path = git_url.sub(%r{^https?://github\.com/}, "").chomp("/").sub(%r{\.git$}, "")
|
2015-12-11 08:08:51 +00:00
|
|
|
begin
|
2016-03-07 19:58:49 +08:00
|
|
|
return Tap.fetch(url_path) if url_path =~ HOMEBREW_TAP_REGEX
|
2015-12-11 08:08:51 +00:00
|
|
|
rescue
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-03-07 18:04:25 +08:00
|
|
|
CoreTap.instance
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
class Step
|
|
|
|
attr_reader :command, :name, :status, :output, :time
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def initialize(test, command, options = {})
|
2014-09-20 14:24:52 +01:00
|
|
|
@test = test
|
|
|
|
@category = test.category
|
|
|
|
@command = command
|
|
|
|
@puts_output_on_success = options[:puts_output_on_success]
|
|
|
|
@name = command[1].delete("-")
|
|
|
|
@status = :running
|
|
|
|
@repository = options[:repository] || HOMEBREW_REPOSITORY
|
|
|
|
@time = 0
|
|
|
|
end
|
|
|
|
|
|
|
|
def log_file_path
|
|
|
|
file = "#{@category}.#{@name}.txt"
|
|
|
|
root = @test.log_root
|
|
|
|
root ? root + file : file
|
|
|
|
end
|
|
|
|
|
|
|
|
def command_short
|
|
|
|
(@command - %w[brew --force --retry --verbose --build-bottle --rb]).join(" ")
|
|
|
|
end
|
|
|
|
|
|
|
|
def passed?
|
|
|
|
@status == :passed
|
|
|
|
end
|
|
|
|
|
|
|
|
def failed?
|
|
|
|
@status == :failed
|
|
|
|
end
|
|
|
|
|
|
|
|
def puts_command
|
2015-09-15 17:32:28 +01:00
|
|
|
if ENV["TRAVIS"]
|
2015-09-18 21:44:49 +08:00
|
|
|
@@travis_step_num ||= 0
|
|
|
|
@travis_fold_id = @command.first(2).join(".") + ".#{@@travis_step_num += 1}"
|
2015-09-15 17:32:28 +01:00
|
|
|
@travis_timer_id = rand(2**32).to_s(16)
|
2015-09-18 21:44:49 +08:00
|
|
|
puts "travis_fold:start:#{@travis_fold_id}"
|
2015-09-15 17:32:28 +01:00
|
|
|
puts "travis_time:start:#{@travis_timer_id}"
|
|
|
|
end
|
2015-09-18 10:28:13 +01:00
|
|
|
puts "#{Tty.blue}==>#{Tty.white} #{@command.join(" ")}#{Tty.reset}"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def puts_result
|
2015-09-15 17:32:28 +01:00
|
|
|
if ENV["TRAVIS"]
|
|
|
|
travis_start_time = @start_time.to_i*1000000000
|
|
|
|
travis_end_time = @end_time.to_i*1000000000
|
|
|
|
travis_duration = travis_end_time - travis_start_time
|
2015-09-18 19:06:51 +08:00
|
|
|
puts "#{Tty.white}==>#{Tty.green} PASSED#{Tty.reset}" if passed?
|
2015-09-15 17:32:28 +01:00
|
|
|
puts "travis_time:end:#{@travis_timer_id},start=#{travis_start_time},finish=#{travis_end_time},duration=#{travis_duration}"
|
2015-09-18 21:44:49 +08:00
|
|
|
puts "travis_fold:end:#{@travis_fold_id}"
|
2015-09-15 17:32:28 +01:00
|
|
|
end
|
2015-09-18 19:06:51 +08:00
|
|
|
puts "#{Tty.white}==>#{Tty.red} FAILED#{Tty.reset}" if failed?
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def has_output?
|
|
|
|
@output && !@output.empty?
|
|
|
|
end
|
|
|
|
|
2015-09-15 17:32:28 +01:00
|
|
|
def time
|
|
|
|
@end_time - @start_time
|
|
|
|
end
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
def run
|
2015-09-15 17:32:28 +01:00
|
|
|
@start_time = Time.now
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
puts_command
|
|
|
|
if ARGV.include? "--dry-run"
|
2015-09-15 17:32:28 +01:00
|
|
|
@end_time = Time.now
|
2014-09-20 14:24:52 +01:00
|
|
|
@status = :passed
|
2015-09-15 17:32:28 +01:00
|
|
|
puts_result
|
2014-09-20 14:24:52 +01:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2015-04-27 23:42:35 +08:00
|
|
|
verbose = ARGV.verbose?
|
2016-04-05 17:23:24 -04:00
|
|
|
# Step may produce arbitrary output and we read it bytewise, so must
|
|
|
|
# buffer it as binary and convert to UTF-8 once complete
|
|
|
|
output = Homebrew.ruby_has_encoding? ? "".encode!("BINARY") : ""
|
2015-04-27 23:42:35 +08:00
|
|
|
working_dir = Pathname.new(@command.first == "git" ? @repository : Dir.pwd)
|
|
|
|
read, write = IO.pipe
|
|
|
|
|
|
|
|
begin
|
|
|
|
pid = fork do
|
|
|
|
read.close
|
|
|
|
$stdout.reopen(write)
|
|
|
|
$stderr.reopen(write)
|
|
|
|
write.close
|
|
|
|
working_dir.cd { exec(*@command) }
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
2015-04-27 23:42:35 +08:00
|
|
|
write.close
|
2016-04-05 17:23:24 -04:00
|
|
|
while buf = read.readpartial(4096)
|
2015-09-23 21:23:17 +08:00
|
|
|
if verbose
|
|
|
|
print buf
|
|
|
|
$stdout.flush
|
|
|
|
end
|
2016-04-05 17:23:24 -04:00
|
|
|
output << buf
|
2015-04-27 23:42:35 +08:00
|
|
|
end
|
2016-04-05 17:23:24 -04:00
|
|
|
rescue EOFError
|
2015-04-27 23:42:35 +08:00
|
|
|
ensure
|
|
|
|
read.close
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
2015-04-27 23:42:35 +08:00
|
|
|
Process.wait(pid)
|
2015-09-15 17:32:28 +01:00
|
|
|
@end_time = Time.now
|
2014-09-20 14:24:52 +01:00
|
|
|
@status = $?.success? ? :passed : :failed
|
|
|
|
puts_result
|
|
|
|
|
2016-04-05 17:23:24 -04:00
|
|
|
|
|
|
|
unless output.empty?
|
2016-04-06 06:35:28 +02:00
|
|
|
@output = Homebrew.fix_encoding!(output)
|
2015-08-03 13:09:07 +01:00
|
|
|
puts @output if (failed? || @puts_output_on_success) && !verbose
|
2015-04-27 23:42:35 +08:00
|
|
|
File.write(log_file_path, @output) if ARGV.include? "--keep-logs"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
2014-10-27 13:02:30 +00:00
|
|
|
|
2015-05-01 00:00:55 -04:00
|
|
|
exit 1 if ARGV.include?("--fail-fast") && failed?
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Test
|
|
|
|
attr_reader :log_root, :category, :name, :steps
|
|
|
|
|
2015-09-11 15:29:16 +01:00
|
|
|
def initialize(argument, options={})
|
2014-09-20 14:24:52 +01:00
|
|
|
@hash = nil
|
|
|
|
@url = nil
|
|
|
|
@formulae = []
|
2014-12-29 07:55:01 +00:00
|
|
|
@added_formulae = []
|
|
|
|
@modified_formula = []
|
2014-09-20 14:24:52 +01:00
|
|
|
@steps = []
|
2016-03-07 18:04:25 +08:00
|
|
|
@tap = options.fetch(:tap, CoreTap.instance)
|
2016-01-14 15:52:10 +08:00
|
|
|
@repository = @tap.path
|
2016-01-15 19:44:04 +08:00
|
|
|
@skip_homebrew = options.fetch(:skip_homebrew, false)
|
2014-09-20 14:24:52 +01:00
|
|
|
|
2016-01-17 15:06:07 +08:00
|
|
|
if quiet_system "git", "-C", @repository.to_s, "rev-parse", "--verify", "-q", argument
|
2014-09-20 14:24:52 +01:00
|
|
|
@hash = argument
|
2016-01-15 19:44:04 +08:00
|
|
|
elsif url_match = argument.match(HOMEBREW_PULL_OR_COMMIT_URL_REGEX)
|
2014-09-20 14:24:52 +01:00
|
|
|
@url = url_match[0]
|
2016-01-15 20:12:41 +08:00
|
|
|
elsif canonical_formula_name = safe_formula_canonical_name(argument)
|
|
|
|
@formulae = [canonical_formula_name]
|
2014-09-20 14:24:52 +01:00
|
|
|
else
|
2014-10-21 15:34:20 +01:00
|
|
|
raise ArgumentError.new("#{argument} is not a pull request URL, commit URL or formula name.")
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
@category = __method__
|
|
|
|
@brewbot_root = Pathname.pwd + "brewbot"
|
|
|
|
FileUtils.mkdir_p @brewbot_root
|
|
|
|
end
|
|
|
|
|
|
|
|
def no_args?
|
2015-08-03 13:09:07 +01:00
|
|
|
@hash == "HEAD"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
2016-01-15 20:12:41 +08:00
|
|
|
def safe_formula_canonical_name(formula_name)
|
|
|
|
Formulary.factory(formula_name).full_name
|
2015-12-11 20:16:05 +00:00
|
|
|
rescue TapFormulaUnavailableError => e
|
2016-04-19 14:19:50 +08:00
|
|
|
raise if e.tap.installed?
|
2015-12-11 20:16:05 +00:00
|
|
|
test "brew", "tap", e.tap.name
|
|
|
|
retry unless steps.last.failed?
|
2015-10-07 17:34:29 +08:00
|
|
|
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
|
2015-09-14 09:06:16 +01:00
|
|
|
end
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
def git(*args)
|
2016-01-15 19:59:22 +08:00
|
|
|
@repository.cd { Utils.popen_read("git", *args) }
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def download
|
2015-08-03 13:09:07 +01:00
|
|
|
def shorten_revision(revision)
|
2014-09-20 14:24:52 +01:00
|
|
|
git("rev-parse", "--short", revision).strip
|
|
|
|
end
|
|
|
|
|
|
|
|
def current_sha1
|
2015-08-03 13:09:07 +01:00
|
|
|
shorten_revision "HEAD"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def current_branch
|
|
|
|
git("symbolic-ref", "HEAD").gsub("refs/heads/", "").strip
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def single_commit?(start_revision, end_revision)
|
2014-09-20 14:24:52 +01:00
|
|
|
git("rev-list", "--count", "#{start_revision}..#{end_revision}").to_i == 1
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def diff_formulae(start_revision, end_revision, path, filter)
|
2014-12-27 13:03:40 +00:00
|
|
|
git(
|
|
|
|
"diff-tree", "-r", "--name-only", "--diff-filter=#{filter}",
|
|
|
|
start_revision, end_revision, "--", path
|
|
|
|
).lines.map do |line|
|
2016-01-15 20:12:41 +08:00
|
|
|
file = Pathname.new line.chomp
|
|
|
|
next unless file.extname == ".rb"
|
|
|
|
@tap.formula_file_to_name(file)
|
2015-08-11 21:31:03 +08:00
|
|
|
end.compact
|
2014-12-27 13:03:40 +00:00
|
|
|
end
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
@category = __method__
|
|
|
|
@start_branch = current_branch
|
|
|
|
|
2015-09-15 17:32:28 +01:00
|
|
|
travis_pr = ENV["TRAVIS_PULL_REQUEST"] && ENV["TRAVIS_PULL_REQUEST"] != "false"
|
|
|
|
|
2015-09-25 09:15:10 +01:00
|
|
|
# Use Jenkins GitHub Pull Request Builder plugin variables for
|
|
|
|
# pull request jobs.
|
|
|
|
if ENV["ghprbPullLink"]
|
2015-08-03 13:09:07 +01:00
|
|
|
@url = ENV["ghprbPullLink"]
|
2014-10-16 22:06:46 +01:00
|
|
|
@hash = nil
|
2015-09-18 11:23:53 +01:00
|
|
|
test "git", "checkout", "origin/master"
|
2015-09-25 09:15:10 +01:00
|
|
|
# Use Travis CI pull-request variables for pull request jobs.
|
2015-09-15 17:32:28 +01:00
|
|
|
elsif travis_pr
|
|
|
|
@url = "https://github.com/#{ENV["TRAVIS_REPO_SLUG"]}/pull/#{ENV["TRAVIS_PULL_REQUEST"]}"
|
|
|
|
@hash = nil
|
2014-10-16 22:06:46 +01:00
|
|
|
end
|
2014-09-20 14:24:52 +01:00
|
|
|
|
2015-09-25 09:15:10 +01:00
|
|
|
# Use Jenkins Git plugin variables for master branch jobs.
|
|
|
|
if ENV["GIT_PREVIOUS_COMMIT"] && ENV["GIT_COMMIT"]
|
|
|
|
diff_start_sha1 = ENV["GIT_PREVIOUS_COMMIT"]
|
|
|
|
diff_end_sha1 = ENV["GIT_COMMIT"]
|
|
|
|
# Use Travis CI Git variables for master or branch jobs.
|
|
|
|
elsif ENV["TRAVIS_COMMIT_RANGE"]
|
|
|
|
diff_start_sha1, diff_end_sha1 = ENV["TRAVIS_COMMIT_RANGE"].split "..."
|
|
|
|
# Otherwise just use the current SHA-1 (which may be overriden later)
|
|
|
|
else
|
|
|
|
diff_end_sha1 = diff_start_sha1 = current_sha1
|
|
|
|
end
|
|
|
|
|
2015-10-13 22:17:17 +08:00
|
|
|
diff_start_sha1 = git("merge-base", diff_start_sha1, diff_end_sha1).strip
|
|
|
|
|
2015-09-25 09:15:10 +01:00
|
|
|
# Handle no arguments being passed on the command-line e.g. `brew test-bot`.
|
2014-09-20 14:24:52 +01:00
|
|
|
if no_args?
|
2015-08-03 13:09:07 +01:00
|
|
|
if diff_start_sha1 == diff_end_sha1 || \
|
|
|
|
single_commit?(diff_start_sha1, diff_end_sha1)
|
2014-09-20 14:24:52 +01:00
|
|
|
@name = diff_end_sha1
|
|
|
|
else
|
|
|
|
@name = "#{diff_start_sha1}-#{diff_end_sha1}"
|
|
|
|
end
|
2015-09-25 09:15:10 +01:00
|
|
|
# Handle formulae arguments being passed on the command-line e.g. `brew test-bot wget fish`.
|
|
|
|
elsif @formulae && @formulae.any?
|
|
|
|
@name = "#{@formulae.first}-#{diff_end_sha1}"
|
2015-10-20 12:28:09 +01:00
|
|
|
diff_start_sha1 = diff_end_sha1
|
2015-09-25 09:15:10 +01:00
|
|
|
# Handle a hash being passed on the command-line e.g. `brew test-bot 1a2b3c`.
|
2014-09-20 14:24:52 +01:00
|
|
|
elsif @hash
|
|
|
|
test "git", "checkout", @hash
|
|
|
|
diff_start_sha1 = "#{@hash}^"
|
|
|
|
diff_end_sha1 = @hash
|
|
|
|
@name = @hash
|
2015-09-25 09:15:10 +01:00
|
|
|
# Handle a URL being passed on the command-line or through Jenkins/Travis
|
|
|
|
# environment variables e.g.
|
2016-05-01 14:55:45 +02:00
|
|
|
# `brew test-bot https://github.com/Homebrew/homebrew-core/pull/678`.
|
2014-09-20 14:24:52 +01:00
|
|
|
elsif @url
|
2015-09-25 09:15:10 +01:00
|
|
|
# TODO: in future Travis CI may need to also use `brew pull` to e.g. push
|
|
|
|
# the right commit to BrewTestBot.
|
|
|
|
unless travis_pr
|
|
|
|
diff_start_sha1 = current_sha1
|
2016-04-02 23:30:11 +08:00
|
|
|
if ARGV.include?("--legacy")
|
|
|
|
test "brew", "pull", "--clean", "--legacy", @url
|
|
|
|
else
|
|
|
|
test "brew", "pull", "--clean", @url
|
|
|
|
end
|
2015-09-25 09:15:10 +01:00
|
|
|
diff_end_sha1 = current_sha1
|
|
|
|
end
|
2015-08-03 13:09:07 +01:00
|
|
|
@short_url = @url.gsub("https://github.com/", "")
|
|
|
|
if @short_url.include? "/commit/"
|
2014-09-20 14:24:52 +01:00
|
|
|
# 7 characters should be enough for a commit (not 40).
|
|
|
|
@short_url.gsub!(/(commit\/\w{7}).*/, '\1')
|
|
|
|
@name = @short_url
|
|
|
|
else
|
|
|
|
@name = "#{@short_url}-#{diff_end_sha1}"
|
|
|
|
end
|
|
|
|
else
|
2015-09-25 09:15:10 +01:00
|
|
|
raise "Cannot set @name: invalid command-line arguments!"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
@log_root = @brewbot_root + @name
|
|
|
|
FileUtils.mkdir_p @log_root
|
|
|
|
|
|
|
|
return unless diff_start_sha1 != diff_end_sha1
|
2015-09-15 17:32:28 +01:00
|
|
|
return if @url && steps.last && !steps.last.passed?
|
2014-09-20 14:24:52 +01:00
|
|
|
|
2016-01-14 15:52:10 +08:00
|
|
|
formula_path = @tap.formula_dir.to_s
|
2014-12-29 07:55:01 +00:00
|
|
|
@added_formulae += diff_formulae(diff_start_sha1, diff_end_sha1, formula_path, "A")
|
|
|
|
@modified_formula += diff_formulae(diff_start_sha1, diff_end_sha1, formula_path, "M")
|
2014-12-27 13:03:40 +00:00
|
|
|
@formulae += @added_formulae + @modified_formula
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def skip(formula_name)
|
2014-11-10 20:40:29 -06:00
|
|
|
puts "#{Tty.blue}==>#{Tty.white} SKIPPING: #{formula_name}#{Tty.reset}"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def satisfied_requirements?(formula, spec, dependency = nil)
|
2014-11-10 20:40:29 -06:00
|
|
|
requirements = formula.send(spec).requirements
|
2014-09-20 14:24:52 +01:00
|
|
|
|
|
|
|
unsatisfied_requirements = requirements.reject do |requirement|
|
2014-11-18 16:43:13 +00:00
|
|
|
satisfied = false
|
2015-02-03 16:46:36 +00:00
|
|
|
satisfied ||= requirement.satisfied?
|
|
|
|
satisfied ||= requirement.optional?
|
2014-11-18 16:43:13 +00:00
|
|
|
if !satisfied && requirement.default_formula?
|
2015-06-06 19:55:31 +08:00
|
|
|
default = Formula[requirement.default_formula]
|
2015-05-27 21:19:15 +08:00
|
|
|
satisfied = satisfied_requirements?(default, :stable, formula.full_name)
|
2014-11-18 16:43:13 +00:00
|
|
|
end
|
|
|
|
satisfied
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
if unsatisfied_requirements.empty?
|
|
|
|
true
|
|
|
|
else
|
2015-05-27 21:19:15 +08:00
|
|
|
name = formula.full_name
|
2014-11-10 20:40:29 -06:00
|
|
|
name += " (#{spec})" unless spec == :stable
|
2014-11-18 16:43:13 +00:00
|
|
|
name += " (#{dependency} dependency)" if dependency
|
2014-11-10 20:40:29 -06:00
|
|
|
skip name
|
|
|
|
puts unsatisfied_requirements.map(&:message)
|
2014-09-20 14:24:52 +01:00
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def setup
|
|
|
|
@category = __method__
|
|
|
|
return if ARGV.include? "--skip-setup"
|
2015-12-07 14:08:55 +00:00
|
|
|
test "brew", "doctor" if !ENV["TRAVIS"] && ENV["HOMEBREW_RUBY"] != "1.8.7"
|
2014-09-20 14:24:52 +01:00
|
|
|
test "brew", "--env"
|
|
|
|
test "brew", "config"
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def formula(formula_name)
|
2014-11-10 20:40:29 -06:00
|
|
|
@category = "#{__method__}.#{formula_name}"
|
2014-09-20 14:24:52 +01:00
|
|
|
|
2016-04-06 17:57:05 +01:00
|
|
|
test "brew", "uses", formula_name
|
2014-12-18 17:03:17 +00:00
|
|
|
|
2016-01-15 20:26:20 +08:00
|
|
|
formula = Formulary.factory(formula_name)
|
2015-05-11 15:44:42 +08:00
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
installed_gcc = false
|
2015-01-17 19:12:10 +01:00
|
|
|
|
|
|
|
deps = []
|
|
|
|
reqs = []
|
|
|
|
|
2016-01-15 20:26:20 +08:00
|
|
|
fetch_args = [formula_name]
|
2016-01-12 00:02:14 +00:00
|
|
|
fetch_args << "--build-bottle" if !ARGV.include?("--fast") && !ARGV.include?("--no-bottle") && !formula.bottle_disabled?
|
2015-12-10 10:41:43 +00:00
|
|
|
fetch_args << "--force" if ARGV.include? "--cleanup"
|
|
|
|
|
2016-01-15 20:26:20 +08:00
|
|
|
audit_args = [formula_name]
|
2015-12-10 10:41:43 +00:00
|
|
|
audit_args << "--strict" << "--online" if @added_formulae.include? formula_name
|
|
|
|
|
2015-01-17 19:12:10 +01:00
|
|
|
if formula.stable
|
2015-12-10 10:41:43 +00:00
|
|
|
unless satisfied_requirements?(formula, :stable)
|
|
|
|
test "brew", "fetch", "--retry", *fetch_args
|
|
|
|
test "brew", "audit", *audit_args
|
|
|
|
return
|
|
|
|
end
|
2015-01-17 19:12:10 +01:00
|
|
|
|
2015-08-24 14:41:56 -07:00
|
|
|
deps |= formula.stable.deps.to_a.reject(&:optional?)
|
|
|
|
reqs |= formula.stable.requirements.to_a.reject(&:optional?)
|
2015-01-17 19:12:10 +01:00
|
|
|
elsif formula.devel
|
2015-12-10 10:41:43 +00:00
|
|
|
unless satisfied_requirements?(formula, :devel)
|
|
|
|
test "brew", "fetch", "--retry", "--devel", *fetch_args
|
|
|
|
test "brew", "audit", "--devel", *audit_args
|
|
|
|
return
|
|
|
|
end
|
2015-01-17 19:12:10 +01:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
if formula.devel && !ARGV.include?("--HEAD")
|
2015-08-24 14:41:56 -07:00
|
|
|
deps |= formula.devel.deps.to_a.reject(&:optional?)
|
|
|
|
reqs |= formula.devel.requirements.to_a.reject(&:optional?)
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
2015-08-21 12:05:54 +08:00
|
|
|
begin
|
2015-08-24 14:41:56 -07:00
|
|
|
deps.each { |d| d.to_formula.recursive_dependencies }
|
2015-08-21 12:05:54 +08:00
|
|
|
rescue TapFormulaUnavailableError => e
|
|
|
|
raise if e.tap.installed?
|
|
|
|
safe_system "brew", "tap", e.tap.name
|
|
|
|
retry
|
|
|
|
end
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
begin
|
2014-12-29 12:20:30 +00:00
|
|
|
deps.each do |dep|
|
|
|
|
CompilerSelector.select_for(dep.to_formula)
|
|
|
|
end
|
2014-11-10 20:40:29 -06:00
|
|
|
CompilerSelector.select_for(formula)
|
2014-09-20 14:24:52 +01:00
|
|
|
rescue CompilerSelectionError => e
|
|
|
|
unless installed_gcc
|
2015-05-26 15:11:17 +01:00
|
|
|
run_as_not_developer { test "brew", "install", "gcc" }
|
2014-09-20 14:24:52 +01:00
|
|
|
installed_gcc = true
|
|
|
|
OS::Mac.clear_version_cache
|
|
|
|
retry
|
|
|
|
end
|
2016-01-15 20:26:20 +08:00
|
|
|
skip formula_name
|
2014-09-20 14:24:52 +01:00
|
|
|
puts e.message
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2015-07-11 14:41:27 -04:00
|
|
|
conflicts = formula.conflicts
|
|
|
|
formula.recursive_dependencies.each do |dependency|
|
|
|
|
conflicts += dependency.to_formula.conflicts
|
|
|
|
end
|
|
|
|
|
|
|
|
conflicts.each do |conflict|
|
|
|
|
confict_formula = Formulary.factory(conflict.name)
|
|
|
|
|
|
|
|
if confict_formula.installed? && confict_formula.linked_keg.exist?
|
|
|
|
test "brew", "unlink", "--force", conflict.name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-04-06 23:23:55 -07:00
|
|
|
installed = Utils.popen_read("brew", "list").split("\n")
|
2016-04-16 00:23:40 +08:00
|
|
|
dependencies = Utils.popen_read("brew", "deps", "--include-build", formula_name).split("\n")
|
2015-07-11 14:41:27 -04:00
|
|
|
|
|
|
|
(installed & dependencies).each do |installed_dependency|
|
|
|
|
installed_dependency_formula = Formulary.factory(installed_dependency)
|
|
|
|
if installed_dependency_formula.installed? &&
|
2015-08-03 13:09:07 +01:00
|
|
|
!installed_dependency_formula.keg_only? &&
|
|
|
|
!installed_dependency_formula.linked_keg.exist?
|
2015-07-11 14:41:27 -04:00
|
|
|
test "brew", "link", installed_dependency
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-04-06 23:23:55 -07:00
|
|
|
dependencies -= installed
|
|
|
|
unchanged_dependencies = dependencies - @formulae
|
|
|
|
changed_dependences = dependencies - unchanged_dependencies
|
|
|
|
|
2016-04-16 00:23:40 +08:00
|
|
|
runtime_dependencies = Utils.popen_read("brew", "deps", formula_name).split("\n")
|
2015-04-06 23:23:55 -07:00
|
|
|
build_dependencies = dependencies - runtime_dependencies
|
|
|
|
unchanged_build_dependencies = build_dependencies - @formulae
|
|
|
|
|
2016-04-16 00:23:40 +08:00
|
|
|
dependents = Utils.popen_read("brew", "uses", formula_name).split("\n")
|
2015-04-06 23:23:55 -07:00
|
|
|
dependents -= @formulae
|
2015-08-03 13:09:07 +01:00
|
|
|
dependents = dependents.map { |d| Formulary.factory(d) }
|
2015-04-06 23:23:55 -07:00
|
|
|
|
2016-04-17 21:25:11 -04:00
|
|
|
bottled_dependents = dependents.select { |d| d.bottled? }
|
|
|
|
testable_dependents = dependents.select { |d| d.bottled? && d.test_defined? }
|
2015-04-06 23:23:55 -07:00
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
if (deps | reqs).any? { |d| d.name == "mercurial" && d.build? }
|
2015-05-26 15:11:17 +01:00
|
|
|
run_as_not_developer { test "brew", "install", "mercurial" }
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
test "brew", "fetch", "--retry", *unchanged_dependencies unless unchanged_dependencies.empty?
|
2015-08-04 00:12:42 +02:00
|
|
|
|
|
|
|
unless changed_dependences.empty?
|
|
|
|
test "brew", "fetch", "--retry", "--build-bottle", *changed_dependences
|
2015-12-29 11:54:43 +00:00
|
|
|
unless ARGV.include?("--fast")
|
|
|
|
# Install changed dependencies as new bottles so we don't have checksum problems.
|
|
|
|
test "brew", "install", "--build-bottle", *changed_dependences
|
|
|
|
# Run postinstall on them because the tested formula might depend on
|
|
|
|
# this step
|
|
|
|
test "brew", "postinstall", *changed_dependences
|
|
|
|
end
|
2015-08-04 00:12:42 +02:00
|
|
|
end
|
2015-12-10 10:41:43 +00:00
|
|
|
test "brew", "fetch", "--retry", *fetch_args
|
2016-01-15 20:26:20 +08:00
|
|
|
test "brew", "uninstall", "--force", formula_name if formula.installed?
|
2016-04-10 22:53:56 -04:00
|
|
|
|
|
|
|
# shared_*_args are applied to both the main and --devel spec
|
|
|
|
shared_install_args = ["--verbose"]
|
|
|
|
shared_install_args << "--keep-tmp" if ARGV.keep_tmp?
|
|
|
|
# install_args is just for the main (stable, or devel if in a devel-only tap) spec
|
|
|
|
install_args = []
|
2016-01-12 00:02:14 +00:00
|
|
|
install_args << "--build-bottle" if !ARGV.include?("--fast") && !ARGV.include?("--no-bottle") && !formula.bottle_disabled?
|
2014-09-20 14:24:52 +01:00
|
|
|
install_args << "--HEAD" if ARGV.include? "--HEAD"
|
2015-01-19 13:39:38 +00:00
|
|
|
|
|
|
|
# Pass --devel or --HEAD to install in the event formulae lack stable. Supports devel-only/head-only.
|
|
|
|
# head-only should not have devel, but devel-only can have head. Stable can have all three.
|
|
|
|
if devel_only_tap? formula
|
|
|
|
install_args << "--devel"
|
2015-10-14 13:53:24 +01:00
|
|
|
formula_bottled = false
|
2015-01-19 13:39:38 +00:00
|
|
|
elsif head_only_tap? formula
|
|
|
|
install_args << "--HEAD"
|
2015-10-14 13:53:24 +01:00
|
|
|
formula_bottled = false
|
|
|
|
else
|
|
|
|
formula_bottled = formula.bottled?
|
2015-01-19 13:39:38 +00:00
|
|
|
end
|
|
|
|
|
2016-04-10 22:53:56 -04:00
|
|
|
install_args.concat(shared_install_args)
|
2016-01-15 20:26:20 +08:00
|
|
|
install_args << formula_name
|
2014-09-20 14:24:52 +01:00
|
|
|
# Don't care about e.g. bottle failures for dependencies.
|
2015-10-14 17:40:12 +02:00
|
|
|
install_passed = false
|
2015-05-18 18:44:27 +08:00
|
|
|
run_as_not_developer do
|
2015-10-20 14:53:50 +08:00
|
|
|
if !ARGV.include?("--fast") || formula_bottled || formula.bottle_unneeded?
|
2015-10-14 13:53:24 +01:00
|
|
|
test "brew", "install", "--only-dependencies", *install_args unless dependencies.empty?
|
|
|
|
test "brew", "install", *install_args
|
|
|
|
install_passed = steps.last.passed?
|
|
|
|
end
|
2015-05-18 18:44:27 +08:00
|
|
|
end
|
2014-12-27 13:03:40 +00:00
|
|
|
test "brew", "audit", *audit_args
|
2014-09-20 14:24:52 +01:00
|
|
|
if install_passed
|
2016-01-12 00:02:14 +00:00
|
|
|
if formula.stable? && !ARGV.include?("--fast") && !ARGV.include?("--no-bottle") && !formula.bottle_disabled?
|
2016-01-15 20:26:20 +08:00
|
|
|
bottle_args = ["--verbose", "--rb", formula_name]
|
2015-09-11 10:15:21 +01:00
|
|
|
bottle_args << "--keep-old" if ARGV.include? "--keep-old"
|
2014-11-24 08:26:43 +00:00
|
|
|
test "brew", "bottle", *bottle_args
|
2014-09-20 14:24:52 +01:00
|
|
|
bottle_step = steps.last
|
2015-08-03 13:09:07 +01:00
|
|
|
if bottle_step.passed? && bottle_step.has_output?
|
2014-09-20 14:24:52 +01:00
|
|
|
bottle_filename =
|
|
|
|
bottle_step.output.gsub(/.*(\.\/\S+#{bottle_native_regex}).*/m, '\1')
|
2015-10-15 15:00:05 +08:00
|
|
|
bottle_rb_filename = bottle_filename.gsub(/\.(\d+\.)?tar\.gz$/, ".rb")
|
|
|
|
bottle_merge_args = ["--merge", "--write", "--no-commit", bottle_rb_filename]
|
|
|
|
bottle_merge_args << "--keep-old" if ARGV.include? "--keep-old"
|
|
|
|
test "brew", "bottle", *bottle_merge_args
|
2016-01-15 20:26:20 +08:00
|
|
|
test "brew", "uninstall", "--force", formula_name
|
2015-11-03 18:50:59 +08:00
|
|
|
FileUtils.ln bottle_filename, HOMEBREW_CACHE/bottle_filename, :force => true
|
2016-01-15 20:48:12 +08:00
|
|
|
@formulae.delete(formula_name)
|
2015-03-20 21:32:22 +00:00
|
|
|
if unchanged_build_dependencies.any?
|
|
|
|
test "brew", "uninstall", "--force", *unchanged_build_dependencies
|
|
|
|
unchanged_dependencies -= unchanged_build_dependencies
|
|
|
|
end
|
2014-09-20 14:24:52 +01:00
|
|
|
test "brew", "install", bottle_filename
|
|
|
|
end
|
|
|
|
end
|
2016-04-10 22:53:56 -04:00
|
|
|
shared_test_args = ["--verbose"]
|
|
|
|
shared_test_args << "--keep-tmp" if ARGV.keep_tmp?
|
|
|
|
test "brew", "test", formula_name, *shared_test_args if formula.test_defined?
|
2016-04-17 21:25:11 -04:00
|
|
|
bottled_dependents.each do |dependent|
|
2015-01-08 20:49:24 +00:00
|
|
|
unless dependent.installed?
|
|
|
|
test "brew", "fetch", "--retry", dependent.name
|
|
|
|
next if steps.last.failed?
|
2015-08-03 13:09:07 +01:00
|
|
|
conflicts = dependent.conflicts.map { |c| Formulary.factory(c.name) }.select(&:installed?)
|
2015-01-09 22:33:57 +08:00
|
|
|
conflicts.each do |conflict|
|
|
|
|
test "brew", "unlink", conflict.name
|
|
|
|
end
|
2015-12-29 11:54:43 +00:00
|
|
|
unless ARGV.include?("--fast")
|
|
|
|
run_as_not_developer { test "brew", "install", dependent.name }
|
|
|
|
next if steps.last.failed?
|
|
|
|
end
|
2015-01-08 20:49:24 +00:00
|
|
|
end
|
|
|
|
if dependent.installed?
|
2016-04-17 21:25:11 -04:00
|
|
|
test "brew", "linkage", "--test", dependent.name
|
|
|
|
if testable_dependents.include? dependent
|
|
|
|
test "brew", "test", "--verbose", dependent.name
|
|
|
|
end
|
2014-12-28 18:16:49 +00:00
|
|
|
end
|
2014-12-18 17:03:17 +00:00
|
|
|
end
|
2016-01-15 20:26:20 +08:00
|
|
|
test "brew", "uninstall", "--force", formula_name
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
2015-10-14 13:53:24 +01:00
|
|
|
if formula.devel && formula.stable? \
|
|
|
|
&& !ARGV.include?("--HEAD") && !ARGV.include?("--fast") \
|
2014-11-10 20:40:29 -06:00
|
|
|
&& satisfied_requirements?(formula, :devel)
|
2015-12-10 10:41:43 +00:00
|
|
|
test "brew", "fetch", "--retry", "--devel", *fetch_args
|
2016-04-10 22:53:56 -04:00
|
|
|
run_as_not_developer do
|
|
|
|
test "brew", "install", "--devel", formula_name, *shared_install_args
|
|
|
|
end
|
2014-09-20 14:24:52 +01:00
|
|
|
devel_install_passed = steps.last.passed?
|
2014-12-27 13:03:40 +00:00
|
|
|
test "brew", "audit", "--devel", *audit_args
|
2014-09-20 14:24:52 +01:00
|
|
|
if devel_install_passed
|
2016-04-10 22:53:56 -04:00
|
|
|
test "brew", "test", "--devel", formula_name, *shared_test_args if formula.test_defined?
|
2016-01-15 20:26:20 +08:00
|
|
|
test "brew", "uninstall", "--devel", "--force", formula_name
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
end
|
2015-03-20 21:32:22 +00:00
|
|
|
test "brew", "uninstall", "--force", *unchanged_dependencies if unchanged_dependencies.any?
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def homebrew
|
|
|
|
@category = __method__
|
2015-09-11 15:29:16 +01:00
|
|
|
return if @skip_homebrew
|
2015-09-12 22:15:23 +08:00
|
|
|
test "brew", "tests"
|
2016-03-07 18:04:25 +08:00
|
|
|
if @tap.core_tap?
|
2015-12-20 01:02:53 +08:00
|
|
|
tests_args = ["--no-compat"]
|
2015-09-11 23:05:17 +08:00
|
|
|
readall_args = ["--aliases"]
|
2015-12-20 01:02:53 +08:00
|
|
|
if RUBY_VERSION.split(".").first.to_i >= 2
|
|
|
|
tests_args << "--coverage" if ENV["TRAVIS"]
|
|
|
|
readall_args << "--syntax"
|
|
|
|
end
|
|
|
|
test "brew", "tests", *tests_args
|
2015-09-11 23:05:17 +08:00
|
|
|
test "brew", "readall", *readall_args
|
2016-01-14 15:52:10 +08:00
|
|
|
else
|
|
|
|
test "brew", "readall", @tap.name
|
2015-09-11 23:05:17 +08:00
|
|
|
end
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def cleanup_before
|
|
|
|
@category = __method__
|
2015-08-03 13:09:07 +01:00
|
|
|
return unless ARGV.include? "--cleanup"
|
2015-08-11 14:18:26 +01:00
|
|
|
git "gc", "--auto"
|
2014-09-20 14:24:52 +01:00
|
|
|
git "stash"
|
|
|
|
git "am", "--abort"
|
|
|
|
git "rebase", "--abort"
|
|
|
|
git "reset", "--hard"
|
|
|
|
git "checkout", "-f", "master"
|
2016-04-03 00:58:53 -04:00
|
|
|
git "clean", "-ffdx"
|
|
|
|
HOMEBREW_REPOSITORY.cd do
|
|
|
|
safe_system "git", "reset", "--hard"
|
|
|
|
safe_system "git", "checkout", "-f", "master"
|
|
|
|
# This will uninstall all formulae, as long as
|
|
|
|
# HOMEBREW_REPOSITORY == HOMEBREW_PREFIX, which is true on the test bots
|
2016-04-03 01:29:28 -04:00
|
|
|
safe_system "git", "clean", "-ffdx", "--exclude=/Library/Taps/" unless ENV["HOMEBREW_RUBY"] == "1.8.7"
|
2016-04-03 00:58:53 -04:00
|
|
|
end
|
2016-04-03 12:42:44 +08:00
|
|
|
pr_locks = "#{@repository}/.git/refs/remotes/*/pr/*/*.lock"
|
2015-08-03 13:09:07 +01:00
|
|
|
Dir.glob(pr_locks) { |lock| FileUtils.rm_rf lock }
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def cleanup_after
|
|
|
|
@category = __method__
|
|
|
|
|
2015-09-15 17:32:28 +01:00
|
|
|
if @start_branch && !@start_branch.empty? && \
|
|
|
|
(ARGV.include?("--cleanup") || @url || @hash)
|
2015-12-07 14:08:55 +00:00
|
|
|
checkout_args = [@start_branch]
|
|
|
|
checkout_args << "-f" if ARGV.include? "--cleanup"
|
2014-09-20 14:24:52 +01:00
|
|
|
test "git", "checkout", *checkout_args
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
if ARGV.include? "--cleanup"
|
2014-09-20 14:24:52 +01:00
|
|
|
test "git", "reset", "--hard"
|
|
|
|
git "stash", "pop"
|
2015-08-13 08:22:24 +01:00
|
|
|
test "brew", "cleanup", "--prune=7"
|
2015-08-11 14:18:26 +01:00
|
|
|
git "gc", "--auto"
|
2015-12-07 14:08:55 +00:00
|
|
|
test "git", "clean", "-ffdx"
|
2016-04-03 00:58:53 -04:00
|
|
|
HOMEBREW_REPOSITORY.cd do
|
|
|
|
safe_system "git", "reset", "--hard"
|
2016-04-18 17:39:52 +01:00
|
|
|
Tap.names.each { |s| safe_system "brew", "untap", s if s != "homebrew/core" }
|
2016-04-03 01:29:28 -04:00
|
|
|
safe_system "git", "clean", "-ffdx", "--exclude=/Library/Taps/"
|
2016-04-03 00:58:53 -04:00
|
|
|
end
|
2015-08-13 10:55:29 +01:00
|
|
|
if ARGV.include? "--local"
|
|
|
|
FileUtils.rm_rf ENV["HOMEBREW_HOME"]
|
|
|
|
FileUtils.rm_rf ENV["HOMEBREW_LOGS"]
|
|
|
|
end
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
FileUtils.rm_rf @brewbot_root unless ARGV.include? "--keep-logs"
|
|
|
|
end
|
|
|
|
|
|
|
|
def test(*args)
|
|
|
|
options = Hash === args.last ? args.pop : {}
|
|
|
|
options[:repository] = @repository
|
|
|
|
step = Step.new self, args, options
|
|
|
|
step.run
|
|
|
|
steps << step
|
|
|
|
step
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_results
|
2015-02-17 18:02:05 +08:00
|
|
|
steps.all? do |step|
|
2014-09-20 14:24:52 +01:00
|
|
|
case step.status
|
2015-02-17 18:02:05 +08:00
|
|
|
when :passed then true
|
2014-09-20 14:24:52 +01:00
|
|
|
when :running then raise
|
2015-02-17 18:02:05 +08:00
|
|
|
when :failed then false
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def formulae
|
|
|
|
changed_formulae_dependents = {}
|
|
|
|
|
|
|
|
@formulae.each do |formula|
|
2016-04-16 00:23:40 +08:00
|
|
|
formula_dependencies = Utils.popen_read("brew", "deps", "--include-build", formula).split("\n")
|
2014-09-20 14:24:52 +01:00
|
|
|
unchanged_dependencies = formula_dependencies - @formulae
|
|
|
|
changed_dependences = formula_dependencies - unchanged_dependencies
|
|
|
|
changed_dependences.each do |changed_formula|
|
|
|
|
changed_formulae_dependents[changed_formula] ||= 0
|
|
|
|
changed_formulae_dependents[changed_formula] += 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
changed_formulae = changed_formulae_dependents.sort do |a1, a2|
|
2014-09-20 14:24:52 +01:00
|
|
|
a2[1].to_i <=> a1[1].to_i
|
|
|
|
end
|
|
|
|
changed_formulae.map!(&:first)
|
|
|
|
unchanged_formulae = @formulae - changed_formulae
|
|
|
|
changed_formulae + unchanged_formulae
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def head_only_tap?(formula)
|
2015-09-18 22:19:53 +08:00
|
|
|
formula.head && formula.devel.nil? && formula.stable.nil? && formula.tap == "homebrew/homebrew-head-only"
|
2015-01-19 13:39:38 +00:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def devel_only_tap?(formula)
|
2015-09-18 22:19:53 +08:00
|
|
|
formula.devel && formula.stable.nil? && formula.tap == "homebrew/homebrew-devel-only"
|
2015-01-19 13:39:38 +00:00
|
|
|
end
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
def run
|
|
|
|
cleanup_before
|
2015-10-15 15:19:12 +08:00
|
|
|
begin
|
|
|
|
download
|
|
|
|
setup
|
|
|
|
homebrew
|
|
|
|
formulae.each do |f|
|
|
|
|
formula(f)
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
cleanup_after
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
check_results
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-09-18 20:33:50 +08:00
|
|
|
def test_ci_upload(tap)
|
2015-09-18 20:28:36 +08:00
|
|
|
jenkins = ENV["JENKINS_HOME"]
|
|
|
|
job = ENV["UPSTREAM_JOB_NAME"]
|
|
|
|
id = ENV["UPSTREAM_BUILD_ID"]
|
|
|
|
raise "Missing Jenkins variables!" if !jenkins || !job || !id
|
|
|
|
|
|
|
|
bintray_user = ENV["BINTRAY_USER"]
|
|
|
|
bintray_key = ENV["BINTRAY_KEY"]
|
|
|
|
if !bintray_user || !bintray_key
|
|
|
|
raise "Missing BINTRAY_USER or BINTRAY_KEY variables!"
|
|
|
|
end
|
|
|
|
|
|
|
|
# Don't pass keys/cookies to subprocesses..
|
|
|
|
ENV["BINTRAY_KEY"] = nil
|
|
|
|
ENV["HUDSON_SERVER_COOKIE"] = nil
|
|
|
|
ENV["JENKINS_SERVER_COOKIE"] = nil
|
|
|
|
ENV["HUDSON_COOKIE"] = nil
|
|
|
|
|
|
|
|
ARGV << "--verbose"
|
|
|
|
ARGV << "--keep-old" if ENV["UPSTREAM_BOTTLE_KEEP_OLD"]
|
2016-04-02 23:30:11 +08:00
|
|
|
ARGV << "--legacy" if ENV["UPSTREAM_BOTTLE_LEGACY"]
|
2015-09-18 20:28:36 +08:00
|
|
|
|
|
|
|
bottles = Dir["#{jenkins}/jobs/#{job}/configurations/axis-version/*/builds/#{id}/archive/*.bottle*.*"]
|
|
|
|
return if bottles.empty?
|
|
|
|
FileUtils.cp bottles, Dir.pwd, :verbose => true
|
|
|
|
|
|
|
|
ENV["GIT_AUTHOR_NAME"] = ENV["GIT_COMMITTER_NAME"] = "BrewTestBot"
|
|
|
|
ENV["GIT_AUTHOR_EMAIL"] = ENV["GIT_COMMITTER_EMAIL"] = "brew-test-bot@googlegroups.com"
|
2016-01-14 15:52:10 +08:00
|
|
|
ENV["GIT_WORK_TREE"] = tap.path
|
2015-09-18 20:28:36 +08:00
|
|
|
ENV["GIT_DIR"] = "#{ENV["GIT_WORK_TREE"]}/.git"
|
|
|
|
|
|
|
|
pr = ENV["UPSTREAM_PULL_REQUEST"]
|
|
|
|
number = ENV["UPSTREAM_BUILD_NUMBER"]
|
|
|
|
|
|
|
|
quiet_system "git", "am", "--abort"
|
|
|
|
quiet_system "git", "rebase", "--abort"
|
|
|
|
safe_system "git", "checkout", "-f", "master"
|
|
|
|
safe_system "git", "reset", "--hard", "origin/master"
|
|
|
|
safe_system "brew", "update"
|
|
|
|
|
|
|
|
if pr
|
2016-04-02 23:30:11 +08:00
|
|
|
if ARGV.include?("--legacy")
|
2016-05-01 14:59:39 +02:00
|
|
|
pull_pr = "https://github.com/Homebrew/legacy-homebrew/pull/#{pr}"
|
2016-04-02 23:30:11 +08:00
|
|
|
safe_system "brew", "pull", "--clean", "--legacy", pull_pr
|
|
|
|
else
|
|
|
|
pull_pr = "https://github.com/#{tap.user}/homebrew-#{tap.repo}/pull/#{pr}"
|
|
|
|
safe_system "brew", "pull", "--clean", pull_pr
|
|
|
|
end
|
2015-09-18 20:28:36 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
bottle_args = ["--merge", "--write", *Dir["*.bottle.rb"]]
|
|
|
|
bottle_args << "--keep-old" if ARGV.include? "--keep-old"
|
2015-09-21 14:01:19 +01:00
|
|
|
system "brew", "bottle", *bottle_args
|
2015-09-18 20:28:36 +08:00
|
|
|
|
2016-04-03 00:09:30 +08:00
|
|
|
remote = "git@github.com:BrewTestBot/homebrew-#{tap.repo}.git"
|
2015-09-18 20:28:36 +08:00
|
|
|
tag = pr ? "pr-#{pr}" : "testing-#{number}"
|
|
|
|
|
2015-12-11 16:19:51 +08:00
|
|
|
bintray_repo = Bintray.repository(tap)
|
2015-09-18 20:28:36 +08:00
|
|
|
bintray_repo_url = "https://api.bintray.com/packages/homebrew/#{bintray_repo}"
|
|
|
|
formula_packaged = {}
|
|
|
|
|
|
|
|
Dir.glob("*.bottle*.tar.gz") do |filename|
|
|
|
|
formula_name, canonical_formula_name = bottle_resolve_formula_names filename
|
|
|
|
formula = Formulary.factory canonical_formula_name
|
|
|
|
version = formula.pkg_version
|
|
|
|
bintray_package = Bintray.package formula_name
|
|
|
|
|
|
|
|
if system "curl", "-I", "--silent", "--fail", "--output", "/dev/null",
|
|
|
|
"#{BottleSpecification::DEFAULT_DOMAIN}/#{bintray_repo}/#{filename}"
|
|
|
|
raise <<-EOS.undent
|
|
|
|
#{filename} is already published. Please remove it manually from
|
|
|
|
https://bintray.com/homebrew/#{bintray_repo}/#{bintray_package}/view#files
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
|
|
|
unless formula_packaged[formula_name]
|
|
|
|
package_url = "#{bintray_repo_url}/#{bintray_package}"
|
|
|
|
unless system "curl", "--silent", "--fail", "--output", "/dev/null", package_url
|
2016-02-27 22:13:31 -08:00
|
|
|
package_blob = <<-EOS.undent
|
|
|
|
{"name": "#{bintray_package}",
|
|
|
|
"public_download_numbers": true,
|
|
|
|
"public_stats": true}
|
|
|
|
EOS
|
2015-09-18 20:28:36 +08:00
|
|
|
curl "--silent", "--fail", "-u#{bintray_user}:#{bintray_key}",
|
|
|
|
"-H", "Content-Type: application/json",
|
2016-02-27 22:13:31 -08:00
|
|
|
"-d", package_blob, bintray_repo_url
|
2015-09-18 20:28:36 +08:00
|
|
|
puts
|
|
|
|
end
|
|
|
|
formula_packaged[formula_name] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
content_url = "https://api.bintray.com/content/homebrew"
|
|
|
|
content_url += "/#{bintray_repo}/#{bintray_package}/#{version}/#{filename}"
|
|
|
|
content_url += "?override=1"
|
|
|
|
curl "--silent", "--fail", "-u#{bintray_user}:#{bintray_key}",
|
|
|
|
"-T", filename, content_url
|
|
|
|
puts
|
|
|
|
end
|
|
|
|
|
|
|
|
safe_system "git", "tag", "--force", tag
|
2016-05-06 11:00:22 +01:00
|
|
|
safe_system "git", "push", "--force", remote, "master:master", "refs/tags/#{tag}"
|
2015-09-18 20:28:36 +08:00
|
|
|
end
|
|
|
|
|
2015-09-18 20:33:50 +08:00
|
|
|
def sanitize_ARGV_and_ENV
|
2015-08-03 13:09:07 +01:00
|
|
|
if Pathname.pwd == HOMEBREW_PREFIX && ARGV.include?("--cleanup")
|
|
|
|
odie "cannot use --cleanup from HOMEBREW_PREFIX as it will delete all output."
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
ENV["HOMEBREW_DEVELOPER"] = "1"
|
2015-08-17 13:58:12 +08:00
|
|
|
ENV["HOMEBREW_SANDBOX"] = "1"
|
2015-08-03 13:09:07 +01:00
|
|
|
ENV["HOMEBREW_NO_EMOJI"] = "1"
|
2015-09-21 15:11:22 +01:00
|
|
|
ENV["HOMEBREW_FAIL_LOG_LINES"] = "150"
|
2016-03-28 10:26:21 -07:00
|
|
|
ENV["HOMEBREW_EXPERIMENTAL_FILTER_FLAGS_ON_DEPS"] = "1"
|
2015-09-23 15:40:27 +08:00
|
|
|
|
|
|
|
if ENV["TRAVIS"]
|
|
|
|
ARGV << "--verbose"
|
2015-09-24 18:42:50 +08:00
|
|
|
ARGV << "--ci-master" if ENV["TRAVIS_PULL_REQUEST"] == "false"
|
2015-09-23 15:40:27 +08:00
|
|
|
ENV["HOMEBREW_VERBOSE_USING_DOTS"] = "1"
|
|
|
|
end
|
2015-09-15 17:32:28 +01:00
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
if ARGV.include?("--ci-master") || ARGV.include?("--ci-pr") \
|
|
|
|
|| ARGV.include?("--ci-testing")
|
2015-11-19 13:29:40 +00:00
|
|
|
ARGV << "--cleanup" if ENV["JENKINS_HOME"]
|
2015-03-20 20:22:35 +00:00
|
|
|
ARGV << "--junit" << "--local"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
2015-08-03 13:09:07 +01:00
|
|
|
if ARGV.include? "--ci-master"
|
2015-11-10 09:29:24 +00:00
|
|
|
ARGV << "--fast"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
if ARGV.include? "--local"
|
2015-08-13 10:55:29 +01:00
|
|
|
ENV["HOMEBREW_HOME"] = ENV["HOME"] = "#{Dir.pwd}/home"
|
2015-08-03 13:09:07 +01:00
|
|
|
mkdir_p ENV["HOME"]
|
|
|
|
ENV["HOMEBREW_LOGS"] = "#{Dir.pwd}/logs"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
2015-09-18 20:33:50 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_bot
|
|
|
|
sanitize_ARGV_and_ENV
|
|
|
|
|
|
|
|
tap = resolve_test_tap
|
2016-04-19 15:30:18 +08:00
|
|
|
# Tap repository if required, this is done before everything else
|
|
|
|
# because Formula parsing and/or git commit hash lookup depends on it.
|
|
|
|
# At the same time, make sure Tap is not a shallow clone.
|
|
|
|
# bottle revision and bottle upload rely on full clone.
|
|
|
|
safe_system "brew", "tap", tap.name, "--full"
|
2015-09-18 20:33:50 +08:00
|
|
|
|
2015-12-11 17:14:51 +08:00
|
|
|
if ARGV.include? "--ci-upload"
|
2015-09-18 20:33:50 +08:00
|
|
|
return test_ci_upload(tap)
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
tests = []
|
|
|
|
any_errors = false
|
2015-09-11 22:44:00 +08:00
|
|
|
skip_homebrew = ARGV.include?("--skip-homebrew")
|
2014-09-20 14:24:52 +01:00
|
|
|
if ARGV.named.empty?
|
|
|
|
# With no arguments just build the most recent commit.
|
2015-09-11 22:44:00 +08:00
|
|
|
head_test = Test.new("HEAD", :tap => tap, :skip_homebrew => skip_homebrew)
|
2015-03-07 15:36:07 +00:00
|
|
|
any_errors = !head_test.run
|
|
|
|
tests << head_test
|
2014-09-20 14:24:52 +01:00
|
|
|
else
|
|
|
|
ARGV.named.each do |argument|
|
2014-10-21 15:34:20 +01:00
|
|
|
test_error = false
|
|
|
|
begin
|
2015-09-11 22:36:27 +08:00
|
|
|
test = Test.new(argument, :tap => tap, :skip_homebrew => skip_homebrew)
|
|
|
|
skip_homebrew = true
|
2014-10-21 15:34:20 +01:00
|
|
|
rescue ArgumentError => e
|
|
|
|
test_error = true
|
|
|
|
ofail e.message
|
2014-11-10 19:24:46 -06:00
|
|
|
else
|
|
|
|
test_error = !test.run
|
2014-11-10 19:25:05 -06:00
|
|
|
tests << test
|
2014-10-21 15:34:20 +01:00
|
|
|
end
|
2014-10-21 12:12:24 +01:00
|
|
|
any_errors ||= test_error
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if ARGV.include? "--junit"
|
|
|
|
xml_document = REXML::Document.new
|
|
|
|
xml_document << REXML::XMLDecl.new
|
2014-12-02 21:14:53 -05:00
|
|
|
testsuites = xml_document.add_element "testsuites"
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
tests.each do |test|
|
2014-12-02 21:14:53 -05:00
|
|
|
testsuite = testsuites.add_element "testsuite"
|
|
|
|
testsuite.add_attribute "name", "brew-test-bot.#{MacOS.cat}"
|
|
|
|
testsuite.add_attribute "tests", test.steps.count
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
test.steps.each do |step|
|
2014-12-02 21:14:53 -05:00
|
|
|
testcase = testsuite.add_element "testcase"
|
|
|
|
testcase.add_attribute "name", step.command_short
|
|
|
|
testcase.add_attribute "status", step.status
|
|
|
|
testcase.add_attribute "time", step.time
|
2014-12-02 21:14:52 -05:00
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
if step.has_output?
|
2016-04-05 17:23:24 -04:00
|
|
|
output = sanitize_output_for_xml(step.output)
|
2014-12-02 21:14:53 -05:00
|
|
|
cdata = REXML::CData.new output
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
if step.passed?
|
2014-12-02 21:14:53 -05:00
|
|
|
elem = testcase.add_element "system-out"
|
2014-09-20 14:24:52 +01:00
|
|
|
else
|
2014-12-02 21:14:53 -05:00
|
|
|
elem = testcase.add_element "failure"
|
|
|
|
elem.add_attribute "message", "#{step.status}: #{step.command.join(" ")}"
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
2014-12-02 21:14:53 -05:00
|
|
|
|
|
|
|
elem << cdata
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
open("brew-test-bot.xml", "w") do |xml_file|
|
|
|
|
pretty_print_indent = 2
|
|
|
|
xml_document.write(xml_file, pretty_print_indent)
|
|
|
|
end
|
|
|
|
end
|
2015-10-15 15:19:12 +08:00
|
|
|
ensure
|
2015-11-04 14:36:44 +08:00
|
|
|
if ARGV.include? "--clean-cache"
|
|
|
|
HOMEBREW_CACHE.children.each(&:rmtree)
|
|
|
|
else
|
|
|
|
Dir.glob("*.bottle*.tar.gz") do |bottle_file|
|
|
|
|
FileUtils.rm_f HOMEBREW_CACHE/bottle_file
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-09-20 14:24:52 +01:00
|
|
|
Homebrew.failed = any_errors
|
|
|
|
end
|
2016-04-05 17:23:24 -04:00
|
|
|
|
|
|
|
def sanitize_output_for_xml(output)
|
|
|
|
unless output.empty?
|
|
|
|
# Remove invalid XML CData characters from step output.
|
|
|
|
if ruby_has_encoding?
|
|
|
|
# This is the regex for valid XML chars, but only works in Ruby 2.0+
|
|
|
|
# /[\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/
|
|
|
|
# For 1.9 compatibility, use the inverse of that, which stays under \u10000
|
|
|
|
# invalid_xml_pat = /[\x00-\x08\x0B\x0C\x0E-\x1F\uD800-\uDFFF\uFFFE\uFFFF]/
|
|
|
|
# But Ruby won't allow you to reference surrogates, so we have:
|
|
|
|
invalid_xml_pat = /[\x00-\x08\x0B\x0C\x0E-\x1F\uFFFE\uFFFF]/
|
|
|
|
output = output.gsub(invalid_xml_pat, "\uFFFD")
|
|
|
|
else
|
|
|
|
output = output.delete("\000\a\b\e\f\x2\x1f")
|
|
|
|
end
|
|
|
|
|
2016-04-06 16:48:07 -04:00
|
|
|
# Truncate to 1MB to avoid hitting CI limits
|
|
|
|
if output.bytesize > MAX_STEP_OUTPUT_SIZE
|
2016-04-05 17:23:24 -04:00
|
|
|
if ruby_has_encoding?
|
|
|
|
binary_output = output.force_encoding("BINARY")
|
2016-04-06 16:48:07 -04:00
|
|
|
output = binary_output.slice(-MAX_STEP_OUTPUT_SIZE, MAX_STEP_OUTPUT_SIZE)
|
2016-04-05 17:23:24 -04:00
|
|
|
fix_encoding!(output)
|
|
|
|
else
|
2016-04-06 16:48:07 -04:00
|
|
|
output = output.slice(-MAX_STEP_OUTPUT_SIZE, MAX_STEP_OUTPUT_SIZE)
|
2016-04-05 17:23:24 -04:00
|
|
|
end
|
|
|
|
output = "truncated output to 1MB:\n" + output
|
|
|
|
end
|
|
|
|
end
|
|
|
|
output
|
|
|
|
end
|
2014-09-20 14:24:52 +01:00
|
|
|
end
|
2016-04-05 17:23:24 -04:00
|
|
|
|