lazylead 0.4.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.docs/accuracy.md +2 -2
  3. data/.docs/duedate_expired.md +3 -3
  4. data/.docs/propagate_down.md +3 -3
  5. data/.gitattributes +1 -0
  6. data/.github/dependabot.yml +6 -0
  7. data/Rakefile +6 -3
  8. data/bin/lazylead +7 -5
  9. data/lazylead.gemspec +5 -3
  10. data/lib/lazylead/cli/app.rb +5 -2
  11. data/lib/lazylead/exchange.rb +15 -9
  12. data/lib/lazylead/log.rb +2 -1
  13. data/lib/lazylead/model.rb +35 -1
  14. data/lib/lazylead/opts.rb +13 -0
  15. data/lib/lazylead/postman.rb +13 -7
  16. data/lib/lazylead/schedule.rb +16 -15
  17. data/lib/lazylead/smtp.rb +3 -1
  18. data/lib/lazylead/system/jira.rb +65 -4
  19. data/lib/lazylead/task/accuracy/accuracy.rb +13 -8
  20. data/lib/lazylead/task/accuracy/affected_build.rb +2 -6
  21. data/lib/lazylead/task/accuracy/attachment.rb +44 -0
  22. data/lib/lazylead/task/accuracy/environment.rb +39 -0
  23. data/lib/lazylead/task/accuracy/logs.rb +42 -0
  24. data/lib/lazylead/task/accuracy/records.rb +45 -0
  25. data/lib/lazylead/task/accuracy/requirement.rb +9 -0
  26. data/lib/lazylead/task/accuracy/servers.rb +50 -0
  27. data/lib/lazylead/task/accuracy/stacktrace.rb +103 -0
  28. data/lib/lazylead/task/accuracy/testcase.rb +91 -0
  29. data/lib/lazylead/task/accuracy/wiki.rb +41 -0
  30. data/lib/lazylead/task/assignment.rb +96 -0
  31. data/lib/lazylead/task/echo.rb +18 -0
  32. data/lib/lazylead/task/fix_version.rb +13 -2
  33. data/lib/lazylead/task/svn/diff.rb +76 -0
  34. data/lib/lazylead/task/svn/grep.rb +111 -0
  35. data/lib/lazylead/task/svn/touch.rb +101 -0
  36. data/lib/lazylead/version.rb +1 -1
  37. data/lib/messages/illegal_assignee_change.erb +123 -0
  38. data/lib/messages/illegal_fixversion_change.erb +2 -0
  39. data/lib/messages/svn_diff.erb +110 -0
  40. data/lib/messages/svn_diff_attachment.erb +117 -0
  41. data/lib/messages/svn_grep.erb +114 -0
  42. data/lib/messages/svn_touch.erb +1 -1
  43. data/license.txt +1 -1
  44. data/readme.md +5 -5
  45. data/test/lazylead/cli/app_test.rb +11 -11
  46. data/test/lazylead/system/jira_test.rb +41 -4
  47. data/test/lazylead/task/accuracy/accuracy_test.rb +1 -1
  48. data/test/lazylead/task/accuracy/affected_build_test.rb +2 -2
  49. data/test/lazylead/task/accuracy/attachment_test.rb +50 -0
  50. data/test/lazylead/task/accuracy/environment_test.rb +42 -0
  51. data/test/lazylead/task/accuracy/logs_test.rb +138 -0
  52. data/test/lazylead/task/accuracy/records_test.rb +60 -0
  53. data/test/lazylead/task/accuracy/score_test.rb +46 -0
  54. data/test/lazylead/task/accuracy/servers_test.rb +66 -0
  55. data/test/lazylead/task/accuracy/stacktrace_test.rb +340 -0
  56. data/test/lazylead/task/accuracy/testcase_test.rb +225 -0
  57. data/test/lazylead/task/accuracy/wiki_test.rb +40 -0
  58. data/test/lazylead/task/{touch_test.rb → assignment_test.rb} +17 -27
  59. data/test/lazylead/task/svn/diff_test.rb +97 -0
  60. data/test/lazylead/task/svn/grep_test.rb +61 -0
  61. data/test/lazylead/task/svn/touch_test.rb +61 -0
  62. data/test/test.rb +25 -0
  63. data/upgrades/sqlite/001-install-main-lazylead-tables.sql +1 -5
  64. data/upgrades/sqlite/999.testdata.sql +12 -17
  65. metadata +88 -21
  66. data/.travis.yml +0 -16
  67. data/lib/lazylead/task/touch.rb +0 -102
@@ -23,6 +23,7 @@
23
23
  # OR OTHER DEALINGS IN THE SOFTWARE.
24
24
 
25
25
  require "mail"
26
+ require "colorize"
26
27
  require_relative "log"
27
28
  require_relative "salt"
28
29
 
@@ -45,7 +46,8 @@ module Lazylead
45
46
  Mail.defaults do
46
47
  delivery_method :test
47
48
  end
48
- @log.warn "SMTP connection enabled in test mode."
49
+ @log.warn "SMTP connection enabled in " \
50
+ "#{'test'.colorize(:light_yellow)} mode."
49
51
  else
50
52
  setup_smtp
51
53
  end
@@ -25,6 +25,7 @@
25
25
  require "jira-ruby"
26
26
  require "forwardable"
27
27
  require_relative "../salt"
28
+ require_relative "../opts"
28
29
 
29
30
  module Lazylead
30
31
  # Jira system for manipulation with issues.
@@ -45,16 +46,32 @@ module Lazylead
45
46
  " and salt #{@salt.id} (found=#{@salt.specified?})"
46
47
  end
47
48
 
48
- def issues(jql, opts = {})
49
+ # Find the jira issues by 'JQL'
50
+ # @param 'jql' - Jira search query
51
+ # @param 'opts' - Parameters for Jira search query.
52
+ # :max_results => maximum number of tickets per one iteration.
53
+ # :fields => ticket fields to be fetched like assignee, summary, etc.
54
+ def issues(jql, opts = { max_results: 50, fields: nil, expand: nil })
49
55
  raw do |jira|
50
- jira.Issue.jql(jql, opts).map { |i| Lazylead::Issue.new(i, jira) }
56
+ start = 0
57
+ tickets = []
58
+ total = jira.Issue.jql(jql, max_results: 0)
59
+ @log.debug "Found #{total} ticket(s) in '#{jql}'"
60
+ loop do
61
+ tickets.concat(jira.Issue.jql(jql, opts.merge(start_at: start))
62
+ .map { |i| Lazylead::Issue.new(i, jira) })
63
+ @log.debug "Fetched #{tickets.size}"
64
+ start += opts.fetch(:max_results, 50).to_i
65
+ break if start > total
66
+ end
67
+ tickets
51
68
  end
52
69
  end
53
70
 
54
71
  # Execute request to the ticketing system using raw client.
55
72
  # For Jira the raw client is 'jira-ruby' gem.
56
73
  def raw
57
- raise "ll-06: No block given to method" unless block_given?
74
+ raise "ll-009: No block given to method" unless block_given?
58
75
  yield client
59
76
  end
60
77
 
@@ -150,6 +167,11 @@ module Lazylead
150
167
  @issue.key
151
168
  end
152
169
 
170
+ def description
171
+ return "" if @issue.description.nil?
172
+ @issue.description
173
+ end
174
+
153
175
  def summary
154
176
  fields["summary"]
155
177
  end
@@ -175,9 +197,24 @@ module Lazylead
175
197
  end
176
198
 
177
199
  def fields
200
+ return {} if @issue.nil?
201
+ return {} unless @issue.respond_to? :fields
202
+ return {} if @issue.fields.nil?
203
+ return {} unless @issue.fields.respond_to? :[]
178
204
  @issue.fields
179
205
  end
180
206
 
207
+ def [](name)
208
+ return "" if fields[name].nil? || fields[name].blank?
209
+ fields[name]
210
+ end
211
+
212
+ def components
213
+ return [] unless @issue.respond_to? :components
214
+ return [] if @issue.components.nil?
215
+ @issue.components.map(&:name)
216
+ end
217
+
181
218
  def history
182
219
  return [] unless @issue.respond_to? :changelog
183
220
  return [] if @issue.changelog == nil? || @issue.changelog.empty?
@@ -206,6 +243,30 @@ module Lazylead
206
243
  def post(markdown)
207
244
  @issue.comments.build.save!(body: markdown)
208
245
  end
246
+
247
+ def remote_links
248
+ @issue.remotelink.all
249
+ end
250
+
251
+ def attachments
252
+ @issue.attachments
253
+ end
254
+
255
+ def add_label(label, *more)
256
+ lbl = labels
257
+ lbl = [] if lbl.nil?
258
+ lbl << label
259
+ lbl += more if more.size.positive?
260
+ save!("fields" => { "labels" => lbl.uniq })
261
+ end
262
+
263
+ def labels
264
+ fields["labels"]
265
+ end
266
+
267
+ def save!(opts)
268
+ @issue.save(opts)
269
+ end
209
270
  end
210
271
 
211
272
  # The jira issue comments
@@ -281,7 +342,7 @@ module Lazylead
281
342
 
282
343
  # Execute request to the ticketing system using raw client.
283
344
  def raw
284
- raise "ll-08: No block given to method" unless block_given?
345
+ raise "ll-008: No block given to method" unless block_given?
285
346
  yield(OpenStruct.new(Issue: self))
286
347
  end
287
348
 
@@ -43,7 +43,7 @@ module Lazylead
43
43
  end
44
44
 
45
45
  def run(sys, postman, opts)
46
- require_rules
46
+ Dir[File.join(__dir__, "*.rb")].sort.each { |f| require f }
47
47
  rules = opts.slice("rules", ",")
48
48
  .map(&:constantize)
49
49
  .map(&:new)
@@ -53,12 +53,6 @@ module Lazylead
53
53
  .each(&:post)
54
54
  postman.send opts.merge(tickets: raised) unless raised.empty?
55
55
  end
56
-
57
- # Load all ticket accuracy rules for future verification
58
- def require_rules
59
- rules = File.dirname(__FILE__)
60
- $LOAD_PATH.unshift(rules) unless $LOAD_PATH.include?(rules)
61
- end
62
56
  end
63
57
  end
64
58
 
@@ -85,7 +79,9 @@ module Lazylead
85
79
 
86
80
  # Post the comment with score and accuracy to the ticket.
87
81
  def post
88
- @issue.post(comment) unless @opts.key? "silent"
82
+ return if @opts.key? "silent"
83
+ @issue.post comment
84
+ @issue.add_label "LL.accuracy", "#{grade(@accuracy)}%"
89
85
  end
90
86
 
91
87
  # The jira comment in markdown format
@@ -136,5 +132,14 @@ module Lazylead
136
132
  .sort_by { |e| e[0] }
137
133
  end
138
134
  end
135
+
136
+ # Calculate grade for accuracy
137
+ # For example,
138
+ # grade(7.5) => 0
139
+ # grade(12) => 10
140
+ # grade(25.5) => 20
141
+ def grade(value)
142
+ (value / 10).floor * 10
143
+ end
139
144
  end
140
145
  end
@@ -22,22 +22,18 @@
22
22
  # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
23
23
  # OR OTHER DEALINGS IN THE SOFTWARE.
24
24
 
25
- require_relative "../../log"
26
- require_relative "../../email"
27
- require_relative "../../version"
28
- require_relative "../../postman"
29
25
  require_relative "requirement"
30
26
 
31
27
  module Lazylead
32
28
  # A requirement that Jira field "Affects Version/s" provided by the reporter.
33
- class RequirementAffectedBuild < Requirement
29
+ class AffectedBuild < Requirement
34
30
  def initialize(score = 0.5)
35
31
  super "Affected build", score, "Affects Version/s"
36
32
  end
37
33
 
38
34
  # @return true if an issue has non-empty "Affects Version/s" field
39
35
  def passed(issue)
40
- !issue.fields["versions"].nil? && !issue.fields["versions"].empty?
36
+ non_blank? issue, "versions"
41
37
  end
42
38
  end
43
39
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 Yurii Dubinka
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"),
9
+ # to deal in the Software without restriction, including without limitation
10
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
11
+ # and/or sell copies of the Software, and to permit persons to whom
12
+ # the Software is furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included
15
+ # in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
23
+ # OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ require_relative "requirement"
26
+
27
+ module Lazylead
28
+ # Check that ticket has an attachment.
29
+ class Attachment < Lazylead::Requirement
30
+ def initialize(desc, score, field)
31
+ super(desc, score, field)
32
+ end
33
+
34
+ def passed(issue)
35
+ issue.attachments.any?(&method(:matching))
36
+ end
37
+
38
+ # Check a single attachment from ticket.
39
+ # Potential extension point for custom verification logic.
40
+ def matching(attachment)
41
+ !attachment.nil?
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 Yurii Dubinka
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"),
9
+ # to deal in the Software without restriction, including without limitation
10
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
11
+ # and/or sell copies of the Software, and to permit persons to whom
12
+ # the Software is furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included
15
+ # in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
23
+ # OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ require_relative "requirement"
26
+
27
+ module Lazylead
28
+ # A requirement that Jira field "Environment" provided by the reporter.
29
+ class Environment < Requirement
30
+ def initialize(score = 0.5)
31
+ super "Environment details (URL, patches)", score, "Environment"
32
+ end
33
+
34
+ # @return true if an issue has non-empty "Environment" field
35
+ def passed(issue)
36
+ non_blank? issue, "environment"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 Yurii Dubinka
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"),
9
+ # to deal in the Software without restriction, including without limitation
10
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
11
+ # and/or sell copies of the Software, and to permit persons to whom
12
+ # the Software is furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included
15
+ # in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
23
+ # OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ require_relative "attachment"
26
+
27
+ module Lazylead
28
+ # Check that ticket has log file(s) in attachment.
29
+ class Logs < Lazylead::Attachment
30
+ def initialize
31
+ super("Log files", 2, "Attachments")
32
+ end
33
+
34
+ # Ensure that ticket has a '*.log' file more '5KB'
35
+ def matching(attachment)
36
+ name = attachment.attrs["filename"].downcase
37
+ return false unless attachment.attrs["size"].to_i > 5120
38
+ return true if File.extname(name).start_with?(".log", ".txt")
39
+ %w[.log.zip .log.gz .log.tar.gz].any? { |l| name.end_with? l }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 Yurii Dubinka
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"),
9
+ # to deal in the Software without restriction, including without limitation
10
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
11
+ # and/or sell copies of the Software, and to permit persons to whom
12
+ # the Software is furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included
15
+ # in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
23
+ # OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ require_relative "attachment"
26
+
27
+ module Lazylead
28
+ # Check that ticket has video record(s) with reproducing results.
29
+ class Records < Lazylead::Attachment
30
+ def initialize(ext = [])
31
+ super("Recorded internal reproducing results", 5, "Attachments")
32
+ @ext = ext
33
+ return unless @ext.empty?
34
+ @ext = %w[.webm .mkv .flv .flv .vob .ogv .ogg .drc .gif .gifv .mng .avi
35
+ .mts .m2ts .ts .mov .qt .wmv .yuv .rm .rmvb .viv .asf .amv .mp4
36
+ .m4p .m4v .mpg .mp2 .mpeg .mpe .mpv .mpg .mpeg .m2v .m4v .svi
37
+ .3gp .3g2 .mxf .roq .nsv .flv .f4v .f4p .f4a .f4b .gif]
38
+ end
39
+
40
+ # Ensure that ticket has an attachment with video-file extension
41
+ def matching(attach)
42
+ @ext.any? { |e| e.eql? File.extname(attach.attrs["filename"]).downcase }
43
+ end
44
+ end
45
+ end
@@ -36,5 +36,14 @@ module Lazylead
36
36
  def passed(_)
37
37
  true
38
38
  end
39
+
40
+ def blank?(issue, field)
41
+ return false if issue.nil?
42
+ issue.fields[field].nil? || issue.fields[field].blank?
43
+ end
44
+
45
+ def non_blank?(issue, field)
46
+ !blank? issue, field
47
+ end
39
48
  end
40
49
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The MIT License
4
+ #
5
+ # Copyright (c) 2019-2020 Yurii Dubinka
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"),
9
+ # to deal in the Software without restriction, including without limitation
10
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
11
+ # and/or sell copies of the Software, and to permit persons to whom
12
+ # the Software is furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included
15
+ # in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
23
+ # OR OTHER DEALINGS IN THE SOFTWARE.
24
+
25
+ require_relative "requirement"
26
+
27
+ module Lazylead
28
+ # Check that ticket has expected links to failed entities on dedicated
29
+ # servers.
30
+ class Servers < Lazylead::Requirement
31
+ #
32
+ # @param envs regexp expressions to match servers in description.
33
+ #
34
+ def initialize(score: 2, envs: [], desc: "Internal reproducing results")
35
+ super desc, score, "Description/Environment"
36
+ @envs = envs
37
+ end
38
+
39
+ def passed(issue)
40
+ return true if @envs.empty?
41
+ lines = issue["environment"].to_s + "\n" + issue.description
42
+ lines.split("\n")
43
+ .reject(&:blank?)
44
+ .map(&:strip)
45
+ .flat_map { |l| l.split(" ").map(&:strip) }
46
+ .select { |w| w.include?("http://") || w.include?("https://") }
47
+ .any? { |u| @envs.any? { |e| u.match? e } }
48
+ end
49
+ end
50
+ end