Skip to content

Instantly share code, notes, and snippets.

@tomraithel
Forked from evant/code-review
Last active August 2, 2017 11:52
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 tomraithel/79f535e6bd21e7f821caf338f46ae7be to your computer and use it in GitHub Desktop.
Save tomraithel/79f535e6bd21e7f821caf338f46ae7be to your computer and use it in GitHub Desktop.
A ruby script to automatically create crucible reviews
#!/usr/bin/env ruby
# A script to create a review on crucible based on the current git branch
#
# To configure settings, create a file named .code-review in your home directory
# The format should be:
# ------------------------------------------------------------------------------
# crucible:
# url: <crucible url>
# username: <username>
# password: <password>
#
# projects:
# <project name>: # The name of the directory your project is in
# key: <key> # Project key found in crucible
# repo: <repository> # Project repository found in crucible
# reviewers: # The usernames of the reviewers
# - <reviewer1>
# - <reviewer2>
require 'net/http'
require 'json'
require 'rexml/document'
require 'yaml'
require 'pathname'
config = YAML.load_file(File.expand_path("~/.code-review"))
project_name = nil
Pathname.new(File.expand_path(".")).ascend do |path|
if path.children.any? { |file| file.basename.to_s == ".git" }
project_name = path.to_s
break
end
end
unless project_name
warn "Not a git repository (or any of the parent directories)"
exit 1
end
crucible = config['crucible']
project = config['projects'][project_name]
unless project
warn "no configuation found for project: #{project_name}"
exit 1
end
crucible_url = crucible['url']
crucible_user_name = crucible['username']
crucible_password = crucible['password']
project_key = project['key']
repository = project['repo']
reviewers = project['reviewers']
# get info about the current branch from git
display_name = `git config user.name`
id = `git rev-parse --short HEAD`.strip
branch = `git rev-parse --abbrev-ref HEAD`.strip
commit_abbrev = `git log -1 --pretty=%B --abbrev-commit`.strip
commit = `git log -1 --pretty=%B`
def description(branch, commit)
res = commit
res
end
def request(req)
res = Net::HTTP.start(req.uri.host, req.uri.port, :use_ssl => true) do |http|
http.request req
end
unless res.is_a?(Net::HTTPSuccess)
warn(if block_given? then yield(res) else res.body end)
exit 1
end
res
end
def request_json(req, crucible_user_name, crucible_password, &block)
req.basic_auth crucible_user_name, crucible_password
req["Content-Type"] = "application/json"
req["Accept"] = "application/json"
request(req) do |res|
if block then yield(res) else JSON.parse(res.body)["message"] end
end
end
def delete_review(crucible_url, review_id, crucible_user_name, crucible_password)
# first we must move the review to abandoned
uri = URI("#{crucible_url}/rest-service/reviews-v1/#{review_id}/transition?action=action:abandonReview")
req = Net::HTTP::Post.new(uri)
request_json(req, crucible_user_name, crucible_password)
# now we delete the review
uri = URI("#{crucible_url}/rest-service/reviews-v1/#{review_id}")
req = Net::HTTP::Delete.new(uri)
request_json(req, crucible_user_name, crucible_password)
end
# trap Ctrl-C so we delete the review if it has been created
review_id = nil
deleting = false
trap("SIGINT") {
break if deleting
if review_id
deleting = true
puts "Deleting review..."
delete_review(crucible_url, review_id, crucible_user_name, crucible_password)
exit 0
else
exit 1
end
}
# create a new review
uri = URI("#{crucible_url}/rest-service/reviews-v1")
req = Net::HTTP::Post.new(uri)
req.body = {
reviewData: {
author: {
displayName: display_name,
userName: crucible_user_name
},
name: "#{branch}: #{commit_abbrev}",
description: description(branch, commit),
projectKey: project_key,
state: "Review",
allowReviewersToJoin: true
}
}.to_json
res = request_json(req, crucible_user_name, crucible_password)
review_id = JSON.parse(res.body)["permaId"]["id"]
# now add the changeset (this request doesn't work with json for some reason)
def add_changeset(crucible_url, review_id, repository, id, crucible_user_name, crucible_password)
uri = URI("#{crucible_url}/rest-service/reviews-v1/#{review_id}/addChangeset")
req = Net::HTTP::Post.new(uri)
req.basic_auth crucible_user_name, crucible_password
req["Content-Type"] = "application/xml"
req["Accept"] = "application/xml"
req.body = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>
<addChangeset>
<repository>#{repository}</repository>
<changesets>
<changesetData>
<id>#{id}</id>
</changesetData>
</changesets>
</addChangeset>"
res = Net::HTTP.start(req.uri.host, req.uri.port, :use_ssl => true) do |http|
http.request req
end
unless res.is_a?(Net::HTTPSuccess)
error = REXML::Document.new(res.body).elements['error']
if error.elements['code'].first == 'IllegalArgument'
warn "Failed to add changeset: #{id}, retrying..."
sleep 3 # sleep a bit so we aren't pinging server too much
add_changeset(crucible_url, review_id, repository, id, crucible_user_name, crucible_password)
else
error.elements['message'].first
end
end
end
add_changeset(crucible_url, review_id, repository, id, crucible_user_name, crucible_password)
puts "Created review: #{crucible_url}/cru/#{review_id}"
# add reviews to the review if they exist
if !reviewers.nil? && !reviewers.empty?
uri = URI("#{crucible_url}/rest-service/reviews-v1/#{review_id}/reviewers")
req = Net::HTTP::Post.new(uri)
req.body = reviewers.join(',')
request_json(req, crucible_user_name, crucible_password)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment