lib/danger/request_source/github.rb in danger-0.8.1 vs lib/danger/request_source/github.rb in danger-0.8.2
- old
+ new
@@ -1,203 +1,227 @@
# coding: utf-8
require 'octokit'
require 'redcarpet'
module Danger
- class GitHub
- attr_accessor :ci_source, :pr_json, :issue_json, :environment, :support_tokenless_auth, :ignored_violations, :github_host
+ module RequestSources
+ class GitHub < RequestSource
+ attr_accessor :pr_json, :issue_json, :support_tokenless_auth
- def initialize(ci_source, environment)
- self.ci_source = ci_source
- self.environment = environment
- self.support_tokenless_auth = false
+ def initialize(ci_source, environment)
+ self.ci_source = ci_source
+ self.environment = environment
+ self.support_tokenless_auth = false
- Octokit.auto_paginate = true
- @token = @environment["DANGER_GITHUB_API_TOKEN"]
- self.github_host = @environment["DANGER_GITHUB_HOST"] || "github.com"
- if @environment["DANGER_GITHUB_API_HOST"]
- Octokit.api_endpoint = @environment["DANGER_GITHUB_API_HOST"]
+ Octokit.auto_paginate = true
+ @token = @environment["DANGER_GITHUB_API_TOKEN"]
+ if @environment["DANGER_GITHUB_API_HOST"]
+ Octokit.api_endpoint = @environment["DANGER_GITHUB_API_HOST"]
+ end
end
- end
- def client
- raise "No API token given, please provide one using `DANGER_GITHUB_API_TOKEN`" if !@token && !support_tokenless_auth
- @client ||= Octokit::Client.new(access_token: @token)
- end
+ def scm
+ @scm ||= GitRepo.new
+ end
- def markdown_parser
- @markdown_parser ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, no_intra_emphasis: true)
- end
+ def host
+ @host = @environment["DANGER_GITHUB_HOST"] || "github.com"
+ end
- def fetch_details
- self.pr_json = client.pull_request(ci_source.repo_slug, ci_source.pull_request_id)
- fetch_issue_details(self.pr_json)
- self.ignored_violations = ignored_violations_from_pr(self.pr_json)
- end
+ def client
+ raise "No API token given, please provide one using `DANGER_GITHUB_API_TOKEN`" if !@token && !support_tokenless_auth
+ @client ||= Octokit::Client.new(access_token: @token)
+ end
- def ignored_violations_from_pr(pr_json)
- pr_body = pr_json[:body]
- return [] if pr_body.nil?
- pr_body.chomp.scan(/>\s*danger\s*:\s*ignore\s*"(.*)"/i).flatten
- end
+ def markdown_parser
+ @markdown_parser ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, no_intra_emphasis: true)
+ end
- def fetch_issue_details(pr_json)
- href = pr_json[:_links][:issue][:href]
- self.issue_json = client.get(href)
- end
+ def setup_danger_branches
+ # we can use a github specific feature here:
+ pull_id = self.ci_source.pull_request_id
+ test_branch = self.pr_json[:base][:sha]
- # Sending data to GitHub
- def update_pull_request!(warnings: [], errors: [], messages: [], markdowns: [])
- comment_result = {}
+ # Next, we want to ensure that we have a version of the current branch at a known location
+ self.scm.exec "branch #{EnvironmentManager.danger_base_branch} #{test_branch}"
- issues = client.issue_comments(ci_source.repo_slug, ci_source.pull_request_id)
- editable_issues = issues.reject { |issue| issue[:body].include?("generated_by_danger") == false }
+ # OK, so we want to ensure that we have a known head branch, this will always represent
+ # the head of the PR ( e.g. the most recent commit that will be merged. )
+ self.scm.exec "fetch origin +refs/pull/#{pull_id}/merge:#{EnvironmentManager.danger_head_branch}"
+ end
- if editable_issues.empty?
- previous_violations = {}
- else
- comment = editable_issues.first[:body]
- previous_violations = parse_comment(comment)
+ def fetch_details
+ self.pr_json = client.pull_request(ci_source.repo_slug, ci_source.pull_request_id)
+ fetch_issue_details(self.pr_json)
+ self.ignored_violations = ignored_violations_from_pr(self.pr_json)
end
- if previous_violations.empty? && (warnings + errors + messages + markdowns).empty?
- # Just remove the comment, if there's nothing to say.
- delete_old_comments!
- else
- body = generate_comment(warnings: warnings,
- errors: errors,
- messages: messages,
- markdowns: markdowns,
- previous_violations: previous_violations)
+ def ignored_violations_from_pr(pr_json)
+ pr_body = pr_json[:body]
+ return [] if pr_body.nil?
+ pr_body.chomp.scan(/>\s*danger\s*:\s*ignore\s*"(.*)"/i).flatten
+ end
+ def fetch_issue_details(pr_json)
+ href = pr_json[:_links][:issue][:href]
+ self.issue_json = client.get(href)
+ end
+
+ # Sending data to GitHub
+ def update_pull_request!(warnings: [], errors: [], messages: [], markdowns: [], danger_id: 'danger')
+ comment_result = {}
+
+ issues = client.issue_comments(ci_source.repo_slug, ci_source.pull_request_id)
+ editable_issues = issues.reject { |issue| issue[:body].include?("generated_by_#{danger_id}") == false }
+
if editable_issues.empty?
- comment_result = client.add_comment(ci_source.repo_slug, ci_source.pull_request_id, body)
+ previous_violations = {}
else
- original_id = editable_issues.first[:id]
- comment_result = client.update_comment(ci_source.repo_slug, original_id, body)
+ comment = editable_issues.first[:body]
+ previous_violations = parse_comment(comment)
end
- end
- # Now, set the pull request status.
- # Note: this can terminate the entire process.
- submit_pull_request_status!(warnings: warnings,
+ if previous_violations.empty? && (warnings + errors + messages + markdowns).empty?
+ # Just remove the comment, if there's nothing to say.
+ delete_old_comments!(danger_id: danger_id)
+ else
+ body = generate_comment(warnings: warnings,
errors: errors,
- details_url: comment_result['html_url'])
- end
+ messages: messages,
+ markdowns: markdowns,
+ previous_violations: previous_violations,
+ danger_id: danger_id)
- def submit_pull_request_status!(warnings: nil, errors: nil, details_url: nil)
- status = (errors.count == 0 ? 'success' : 'failure')
- message = generate_github_description(warnings: warnings, errors: errors)
- client.create_status(ci_source.repo_slug, latest_pr_commit_ref, status, {
- description: message,
- context: "danger/danger",
- target_url: details_url
- })
- rescue
- # This usually means the user has no commit access to this repo
- # That's always the case for open source projects where you can only
- # use a read-only GitHub account
- if errors.count > 0
- # We need to fail the actual build here
- abort("\nDanger has failed this build. \nFound #{'error'.danger_pluralize(errors.count)} and I don't have write access to the PR set a PR status.")
- else
- puts message
+ if editable_issues.empty?
+ comment_result = client.add_comment(ci_source.repo_slug, ci_source.pull_request_id, body)
+ else
+ original_id = editable_issues.first[:id]
+ comment_result = client.update_comment(ci_source.repo_slug, original_id, body)
+ end
+ end
+
+ # Now, set the pull request status.
+ # Note: this can terminate the entire process.
+ submit_pull_request_status!(warnings: warnings,
+ errors: errors,
+ details_url: comment_result['html_url'])
end
- end
- # Get rid of the previously posted comment, to only have the latest one
- def delete_old_comments!(except: nil)
- issues = client.issue_comments(ci_source.repo_slug, ci_source.pull_request_id)
- issues.each do |issue|
- next unless issue[:body].include?("generated_by_danger")
- next if issue[:id] == except
- client.delete_comment(ci_source.repo_slug, issue[:id])
+ def submit_pull_request_status!(warnings: nil, errors: nil, details_url: nil)
+ status = (errors.count == 0 ? 'success' : 'failure')
+ message = generate_github_description(warnings: warnings, errors: errors)
+ client.create_status(ci_source.repo_slug, latest_pr_commit_ref, status, {
+ description: message,
+ context: "danger/danger",
+ target_url: details_url
+ })
+ rescue
+ # This usually means the user has no commit access to this repo
+ # That's always the case for open source projects where you can only
+ # use a read-only GitHub account
+ if errors.count > 0
+ # We need to fail the actual build here
+ abort("\nDanger has failed this build. \nFound #{'error'.danger_pluralize(errors.count)} and I don't have write access to the PR set a PR status.")
+ else
+ puts message
+ end
end
- end
- def random_compliment
- compliment = ["Well done.", "Congrats.", "Woo!",
- "Yay.", "Jolly good show.", "Good on 'ya.", "Nice work."]
- compliment.sample
- end
+ # Get rid of the previously posted comment, to only have the latest one
+ def delete_old_comments!(except: nil, danger_id: 'danger')
+ issues = client.issue_comments(ci_source.repo_slug, ci_source.pull_request_id)
+ issues.each do |issue|
+ next unless issue[:body].include?("generated_by_#{danger_id}")
+ next if issue[:id] == except
+ client.delete_comment(ci_source.repo_slug, issue[:id])
+ end
+ end
- def generate_github_description(warnings: nil, errors: nil)
- if errors.empty? && warnings.empty?
- return "All green. #{random_compliment}"
- else
- message = "⚠ "
- message += "#{'Error'.danger_pluralize(errors.count)}. " unless errors.empty?
- message += "#{'Warning'.danger_pluralize(warnings.count)}. " unless warnings.empty?
- message += "Don't worry, everything is fixable."
- return message
+ def random_compliment
+ compliment = ["Well done.", "Congrats.", "Woo!",
+ "Yay.", "Jolly good show.", "Good on 'ya.", "Nice work."]
+ compliment.sample
end
- end
- def generate_comment(warnings: [], errors: [], messages: [], markdowns: [], previous_violations: {})
- require 'erb'
+ def generate_github_description(warnings: nil, errors: nil)
+ if errors.empty? && warnings.empty?
+ return "All green. #{random_compliment}"
+ else
+ message = "⚠ "
+ message += "#{'Error'.danger_pluralize(errors.count)}. " unless errors.empty?
+ message += "#{'Warning'.danger_pluralize(warnings.count)}. " unless warnings.empty?
+ message += "Don't worry, everything is fixable."
+ return message
+ end
+ end
- md_template = File.join(Danger.gem_path, "lib/danger/comment_generators/github.md.erb")
+ def generate_comment(warnings: [], errors: [], messages: [], markdowns: [], previous_violations: {}, danger_id: 'danger')
+ require 'erb'
- # erb: http://www.rrn.dk/rubys-erb-templating-system
- # for the extra args: http://stackoverflow.com/questions/4632879/erb-template-removing-the-trailing-line
- @tables = [
- table("Error", "no_entry_sign", errors, previous_violations),
- table("Warning", "warning", warnings, previous_violations),
- table("Message", "book", messages, previous_violations)
- ]
- @markdowns = markdowns
+ md_template = File.join(Danger.gem_path, "lib/danger/comment_generators/github.md.erb")
- return ERB.new(File.read(md_template), 0, "-").result(binding)
- end
+ # erb: http://www.rrn.dk/rubys-erb-templating-system
+ # for the extra args: http://stackoverflow.com/questions/4632879/erb-template-removing-the-trailing-line
+ @tables = [
+ table("Error", "no_entry_sign", errors, previous_violations),
+ table("Warning", "warning", warnings, previous_violations),
+ table("Message", "book", messages, previous_violations)
+ ]
+ @markdowns = markdowns
+ @danger_id = danger_id
- def table(name, emoji, violations, all_previous_violations)
- content = violations.map { |v| process_markdown(v) }
- kind = table_kind_from_title(name)
- previous_violations = all_previous_violations[kind] || []
- messages = content.map(&:message)
- resolved_violations = previous_violations.reject { |s| messages.include? s }
- count = content.count
- { name: name, emoji: emoji, content: content, resolved: resolved_violations, count: count }
- end
+ return ERB.new(File.read(md_template), 0, "-").result(binding)
+ end
- def parse_comment(comment)
- tables = parse_tables_from_comment(comment)
- violations = {}
- tables.each do |table|
- next unless table =~ %r{<th width="100%"(.*?)</th>}im
- title = Regexp.last_match(1)
- kind = table_kind_from_title(title)
- next unless kind
-
- violations[kind] = violations_from_table(table)
+ def table(name, emoji, violations, all_previous_violations)
+ content = violations.map { |v| process_markdown(v) }.uniq
+ kind = table_kind_from_title(name)
+ previous_violations = all_previous_violations[kind] || []
+ messages = content.map(&:message)
+ resolved_violations = previous_violations.uniq - messages
+ count = content.count
+ { name: name, emoji: emoji, content: content, resolved: resolved_violations, count: count }
end
- violations.reject { |_, v| v.empty? }
- end
+ def parse_comment(comment)
+ tables = parse_tables_from_comment(comment)
+ violations = {}
+ tables.each do |table|
+ next unless table =~ %r{<th width="100%"(.*?)</th>}im
+ title = Regexp.last_match(1)
+ kind = table_kind_from_title(title)
+ next unless kind
- def violations_from_table(table)
- regex = %r{<td data-sticky="true">(?:<del>)?(.*?)(?:</del>)?\s*</td>}im
- table.scan(regex).flatten.map(&:strip)
- end
+ violations[kind] = violations_from_table(table)
+ end
- def table_kind_from_title(title)
- if title =~ /error/i
- :error
- elsif title =~ /warning/i
- :warning
- elsif title =~ /message/i
- :message
+ violations.reject { |_, v| v.empty? }
end
- end
- def parse_tables_from_comment(comment)
- comment.split('</table>')
- end
+ def violations_from_table(table)
+ regex = %r{<td data-sticky="true">(?:<del>)?(.*?)(?:</del>)?\s*</td>}im
+ table.scan(regex).flatten.map(&:strip)
+ end
- def process_markdown(violation)
- html = markdown_parser.render(violation.message)
- match = html.match(%r{^<p>(.*)</p>$})
- message = match.nil? ? html : match.captures.first
- Violation.new(message, violation.sticky)
+ def table_kind_from_title(title)
+ if title =~ /error/i
+ :error
+ elsif title =~ /warning/i
+ :warning
+ elsif title =~ /message/i
+ :message
+ end
+ end
+
+ def parse_tables_from_comment(comment)
+ comment.split('</table>')
+ end
+
+ def process_markdown(violation)
+ html = markdown_parser.render(violation.message)
+ match = html.match(%r{^<p>(.*)</p>$})
+ message = match.nil? ? html : match.captures.first
+ Violation.new(message, violation.sticky)
+ end
end
end
end