You push your latest feature branch to Github, CircleCI runs your specs and some of them fail.
Typically, you could open the build in CircleCI and inspect the failing specs to determine what went wrong.
What if you could rerun the specs locally with a single command, to inspect the failures from the comfort of your favorite IDE?
bin/rails my:ci_rerun RAILS_ENV=test to the rescue!
lib/tasks/my.rake file in the project, which contains my personal rake tasks.
TIP: Exclude
my.rakefrom git via your.gitignore_global
project_slug method to match your project.# lib/tasks/my.rake
namespace :my do
task ci_rerun: :environment do
pipeline_id = latest_pipeline_by_branch['id']
workflow_id = fetch_workflow_id(pipeline_id)
job_number = fetch_job_number(workflow_id)
failed_tests = extract_failed_tests(fetch_job_details(job_number))
run_failed_tests(failed_tests)
end
def build_branch(pipeline_id)
api_call("pipeline/#{pipeline_id}")['vcs']['branch']
end
def api_call(relative_url)
uri = URI("https://circleci.com/api/v2/#{relative_url}")
req = Net::HTTP::Get.new(uri)
req['Circle-Token'] = ENV.fetch('DEVELOPER_CIRCLE_CI_API_TOKEN')
result = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req)
end
raise "Failed to fetch data for #{relative_url}" unless result.is_a?(Net::HTTPSuccess)
JSON.parse(result.body)
end
def fetch_job_number(workflow_id)
job = api_call("workflow/#{workflow_id}/job")['items'].detect do |job|
job['name'] == 'test'
end
raise 'No job found' unless job
puts "Job Number: #{job['job_number']}"
job['job_number']
end
def extract_failed_tests(job_details)
return [] unless job_details['items']
job_details['items']
.select { |item| item['result'] == 'failure' }
.map { |item| { file: item['file'], name: item['name'] } }
.uniq
end
def fetch_job_details(job_number)
api_call("project/#{project_slug}/#{job_number}/tests")
end
def latest_pipeline_by_branch
pipeline = api_call("project/#{project_slug}/pipeline?branch=#{local_branch_name}")['items'].first
raise "No pipeline found for branch #{local_branch_name}" if pipeline.blank?
puts "Pipeline ID: #{pipeline['id']}"
pipeline
end
def local_branch_name
`git rev-parse --abbrev-ref HEAD`.strip
end
def project_slug
'github/Organization/Project' # Update this to match your project
end
def run_failed_tests(failed_tests)
return if failed_tests.empty?
test_commands = failed_tests.map do |test|
"#{test[:file]} --example \"#{test[:name]}\""
end.join(' ')
system("rspec #{test_commands}")
end
def fetch_workflow_id(pipeline_id)
workflow = api_call("pipeline/#{pipeline_id}/workflow")['items'].first
puts "Workflow ID: #{workflow['id']}"
workflow['id']
end
end
Make sure you set the following environment variable:
DEVELOPER_CIRCLE_CI_API_TOKEN, which you can find in your CircleCI account.Then, run the following command:
bin/rails my:ci_rerun RAILS_ENV=test
The script will fetch the latest pipeline_id for your current branch, find the failed job, extract the failed tests, and rerun them locally.