Skip to content

Instantly share code, notes, and snippets.

@grosser
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grosser/df68f5461d45601f37f0 to your computer and use it in GitHub Desktop.
Save grosser/df68f5461d45601f37f0 to your computer and use it in GitHub Desktop.
Analyze travis failures from forking_test_runner across all jobs in the build
#!/usr/bin/env ruby
#
# show travis build failures for given build id/url, branch or current branch
# streams logs and shows errors as they happen
#
def usage
puts <<-TEXT.gsub(/^ /, "")
Setup
-----
gem install travis ruby-progressbar
travis login --pro
# create a new token at https://github.com/settings/tokens/new with repo access
git config github.token NEW_TOKEN --local
Usage
-----
#{$0} # show latest build on current branch
#{$0} <build-id or build-url> # show build
#{$0} branch-name # show build on this branch
TEXT
exit 1
end
raise "cannot be used with bundle exec" if defined?(Bundler)
begin
gem "travis"
require "travis"
gem "ruby-progressbar"
require "ruby-progressbar"
rescue LoadError
usage
end
def built_branches
YAML.load_file('.travis.yml').fetch('branches').fetch('only')
end
def find_build_for_branch(slug, branch)
if built_branches.include?(branch)
Travis::Pro::Repository.find(slug).last_on_branch(branch)
else
find_build_via_pr(slug, branch)
end
end
def find_build_via_pr(slug, branch)
# fetch PR number for current branch from github
github_token = `git config github.token`.strip
usage if github_token.empty?
github_url = "https://api.github.com/repos/#{slug}/pulls?state=open&head=#{slug.split("/").first}:#{branch}"
github_response = `curl --silent -H "Authorization: token #{github_token}" '#{github_url}'`
pr_number = github_response[/"number": (\d+),/, 1] || raise("Could not find PR number")
# find the build with that PR and show some progress
puts "Looking for PR #{pr_number}"
Travis::Pro::Repository.find(slug).each_build do |b|
if b.pull_request_number == pr_number.to_i
print "\n"
return b
else
print "."
end
end
raise "Could not find a build for PR"
end
# use static estimate since real estimate is inaccurate
# - startup is very slow so estimate is too big
# - when starting mid-build estimate is too low
ProgressBar::Components::Time.class_eval do
def estimated_seconds_remaining
remaining = (ESTIMATED_BUILD_TIME / progress.total) * (progress.total - progress.progress)
remaining.round
end
end
ESTIMATED_BUILD_TIME = 15*60.0
slug = "my_org/my_repo"
base_url = "https://something.travis-ci.com/#{slug}"
travis_config = File.expand_path("~/.travis/config.yml")
usage unless File.exist?(travis_config)
Travis::Pro.access_token = YAML.load_file(travis_config).fetch("endpoints").fetch(Travis::Client::PRO_URI).fetch("access_token")
current_branch = `git rev-parse --abbrev-ref HEAD`.strip
build = if ARGV[0] =~ /\A(\d+)\z|\Ahttp.*\/(\d+)\Z/ # id or full url
Travis::Pro::Build.find($1 || $2)
elsif ARGV[0] =~ /\A[a-z\d_\/-]+\z/ # branch name
find_build_for_branch(slug, ARGV[0])
else
find_build_for_branch(slug, current_branch)
end
puts "Analyzing #{current_branch} build #{base_url}/builds/#{build.id}"
progressbar = ProgressBar.create(title: "Tests", total: Dir["test/**/*_test.rb"].size, format: '%t %p%% (%c/%C) |%e | %a | %B')
failed_tests = []
build.jobs.map do |job|
Thread.new do
reported = []
collected = ""
begin
job.log.body do |chunk|
collected << chunk
failed = collected.scan(/^------ <<< (\S+) ---- Failed/m).map(&:first)
finished = collected.scan(/^------ >>> (\S+)/).map(&:first)
failed -= reported
finished -= reported
reported += finished
Thread.exclusive do
failed.each do |failure|
failed_tests << failure
puts collected[/^------ >>> #{failure}.*?------ <<< #{failure} ---- Failed/m]
end
finished.each { progressbar.increment unless progressbar.finished? }
end
end
rescue Travis::Client::SSLError, Errno::ECONNRESET
# SSL error fetching log for job for whatever reason some of the logs cannot be fetched ...
rescue NoMethodError
# pusher blows up with NoMethodError on every stream end when streaming (job was live and then finishes)
# https://github.com/travis-ci/travis.rb/issues/198
end
end
end.each(&:join)
progressbar.finish
failed_jobs = build.jobs.reject(&:passed?)
if failed_jobs.any?
puts "\nFailed jobs:"
failed_jobs.each do |job|
puts "#{base_url}/jobs/#{job.id}"
end
if failed_tests.any?
puts "\nFailed tests:"
puts failed_tests
end
exit 1
else
puts "SUCCESS!"
exit 0
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment