apadmi_grout 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_action.rb +24 -0
- data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_ado_action.rb +91 -0
- data/lib/apadmi/grout/{jira/actions/find_tickets_to_move_action.rb → actions/find_tickets_to_move_action/find_tickets_to_move_jira_action.rb} +16 -17
- data/lib/apadmi/grout/actions/generate_release_notes_action/generate_release_notes_action.rb +48 -0
- data/lib/apadmi/grout/actions/generate_release_notes_action/issue_classifier.rb +51 -0
- data/lib/apadmi/grout/{release_notes/actions → actions}/issues_from_changelog_action.rb +11 -6
- data/lib/apadmi/grout/actions/move_tickets_action.rb +34 -0
- data/lib/apadmi/grout/di.rb +53 -9
- data/lib/apadmi/grout/{jira/models/find_tickets_options.rb → models/ado_config.rb} +2 -2
- data/lib/apadmi/grout/models/find_tickets_options.rb +44 -0
- data/lib/apadmi/grout/{jira/models → models}/flag_messages.rb +0 -0
- data/lib/apadmi/grout/models/issue.rb +49 -0
- data/lib/apadmi/grout/{jira/models → models}/pull_request.rb +0 -0
- data/lib/apadmi/grout/models/release_notes_config.rb +47 -0
- data/lib/apadmi/grout/{release_notes/models → models}/release_notes_templates.rb +6 -6
- data/lib/apadmi/grout/service/board_service/ado_board_service.rb +199 -0
- data/lib/apadmi/grout/service/board_service/board_service.rb +59 -0
- data/lib/apadmi/grout/{jira/wrapper/jira_wrapper.rb → service/board_service/jira_board_service.rb} +65 -107
- data/lib/apadmi/grout/utils/git_utils.rb +18 -0
- data/lib/apadmi/grout/utils/network_service.rb +88 -0
- data/lib/apadmi/grout/version.rb +1 -1
- data/lib/apadmi_grout.rb +3 -21
- metadata +20 -13
- data/lib/apadmi/grout/jira/actions/move_jira_tickets_action.rb +0 -58
- data/lib/apadmi/grout/jira/models/version.rb +0 -23
- data/lib/apadmi/grout/release_notes/actions/generate_release_notes_action.rb +0 -39
- data/lib/apadmi/grout/release_notes/models/release_notes_config.rb +0 -74
@@ -9,7 +9,7 @@ module Apadmi
|
|
9
9
|
|
10
10
|
### Version
|
11
11
|
{{config.app_version}}
|
12
|
-
|
12
|
+
|
13
13
|
### Release date
|
14
14
|
{{config.date}}
|
15
15
|
|
@@ -30,35 +30,35 @@ Commit Hash: {{config.commit_hash}}
|
|
30
30
|
%{### New functionality
|
31
31
|
{{# classified_issues.features.first}}
|
32
32
|
{{# classified_issues.features}}
|
33
|
-
* [{{ key }} - {{ summary }}]({{
|
33
|
+
* [{{ key }} - {{ summary }}]({{ url }})
|
34
34
|
{{/ classified_issues.features}}
|
35
35
|
|
36
36
|
{{/ classified_issues.features.first}}
|
37
37
|
{{# classified_issues.improvements.first}}
|
38
38
|
### Improvements
|
39
39
|
{{# classified_issues.improvements}}
|
40
|
-
* [{{ key }} - {{ summary }}]({{
|
40
|
+
* [{{ key }} - {{ summary }}]({{ url }})
|
41
41
|
{{/ classified_issues.improvements}}
|
42
42
|
|
43
43
|
{{/ classified_issues.improvements.first}}
|
44
44
|
{{# classified_issues.tasks.first}}
|
45
45
|
### Completed Tasks
|
46
46
|
{{# classified_issues.tasks}}
|
47
|
-
* [{{ key }} - {{ summary }}]({{
|
47
|
+
* [{{ key }} - {{ summary }}]({{ url }})
|
48
48
|
{{/ classified_issues.tasks}}
|
49
49
|
|
50
50
|
{{/ classified_issues.tasks.first}}
|
51
51
|
{{# classified_issues.defects.first}}
|
52
52
|
### Fixed defects
|
53
53
|
{{# classified_issues.defects}}
|
54
|
-
* [{{ key }} - {{ summary }}]({{
|
54
|
+
* [{{ key }} - {{ summary }}]({{ url }})
|
55
55
|
{{/ classified_issues.defects}}
|
56
56
|
|
57
57
|
{{/ classified_issues.defects.first}}
|
58
58
|
{{# classified_issues.others.first}}
|
59
59
|
### Other issue types
|
60
60
|
{{# classified_issues.others}}
|
61
|
-
* [{{ key }} - {{ summary }}]({{
|
61
|
+
* [{{issue_type}} - {{ key }} - {{ summary }}]({{ url }})
|
62
62
|
{{/ classified_issues.others}}
|
63
63
|
|
64
64
|
{{/ classified_issues.others.first}}}
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./board_service"
|
4
|
+
|
5
|
+
module Apadmi
|
6
|
+
module Grout
|
7
|
+
# Provides a layer of abstraction on top of the ADO api
|
8
|
+
class AdoBoardService < BoardService
|
9
|
+
API_VERSION_PARAM = "api-version=6.0"
|
10
|
+
|
11
|
+
# @param [Apadmi::Grout::NetworkService] network_service
|
12
|
+
# @param [Apadmi::Grout::AdoConfig] ado_config
|
13
|
+
# @param [Logger] logger
|
14
|
+
def initialize(network_service, ado_config, logger)
|
15
|
+
@network_service = network_service
|
16
|
+
@ado_config = ado_config
|
17
|
+
@logger = logger
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [String[]] keys
|
21
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
22
|
+
def find_issues_by_keys(keys)
|
23
|
+
return [] if keys.length <= 0
|
24
|
+
|
25
|
+
keys = keys.map { |k| sanitise_key(k) } # Allows supporting raw keys, and keys prefixed with a hash which is common ADO practice
|
26
|
+
|
27
|
+
res = @network_service.do_get("/wit/workitems?ids=#{keys.join(",")}&api-version=7.1-preview.2")
|
28
|
+
parsed = JSON.parse(res.body)
|
29
|
+
items = parsed["value"]
|
30
|
+
|
31
|
+
return if items.nil? || items.empty?
|
32
|
+
|
33
|
+
items.map { |i| Apadmi::Grout::Issue.from_ado_hash(i) }
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param component [String] Included to be consistent with JIRA, this will boil down to a tag in ADO
|
37
|
+
# @param status [String]
|
38
|
+
# @param ticket_types [String[]]
|
39
|
+
# @param options [Apadmi::Grout::AdoFindTicketsOptions] Additional options to filter by
|
40
|
+
|
41
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
42
|
+
def search_unblocked_issues(component, status, ticket_types = [], options = nil)
|
43
|
+
tags = (options&.required_tags || []).push(*component).filter { |it| !it.blank? }
|
44
|
+
not_tags = (options&.not_tags || []).filter { |it| !it.blank? }
|
45
|
+
|
46
|
+
raise "Tags can either be required or not required, not both" unless (tags & not_tags).empty?
|
47
|
+
|
48
|
+
tags_filter = tags.map { |tag| " AND [System.Tags] CONTAINS '#{tag}' " }.join(" ")
|
49
|
+
not_tags_filter = not_tags.map { |tag| " AND [System.Tags] NOT CONTAINS '#{tag}'" }.join(" ")
|
50
|
+
|
51
|
+
status_filter = ("AND [System.State]='#{status}' " unless status.blank?) || ""
|
52
|
+
type_filter = ("AND [System.WorkItemType] IN (#{ticket_types.map { |t| "'#{t}'" }.join(", ")})" unless ticket_types.empty?) || ""
|
53
|
+
wiql = %(
|
54
|
+
Select [System.Id]
|
55
|
+
From WorkItems
|
56
|
+
Where [System.Id] >= 1
|
57
|
+
#{status_filter}
|
58
|
+
#{tags_filter}
|
59
|
+
#{not_tags_filter}
|
60
|
+
#{type_filter}
|
61
|
+
)
|
62
|
+
|
63
|
+
get_issues_by_wiql(wiql)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [String] key
|
67
|
+
# @param [String] comment
|
68
|
+
def flag_ticket(key, comment)
|
69
|
+
key = sanitise_key(key)
|
70
|
+
|
71
|
+
add_tag(key, @ado_config.flag_tag)
|
72
|
+
add_comment(key, comment) unless comment.blank?
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param [String] key
|
76
|
+
def un_flag_ticket(key)
|
77
|
+
key = sanitise_key(key)
|
78
|
+
|
79
|
+
remove_tag(key, @ado_config.flag_tag)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param [String] key
|
83
|
+
# @param [String] comment
|
84
|
+
def add_comment(key, comment)
|
85
|
+
key = sanitise_key(key)
|
86
|
+
|
87
|
+
payload = {
|
88
|
+
"text" => comment
|
89
|
+
}
|
90
|
+
@network_service.do_post("/wit/workitems/#{key}/comments?#{API_VERSION_PARAM}-preview.3", payload.to_json)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param [String] key
|
94
|
+
# @param [String] tag
|
95
|
+
def add_tag(key, tag)
|
96
|
+
payload = [{
|
97
|
+
"op" => "add",
|
98
|
+
"path" => "/fields/System.Tags",
|
99
|
+
"value" => tag
|
100
|
+
}]
|
101
|
+
@network_service.do_patch("/wit/workitems/#{key}?#{API_VERSION_PARAM}", payload.to_json)
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param [String] key
|
105
|
+
# @param [String] tag
|
106
|
+
def remove_tag(key, tag)
|
107
|
+
current_tags = get_work_item(key)["fields"]["System.Tags"].split("; ")
|
108
|
+
new_tags = current_tags.filter { |t| t != tag }
|
109
|
+
|
110
|
+
payload = [{
|
111
|
+
"op" => "replace",
|
112
|
+
"path" => "/fields/System.Tags",
|
113
|
+
"value" => new_tags.join("; ")
|
114
|
+
}]
|
115
|
+
@network_service.do_patch("/wit/workitems/#{key}?#{API_VERSION_PARAM}", payload.to_json)
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param [String] key
|
119
|
+
def flagged?(key)
|
120
|
+
current_tags = get_work_item(key)["fields"]["System.Tags"].split("; ")
|
121
|
+
current_tags.include?(@ado_config.flag_tag)
|
122
|
+
end
|
123
|
+
|
124
|
+
# @param [String] key
|
125
|
+
# @return [RawWorkItemHash]
|
126
|
+
def get_work_item(key)
|
127
|
+
key = sanitise_key(key)
|
128
|
+
|
129
|
+
res = @network_service.do_get("/wit/workitems/#{key}?#{API_VERSION_PARAM}")
|
130
|
+
JSON.parse(res.body)
|
131
|
+
end
|
132
|
+
|
133
|
+
# @param [String] key
|
134
|
+
# @param [Array<String>] versions
|
135
|
+
def assign_fixversions(key, versions)
|
136
|
+
key = sanitise_key(key)
|
137
|
+
|
138
|
+
payload = [{
|
139
|
+
"op" => "replace",
|
140
|
+
"path" => "/fields/Microsoft.VSTS.Build.IntegrationBuild",
|
141
|
+
"value" => versions.join("; ")
|
142
|
+
}]
|
143
|
+
@network_service.do_patch("/wit/workitems/#{key}?#{API_VERSION_PARAM}", payload.to_json)
|
144
|
+
end
|
145
|
+
|
146
|
+
# @param [String] key
|
147
|
+
# @return [Array<String>] fix versions
|
148
|
+
def get_ticket_fixversions(key)
|
149
|
+
key = sanitise_key(key)
|
150
|
+
|
151
|
+
get_work_item(key)["fields"]["Microsoft.VSTS.Build.IntegrationBuild"].split("; ")
|
152
|
+
end
|
153
|
+
|
154
|
+
# @param [Apadmi::Grout::Issue] issue
|
155
|
+
# @param [String] state_name
|
156
|
+
def transition_issue(issue, state_name)
|
157
|
+
key = sanitise_key(issue.key)
|
158
|
+
|
159
|
+
payload = [{
|
160
|
+
"op" => "add",
|
161
|
+
"path" => "/fields/System.State",
|
162
|
+
"value" => state_name
|
163
|
+
}]
|
164
|
+
@network_service.do_patch("/wit/workitems/#{key}?#{API_VERSION_PARAM}", payload.to_json)
|
165
|
+
end
|
166
|
+
|
167
|
+
# @param [String] key
|
168
|
+
# @return [String]
|
169
|
+
def get_ticket_status(key)
|
170
|
+
key = sanitise_key(key)
|
171
|
+
|
172
|
+
get_work_item(key)["fields"]["System.State"]
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def sanitise_key(key)
|
178
|
+
key = key.to_s.strip.delete_prefix "#"
|
179
|
+
raise "Invalid key" if key.blank?
|
180
|
+
|
181
|
+
key
|
182
|
+
end
|
183
|
+
|
184
|
+
# @param wiql_query [String]
|
185
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
186
|
+
def get_issues_by_wiql(wiql_query)
|
187
|
+
@logger.message("Executing wiql")
|
188
|
+
@logger.message(wiql_query)
|
189
|
+
|
190
|
+
payload = { "query" => wiql_query }
|
191
|
+
res = @network_service.do_post("/wit/wiql?#{API_VERSION_PARAM}", payload.to_json)
|
192
|
+
parsed = JSON.parse(res.body)
|
193
|
+
items = parsed["workItems"] || []
|
194
|
+
ids = items.map { |item| item["id"] } || []
|
195
|
+
find_issues_by_keys(ids)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apadmi
|
4
|
+
module Grout
|
5
|
+
# A generic board service to be implemented by whatever
|
6
|
+
# board providers we have e.g. JIRA and ADO
|
7
|
+
class BoardService
|
8
|
+
# @param [String[]] keys
|
9
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
10
|
+
def find_issues_by_keys(_keys)
|
11
|
+
raise "Unimplemented :("
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [String] _component
|
15
|
+
# @param [String] _status
|
16
|
+
# @param [String[]] _ticket_types
|
17
|
+
# @param [Apadmi::Grout::JiraFindTicketsOptions|Apadmi::Grout::JiraFindTicketsOptions] _options
|
18
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
19
|
+
def search_unblocked_issues(_component, _status, _ticket_types = [], _options = nil)
|
20
|
+
raise "Unimplemented :("
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [String] _key
|
24
|
+
# @param [String] _comment
|
25
|
+
def flag_ticket(_key, _comment)
|
26
|
+
raise "Unimplemented :("
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [String] _key
|
30
|
+
def un_flag_ticket(_key)
|
31
|
+
raise "Unimplemented :("
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [String] _key
|
35
|
+
# @return bool
|
36
|
+
def flagged?(_key)
|
37
|
+
raise "Unimplemented :("
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [String] _key
|
41
|
+
# @param [Array<String>] _version_strings
|
42
|
+
def assign_fixversions(_key, _version_strings)
|
43
|
+
raise "Unimplemented :("
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param [String] _key
|
47
|
+
# @return [Array<String>]
|
48
|
+
def get_ticket_fixversions(_key)
|
49
|
+
raise "Unimplemented :("
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [Apadmi::Grout::Issue] _issue
|
53
|
+
# @param [String] _state_name
|
54
|
+
def transition_issue(_issue, _state_name)
|
55
|
+
raise "Unimplemented :("
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/apadmi/grout/{jira/wrapper/jira_wrapper.rb → service/board_service/jira_board_service.rb}
RENAMED
@@ -1,23 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Build tools
|
4
3
|
require "jira-ruby"
|
5
|
-
require "faraday"
|
6
4
|
|
7
5
|
module Apadmi
|
8
6
|
module Grout
|
9
7
|
# Provides a layer of abstraction on top of the Jira api
|
10
|
-
class
|
11
|
-
APPLICATION_JSON = "application/json"
|
12
|
-
CONTENT_TYPE = "Content-Type"
|
13
|
-
private_constant :CONTENT_TYPE, :APPLICATION_JSON
|
14
|
-
|
8
|
+
class JiraBoardService < BoardService
|
15
9
|
# @param [String] username
|
16
10
|
# @param [String] token
|
17
11
|
# @param [String] base_url
|
18
12
|
# @param [String] context_path
|
19
13
|
# @param [String] project
|
20
|
-
|
14
|
+
# @param [Apadmi::Grout::NetworkService] network_service
|
15
|
+
def initialize(username, token, base_url, context_path, project, network_service)
|
21
16
|
@options = {
|
22
17
|
username: username,
|
23
18
|
password: token,
|
@@ -26,29 +21,27 @@ module Apadmi
|
|
26
21
|
auth_type: :basic
|
27
22
|
}
|
28
23
|
@project = project
|
24
|
+
@network_service = network_service
|
29
25
|
@jira_client = JIRA::Client.new(@options)
|
30
26
|
end
|
31
27
|
|
32
|
-
# @return returns the ticket prefix for the given project
|
33
|
-
def issue_id_prefix
|
34
|
-
@project
|
35
|
-
end
|
36
|
-
|
37
28
|
# @param [String[]] keys
|
38
|
-
# @return [Array<
|
29
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
39
30
|
def find_issues_by_keys(keys)
|
40
31
|
return [] if keys.length <= 0
|
41
32
|
|
42
33
|
jql_search = "project = '#{@project}' AND issue IN (#{keys.join(", ")})"
|
43
|
-
@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
|
34
|
+
issues = @jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
|
35
|
+
convert(issues)
|
44
36
|
end
|
45
37
|
|
46
38
|
# @param [String] component
|
47
39
|
# @param [String] status
|
48
40
|
# @param [String[]] ticket_types
|
49
|
-
# @param [
|
50
|
-
# @return [Array<
|
51
|
-
def search_unblocked_issues(component, status, ticket_types = [],
|
41
|
+
# @param [Apadmi::Grout::JiraFindTicketsOptions] options
|
42
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
43
|
+
def search_unblocked_issues(component, status, ticket_types = [], options = nil)
|
44
|
+
allow_no_sprint = options&.include_no_sprint_tickets || false
|
52
45
|
component_filter = (" AND component = '#{component}' " unless component.empty?) || ""
|
53
46
|
status_filter = (" AND status = '#{status}' " unless status.empty?) || ""
|
54
47
|
type_filter = ("AND (#{ticket_types.map { |type| "type = #{type}" }.join("OR ")})" unless ticket_types.empty?) || ""
|
@@ -61,20 +54,31 @@ module Apadmi
|
|
61
54
|
AND Flagged is EMPTY
|
62
55
|
}
|
63
56
|
|
64
|
-
@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
|
57
|
+
issues = @jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
|
58
|
+
convert(issues)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param [Apadmi::Grout::Issue] issue
|
62
|
+
# @param [String] state_name
|
63
|
+
def transition_issue(issue, state_name)
|
64
|
+
transitions = @jira_client.Transition.all(issue: issue.raw_object)
|
65
|
+
transition = transitions.find { |elem| elem.name.downcase == state_name.downcase }
|
66
|
+
|
67
|
+
trans = issue.raw_object.transitions.build
|
68
|
+
trans.save!("transition" => { "id" => transition.id })
|
65
69
|
end
|
66
70
|
|
67
71
|
# @param [String] key
|
68
72
|
# @param [String] comment
|
69
73
|
def flag_ticket(key, comment)
|
70
74
|
payload = "{\"flag\":true,\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"#{comment}\"}"
|
71
|
-
do_post("
|
75
|
+
@network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
|
72
76
|
end
|
73
77
|
|
74
78
|
# @param [String] key
|
75
79
|
def un_flag_ticket(key)
|
76
80
|
payload = "{\"flag\":false,\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"Unflagged by CI\"}"
|
77
|
-
do_post("
|
81
|
+
@network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
|
78
82
|
end
|
79
83
|
|
80
84
|
# @param [String] key
|
@@ -89,7 +93,7 @@ module Apadmi
|
|
89
93
|
# @param [String] comment
|
90
94
|
def add_comment(key, comment)
|
91
95
|
payload = "{\"issueKeys\":[\"#{key}\"],\"commentVisibility\":\"\",\"comment\":\"#{comment}\"}"
|
92
|
-
do_post("
|
96
|
+
@network_service.do_post("/rest/greenhopper/1.0/xboard/issue/flag/flag.json", payload)
|
93
97
|
end
|
94
98
|
|
95
99
|
# @param [String] key
|
@@ -104,14 +108,14 @@ module Apadmi
|
|
104
108
|
# @param [String] key
|
105
109
|
# @param [String] comment_id
|
106
110
|
def remove_comment(key, comment_id)
|
107
|
-
do_delete("
|
111
|
+
@network_service.do_delete("/rest/api/2/issue/#{key}/comment/#{comment_id}")
|
108
112
|
end
|
109
113
|
|
110
114
|
# @param [String] component_key
|
111
|
-
# @return [Array<
|
115
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
112
116
|
def get_tickets_by_component(component_key)
|
113
117
|
jql_search = "project = '#{@project}' AND component IN ('#{component_key}')"
|
114
|
-
@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
|
118
|
+
convert(@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq)
|
115
119
|
end
|
116
120
|
|
117
121
|
# @param [String] key
|
@@ -120,33 +124,18 @@ module Apadmi
|
|
120
124
|
@jira_client.Transition.all(issue: issue)
|
121
125
|
end
|
122
126
|
|
123
|
-
# @param [String] ticket
|
124
|
-
# @param [String] state_name
|
125
|
-
def transition_ticket(ticket, state_name)
|
126
|
-
issue = @jira_client.Issue.find(ticket)
|
127
|
-
transition_issue(issue, state_name)
|
128
|
-
end
|
129
|
-
|
130
|
-
# @param [JIRA::Resource::Issue] issue
|
131
|
-
# @param [String] state_name
|
132
|
-
def transition_issue(issue, state_name)
|
133
|
-
transitions = @jira_client.Transition.all(issue: issue)
|
134
|
-
transition = transitions.find { |elem| elem.name == state_name }
|
135
|
-
|
136
|
-
trans = issue.transitions.build
|
137
|
-
trans.save!("transition" => { "id" => transition.id })
|
138
|
-
end
|
139
|
-
|
140
127
|
# @param [String] key
|
128
|
+
# @return [String]
|
141
129
|
def get_ticket_status(key)
|
142
130
|
issue = @jira_client.Issue.find(key)
|
143
|
-
issue.status
|
131
|
+
issue.status.name
|
144
132
|
end
|
145
133
|
|
146
|
-
# @param [
|
134
|
+
# @param [Apadmi::Grout::Issue] issue
|
147
135
|
# @return [Array<PullRequest>]
|
148
136
|
def get_ticket_prs(issue)
|
149
|
-
|
137
|
+
query_string = "issueId=#{issue.raw_object.id}&applicationType=bitbucket&dataType=pullrequest"
|
138
|
+
response = @network_service.do_get("/rest/dev-status/latest/issue/details?#{query_string}")
|
150
139
|
|
151
140
|
return [] if JSON.parse(response.body)["detail"].empty?
|
152
141
|
|
@@ -157,11 +146,11 @@ module Apadmi
|
|
157
146
|
|
158
147
|
# @param [String] keys
|
159
148
|
# @param [String] component
|
160
|
-
# @return [Array<
|
149
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
161
150
|
def get_ticket_subtask(keys, component = nil)
|
162
151
|
jql_search = "project = '#{@project}' AND parent IN #{keys.to_s.gsub("[", "(").gsub("]", ")")}"\
|
163
152
|
+ (component.nil? ? "" : " AND component IN ('#{component}')")
|
164
|
-
@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
|
153
|
+
convert(@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq)
|
165
154
|
end
|
166
155
|
|
167
156
|
# @param [String] release_date
|
@@ -182,7 +171,7 @@ module Apadmi
|
|
182
171
|
def delete_version(version_id, move_version_id)
|
183
172
|
payload = "{\"moveFixIssuesTo\": #{move_version_id}, \"moveAffectedIssuesTo\": #{move_version_id}, "\
|
184
173
|
"\"customFieldReplacementList\": []}"
|
185
|
-
do_post("
|
174
|
+
@network_service.do_post("/rest/api/2/version/#{version_id}/removeAndSwap", payload)
|
186
175
|
end
|
187
176
|
|
188
177
|
# @return [Array<JIRA::Resource::Version>]
|
@@ -190,75 +179,44 @@ module Apadmi
|
|
190
179
|
@jira_client.Project.find(@project).versions
|
191
180
|
end
|
192
181
|
|
193
|
-
# @param [String]
|
194
|
-
# @param [Array<
|
195
|
-
def assign_fixversions(
|
182
|
+
# @param [String] key
|
183
|
+
# @param [Array<String>] version_strings
|
184
|
+
def assign_fixversions(key, version_strings)
|
185
|
+
versions = create_or_get_versions(version_strings)
|
196
186
|
fixversions = versions.map { |v| "{\"id\": \"#{v.id}\"}" }.join(", ")
|
197
187
|
payload = "{\"fields\" : {\"fixVersions\": [#{fixversions}] }}"
|
198
|
-
do_put("
|
199
|
-
end
|
200
|
-
|
201
|
-
# @param [String] ticket
|
202
|
-
# @return [Array<Version>]
|
203
|
-
def get_ticket_fixversions(ticket)
|
204
|
-
response = do_get("#{@options[:site]}/rest/api/2/issue/#{ticket}")
|
205
|
-
JSON.parse(response.body)["fields"]["fixVersions"].map do |version|
|
206
|
-
Version.new(version)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# @param [Faraday::Response] response
|
211
|
-
def throw_if_error(response)
|
212
|
-
raise "Network call failed #{response.status} #{response.body}" unless (200..210).include?(response.status)
|
188
|
+
@network_service.do_put("/rest/api/2/issue/#{key}", payload)
|
213
189
|
end
|
214
190
|
|
215
|
-
# @param [String]
|
216
|
-
# @return [
|
217
|
-
def
|
218
|
-
|
219
|
-
response
|
220
|
-
throw_if_error(response)
|
221
|
-
response
|
222
|
-
end
|
223
|
-
|
224
|
-
# @param [String] uri
|
225
|
-
# @param [String] payload
|
226
|
-
# @return [Faraday::Response]
|
227
|
-
def do_put(uri, payload)
|
228
|
-
conn = setup_con
|
229
|
-
response = conn.put(uri, payload, CONTENT_TYPE => APPLICATION_JSON)
|
230
|
-
throw_if_error(response)
|
231
|
-
response
|
191
|
+
# @param [String] key
|
192
|
+
# @return [Array<String>]
|
193
|
+
def get_ticket_fixversions(key)
|
194
|
+
response = @network_service.do_get("/rest/api/2/issue/#{key}")
|
195
|
+
JSON.parse(response.body)["fields"]["fixVersions"].map { |v| v["name"] } || []
|
232
196
|
end
|
233
197
|
|
234
|
-
|
235
|
-
# @param [String] payload
|
236
|
-
# @return [Faraday::Response]
|
237
|
-
def do_post(uri, payload)
|
238
|
-
conn = setup_con
|
239
|
-
response = conn.post(uri, payload, CONTENT_TYPE => APPLICATION_JSON)
|
240
|
-
throw_if_error(response)
|
241
|
-
response
|
242
|
-
end
|
198
|
+
private
|
243
199
|
|
244
|
-
# @param [String]
|
245
|
-
# @return [
|
246
|
-
def
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
200
|
+
# @param [Array<String>] version_strings
|
201
|
+
# @return [Array<JIRA::Resource::Version>]
|
202
|
+
def create_or_get_versions(version_strings)
|
203
|
+
all = all_versions
|
204
|
+
version_strings.map do |version|
|
205
|
+
existing_version = all.find { |v| v.name == version }
|
206
|
+
if !existing_version.nil?
|
207
|
+
existing_version
|
208
|
+
else
|
209
|
+
date = Time.now.strftime("%Y-%m-%d")
|
210
|
+
create_version(date, version)
|
211
|
+
end
|
212
|
+
end
|
251
213
|
end
|
252
214
|
|
253
|
-
# @
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
conn.headers["Authorization"] = "Basic #{Base64.strict_encode64(auth_str)}"
|
258
|
-
conn
|
215
|
+
# @param [Array<JIRA::Resource::Issue>]
|
216
|
+
# @return [Array<Apadmi::Grout::Issue>]
|
217
|
+
def convert(issues)
|
218
|
+
Apadmi::Grout::Issue.from_jira_issues(issues, @options[:site])
|
259
219
|
end
|
260
|
-
|
261
|
-
private :setup_con, :do_delete, :do_get, :do_post, :do_put
|
262
220
|
end
|
263
221
|
end
|
264
222
|
end
|
@@ -25,6 +25,14 @@ module Apadmi
|
|
25
25
|
stdout.strip
|
26
26
|
end
|
27
27
|
|
28
|
+
# Runs a git fetch all
|
29
|
+
def self.fetch_all
|
30
|
+
stdout, stderr, = Open3.capture3("git fetch --all")
|
31
|
+
raise "Failed to fetch #{stderr}" unless stderr.strip.empty?
|
32
|
+
|
33
|
+
stdout.strip
|
34
|
+
end
|
35
|
+
|
28
36
|
# Gets all the merges accessible from the current HEAD which matches at least one of the given grep conditions
|
29
37
|
# @param grep_conditions [Array<String>] values to be passed in as grep cases (https://git-scm.com/docs/git-log)
|
30
38
|
def self.merge_changelog(grep_conditions)
|
@@ -34,6 +42,16 @@ module Apadmi
|
|
34
42
|
|
35
43
|
stdout
|
36
44
|
end
|
45
|
+
|
46
|
+
# Gets all the merges that are NOT accessible from HEAD which matches at least one of the given grep conditions
|
47
|
+
# @param grep_conditions [Array<String>] values to be passed in as grep cases (https://git-scm.com/docs/git-log)
|
48
|
+
def self.invert_changelog(grep_conditions)
|
49
|
+
command = "git log --all ^HEAD --merges --format=%s#{grep_conditions.map { |c| " --grep #{c}" }.join(" ")}"
|
50
|
+
stdout, stderr, = Open3.capture3(command)
|
51
|
+
raise "Failed to get changelog: #{stderr}" unless stderr.strip.empty?
|
52
|
+
|
53
|
+
stdout
|
54
|
+
end
|
37
55
|
end
|
38
56
|
end
|
39
57
|
end
|