class Sppuppet
Constants
- BLOCK_VOTE
- DELETE_BRANCH_COMMENT
- INCIDENT
- MERGE_COMMENT
Match regexps
- MINUS_VOTE
- PLUS_VOTE
Public Class Methods
new(settings, client, project, data, event)
click to toggle source
# File lib/tutter/action/sppuppet.rb, line 13 def initialize(settings, client, project, data, event) @settings = settings @settings['plus_ones_required'] ||= 1 @settings['owner_plus_ones_required'] ||= 0 @settings['owners'] ||= [] @delete_branch = @settings['chop_on_merge'] ||= false @client = client @project = project @data = data @event = event end
Public Instance Methods
maybe_merge(pull_request_id, merge_command, merger = nil)
click to toggle source
# File lib/tutter/action/sppuppet.rb, line 76 def maybe_merge(pull_request_id, merge_command, merger = nil) owner_votes = {} votes = {} incident_merge_override = false pr = @client.pull_request @project, pull_request_id # We fetch the latest commit and it's date. last_commit = @client.pull_request_commits(@project, pull_request_id).last last_commit_date = last_commit.commit.committer.date comments = @client.issue_comments(@project, pull_request_id) # Check each comment for +1 and merge comments comments.each do |i| # Comment is older than last commit. # We only want to check newer comments next if last_commit_date > i.created_at commenter = i.attrs[:user].attrs[:login] # Skip comments from tutter itself next if commenter == @client.user.login if MERGE_COMMENT.match(i.body) merger ||= commenter # Count as a +1 if it is not the author unless pr.user.login == commenter votes[commenter] = 1 if @settings['owners'].include?(commenter) owner_votes[commenter] = 1 end end end if DELETE_BRANCH_COMMENT.match(i.body) @delete_branch = true end if PLUS_VOTE.match(i.body) && pr.user.login != commenter votes[commenter] = 1 if @settings['owners'].include?(commenter) owner_votes[commenter] = 1 end end if MINUS_VOTE.match(i.body) && pr.user.login != commenter votes[commenter] = -1 if @settings['owners'].include?(commenter) owner_votes[commenter] = -1 end end if BLOCK_VOTE.match(i.body) msg = 'Commit cannot be merged so long as a -2 comment appears in the PR.' return post_comment(pull_request_id, msg) end if INCIDENT.match(i.body) incident_merge_override = true end end if pr.mergeable_state != 'clean' && pr.mergeable_state != 'has_hooks' && !incident_merge_override msg = "Merge state is not clean. Current state: #{pr.mergeable_state}\n" reassure = "I will try to merge this for you when the build turn green\n" + "If your build fails or becomes stuck for some reason, just say 'rebuild'\n" + "If you have an incident and want to skip the tests or the peer review, please post the link to the jira ticket.\n\n" + 'If the pr is already merged according to github, you can ignore this message.' if merge_command return post_comment(pull_request_id, msg + reassure) else return 200, msg end end return 200, 'No merge comment found' unless merger num_votes = votes.values.reduce(0) { |a, e| a + e } if num_votes < @settings['plus_ones_required'] && !incident_merge_override msg = "Not enough plus ones. #{@settings['plus_ones_required']} required, and only have #{num_votes}" return post_comment(pull_request_id, msg) end num_owner_votes = owner_votes.values.reduce(0) { |a, e| a + e } if num_owner_votes < @settings['owner_plus_ones_required'] && !incident_merge_override msg = "Not enough plus ones from owners. #{@settings['owner_plus_ones_required']} required, and only have #{num_owner_votes}" return post_comment(pull_request_id, msg) end # TODO: Word wrap description merge_msg = <<MERGE_MSG Title: #{pr.title} Opened by: #{pr.user.login} Reviewers: #{votes.keys.join ', '} Deployer: #{merger} URL: #{pr.url} Tests: #{@client.combined_status(@project, pr.head.sha).statuses.map { |s| [s.state, s.description, s.target_url].join(", ") }.join("\n ")} #{pr.body} MERGE_MSG if incident_merge_override @client.add_labels_to_an_issue @project, pull_request_id, ['incident'] end begin merge_commit = @client.merge_pull_request(@project, pull_request_id, merge_msg) # If a owner posted a chop comment and was successfully merged delete the branch ref @client.delete_branch(pr.head.repo.full_name, pr.head.ref) if @delete_branch rescue Octokit::MethodNotAllowed => e return post_comment(pull_request_id, "Pull request not mergeable: #{e.message}") end return 200, "merging #{pull_request_id} #{@project}" end
post_comment(issue, comment)
click to toggle source
# File lib/tutter/action/sppuppet.rb, line 188 def post_comment(issue, comment) begin @client.add_comment(@project, issue, comment) return 200, "Commented:\n" + comment rescue Octokit::NotFound return 404, 'Octokit returned 404, this could be an issue with your access token' rescue Octokit::Unauthorized return 401, "Authorization to #{@project} failed, please verify your access token" rescue Octokit::TooManyLoginAttempts return 429, "Account for #{@project} has been temporary locked down due to to many failed login attempts" end end
run()
click to toggle source
# File lib/tutter/action/sppuppet.rb, line 25 def run case @event when 'issue_comment' if @data['action'] != 'created' # Not a new comment, ignore return 200, 'not a new comment, skipping' end if @data['sender']['login'] == @client.user.login return 200, 'Skipping own comment' end pull_request_id = @data['issue']['number'] merge_command = MERGE_COMMENT.match(@data['comment']['body']) return 200, 'Not a merge comment' unless merge_command return maybe_merge(pull_request_id, true, @data['sender']['login']) when 'status' return 200, 'Merge state not clean' unless @data['state'] == 'success' commit_sha = @data['commit']['sha'] @client.pull_requests(@project).each do |pr| return maybe_merge(pr.number, false) if pr.head.sha == commit_sha end return 200, "Found no pull requests matching #{commit_sha}" when 'pull_request' # If a new pull request is opened, comment with instructions if @data['action'] == 'opened' && @settings['post_instructions'] issue = @data['number'] if @settings['owner_plus_ones_required'] > 0 owners_required_text = " and at least #{@settings['owner_plus_ones_required']} of the owners " else owners_required_text = "" end instructions_text = "To merge at least #{@settings['plus_ones_required']} person other than " + "the submitter #{owners_required_text}needs to write a comment containing only _+1_ or :+1:.\n" + "Then write _!merge_ or :shipit: to trigger merging.\n" + "Also write :scissors: and tutter will clean up by deleting your branch after merge." comment = @settings['instructions'] || instructions_text return post_comment(issue, comment) else return 200, 'Not posting instructions' end else return 200, "Unhandled event type #{@event}" end end