lazylead 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/ .dockerignore +12 -0
  3. data/.0pdd.yml +5 -0
  4. data/.circleci/config.yml +50 -0
  5. data/.circleci/release_image.sh +13 -0
  6. data/.circleci/validate.sh +10 -0
  7. data/.docker/Dockerfile +31 -0
  8. data/.docker/build.sh +6 -0
  9. data/.docker/docker-compose.yml +23 -0
  10. data/.docker/readme.md +21 -0
  11. data/.gitattributes +9 -0
  12. data/.github/CODE_OF_CONDUCT.md +76 -0
  13. data/.github/CONTRIBUTING.md +9 -0
  14. data/.github/ISSUE_TEMPLATE.md +14 -0
  15. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  16. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  17. data/.github/PULL_REQUEST_TEMPLATE.md +11 -0
  18. data/.github/tasks.yml +24 -0
  19. data/.github/trello.md +18 -0
  20. data/.gitignore +12 -0
  21. data/.pdd +5 -0
  22. data/.rubocop.yml +87 -0
  23. data/.ruby-version +1 -0
  24. data/.rultor.yml +31 -0
  25. data/.simplecov +16 -0
  26. data/.travis.yml +16 -0
  27. data/Gemfile +27 -0
  28. data/Guardfile +33 -0
  29. data/Rakefile +93 -0
  30. data/appveyor.yml +50 -0
  31. data/bin/.ruby-version +1 -0
  32. data/bin/lazylead +99 -0
  33. data/build.sh +6 -0
  34. data/deploy.sh +16 -0
  35. data/lazylead.gemspec +106 -0
  36. data/lib/lazylead.rb +52 -0
  37. data/lib/lazylead/allocated.rb +56 -0
  38. data/lib/lazylead/cli/app.rb +87 -0
  39. data/lib/lazylead/confluence.rb +157 -0
  40. data/lib/lazylead/email.rb +74 -0
  41. data/lib/lazylead/exchange.rb +83 -0
  42. data/lib/lazylead/log.rb +71 -0
  43. data/lib/lazylead/model.rb +140 -0
  44. data/lib/lazylead/postman.rb +78 -0
  45. data/lib/lazylead/salt.rb +91 -0
  46. data/lib/lazylead/schedule.rb +88 -0
  47. data/lib/lazylead/smtp.rb +82 -0
  48. data/lib/lazylead/system/empty.rb +36 -0
  49. data/lib/lazylead/system/fake.rb +41 -0
  50. data/lib/lazylead/system/jira.rb +249 -0
  51. data/lib/lazylead/system/synced.rb +41 -0
  52. data/lib/lazylead/task/alert.rb +105 -0
  53. data/lib/lazylead/task/confluence_ref.rb +59 -0
  54. data/lib/lazylead/task/echo.rb +38 -0
  55. data/lib/lazylead/task/fix_version.rb +79 -0
  56. data/lib/lazylead/task/missing_comment.rb +53 -0
  57. data/lib/lazylead/version.rb +27 -0
  58. data/lib/messages/due_date_expired.erb +96 -0
  59. data/lib/messages/illegal_fixversion_change.erb +120 -0
  60. data/lib/messages/missing_comment.erb +128 -0
  61. data/license.txt +21 -0
  62. data/readme.md +160 -0
  63. data/test/lazylead/allocated_test.rb +51 -0
  64. data/test/lazylead/cli/app_test.rb +74 -0
  65. data/test/lazylead/confluence_test.rb +55 -0
  66. data/test/lazylead/exchange_test.rb +68 -0
  67. data/test/lazylead/model_test.rb +65 -0
  68. data/test/lazylead/salt_test.rb +42 -0
  69. data/test/lazylead/smoke_test.rb +38 -0
  70. data/test/lazylead/smtp_test.rb +65 -0
  71. data/test/lazylead/system/jira_test.rb +110 -0
  72. data/test/lazylead/task/confluence_ref_test.rb +66 -0
  73. data/test/lazylead/task/duedate_test.rb +126 -0
  74. data/test/lazylead/task/echo_test.rb +34 -0
  75. data/test/lazylead/task/fix_version_test.rb +52 -0
  76. data/test/lazylead/task/missing_comment_test.rb +56 -0
  77. data/test/lazylead/version_test.rb +36 -0
  78. data/test/sqlite_test.rb +80 -0
  79. data/test/test.rb +103 -0
  80. data/todo.yml +16 -0
  81. data/upgrades/sqlite/001-install-main-lazylead-tables.sql +63 -0
  82. data/upgrades/sqlite/999.testdata.sql +39 -0
  83. metadata +815 -0
@@ -0,0 +1,41 @@
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
+ module Lazylead
26
+ # Thread-save ticketing system.
27
+ class Synced
28
+ def initialize(sys)
29
+ @mutex = Mutex.new
30
+ @sys = sys
31
+ end
32
+
33
+ # @todo #/DEV Unit tests for 'issues' function
34
+ #
35
+ def issues(jql)
36
+ @mutex.synchronize do
37
+ @sys.issues jql
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,105 @@
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 "../log"
26
+ require_relative "../email"
27
+ require_relative "../version"
28
+ require_relative "../postman"
29
+
30
+ module Lazylead
31
+ #
32
+ # Email alerts about particular issue(s).
33
+ #
34
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
35
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
36
+ # License:: MIT
37
+ module Task
38
+ #
39
+ # Notify particular person about tickets based on pre-defined html template.
40
+ #
41
+ # The task supports the following features:
42
+ # - fetch issues from remote ticketing system by query
43
+ # - prepare email based on predefined template (*.erb)
44
+ # - send the required notifications pre-defined "addressee".
45
+ class Alert
46
+ def initialize(log = Log::NOTHING)
47
+ @log = log
48
+ end
49
+
50
+ def run(sys, postman, opts)
51
+ postman.send opts.merge(tickets: sys.issues(opts["sql"]))
52
+ end
53
+ end
54
+
55
+ #
56
+ # A task that sends notifications about issues to their assignees.
57
+ #
58
+ # The task supports the following features:
59
+ # - fetch issues from remote ticketing system by query
60
+ # - group all issues by assignee
61
+ # - prepare email based on predefined template (*.erb)
62
+ # - send the required notifications to each assignee
63
+ #
64
+ # The email message is sending to the assignee regarding all his/her issues,
65
+ # not like one email per each issue.
66
+ class AssigneeAlert
67
+ def initialize(log = Log::NOTHING)
68
+ @log = log
69
+ end
70
+
71
+ def run(sys, postman, opts)
72
+ sys.issues(opts["sql"])
73
+ .group_by(&:assignee)
74
+ .each do |a, t|
75
+ postman.send opts.merge(to: a.email, addressee: a.name, tickets: t)
76
+ end
77
+ end
78
+ end
79
+
80
+ #
81
+ # A task that sends notifications about issues to their reporters.
82
+ #
83
+ # The task supports the following features:
84
+ # - fetch issues from remote ticketing system by query
85
+ # - group all issues by reporter
86
+ # - prepare email based on predefined template (*.erb)
87
+ # - send the required notifications to each reporter
88
+ #
89
+ # The email message is sending to the assignee regarding all his/her issues,
90
+ # not like one email per each issue.
91
+ class ReporterAlert
92
+ def initialize(log = Log::NOTHING)
93
+ @log = log
94
+ end
95
+
96
+ def run(sys, postman, opts)
97
+ sys.issues(opts["sql"])
98
+ .group_by(&:reporter)
99
+ .each do |a, t|
100
+ postman.send opts.merge(to: a.email, addressee: a.name, tickets: t)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,59 @@
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 "json"
26
+ require "faraday"
27
+ require_relative "../system/jira"
28
+ require_relative "../log"
29
+ require_relative "../confluence"
30
+
31
+ module Lazylead
32
+ module Task
33
+ # The lazylead task which adds reference to Confluence page
34
+ # in case if Confluence page was mentioned in issue comments.
35
+ # @todo #/DEV Support sub-task for link search. Potentially, the issue
36
+ # might have sub-tasks where discussion ongoing.
37
+ class ConfluenceRef
38
+ def initialize(log = Log::NOTHING)
39
+ @log = log
40
+ end
41
+
42
+ def run(sys, _, opts)
43
+ confluences = confluences(opts)
44
+ return if confluences.empty?
45
+ sys.issues(opts["jql"])
46
+ .map { |i| Link.new(i, sys, confluences) }
47
+ .each(&:fetch_links)
48
+ .select(&:need_link?)
49
+ .each(&:add_link)
50
+ end
51
+
52
+ def confluences(opts)
53
+ return [] if opts["confluences"].nil? || opts["confluences"].blank?
54
+ JSON.parse(opts["confluences"], object_class: OpenStruct)
55
+ .map { |c| Confluence.new(c) }
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,38 @@
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
+ module Lazylead
26
+ module Task
27
+ # Lazylead task which prints to STDOUT the current class name and team.
28
+ #
29
+ # Author:: Yurii Dubinka (yurii.dubinka@gmail.com)
30
+ # Copyright:: Copyright (c) 2019-2020 Yurii Dubinka
31
+ # License:: MIT
32
+ class Echo
33
+ def run(_, _, _)
34
+ self.class.to_s
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,79 @@
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 "date"
26
+ require_relative "../system/jira"
27
+ require_relative "../log"
28
+
29
+ module Lazylead
30
+ module Task
31
+ # @todo #/DEV Each task should verify input arguments.
32
+ # The common API should be provided for each task.
33
+ class FixVersion
34
+ def initialize(log = Log::NOTHING)
35
+ @log = log
36
+ end
37
+
38
+ def run(sys, postman, opts)
39
+ allowed = opts["allowed"].split(",").map(&:strip).reject(&:blank?)
40
+ postman.send opts.merge(
41
+ versions: sys.issues(opts["jql"], expand: "changelog")
42
+ .map { |i| Version.new(i, allowed) }
43
+ .select(&:changed?)
44
+ )
45
+ end
46
+ end
47
+
48
+ # Instance of "Fix Version" field for the particular task.
49
+ class Version
50
+ attr_reader :issue
51
+
52
+ def initialize(issue, allowed)
53
+ @issue = issue
54
+ @allowed = allowed
55
+ end
56
+
57
+ # Gives true when last change of "Fix Version" field was done
58
+ # by not authorized person.
59
+ def changed?
60
+ @allowed.none? do |a|
61
+ return false if last.nil?
62
+ a == last["author"]["name"]
63
+ end
64
+ end
65
+
66
+ # Detect details about last change of "Fix Version" to non-null value
67
+ def last
68
+ return @last if defined? @last
69
+ @last = issue.history
70
+ .reverse
71
+ .find do |h|
72
+ h["items"].any? do |i|
73
+ i["field"] == "Fix Version" && !i["to"].nil?
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,53 @@
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 "../log"
26
+ require_relative "../postman"
27
+
28
+ module Lazylead
29
+ module Task
30
+ # A task which fetch issues by particular ticketing system query language
31
+ # and find issues where comments has no expected text.
32
+ #
33
+ # Example:
34
+ # you have a sub-task for demo session for your story;
35
+ # demo session is recorded, placed to ftp;
36
+ # nobody mentioned in comment the ftp location for recorded session.
37
+ # Such cases needs to be reported.
38
+ class MissingComment
39
+ def initialize(log = Log::NOTHING)
40
+ @log = log
41
+ end
42
+
43
+ def run(sys, postman, opts)
44
+ opts["details"] = "text '#{opts['text']}'" if opts["details"].blank?
45
+ postman.send opts.merge(
46
+ comments: sys.issues(opts["jql"])
47
+ .map { |i| Comments.new(i, sys) }
48
+ .reject { |c| c.body? opts["text"] }
49
+ )
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,27 @@
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
+ module Lazylead
26
+ VERSION = "0.1.0"
27
+ end
@@ -0,0 +1,96 @@
1
+ <html>
2
+ <head>
3
+ <style>
4
+ /* CSS styles taken from https://github.com/yegor256/tacit */
5
+ th {
6
+ font-weight: 600
7
+ }
8
+
9
+ table tr {
10
+ border-bottom-width: 2.16px
11
+ }
12
+
13
+ table tr th {
14
+ border-bottom-width: 2.16px
15
+ }
16
+
17
+ table tr td, table tr th {
18
+ overflow: hidden;
19
+ padding: 5.4px 3.6px
20
+ }
21
+
22
+ a {
23
+ color: #275a90;
24
+ text-decoration: none
25
+ }
26
+
27
+ a:hover {
28
+ text-decoration: underline
29
+ }
30
+
31
+ * {
32
+ border: 0;
33
+ border-collapse: separate;
34
+ border-spacing: 0;
35
+ box-sizing: border-box;
36
+ margin: 0;
37
+ max-width: 100%;
38
+ padding: 0;
39
+ vertical-align: baseline;
40
+ font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
41
+ font-size: 13px;
42
+ font-stretch: normal;
43
+ font-style: normal;
44
+ font-weight: 400;
45
+ line-height: 29.7px
46
+ }
47
+
48
+ html, body {
49
+ width: 100%
50
+ }
51
+
52
+ html {
53
+ height: 100%
54
+ }
55
+
56
+ body {
57
+ background: #fff;
58
+ color: #1a1919;
59
+ padding: 36px
60
+ }
61
+ </style>
62
+ <title>Expired due date</title>
63
+ </head>
64
+ <body>
65
+ <p>Hi <%= addressee %>,</p>
66
+
67
+ <p>The due date for these tasks is expired or seems to expire soon. Please plan
68
+ them accordingly or actualize the
69
+ <span style="font-weight:bold">"Due date"</span> field for the following
70
+ ticket(s):</p>
71
+ <table>
72
+ <tr>
73
+ <th>Key</th>
74
+ <th>Due date</th>
75
+ <th>Priority</th>
76
+ <th>Reporter</th>
77
+ <th>Summary</th>
78
+ </tr>
79
+ <% tickets.each do |ticket| %>
80
+ <tr>
81
+ <td><a href="<%= ticket.url %>"><%= ticket.key %></a></td>
82
+ <td><%= ticket.duedate %></td>
83
+ <td><%= ticket.priority %></td>
84
+ <td><%= ticket.reporter.name %></td>
85
+ <td><%= ticket.summary %></td>
86
+ </tr>
87
+ <% end %>
88
+ </table>
89
+ <p>In case you have no idea what date should be set - remove obsolete date or
90
+ ask your lead to fill it.</p>
91
+
92
+ <p>Posted by
93
+ <a href="https://github.com/dgroup/lazylead">lazylead v<%= version %></a>.
94
+ </p>
95
+ </body>
96
+ </html>