require "danger/plugin_support/plugin" module Danger # Handles interacting with GitLab inside a Dangerfile. Provides a few functions which wrap `mr_json` and also # through a few standard functions to simplify your code. # # @example Warn when an MR is classed as work in progress. # # warn "MR is classed as Work in Progress" if gitlab.mr_title.include? "[WIP]" # # @example Declare a MR to be simple to avoid specific Danger rules. # # declared_trivial = (gitlab.mr_title + gitlab.mr_body).include?("#trivial") # # @example Ensure that labels have been applied to the MR. # # failure "Please add labels to this MR" if gitlab.mr_labels.empty? # # @example Ensure that all MRs have an assignee. # # warn "This MR does not have any assignees yet." unless gitlab.mr_json["assignee"] # # @example Ensure there is a summary for a MR. # # failure "Please provide a summary in the Merge Request description" if gitlab.mr_body.length < 5 # # @example Only accept MRs to the develop branch. # # failure "Please re-submit this MR to develop, we may have already fixed your issue." if gitlab.branch_for_merge != "develop" # # @example Note when MRs don't reference a milestone, make the warning stick around on subsequent runs # # has_milestone = gitlab.mr_json["milestone"] != nil # warn("This MR does not refer to an existing milestone", sticky: true) unless has_milestone # # @example Note when a MR cannot be manually merged # # can_merge = gitlab.mr_json["mergeable"] # warn("This MR cannot be merged yet.") unless can_merge # # @example Highlight when a celebrity makes a merge request. # # message "Welcome, Danger." if gitlab.mr_author == "dangermcshane" # # @example Send a message with links to a collection of specific files. # # if git.modified_files.include? "config/*.js" # config_files = git.modified_files.select { |path| path.include? "config/" } # message "This MR changes #{ gitlab.html_link(config_files) }" # end # # @example Highlight with a clickable link if a Package.json is changed. # # warn "#{gitlab.html_link("Package.json")} was edited." if git.modified_files.include? "Package.json" # # @example Select a random group member as assignee if no assignee is selected # # if gitlab.mr_json["assignee"].nil? # reviewer = gitlab.api.group_members(gitlab.api.merge_request_approvals(project_id, mr_id).to_hash["approver_groups"].first["group"]["id"]).sample # if gitlab.api.group_members(gitlab.api.merge_request_approvals(project_id, mr_id).to_hash["approver_groups"].first["group"]["id"]).length > 1 # while reviewer.to_hash["id"] == gitlab.mr_json["author"]["id"] do # reviewer = gitlab.api.group_members(gitlab.api.merge_request_approvals(project_id, mr_id).to_hash["approver_groups"].first["group"]["id"]).sample # end # end # message "Reviewer roulete rolled for: #{reviewer.to_hash['name']} (@#{reviewer.to_hash['username']})" # gitlab.api.update_merge_request(project_id, mr_id, { assignee_id: reviewer.to_hash["id"] }) # end # # # @see danger/danger # @tags core, gitlab # class DangerfileGitLabPlugin < Plugin # So that this init can fail. def self.new(dangerfile) return nil if dangerfile.env.request_source.class != Danger::RequestSources::GitLab super end # The instance name used in the Dangerfile # @return [String] # def self.instance_name "gitlab" end def initialize(dangerfile) super(dangerfile) @gitlab = dangerfile.env.request_source end # @!group MR Metadata # The title of the Merge Request # @return [String] # def mr_title @gitlab.mr_json.title.to_s end # @!group MR Metadata # The body text of the Merge Request # @return [String] # def mr_body @gitlab.mr_json.description.to_s end # @!group MR Metadata # The username of the author of the Merge Request # @return [String] # def mr_author @gitlab.mr_json.author.username.to_s end # @!group MR Metadata # The labels assigned to the Merge Request # @return [String] # def mr_labels @gitlab.mr_json.labels end # @!group MR Content # The unified diff produced by GitLab for this MR # see [Unified diff](https://en.wikipedia.org/wiki/Diff_utility#Unified_format) # @return [String] # def mr_diff @gitlab.mr_diff end # @!group MR Changes # The array of changes # @return [Array] # def mr_changes @gitlab.mr_changes.changes end # @!group MR Closes issues # The array of issues that this MR closes # @return [Array] # def mr_closes_issues @gitlab.mr_closes_issues end # @!group MR Commit Metadata # The branch to which the MR is going to be merged into # @deprecated Please use {#branch_for_base} instead # @return [String] # def branch_for_merge branch_for_base end # @!group MR Commit Metadata # The branch to which the MR is going to be merged into. # @return [String] # def branch_for_base @gitlab.mr_json.target_branch end # @!group MR Commit Metadata # The branch to which the MR is going to be merged from. # @return [String] # def branch_for_head @gitlab.mr_json.source_branch end # @!group MR Commit Metadata # The base commit to which the MR is going to be merged as a parent # @return [String] # def base_commit @gitlab.mr_json.diff_refs.base_sha end # @!group MR Commit Metadata # The head commit to which the MR is requesting to be merged from # @return [String] # def head_commit @gitlab.mr_json.diff_refs.head_sha end # @!group GitLab Misc # The hash that represents the MR's JSON. See documentation for the # structure [here](http://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr) # @return [Hash] # def mr_json @gitlab.mr_json.to_hash end # @!group GitLab Misc # Provides access to the GitLab API client used inside Danger. Making # it easy to use the GitLab API inside a Dangerfile. See the gitlab # gem's [documentation](http://www.rubydoc.info/gems/gitlab/Gitlab/Client) # for accessible methods. # @return [GitLab::Client] # def api @gitlab.client end # @!group GitLab Misc # Returns the web_url of the source project. # @return [String] # def repository_web_url @repository_web_url ||= begin project = api.project(mr_json["source_project_id"]) project.web_url end end # @!group GitLab Misc # Returns a list of HTML anchors for a file, or files in the head repository. An example would be: # `file.txt`. It returns a string of multiple anchors if passed an array. # @param [String or Array] paths # A list of strings to convert to gitlab anchors # @param [Bool] full_path # Shows the full path as the link's text, defaults to `true`. # # @return [String] # def html_link(paths, full_path: true) paths = [paths] unless paths.kind_of?(Array) commit = head_commit paths = paths.map do |path| url_path = path.start_with?("/") ? path : "/#{path}" text = full_path ? path : File.basename(path) create_link("#{repository_web_url}/blob/#{commit}#{url_path}", text) end return paths.first if paths.count < 2 paths.first(paths.count - 1).join(", ") + " & " + paths.last end # @!group Gitlab Misc # Use to ignore inline messages which lay outside a diff's range, thereby not posting the comment. # You can set hash to change behavior per each kinds. (ex. `{warning: true, error: false}`) # @param [Bool or Hash] dismiss # Ignore out of range inline messages, defaults to `true` # # @return [void] def dismiss_out_of_range_messages(dismiss = true) if dismiss.kind_of?(Hash) @gitlab.dismiss_out_of_range_messages = dismiss elsif dismiss.kind_of?(TrueClass) @gitlab.dismiss_out_of_range_messages = true elsif dismiss.kind_of?(FalseClass) @gitlab.dismiss_out_of_range_messages = false end end %i(title body author labels json diff).each do |suffix| alias_method "pr_#{suffix}".to_sym, "mr_#{suffix}".to_sym end private def create_link(href, text) "#{text}" end end end