lazylead 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,111 @@
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 "tmpdir"
26
+ require "nokogiri"
27
+ require "active_support/core_ext/hash/conversions"
28
+ require_relative "../../salt"
29
+ require_relative "../../opts"
30
+
31
+ module Lazylead
32
+ module Task
33
+ module Svn
34
+ #
35
+ # Detect particular text in diff commit.
36
+ #
37
+ class Grep
38
+ def initialize(log = Log.new)
39
+ @log = log
40
+ end
41
+
42
+ def run(_, postman, opts)
43
+ text = opts.slice("text", ",")
44
+ commits = svn_log(opts).select { |c| c.includes? text }
45
+ postman.send(opts.merge(entries: commits)) unless commits.empty?
46
+ end
47
+
48
+ # Return all svn commits for particular date range in repo
49
+ def svn_log(opts)
50
+ now = if opts.key? "now"
51
+ DateTime.parse(opts["now"])
52
+ else
53
+ DateTime.now
54
+ end
55
+ start = (now.to_time - opts["period"].to_i).to_datetime
56
+ cmd = [
57
+ "svn log --diff --no-auth-cache",
58
+ "--username #{opts.decrypt('svn_user', 'svn_salt')}",
59
+ "--password #{opts.decrypt('svn_password', 'svn_salt')}",
60
+ "-r {#{start}}:{#{now}} #{opts['svn_url']}"
61
+ ]
62
+ stdout = `#{cmd.join(" ")}`
63
+ stdout.split("-" * 72).reject(&:blank?).reverse
64
+ .map { |e| Entry.new(e) }
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # Single SVN commit details
71
+ class Entry
72
+ def initialize(commit)
73
+ @commit = commit
74
+ end
75
+
76
+ def to_s
77
+ "#{rev} #{msg}"
78
+ end
79
+
80
+ def rev
81
+ header.first
82
+ end
83
+
84
+ def author
85
+ header[1]
86
+ end
87
+
88
+ def time
89
+ header[2]
90
+ end
91
+
92
+ def msg
93
+ lines[1]
94
+ end
95
+
96
+ # The modified lines contains expected text
97
+ def includes?(text)
98
+ text = [text] unless text.respond_to? :each
99
+ lines[4..-1].select { |l| l.start_with? "+" }
100
+ .any? { |l| text.any? { |t| l.include? t } }
101
+ end
102
+
103
+ def lines
104
+ @lines ||= @commit.split("\n").reject(&:blank?)
105
+ end
106
+
107
+ def header
108
+ @header ||= lines.first.split(" | ").reject(&:blank?)
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,101 @@
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 "tmpdir"
26
+ require "nokogiri"
27
+ require "active_support/core_ext/hash/conversions"
28
+ require_relative "../../salt"
29
+ require_relative "../../opts"
30
+
31
+ module Lazylead
32
+ module Task
33
+ module Svn
34
+ #
35
+ # Send notification about modification of critical files in svn repo.
36
+ #
37
+ class Touch
38
+ def initialize(log = Log.new)
39
+ @log = log
40
+ end
41
+
42
+ def run(_, postman, opts)
43
+ files = opts.slice("files", ",")
44
+ commits = touch(files, opts)
45
+ postman.send(opts.merge(entries: commits)) unless commits.empty?
46
+ end
47
+
48
+ # Return all svn commits for a particular date range, which are touching
49
+ # somehow the critical files within the svn repo.
50
+ def touch(files, opts)
51
+ xpath = files.map { |f| "contains(text(),\"#{f}\")" }.join(" or ")
52
+ svn_log(opts).xpath("//logentry[paths/path[#{xpath}]]")
53
+ .map(&method(:to_entry))
54
+ .each do |e|
55
+ if e.paths.path.respond_to? :delete_if
56
+ e.paths.path.delete_if { |p| files.none? { |f| p.include? f } }
57
+ end
58
+ end
59
+ end
60
+
61
+ # Return all svn commits for particular date range in repo
62
+ def svn_log(opts)
63
+ now = if opts.key? "now"
64
+ DateTime.parse(opts["now"])
65
+ else
66
+ DateTime.now
67
+ end
68
+ start = (now.to_time - opts["period"].to_i).to_datetime
69
+ cmd = [
70
+ "svn log --no-auth-cache",
71
+ "--username #{opts.decrypt('svn_user', 'svn_salt')}",
72
+ "--password #{opts.decrypt('svn_password', 'svn_salt')}",
73
+ "--xml -v -r {#{start}}:{#{now}} #{opts['svn_url']}"
74
+ ]
75
+ raw = `#{cmd.join(" ")}`
76
+ Nokogiri.XML(raw, nil, "UTF-8")
77
+ end
78
+
79
+ # Convert single revision(XML text) to entry object.
80
+ # Entry object is a simple ruby struct object.
81
+ def to_entry(xml)
82
+ e = to_struct(Hash.from_xml(xml.to_s.strip)).logentry
83
+ if e.paths.path.respond_to? :each
84
+ e.paths.path.each(&:strip!)
85
+ else
86
+ e.paths.path.strip!
87
+ end
88
+ e
89
+ end
90
+
91
+ # Make a simple ruby struct object from hash hierarchically,
92
+ # considering nested hash(es) (if applicable)
93
+ def to_struct(hsh)
94
+ OpenStruct.new(
95
+ hsh.transform_values { |v| v.is_a?(Hash) ? to_struct(v) : v }
96
+ )
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -23,5 +23,5 @@
23
23
  # OR OTHER DEALINGS IN THE SOFTWARE.
24
24
 
25
25
  module Lazylead
26
- VERSION = "0.5.2"
26
+ VERSION = "0.6.0"
27
27
  end
@@ -0,0 +1,123 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <style>
5
+ /* CSS styles taken from https://github.com/yegor256/tacit */
6
+ th {
7
+ font-weight: 600
8
+ }
9
+
10
+ table tr {
11
+ border-bottom-width: 2.16px
12
+ }
13
+
14
+ table tr th {
15
+ border-bottom-width: 2.16px
16
+ }
17
+
18
+ table tr td, table tr th {
19
+ overflow: hidden;
20
+ padding: 5.4px 3.6px
21
+ }
22
+
23
+ a {
24
+ color: #275a90;
25
+ text-decoration: none
26
+ }
27
+
28
+ a:hover {
29
+ text-decoration: underline
30
+ }
31
+
32
+ pre, code, kbd, samp, var, output {
33
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
34
+ font-size: 13px
35
+ }
36
+
37
+ pre code {
38
+ background: none;
39
+ border: 0;
40
+ line-height: 29.7px;
41
+ padding: 0
42
+ }
43
+
44
+ code, kbd {
45
+ background: #daf1e0;
46
+ border-radius: 3.6px;
47
+ color: #2a6f3b;
48
+ display: inline-block;
49
+ line-height: 18px;
50
+ padding: 3.6px 6.3px 2.7px
51
+ }
52
+
53
+ * {
54
+ border: 0;
55
+ border-collapse: separate;
56
+ border-spacing: 0;
57
+ box-sizing: border-box;
58
+ margin: 0;
59
+ max-width: 100%;
60
+ padding: 0;
61
+ vertical-align: baseline;
62
+ font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
63
+ font-size: 13px;
64
+ font-stretch: normal;
65
+ font-style: normal;
66
+ font-weight: 400;
67
+ line-height: 29.7px
68
+ }
69
+
70
+ html, body {
71
+ width: 100%
72
+ }
73
+
74
+ html {
75
+ height: 100%
76
+ }
77
+
78
+ body {
79
+ background: #fff;
80
+ color: #1a1919;
81
+ padding: 36px
82
+ }
83
+ </style>
84
+ <title>Not authorized "Assignee" change</title>
85
+ </head>
86
+ <body>
87
+ <p>Hi,</p>
88
+
89
+ <p>The <span style='font-weight:bold'>'Assignee'</span> for the following
90
+ ticket(s) changed by not authorized person(s):</p>
91
+ <table summary="ticket(s) where assignee changed">
92
+ <tr>
93
+ <th id="key">Key</th>
94
+ <th id="priority">Priority</th>
95
+ <th id="when">When</th>
96
+ <th id="who">Who</th>
97
+ <th id="to">To</th>
98
+ <th id="summary">Summary</th>
99
+ <th id="reporter">Reporter</th>
100
+ </tr>
101
+ <% assignees.each do |a| %>
102
+ <tr>
103
+ <td><a href='<%= a.issue.url %>'><%= a.issue.key %></a></td>
104
+ <td><%= a.issue.priority %></td>
105
+ <td><%= DateTime.parse(a.last["created"])
106
+ .strftime('%d-%b-%Y %I:%M:%S %p') %></td>
107
+ <td><span style='color: red'><%= a.last["author"]["displayName"] %></span>
108
+ (<%= a.last["author"]["name"] %>)
109
+ </td>
110
+ <td><%= a.to %></td>
111
+ <td><%= a.issue.summary %></td>
112
+ <td><%= a.issue.reporter.name %></td>
113
+ </tr>
114
+ <% end %>
115
+ </table>
116
+
117
+ <p>Authorized person(s) are: <code><%= allowed %></code>.</p>
118
+
119
+ <p>Posted by
120
+ <a href="https://github.com/dgroup/lazylead">lazylead v<%= version %></a>.
121
+ </p>
122
+ </body>
123
+ </html>
@@ -92,6 +92,7 @@
92
92
  <tr>
93
93
  <th id="key">Key</th>
94
94
  <th id="priority">Priority</th>
95
+ <th id="version">Fix Version</th>
95
96
  <th id="when">When</th>
96
97
  <th id="who">Who</th>
97
98
  <th id="reporter">Reporter</th>
@@ -101,6 +102,7 @@
101
102
  <tr>
102
103
  <td><a href='<%= v.issue.url %>'><%= v.issue.key %></a></td>
103
104
  <td><%= v.issue.priority %></td>
105
+ <td><%= v.current %></td>
104
106
  <td><%= DateTime.parse(v.last["created"])
105
107
  .strftime('%d-%b-%Y %I:%M:%S %p') %></td>
106
108
  <td><span style='color: red'><%= v.last["author"]["displayName"] %></span>
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <style>
5
+ /* CSS styles taken from https://github.com/yegor256/tacit */
6
+ pre, code, kbd, samp, var, output {
7
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
8
+ font-size: 14.4px
9
+ }
10
+
11
+ pre code {
12
+ background: none;
13
+ border: 0;
14
+ line-height: 29.7px;
15
+ padding: 0
16
+ }
17
+
18
+ code, kbd {
19
+ background: #daf1e0;
20
+ border-radius: 3.6px;
21
+ color: #2a6f3b;
22
+ display: inline-block;
23
+ line-height: 18px;
24
+ padding: 3.6px 6.3px 2.7px
25
+ }
26
+
27
+ a {
28
+ color: #275a90;
29
+ text-decoration: none
30
+ }
31
+
32
+ a:hover {
33
+ text-decoration: underline
34
+ }
35
+
36
+ * {
37
+ border: 0;
38
+ border-collapse: separate;
39
+ border-spacing: 0;
40
+ box-sizing: border-box;
41
+ margin: 0;
42
+ max-width: 100%;
43
+ padding: 0;
44
+ vertical-align: baseline;
45
+ font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
46
+ font-size: 13px;
47
+ font-stretch: normal;
48
+ font-style: normal;
49
+ font-weight: 400;
50
+ line-height: 29.7px
51
+ }
52
+
53
+ html, body {
54
+ width: 100%
55
+ }
56
+
57
+ html {
58
+ height: 100%
59
+ }
60
+
61
+ body {
62
+ background: #fff;
63
+ color: #1a1919;
64
+ padding: 36px
65
+ }
66
+
67
+ .commit {
68
+ min-width: 100%;
69
+ border-radius: 3.5px;
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;
75
+ }
76
+
77
+ .commit * {
78
+ padding-left: 4px;
79
+ font-size: 8px;
80
+ line-height: 15px;
81
+ }
82
+ </style>
83
+ <title>SVN log</title>
84
+ </head>
85
+ <body>
86
+ <p>Hi,</p>
87
+ <p>The following file(s) changed since <code><%= since_rev %></code> rev in
88
+ <a href="<%= svn_url %>"><%= svn_url %></a>:</p>
89
+ <% stdout.split("------------------------------------------------------------------------").reject(&:blank?).reverse.each do |commit| %>
90
+ <div class="commit">
91
+ <% commit.split("\n").reject(&:blank?).each_with_index do |line, index| %>
92
+ <% if index.zero? %>
93
+ <% details = line.split("|").map(&:strip).reject(&:blank?) %>
94
+ <p style="background: gainsboro;">
95
+ <a href="<%= commit_url %><%= details[0][1..-1] %>"><%= details[0] %></a>
96
+ by <a href="<%= user %><%= details[1] %>"><%= details[1] %></a>
97
+ at <span style="color: #275a90;"><%= details[2] %></span>
98
+ </p>
99
+ <% end %>
100
+ <% if index == 1 %>
101
+ <p style="background: gainsboro;"><%= line %></p>
102
+ <% end %>
103
+ <% end %>
104
+ </div>
105
+ <% end %>
106
+ <p>Posted by
107
+ <a href="https://github.com/dgroup/lazylead">lazylead v<%= version %></a>.
108
+ </p>
109
+ </body>
110
+ </html>