Class: Danger::DangerYajp

Inherits:
Plugin
  • Object
show all
Defined in:
lib/yajp/plugin.rb

Overview

Yet Another Jira Plugin (in short: yajp) provides methods to easily find and manipulate issues from within the Dangerfile. The major difference with the existing Jira plugins is the ability to transition and update issues with the same feeling as manipulating PR data from Danger. This plugin was build in the same mind as Danger, meaning that you will find methods to easily manipulate Jira data, but no predefined warning and/or message. Like Danger, it requires environment variables to work:

* DANGER_JIRA_URL: the URL of the Jira server (ex: `https://jira.company.com/jira`)
* DANGER_JIRA_USER: the Jira user that will use the Jira API
* DANGER_JIRA_API_TOKEN: the token associated to the user (Jira Cloud) or the password of the user (Jira Server)

Examples:

Full example of a Dangerfile

issues = jira.find_issues('KEY')

if issues.empty?
  warn 'This PR does not contain any Jira issue.'
else
  issues.each do |issue|
    message "<a href='#{issue.link}'>#{issue.key} - #{issue.summary}</a>"

    case issue.status.name
    when 'In Progress'
      issue.transition(10, assignee: { name: 'username' }, customfield_11005: 'example')
    when 'To Do', 'Blocked'
      warn "Issue <a href='#{issue.link}'>#{issue.key}</a> is not in Dev status, please make sure the issue you're working on is in the correct status"
    end
  end
end

Access the Jira client of jira-ruby and list all Jira projects

jira.api.Project.all

See Also:

  • juliendms/danger-yajp

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dangerfile) ⇒ DangerYajp



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/yajp/plugin.rb', line 45

def initialize(dangerfile)
  throw Error('The environment variable DANGER_JIRA_URL is required') if ENV['DANGER_JIRA_URL'].nil?

  super
  url_parser = %r{(?<site>https?://[^/]+)(?<context_path>/.+)}.match(ENV.fetch('DANGER_JIRA_URL', nil))

  options = {
    username:       ENV.fetch('DANGER_JIRA_USER', nil),
    password:       ENV.fetch('DANGER_JIRA_API_TOKEN', nil),
    site:           url_parser[:site],
    context_path:   url_parser[:context_path],
    auth_type:      :basic
  }

  @api = JIRA::Client.new(options)
end

Instance Attribute Details

#apiJIRA::Client (readonly)

Give access to the Jira API via jira-ruby client.



43
44
45
# File 'lib/yajp/plugin.rb', line 43

def api
  @api
end

Class Method Details

.instance_nameObject



62
63
64
# File 'lib/yajp/plugin.rb', line 62

def self.instance_name
  return 'jira'
end

Instance Method Details

#find_issues(key, search_title: true, search_commits: false, search_branch: false) ⇒ Array<JIRA::Resource::Issue>

Find Jira issues (keys) in the specified parameters of the PR.

Examples:

Find issues in project KEY from the name of the PR branch

jira.find_issues('KEY', search_title: false, search_branch: true)


78
79
80
81
82
83
84
85
86
87
88
# File 'lib/yajp/plugin.rb', line 78

def find_issues(key, search_title: true, search_commits: false, search_branch: false)
  regexp = build_regexp_from_key(key)
  jira_issues = []

  jira_issues.concat(search_title(regexp)) if search_title
  jira_issues.concat(search_commits(regexp)) if search_commits
  jira_issues.concat(search_branch(regexp)) if search_branch
  jira_issues.concat(search_pr_body(regexp)) if jira_issues.empty?

  @issues = jira_issues.uniq(&:downcase).map { |issue_key| @api.Issue.find(issue_key) }
end

Add a remote link to the PR in the given Jira issues. It uses the link of the PR as the globalId of the remote link, thus avoiding to create duplicates each time the PR is updated.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/yajp/plugin.rb', line 189

def pr_as_remotelink(issue, relation: 'relates to', status: nil)
  issues = issue.kind_of?(Array) ? issue : [] << issue
  result = true

  remote_link_prop = { object: { url: pr_link, title: vcs_host.pr_title, icon: link_icon } }
  remote_link_prop[:globalId] = pr_link
  remote_link_prop[:relationship] = relation

  if status.kind_of?(Hash)
    remote_link_prop[:object][:status] = status
  elsif !status.nil?
    remote_link_prop[:object][:status] = { resolved: status }
  end

  issues.each do |key|
    result &= key.remotelink.build.save(remote_link_prop)
  end

  return result
end

#split_transition_fields(issue, transition_id, **fields) ⇒ Hash

Utility to split the given fields into fields that can be updated on the transition screen corresponding to the transition_id of the given issue.



147
148
149
150
151
152
153
154
155
156
157
# File 'lib/yajp/plugin.rb', line 147

def split_transition_fields(issue, transition_id, **fields)
  transitions = issue.transitions.all.keep_if { |transition| transition.attrs['id'] == transition_id.to_s }
  transition_fields = transitions.first.attrs['fields']
  transition_data = {}

  fields.each_key do |field|
    transition_data[field] = fields.delete(field) if transition_fields&.key?(field.to_s)
  end

  { transition_fields: transition_data, other_fields: fields }
end

#transition_all(transition_id, issue: @issues, **fields) ⇒ Boolean

Transition the given Jira issue(s) using the ID or name of the transition. Transition IDs can be found in Jira under Project Workflow > Edit Workflow in Text Mode. The transition name is the text that appears on the issue screen to transition it. The fields that can be updated with this method are only the fields available in the transition screen of the transition. Otherwise use transition_and_update.

Examples:

Transition the issue my_issue using the transition 'done' and set the fields assignee and customfield_11005 available on the transition screens

jira.transition_all(my_issue, 'done', assignee: { name: 'username' }, customfield_11005: 'example')


103
104
105
106
107
108
109
110
111
112
# File 'lib/yajp/plugin.rb', line 103

def transition_all(transition_id, issue: @issues, **fields)
  issues = issue.kind_of?(Array) ? issue : [] << issue
  result = true

  issues.each do |key|
    result &= key.transition(transition_id, **fields)
  end

  return result
end

#transition_and_update_all(transition_id, issue: @issues, **fields) ⇒ Boolean

Transition and update the given issues. It will use the split_transition_fields method to provide the right fields for the transition action, and use the other fields with the update action.

Examples:

Transition the issue my_issue and set the fields assignee and customfield_11005

jira.transition_and_update_all(my_issue, 10, assignee: { name: 'username' }, customfield_11005: 'example')


171
172
173
174
175
176
177
178
179
# File 'lib/yajp/plugin.rb', line 171

def transition_and_update_all(transition_id, issue: @issues, **fields)
  issues = issue.kind_of?(Array) ? issue : [] << issue
  result = issues.first.split_transition_fields(transition_id, fields)
  transition_fields = result[:transition_fields]
  fields = result[:other_fields]

  result = transition(transition_id, issue: issues, **transition_fields)
  result & update(issue: issues, **fields)
end

#update_all(issue: @issues, **fields) ⇒ Boolean

Update the given Jira issue(s).

Examples:

Update the issue my_issue and set the fields assignee and customfield_11005

jira.update_all(my_issue, assignee: { name: 'username' }, customfield_11005: 'example')


124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/yajp/plugin.rb', line 124

def update_all(issue: @issues, **fields)
  return if fields.empty?

  issues = issue.kind_of?(Array) ? issue : [] << issue
  result = true

  issues.each do |key|
    result &= key.update(**fields)
  end

  return result
end