lazylead 0.9.1 → 0.10.1
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/.rubocop.yml +1 -1
- data/Rakefile +1 -2
- data/lazylead.gemspec +13 -13
- data/lib/lazylead/confluence.rb +1 -2
- data/lib/lazylead/os.rb +55 -0
- data/lib/lazylead/task/accuracy/accuracy.rb +2 -3
- data/lib/lazylead/task/accuracy/attachment.rb +1 -0
- data/lib/lazylead/task/accuracy/environment.rb +3 -8
- data/lib/lazylead/task/accuracy/logs_link.rb +52 -0
- data/lib/lazylead/task/accuracy/records.rb +1 -1
- data/lib/lazylead/task/accuracy/required.rb +43 -0
- data/lib/lazylead/task/accuracy/screenshots.rb +2 -3
- data/lib/lazylead/task/accuracy/servers.rb +7 -7
- data/lib/lazylead/task/accuracy/testcase.rb +27 -20
- data/lib/lazylead/task/fix_version.rb +5 -8
- data/lib/lazylead/task/loading.rb +3 -2
- data/lib/lazylead/task/micromanager.rb +27 -3
- data/lib/lazylead/task/svn/diff.rb +9 -9
- data/lib/lazylead/task/svn/grep.rb +8 -71
- data/lib/lazylead/task/svn/svn.rb +107 -0
- data/lib/lazylead/task/svn/touch.rb +5 -7
- data/lib/lazylead/version.rb +1 -1
- data/lib/messages/illegal_assignee_change.erb +1 -2
- data/lib/messages/illegal_duedate_change.erb +5 -3
- data/lib/messages/loading.erb +2 -2
- data/lib/messages/svn_diff.erb +29 -29
- data/lib/messages/svn_diff_attachment.erb +5 -7
- data/lib/messages/svn_grep.erb +34 -32
- data/readme.md +8 -5
- data/test/lazylead/system/jira_test.rb +8 -0
- data/test/lazylead/task/accuracy/accuracy_test.rb +1 -1
- data/test/lazylead/task/accuracy/logs_link_test.rb +50 -0
- data/test/lazylead/task/accuracy/onlyll_test.rb +1 -1
- data/test/lazylead/task/accuracy/screenshots_test.rb +2 -4
- data/test/lazylead/task/accuracy/testcase_test.rb +34 -0
- data/test/lazylead/task/alert/alertif_test.rb +2 -1
- data/test/lazylead/task/assignment_test.rb +2 -1
- data/test/lazylead/task/created_recently_test.rb +2 -1
- data/test/lazylead/task/duedate_test.rb +2 -2
- data/test/lazylead/task/fix_version_test.rb +2 -1
- data/test/lazylead/task/loading_test.rb +6 -2
- data/test/lazylead/task/micromanager_test.rb +8 -1
- data/test/lazylead/task/missing_comment_test.rb +1 -1
- data/test/lazylead/task/svn/diff_test.rb +1 -1
- data/test/lazylead/task/svn/grep_test.rb +2 -1
- data/test/test.rb +4 -3
- metadata +31 -25
@@ -72,8 +72,9 @@ module Lazylead
|
|
72
72
|
"#{id} has #{total} tasks"
|
73
73
|
end
|
74
74
|
|
75
|
-
def sprints
|
76
|
-
@tasks.group_by(&:sprint).sort
|
75
|
+
def sprints(label)
|
76
|
+
return @tasks.group_by(&:sprint).sort if label.nil? || label.blank?
|
77
|
+
@tasks.group_by { |t| t.sprint(label) }.sort
|
77
78
|
end
|
78
79
|
end
|
79
80
|
|
@@ -43,20 +43,39 @@ module Lazylead
|
|
43
43
|
def run(sys, postman, opts)
|
44
44
|
allowed = opts.slice "allowed", ","
|
45
45
|
dues = sys.issues(opts["jql"], opts.jira_defaults.merge(expand: "changelog"))
|
46
|
-
.map { |i| Due.new(i, allowed) }
|
46
|
+
.map { |i| Due.new(i, allowed, since(opts)) }
|
47
47
|
.select(&:illegal?)
|
48
|
+
.reject(&:obsolete?)
|
48
49
|
return if dues.empty?
|
49
50
|
postman.send opts.merge(dues: dues)
|
50
51
|
end
|
52
|
+
|
53
|
+
# Detect history period where search should start.
|
54
|
+
#
|
55
|
+
# opts["period"] The default period for past is 1 day (86400 seconds).
|
56
|
+
# So, if now 2017-04-06 15:50:58.674+0000
|
57
|
+
# it returns 2017-04-05 15:50:58 +0000
|
58
|
+
#
|
59
|
+
# opts["now"] The current time for unit tests.
|
60
|
+
# If absent the "Time.now" is used.
|
61
|
+
#
|
62
|
+
def since(opts)
|
63
|
+
@since ||= if opts.key? "now"
|
64
|
+
Time.parse(opts["now"])
|
65
|
+
else
|
66
|
+
Time.now - opts.fetch("period", "86400").to_i
|
67
|
+
end
|
68
|
+
end
|
51
69
|
end
|
52
70
|
|
53
71
|
# Instance of "Due" history item for the particular ticket.
|
54
72
|
class Due
|
55
73
|
attr_reader :issue, :when
|
56
74
|
|
57
|
-
def initialize(issue, allowed)
|
75
|
+
def initialize(issue, allowed, since)
|
58
76
|
@issue = issue
|
59
77
|
@allowed = allowed
|
78
|
+
@since = since
|
60
79
|
end
|
61
80
|
|
62
81
|
# Gives true when last change of "Due Date" field was done
|
@@ -66,6 +85,11 @@ module Lazylead
|
|
66
85
|
@allowed.none? { |a| a.eql? last.id }
|
67
86
|
end
|
68
87
|
|
88
|
+
# Give true when "Due Date" changes happens in past and its alert already sent.
|
89
|
+
def obsolete?
|
90
|
+
@when < @since
|
91
|
+
end
|
92
|
+
|
69
93
|
# Detect details about last change of "Due Date" to non-null value
|
70
94
|
def last
|
71
95
|
@last ||= begin
|
@@ -76,7 +100,7 @@ module Lazylead
|
|
76
100
|
@when = @issue["created"]
|
77
101
|
dd = @issue.reporter
|
78
102
|
else
|
79
|
-
@when = dd["created"]
|
103
|
+
@when = dd["created"]
|
80
104
|
dd = Lazylead::User.new(dd["author"])
|
81
105
|
end
|
82
106
|
dd
|
@@ -27,8 +27,10 @@ require "tempfile"
|
|
27
27
|
require "nokogiri"
|
28
28
|
require "backtrace"
|
29
29
|
require "active_support/core_ext/hash/conversions"
|
30
|
+
require_relative "../../os"
|
30
31
|
require_relative "../../salt"
|
31
32
|
require_relative "../../opts"
|
33
|
+
require_relative "svn"
|
32
34
|
|
33
35
|
module Lazylead
|
34
36
|
module Task
|
@@ -43,15 +45,13 @@ module Lazylead
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def run(_, postman, opts)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
stdout.scrub!
|
54
|
-
send_email postman, opts.merge(stdout: stdout) unless stdout.blank?
|
48
|
+
stdout = OS.new.run "svn log --diff --no-auth-cache",
|
49
|
+
"--username #{opts.decrypt('svn_user', 'svn_salt')}",
|
50
|
+
"--password #{opts.decrypt('svn_password', 'svn_salt')}",
|
51
|
+
"-r#{opts['since_rev']}:HEAD #{opts['svn_url']}"
|
52
|
+
return if stdout.blank?
|
53
|
+
commits = Lazylead::Svn::Commits.new(stdout)
|
54
|
+
send_email postman, opts.merge(commits: commits) unless commits.empty?
|
55
55
|
end
|
56
56
|
|
57
57
|
# Send email with svn log as an attachment.
|
@@ -25,8 +25,10 @@
|
|
25
25
|
require "tmpdir"
|
26
26
|
require "nokogiri"
|
27
27
|
require "active_support/core_ext/hash/conversions"
|
28
|
+
require_relative "../../os"
|
28
29
|
require_relative "../../salt"
|
29
30
|
require_relative "../../opts"
|
31
|
+
require_relative "svn"
|
30
32
|
|
31
33
|
module Lazylead
|
32
34
|
module Task
|
@@ -47,14 +49,12 @@ module Lazylead
|
|
47
49
|
|
48
50
|
# Return all svn commits for particular date range in repo
|
49
51
|
def svn_log(opts)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
stdout = `#{cmd.join(" ")}`
|
57
|
-
stdout.split("-" * 72).reject(&:blank?).reverse.map { |e| Entry.new(e) }
|
52
|
+
stdout = OS.new.run "svn log --diff --no-auth-cache",
|
53
|
+
"--username #{opts.decrypt('svn_user', 'svn_salt')}",
|
54
|
+
"--password #{opts.decrypt('svn_password', 'svn_salt')}",
|
55
|
+
"-r {#{from(opts)}}:{#{now(opts)}} #{opts['svn_url']}"
|
56
|
+
return [] if stdout.blank?
|
57
|
+
Lazylead::Svn::Commits.new(stdout)
|
58
58
|
end
|
59
59
|
|
60
60
|
# The start date & time for search range
|
@@ -73,67 +73,4 @@ module Lazylead
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
76
|
-
|
77
|
-
# Single SVN commit details
|
78
|
-
class Entry
|
79
|
-
def initialize(commit)
|
80
|
-
@commit = commit
|
81
|
-
end
|
82
|
-
|
83
|
-
def to_s
|
84
|
-
"#{rev} #{msg}"
|
85
|
-
end
|
86
|
-
|
87
|
-
def rev
|
88
|
-
header.first[1..]
|
89
|
-
end
|
90
|
-
|
91
|
-
def author
|
92
|
-
header[1]
|
93
|
-
end
|
94
|
-
|
95
|
-
def time
|
96
|
-
header[2]
|
97
|
-
end
|
98
|
-
|
99
|
-
def msg
|
100
|
-
lines[1]
|
101
|
-
end
|
102
|
-
|
103
|
-
# The modified lines contains expected text
|
104
|
-
def includes?(text)
|
105
|
-
text = [text] unless text.respond_to? :each
|
106
|
-
lines[4..].select { |l| l.start_with? "+" }
|
107
|
-
.any? { |l| text.any? { |t| l.include? t } }
|
108
|
-
end
|
109
|
-
|
110
|
-
def lines
|
111
|
-
@lines ||= @commit.split("\n").reject(&:blank?)
|
112
|
-
end
|
113
|
-
|
114
|
-
def header
|
115
|
-
@header ||= lines.first.split(" | ").reject(&:blank?)
|
116
|
-
end
|
117
|
-
|
118
|
-
# Detect SVN diff lines with particular text
|
119
|
-
def diff(text)
|
120
|
-
@diff ||= begin
|
121
|
-
files = affected(text).uniq
|
122
|
-
@commit.split("Index: ")
|
123
|
-
.select { |i| files.any? { |f| i.start_with? f } }
|
124
|
-
.map { |i| i.split "\n" }
|
125
|
-
.flatten
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
# Detect affected files with particular text
|
130
|
-
def affected(text)
|
131
|
-
occurrences = lines.each_index.select do |i|
|
132
|
-
lines[i].start_with?("+") && text.any? { |t| lines[i].include? t }
|
133
|
-
end
|
134
|
-
occurrences.map do |occ|
|
135
|
-
lines[2..occ].reverse.find { |l| l.start_with? "Index: " }[7..]
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
76
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The MIT License
|
4
|
+
#
|
5
|
+
# Copyright (c) 2019-2021 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 "forwardable"
|
26
|
+
|
27
|
+
module Lazylead
|
28
|
+
module Svn
|
29
|
+
#
|
30
|
+
# SVN commit built from command-line stdout (OS#run).
|
31
|
+
#
|
32
|
+
class Commit
|
33
|
+
def initialize(raw)
|
34
|
+
@raw = raw
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
"#{rev} #{msg}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def rev
|
42
|
+
header.first[1..]
|
43
|
+
end
|
44
|
+
|
45
|
+
def author
|
46
|
+
header[1]
|
47
|
+
end
|
48
|
+
|
49
|
+
def time
|
50
|
+
DateTime.parse(header[2]).strftime("%d-%m-%Y %H:%M:%S")
|
51
|
+
end
|
52
|
+
|
53
|
+
def msg
|
54
|
+
lines[1]
|
55
|
+
end
|
56
|
+
|
57
|
+
def lines
|
58
|
+
@lines ||= @raw.split("\n").reject(&:blank?)
|
59
|
+
end
|
60
|
+
|
61
|
+
def header
|
62
|
+
@header ||= lines.first.split(" | ").reject(&:blank?)
|
63
|
+
end
|
64
|
+
|
65
|
+
# The modified lines contains expected text
|
66
|
+
def includes?(text)
|
67
|
+
text = [text] unless text.respond_to? :each
|
68
|
+
lines[4..].select { |l| l.start_with? "+" }.any? { |l| text.any? { |t| l.include? t } }
|
69
|
+
end
|
70
|
+
|
71
|
+
# Detect SVN diff lines with particular text
|
72
|
+
def diff(text)
|
73
|
+
@diff ||= begin
|
74
|
+
files = affected(text).uniq
|
75
|
+
@raw.split("Index: ")
|
76
|
+
.select { |i| files.any? { |f| i.start_with? f } }
|
77
|
+
.map { |i| i.split "\n" }
|
78
|
+
.flatten
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Detect affected files with particular text
|
83
|
+
def affected(text)
|
84
|
+
occurrences = lines.each_index.select do |i|
|
85
|
+
lines[i].start_with?("+") && text.any? { |t| lines[i].include? t }
|
86
|
+
end
|
87
|
+
occurrences.map do |occ|
|
88
|
+
lines[2..occ].reverse.find { |l| l.start_with? "Index: " }[7..]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# SVN commits built from command-line stdout (OS#run).
|
95
|
+
#
|
96
|
+
class Commits
|
97
|
+
extend Forwardable
|
98
|
+
def_delegators :@all, :each, :map, :select, :empty?
|
99
|
+
|
100
|
+
# @todo #/DEV Find a way how to avoid @all initialization directly in constructor.
|
101
|
+
# There should be a way how to make lazy initialization of @all with 'forwardable'.
|
102
|
+
def initialize(stdout)
|
103
|
+
@all = stdout.split("-" * 72).reject(&:blank?).reverse.map { |e| Commit.new(e) }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -25,6 +25,7 @@
|
|
25
25
|
require "tmpdir"
|
26
26
|
require "nokogiri"
|
27
27
|
require "active_support/core_ext/hash/conversions"
|
28
|
+
require_relative "../../os"
|
28
29
|
require_relative "../../salt"
|
29
30
|
require_relative "../../opts"
|
30
31
|
|
@@ -64,13 +65,10 @@ module Lazylead
|
|
64
65
|
DateTime.now
|
65
66
|
end
|
66
67
|
start = (now.to_time - opts["period"].to_i).to_datetime
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
"--xml -v -r {#{start}}:{#{now}} #{opts['svn_url']}"
|
72
|
-
]
|
73
|
-
raw = `#{cmd.join(" ")}`
|
68
|
+
raw = OS.new.run "svn log --no-auth-cache",
|
69
|
+
"--username #{opts.decrypt('svn_user', 'svn_salt')}",
|
70
|
+
"--password #{opts.decrypt('svn_password', 'svn_salt')}",
|
71
|
+
"--xml -v -r {#{start}}:{#{now}} #{opts['svn_url']}"
|
74
72
|
Nokogiri.XML(raw, nil, "UTF-8")
|
75
73
|
end
|
76
74
|
|
data/lib/lazylead/version.rb
CHANGED
@@ -102,8 +102,7 @@
|
|
102
102
|
<tr>
|
103
103
|
<td><a href='<%= a.issue.url %>'><%= a.issue.key %></a></td>
|
104
104
|
<td><%= a.issue.priority %></td>
|
105
|
-
<td><%= DateTime.parse(a.last["created"])
|
106
|
-
.strftime('%d-%b-%Y %I:%M:%S %p') %></td>
|
105
|
+
<td><%= DateTime.parse(a.last["created"]).strftime('%d-%b') %></td>
|
107
106
|
<td><span style='color: red'><%= a.last["author"]["displayName"] %></span>
|
108
107
|
(<%= a.last["author"]["name"] %>)
|
109
108
|
</td>
|
@@ -95,18 +95,20 @@
|
|
95
95
|
<th id="when">When</th>
|
96
96
|
<th id="who">Who</th>
|
97
97
|
<th id="to">To</th>
|
98
|
-
<th id="
|
98
|
+
<th id="assignee">Assignee</th>
|
99
99
|
<th id="reporter">Reporter</th>
|
100
|
+
<th id="summary">Summary</th>
|
100
101
|
</tr>
|
101
102
|
<% dues.each do |d| %>
|
102
103
|
<tr>
|
103
104
|
<td><a href='<%= d.issue.url %>'><%= d.issue.key %></a></td>
|
104
105
|
<td><%= d.issue.priority %></td>
|
105
|
-
<td><%= d.when %></td>
|
106
|
+
<td><%= d.when.to_date %></td>
|
106
107
|
<td><span style='color: red'><%= d.last.name %></span> (<%= d.last.id %>)</td>
|
107
108
|
<td><span style='color: red'><%= d.issue.duedate %></span></td>
|
108
|
-
<td><%= d.issue.
|
109
|
+
<td><%= d.issue.assignee.name %></td>
|
109
110
|
<td><%= d.issue.reporter.name %></td>
|
111
|
+
<td><%= d.issue.summary %></td>
|
110
112
|
</tr>
|
111
113
|
<% end %>
|
112
114
|
</table>
|
data/lib/messages/loading.erb
CHANGED
@@ -73,7 +73,7 @@
|
|
73
73
|
<th id="total">
|
74
74
|
<a href="<%= search_link %><%= CGI.escape(jql) %>">Assigned From</a>
|
75
75
|
</th>
|
76
|
-
<th id="duedate">Next
|
76
|
+
<th id="duedate">Next due date</th>
|
77
77
|
</tr>
|
78
78
|
<% assignments.each do |teammate, assignment| %>
|
79
79
|
<% if assignment.free? %>
|
@@ -89,7 +89,7 @@
|
|
89
89
|
<% if assignment.free? %>
|
90
90
|
<span style="color: red">0</span>
|
91
91
|
<% else %>
|
92
|
-
<% assignment.sprints.each do |s| %>
|
92
|
+
<% assignment.sprints(defined?(sprint) ? sprint : "customfield_10480").each do |s| %>
|
93
93
|
<a href="<%= search_link %><%= CGI.escape("#{jql} and assignee=#{teammate}") %>">
|
94
94
|
<%= s.first.blank? ? "No sprint" : s.first %>: <%= s.last.size %>
|
95
95
|
</a>
|
data/lib/messages/svn_diff.erb
CHANGED
@@ -64,20 +64,13 @@
|
|
64
64
|
padding: 36px
|
65
65
|
}
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
overflow: hidden;
|
71
|
-
display: inline-block;
|
72
|
-
line-height: 15px;
|
73
|
-
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
74
|
-
border: 1px solid #BCC6CC;
|
67
|
+
th, td {
|
68
|
+
padding-left: 3px;
|
69
|
+
padding-right: 3px;
|
75
70
|
}
|
76
71
|
|
77
|
-
|
78
|
-
|
79
|
-
font-size: 8px;
|
80
|
-
line-height: 12px;
|
72
|
+
th {
|
73
|
+
font-weight: bold;
|
81
74
|
}
|
82
75
|
</style>
|
83
76
|
<title>SVN log</title>
|
@@ -86,23 +79,30 @@
|
|
86
79
|
<p>Hi,</p>
|
87
80
|
<p>The following file(s) changed since <code><%= since_rev %></code> rev in
|
88
81
|
<a href="<%= svn_url %>"><%= svn_url %></a>:</p>
|
89
|
-
|
90
|
-
<
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
<
|
101
|
-
|
102
|
-
</
|
103
|
-
|
104
|
-
|
105
|
-
|
82
|
+
<table summary="diff">
|
83
|
+
<tr>
|
84
|
+
<th id="rev">rev</th>
|
85
|
+
<th id="author">Author</th>
|
86
|
+
<th id="time">When</th>
|
87
|
+
<th id="msg">Message</th>
|
88
|
+
</tr>
|
89
|
+
<% commits.each do |commit| %>
|
90
|
+
<tr>
|
91
|
+
<td>
|
92
|
+
<div class="auto">
|
93
|
+
<a href="<%= commit_url %><%= commit.rev %>"><%= commit.rev %></a>
|
94
|
+
</div>
|
95
|
+
</td>
|
96
|
+
<td>
|
97
|
+
<div class="auto">
|
98
|
+
<a href="<%= user %><%= commit.author %>"><%= commit.author %></a>
|
99
|
+
</div>
|
100
|
+
</td>
|
101
|
+
<td><%= commit.time %></td>
|
102
|
+
<td><%= commit.lines[1] %></td>
|
103
|
+
</tr>
|
104
|
+
<% end %>
|
105
|
+
</table>
|
106
106
|
<p>Posted by
|
107
107
|
<a href="https://github.com/dgroup/lazylead">lazylead v<%= version %></a>.
|
108
108
|
</p>
|