fastlane-plugin-polidea 1.0.0.pre → 2.2.0.pre.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.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -3
  3. data/lib/fastlane/plugin/polidea.rb +7 -0
  4. data/lib/fastlane/plugin/polidea/actions/add_prefix_schema.rb +3 -0
  5. data/lib/fastlane/plugin/polidea/actions/extract_app_icon.rb +4 -0
  6. data/lib/fastlane/plugin/polidea/actions/extract_app_info.rb +11 -8
  7. data/lib/fastlane/plugin/polidea/actions/extract_app_name.rb +3 -0
  8. data/lib/fastlane/plugin/polidea/actions/extract_version.rb +4 -0
  9. data/lib/fastlane/plugin/polidea/actions/fota_mail.rb +14 -16
  10. data/lib/fastlane/plugin/polidea/actions/fota_s3.rb +28 -33
  11. data/lib/fastlane/plugin/polidea/actions/get_binary_size.rb +4 -0
  12. data/lib/fastlane/plugin/polidea/actions/import_provisioning.rb +5 -0
  13. data/lib/fastlane/plugin/polidea/actions/release_notes.rb +5 -0
  14. data/lib/fastlane/plugin/polidea/actions/release_notes_from_commits.rb +54 -0
  15. data/lib/fastlane/plugin/polidea/actions/shuttle.rb +6 -38
  16. data/lib/fastlane/plugin/polidea/helper/analytics.rb +43 -0
  17. data/lib/fastlane/plugin/polidea/helper/mime.rb +22 -0
  18. data/lib/fastlane/plugin/polidea/helper/page_generator.rb +39 -3
  19. data/lib/fastlane/plugin/polidea/helper/qr_generator.rb +7 -7
  20. data/lib/fastlane/plugin/polidea/helper/shuttle.rb +39 -0
  21. data/lib/fastlane/plugin/polidea/templates/download.erb +481 -0
  22. data/lib/fastlane/plugin/polidea/templates/images/cover-shuttle.jpg +0 -0
  23. data/lib/fastlane/plugin/polidea/templates/images/icon-placeholder.png +0 -0
  24. data/lib/fastlane/plugin/polidea/templates/images/polidea-logo.png +0 -0
  25. data/lib/fastlane/plugin/polidea/templates/images/social-behance.png +0 -0
  26. data/lib/fastlane/plugin/polidea/templates/images/social-dribbble.png +0 -0
  27. data/lib/fastlane/plugin/polidea/templates/images/social-facebook.png +0 -0
  28. data/lib/fastlane/plugin/polidea/templates/images/social-github.png +0 -0
  29. data/lib/fastlane/plugin/polidea/templates/images/social-instagram.png +0 -0
  30. data/lib/fastlane/plugin/polidea/templates/images/social-linkedin.png +0 -0
  31. data/lib/fastlane/plugin/polidea/templates/images/social-twitter.png +0 -0
  32. data/lib/fastlane/plugin/polidea/templates/installation-page/css/installation-page.css +2835 -1082
  33. data/lib/fastlane/plugin/polidea/templates/installation_template.erb +3 -7
  34. data/lib/fastlane/plugin/polidea/templates/mailgun_template.erb +56 -24
  35. data/lib/fastlane/plugin/polidea/templates/secure_installation_template.erb +6 -10
  36. data/lib/fastlane/plugin/polidea/version.rb +1 -1
  37. metadata +97 -20
  38. data/lib/fastlane/plugin/polidea/actions/polidea_store.rb +0 -255
  39. data/lib/fastlane/plugin/polidea/templates/images/logo.png +0 -0
  40. data/lib/fastlane/plugin/polidea/templates/images/polidea-facebook-icon.png +0 -0
  41. data/lib/fastlane/plugin/polidea/templates/images/polidea-github-icon.png +0 -0
  42. data/lib/fastlane/plugin/polidea/templates/images/polidea-twitter-icon.png +0 -0
@@ -0,0 +1,54 @@
1
+ module Fastlane
2
+ module Actions
3
+ module SharedValues
4
+ RELEASE_NOTES = :RELEASE_NOTES
5
+ end
6
+
7
+ class ReleaseNotesFromCommitsAction < Action
8
+ def self.run(config)
9
+ Fastlane::Polidea.session.action_launched("release_notes_from_commits", config)
10
+
11
+ current_version = config[:current_version]
12
+ previous_version = sh("git describe --abbrev=0 --tags #{current_version}^").strip
13
+ messages = sh("git log --oneline --pretty=format:'%s' #{previous_version}..#{current_version} | grep -Ev '^Merge'").strip
14
+ release_notes = messages.split("\n").map { |note| "- #{note}" }.join("\n")
15
+
16
+ UI.success "Extracted release notes:\n#{release_notes}"
17
+ Actions.lane_context[SharedValues::RELEASE_NOTES] = release_notes
18
+ ENV[SharedValues::RELEASE_NOTES.to_s] = release_notes
19
+
20
+ Fastlane::Polidea.session.action_completed("release_notes_from_commits")
21
+
22
+ release_notes
23
+ end
24
+
25
+ def self.description
26
+ "Extracts release notes from git commit messages since last tag"
27
+ end
28
+
29
+ def self.available_options
30
+ [
31
+ FastlaneCore::ConfigItem.new(key: :current_version,
32
+ env_name: "",
33
+ description: "Git ref of current version",
34
+ optional: true,
35
+ default_value: 'HEAD')
36
+ ]
37
+ end
38
+
39
+ def self.output
40
+ [
41
+ ['RELEASE_NOTES', 'Release notes extracted from git commits']
42
+ ]
43
+ end
44
+
45
+ def self.author
46
+ "Piotrek Dubiel"
47
+ end
48
+
49
+ def self.is_supported?(platform)
50
+ true
51
+ end
52
+ end
53
+ end
54
+ end
@@ -6,6 +6,7 @@ module Fastlane
6
6
  module Actions
7
7
  class ShuttleAction < Action
8
8
  def self.run(params)
9
+ Fastlane::Polidea.session.action_launched("shuttle", params)
9
10
  platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME].to_sym
10
11
  options = {
11
12
  api_token: params[:api_token],
@@ -25,6 +26,8 @@ module Fastlane
25
26
  notify(platform, config)
26
27
 
27
28
  UI.success("Successfully uploaded to #{params[:app_identifier]} #{params[:app_version]}.#{params[:build_number]} to Shuttle")
29
+
30
+ Fastlane::Polidea.session.action_completed("shuttle")
28
31
  end
29
32
 
30
33
  #####################################################
@@ -154,8 +157,6 @@ module Fastlane
154
157
  end
155
158
 
156
159
  def self.notify(platform, params)
157
- uri = URI.parse(url(params[:environment], platform, params[:app_identifier]))
158
-
159
160
  build = {
160
161
  href: params[:href],
161
162
  version: params[:app_version],
@@ -167,17 +168,11 @@ module Fastlane
167
168
  build[:prefixSchema] = params[:prefix_schema] if platform == :ios
168
169
  build[:versionCode] = params[:build_number] if platform == :android
169
170
 
170
- UI.verbose("POST #{uri}\n#{JSON.pretty_generate({ build: build })}")
171
- make_request(uri, create_request(uri, params[:api_token], {
172
- build: build
173
- }))
174
- end
175
- private_class_method :notify
171
+ client = Shuttle::Client.new(base_url(params[:environment]), params[:api_token])
176
172
 
177
- def self.url(environment, platform, app_identifier)
178
- "#{base_url(environment)}/cd/apps/#{platform}/#{app_identifier}/builds"
173
+ client.create_build(platform, params[:app_identifier], build)
179
174
  end
180
- private_class_method :url
175
+ private_class_method :notify
181
176
 
182
177
  def self.base_url(environment)
183
178
  case environment
@@ -189,14 +184,6 @@ module Fastlane
189
184
  end
190
185
  private_class_method :base_url
191
186
 
192
- def self.create_request(uri, access_token, params)
193
- req = Net::HTTP::Post.new(uri.request_uri)
194
- req['Content-Type'] = 'application/json'
195
- req['Access-Token'] = access_token
196
- req.body = JSON.generate(params)
197
- req
198
- end
199
-
200
187
  def self.itms_href(plist_url)
201
188
  "itms-services://?action=download-manifest&url=#{URI.encode_www_form_component(plist_url)}"
202
189
  end
@@ -206,25 +193,6 @@ module Fastlane
206
193
  sh("git --no-pager show -s --format='%ae'", print_command: false, print_command_output: false).strip
207
194
  end
208
195
  private_class_method :commit_author
209
-
210
- def self.make_request(uri, request, limit = 10)
211
- raise ArgumentError, 'HTTP redirect too deep' if limit.zero?
212
-
213
- http = Net::HTTP.new(uri.host, uri.port)
214
-
215
- if uri.instance_of?(URI::HTTPS)
216
- http.use_ssl = true
217
- end
218
-
219
- response = http.request(request)
220
- UI.verbose(response.body)
221
- case response
222
- when Net::HTTPSuccess then response
223
- when Net::HTTPRedirection then make_request(URI.parse(response['location']), request, limit - 1)
224
- else
225
- UI.user_error! "#{response.code} #{response.body}"
226
- end
227
- end
228
196
  end
229
197
  end
230
198
  end
@@ -0,0 +1,43 @@
1
+ module Fastlane
2
+ module Polidea
3
+ class Analytics
4
+ GA_TRACKING = "UA-165836496-1"
5
+ private_constant :GA_TRACKING
6
+
7
+ attr_accessor :session_id
8
+ attr_accessor :client
9
+
10
+ def initialize(session_id)
11
+ @session_id = session_id
12
+ @client = FastlaneCore::AnalyticsIngesterClient.new(GA_TRACKING)
13
+ @client_id = "fastlane-plugin-polidea@#{Fastlane::Polidea::VERSION}"
14
+ end
15
+
16
+ def action_launched(action_name, config)
17
+ client.post_request({
18
+ client_id: @session_id,
19
+ category: @client_id,
20
+ action: action_name,
21
+ label: "launched"
22
+ })
23
+ config.values.filter { |k, v| !v.nil? }.keys.each do |param|
24
+ client.post_request({
25
+ client_id: @session_id,
26
+ category: @client_id,
27
+ action: "#{action_name}::#{param}",
28
+ label: "param"
29
+ })
30
+ end
31
+ end
32
+
33
+ def action_completed(action_name)
34
+ client.post_request({
35
+ client_id: @session_id,
36
+ category: @client_id,
37
+ action: action_name,
38
+ label: "completed"
39
+ })
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ module Fastlane
2
+ module Mime
3
+ def self.content_type_for_file(file)
4
+ file_extension = File.extname(file)
5
+ extensions_to_type = {
6
+ ".html" => "text/html",
7
+ ".png" => "image/png",
8
+ ".jpg" => "image/jpeg",
9
+ ".gif" => "image/gif",
10
+ ".svg" => "image/svg+xml",
11
+ ".log" => "text/plain",
12
+ ".css" => "text/css",
13
+ ".js" => "application/javascript"
14
+ }
15
+ if extensions_to_type[file_extension].nil?
16
+ "application/octet-stream"
17
+ else
18
+ extensions_to_type[file_extension]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -4,7 +4,28 @@ require 'json'
4
4
 
5
5
  module Fastlane
6
6
  module PageGenerator
7
- def self.generate(config)
7
+ def self.mail(config)
8
+ UI.message("Generating e-mail...")
9
+ eth = Fastlane::ErbTemplateHelper
10
+ html_template = eth.load_from_path("#{__dir__}/../templates/download.erb")
11
+ eth.render(html_template, {
12
+ author: config[:author],
13
+ last_commit: config[:last_commit],
14
+ is_android: config[:is_android],
15
+ app_icon: config[:app_icon] ? "cid:#{config[:app_icon]}" : "https://s3.eu-central-1.amazonaws.com/fota.polidea.com/mails/images/icon-placeholder.png",
16
+ app_name: config[:app_name],
17
+ app_version: config[:app_version],
18
+ build_number: config[:build_number],
19
+ installation_link: config[:installation_link],
20
+ release_notes: parse_release_notes(config[:release_notes]),
21
+ platform: config[:platform],
22
+ release_date: config[:release_date],
23
+ binary_size: config[:binary_size],
24
+ qr_code: "cid:#{config[:qr_code]}"
25
+ })
26
+ end
27
+
28
+ def self.installation_page(config)
8
29
  if config[:installation_password]
9
30
  generate_private(config)
10
31
  else
@@ -26,7 +47,7 @@ module Fastlane
26
47
  app_name: config[:app_name],
27
48
  app_icon: config[:app_icon],
28
49
  platform: config[:platform],
29
- release_notes: config[:release_notes]
50
+ release_notes: parse_release_notes(config[:release_notes])
30
51
  })
31
52
  end
32
53
  private_class_method :generate_private
@@ -42,7 +63,7 @@ module Fastlane
42
63
  app_name: config[:app_name],
43
64
  app_icon: config[:app_icon],
44
65
  platform: config[:platform],
45
- release_notes: config[:release_notes]
66
+ release_notes: parse_release_notes(config[:release_notes])
46
67
  })
47
68
  end
48
69
  private_class_method :generate_public
@@ -67,5 +88,20 @@ module Fastlane
67
88
  return installation_link, salt, iv
68
89
  end
69
90
  private_class_method :encrypt
91
+
92
+ def self.parse_release_notes(release_notes)
93
+ renderer = Redcarpet::Render::HTML.new({
94
+ filter_html: true,
95
+ no_styles: true
96
+ })
97
+ markdown = Redcarpet::Markdown.new(
98
+ renderer,
99
+ fenced_code_blocks: true,
100
+ autolink: false,
101
+ tables: false
102
+ )
103
+ markdown.render(release_notes || "No release notes.")
104
+ end
105
+ private_class_method :parse_release_notes
70
106
  end
71
107
  end
@@ -8,10 +8,10 @@ module Fastlane
8
8
  @dark_color = dark_color
9
9
 
10
10
  @margin = 20
11
- @image = ::ChunkyPNG::Image.new(2 * @margin + @qr_code.module_count * 5, 2 * @margin + @qr_code.module_count * 5, ::ChunkyPNG::Color::WHITE)
11
+ @image = ::ChunkyPNG::Image.new(2 * @margin + @qr_code.qrcode.module_count * 5, 2 * @margin + @qr_code.qrcode.module_count * 5, ::ChunkyPNG::Color::WHITE)
12
12
 
13
- @logo = ::ChunkyPNG::Image.from_file("#{path_to_resources}/logo.png")
14
- @logo = @logo.resize(2 * @margin + @qr_code.module_count * 5, 2 * @margin + @qr_code.module_count * 5)
13
+ @logo = ::ChunkyPNG::Image.from_file("#{path_to_resources}/polidea-logo.png")
14
+ @logo = @logo.resize(2 * @margin + @qr_code.qrcode.module_count * 5, 2 * @margin + @qr_code.qrcode.module_count * 5)
15
15
  end
16
16
 
17
17
  def generate(path)
@@ -25,11 +25,11 @@ module Fastlane
25
25
  end
26
26
 
27
27
  @qr_code.modules.each_index do |row|
28
- @qr_code.modules.each_index do |column|
29
- if @qr_code.is_dark(row, column)
30
- print_symbol(dark_color, column, row, (column < 8 && row < 8) || (column < 8 && row >= @qr_code.module_count - 8) || (column >= @qr_code.module_count - 8 && row < 8))
28
+ @qr_code.modules[row].each_index do |column|
29
+ if @qr_code.modules[row][column]
30
+ print_symbol(dark_color, column, row, (column < 8 && row < 8) || (column < 8 && row >= @qr_code.qrcode.module_count - 8) || (column >= @qr_code.qrcode.module_count - 8 && row < 8))
31
31
  else
32
- print_symbol(white, column, row, (column < 8 && row < 8) || (column < 8 && row >= @qr_code.module_count - 8) || (column >= @qr_code.module_count - 8 && row < 8))
32
+ print_symbol(white, column, row, (column < 8 && row < 8) || (column < 8 && row >= @qr_code.qrcode.module_count - 8) || (column >= @qr_code.qrcode.module_count - 8 && row < 8))
33
33
  end
34
34
  end
35
35
  end
@@ -0,0 +1,39 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Fastlane
5
+ module Shuttle
6
+ class Client
7
+ def initialize(base_url, access_token)
8
+ retry_options = {
9
+ max: 2,
10
+ interval: 0.05,
11
+ interval_randomness: 0.5,
12
+ backoff_factor: 2
13
+ }
14
+
15
+ @conn = Faraday.new(
16
+ url: base_url,
17
+ headers: {
18
+ 'Content-Type' => 'application/json',
19
+ 'Access-Token' => access_token
20
+ }
21
+ ) do |conn|
22
+ conn.request :retry, retry_options
23
+ conn.request :multipart
24
+
25
+ conn.response :raise_error
26
+
27
+ conn.adapter :net_http
28
+ end
29
+ end
30
+
31
+ def create_build(platform, app_identifier, build)
32
+ @conn.post(
33
+ "cd/apps/#{platform}/#{app_identifier}/builds",
34
+ { build: build }.to_json
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,481 @@
1
+ <!doctype html>
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
3
+
4
+ <head>
5
+ <title> <%= app_name %> <%= app_version %> (<%= build_number %>) for <%= platform %> is ready to install </title>
6
+ <!--[if !mso]><!-- -->
7
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
8
+ <!--<![endif]-->
9
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
10
+ <meta name="viewport" content="width=device-width, initial-scale=1">
11
+ <style type="text/css">
12
+ #outlook a {
13
+ padding: 0;
14
+ }
15
+
16
+ body {
17
+ margin: 0;
18
+ padding: 0;
19
+ -webkit-text-size-adjust: 100%;
20
+ -ms-text-size-adjust: 100%;
21
+ }
22
+
23
+ table,
24
+ td {
25
+ border-collapse: collapse;
26
+ mso-table-lspace: 0pt;
27
+ mso-table-rspace: 0pt;
28
+ }
29
+
30
+ img {
31
+ border: 0;
32
+ height: auto;
33
+ line-height: 100%;
34
+ outline: none;
35
+ text-decoration: none;
36
+ -ms-interpolation-mode: bicubic;
37
+ }
38
+
39
+ p {
40
+ display: block;
41
+ margin: 13px 0;
42
+ }
43
+ </style>
44
+ <!--[if mso]>
45
+ <xml>
46
+ <o:OfficeDocumentSettings>
47
+ <o:AllowPNG/>
48
+ <o:PixelsPerInch>96</o:PixelsPerInch>
49
+ </o:OfficeDocumentSettings>
50
+ </xml>
51
+ <![endif]-->
52
+ <!--[if lte mso 11]>
53
+ <style type="text/css">
54
+ .mj-outlook-group-fix { width:100% !important; }
55
+ </style>
56
+ <![endif]-->
57
+ <!--[if !mso]><!-->
58
+ <link href="https://fonts.googleapis.com/css?family=Overpass+Mono:700" rel="stylesheet" type="text/css">
59
+ <style type="text/css">
60
+ @import url(https://fonts.googleapis.com/css?family=Overpass+Mono:700);
61
+ </style>
62
+ <!--<![endif]-->
63
+ <style type="text/css">
64
+ @media only screen and (min-width:480px) {
65
+ .mj-column-per-100 {
66
+ width: 100% !important;
67
+ max-width: 100%;
68
+ }
69
+
70
+ .mj-column-px-480 {
71
+ width: 480px !important;
72
+ max-width: 480px;
73
+ }
74
+ }
75
+ </style>
76
+ <style type="text/css">
77
+ @media only screen and (max-width:480px) {
78
+ table.mj-full-width-mobile {
79
+ width: 100% !important;
80
+ }
81
+
82
+ td.mj-full-width-mobile {
83
+ width: auto !important;
84
+ }
85
+ }
86
+ </style>
87
+ </head>
88
+
89
+ <body style="background-color:#FFFFFF;">
90
+ <div style="background-color:#FFFFFF;">
91
+ <!-- Company Header -->
92
+ <!--[if mso | IE]>
93
+ <table
94
+ align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
95
+ >
96
+ <tr>
97
+ <td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
98
+ <![endif]-->
99
+ <div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
100
+ <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;">
101
+ <tbody>
102
+ <tr>
103
+ <td style="direction:ltr;font-size:0px;padding:0px;text-align:center;">
104
+ <!--[if mso | IE]>
105
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
106
+
107
+ <tr>
108
+
109
+ <td
110
+ class="" style="vertical-align:top;width:600px;"
111
+ >
112
+ <![endif]-->
113
+ <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
114
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
115
+ <tbody>
116
+ <tr>
117
+ <td style="vertical-align:top;padding:20px 32px;">
118
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
119
+ <tr>
120
+ <td align="left" style="font-size:0px;padding:0px;word-break:break-word;">
121
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
122
+ <tbody>
123
+ <tr>
124
+ <td style="width:156px;">
125
+ <a href="https://www.polidea.com/" target="_blank">
126
+ <img height="auto" src="https://s3.eu-central-1.amazonaws.com/fota.polidea.com/mails/images/polidea-logo.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="156">
127
+ </a>
128
+ </td>
129
+ </tr>
130
+ </tbody>
131
+ </table>
132
+ </td>
133
+ </tr>
134
+ </table>
135
+ </td>
136
+ </tr>
137
+ </tbody>
138
+ </table>
139
+ </div>
140
+ <!--[if mso | IE]>
141
+ </td>
142
+
143
+ </tr>
144
+
145
+ </table>
146
+ <![endif]-->
147
+ </td>
148
+ </tr>
149
+ </tbody>
150
+ </table>
151
+ </div>
152
+ <!--[if mso | IE]>
153
+ </td>
154
+ </tr>
155
+ </table>
156
+ <![endif]-->
157
+ <!-- Image Header -->
158
+ <!--[if mso | IE]>
159
+ <table
160
+ align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
161
+ >
162
+ <tr>
163
+ <td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
164
+ <![endif]-->
165
+ <div style="margin:0px auto;max-width:600px;">
166
+ <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
167
+ <tbody>
168
+ <tr>
169
+ <td style="direction:ltr;font-size:0px;padding:0;text-align:center;">
170
+ <!--[if mso | IE]>
171
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
172
+
173
+ <tr>
174
+
175
+ <td
176
+ class="" style="vertical-align:top;width:600px;"
177
+ >
178
+ <![endif]-->
179
+ <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
180
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
181
+ <tbody>
182
+ <tr>
183
+ <td style="vertical-align:top;padding:0;">
184
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
185
+ <tr>
186
+ <td align="center" style="font-size:0px;padding:0;word-break:break-word;">
187
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
188
+ <tbody>
189
+ <tr>
190
+ <td style="width:600px;">
191
+ <img height="auto" src="https://s3.eu-central-1.amazonaws.com/fota.polidea.com/mails/images/cover-shuttle.jpg" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="600">
192
+ </td>
193
+ </tr>
194
+ </tbody>
195
+ </table>
196
+ </td>
197
+ </tr>
198
+ </table>
199
+ </td>
200
+ </tr>
201
+ </tbody>
202
+ </table>
203
+ </div>
204
+ <!--[if mso | IE]>
205
+ </td>
206
+
207
+ </tr>
208
+
209
+ </table>
210
+ <![endif]-->
211
+ </td>
212
+ </tr>
213
+ </tbody>
214
+ </table>
215
+ </div>
216
+ <!--[if mso | IE]>
217
+ </td>
218
+ </tr>
219
+ </table>
220
+ <![endif]-->
221
+ <!-- Body -->
222
+ <!--[if mso | IE]>
223
+ <table
224
+ align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
225
+ >
226
+ <tr>
227
+ <td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
228
+ <![endif]-->
229
+ <div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
230
+ <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;">
231
+ <tbody>
232
+ <tr>
233
+ <td style="direction:ltr;font-size:0px;padding:0 32px 0;text-align:center;">
234
+ <!--[if mso | IE]>
235
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
236
+
237
+ <tr>
238
+
239
+ <td
240
+ class="" style="vertical-align:top;width:480px;"
241
+ >
242
+ <![endif]-->
243
+ <div class="mj-column-px-480 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
244
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
245
+ <tbody>
246
+ <tr>
247
+ <td style="vertical-align:top;padding:32px 0 0;">
248
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
249
+ <tr>
250
+ <td align="left" style="font-size:0px;padding:0;padding-bottom:18px;word-break:break-word;">
251
+ <div style="font-family:'Acumin', 'Helvetica', Arial;font-size:24px;font-weight:bold;line-height:32px;text-align:left;color:black;">New version is ready to install!</div>
252
+ </td>
253
+ </tr>
254
+ <table>
255
+ <tr>
256
+ <td width="40" vertical-align="top">
257
+ <img width="64" height="64" src="<%= app_icon %>">
258
+ </td>
259
+ <td width="10">
260
+ </td>
261
+ <td style="line-height: 2; font-family: 'Helvetica', Arial;">
262
+ <div style="color: black; font-size: 13px; line-height: 20px; font-weight: bold;"> <%= app_name %> </div>
263
+ <div style="color: black; font-size: 12px; line-height: 20px"> Version: <%= app_version %> (<%= build_number %>) </div>
264
+ </td>
265
+ <td width="20"></td>
266
+ </tr>
267
+ </table>
268
+ <tr>
269
+ <td align="left" vertical-align="middle" style="font-size:0px;padding:32px 0;word-break:break-word;">
270
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;">
271
+ <tr>
272
+ <td align="center" bgcolor="white" role="presentation" style="border:2px solid #2924fb;border-radius:30px;cursor:auto;mso-padding-alt:12px 42px;background:white;" valign="middle">
273
+ <a href="<%= installation_link %>" style="display:inline-block;background:white;color:black;font-family:'Overpass Mono', monospace;font-size:14px;font-weight:bold;line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:12px 42px;mso-padding-alt:0px;border-radius:30px;" target="_blank">
274
+ <meta itemprop="url" content="<%= installation_link %>">
275
+ <meta itemprop="name" content="Installation page"> Download </a>
276
+ </td>
277
+ </tr>
278
+ </table>
279
+ </td>
280
+ </tr>
281
+ <tr>
282
+ <td align="left" style="font-size:0px;padding:0;padding-bottom:18px;word-break:break-word;">
283
+ <div style="font-family:'Acumin', 'Helvetica', Arial;font-size:18px;font-weight:bold;line-height:24px;text-align:left;color:black;">What's new:</div>
284
+ </td>
285
+ </tr>
286
+ <tr>
287
+ <td align="left" style="font-size:0px;padding:0;word-break:break-word;">
288
+ <table cellpadding="0" cellspacing="0" width="100%" border="0" style="color:#000000;font-family:'Acumin', 'Helvetica', Arial;font-size:14px;line-height:22px;table-layout:auto;width:100%;border:none;">
289
+ <tr>
290
+ <td class="release-notes" style="padding-left: 16px; padding-right: 16px; padding-top: 8px; padding-bottom: 8px; color: black; background-color: #F8F8F8;" bgcolor="#F8F8F8"> <%= release_notes %> </td>
291
+ </tr>
292
+ </table>
293
+ </td>
294
+ </tr>
295
+ <tr>
296
+ <td align="left" style="font-size:0px;padding:16px 0;word-break:break-word;">
297
+ <table cellpadding="0" cellspacing="0" width="37" border="0" style="color:#000000;font-family:'Acumin', 'Helvetica', Arial;font-size:14px;line-height:22px;table-layout:auto;width:37px;border:none;">
298
+ <tr>
299
+ <td width="37" bgcolor="#f96248" style="background-color: #f96248; padding-bottom: 1px">
300
+ </td>
301
+ </tr>
302
+ </table>
303
+ </td>
304
+ </tr>
305
+ <tr>
306
+ <td align="left" style="font-size:0px;padding:0;word-break:break-word;">
307
+ <div style="font-family:'Acumin', 'Helvetica', Arial;font-size:14px;line-height:20px;text-align:left;color:black;">Platform: <%= platform %></div>
308
+ </td>
309
+ </tr>
310
+ <tr>
311
+ <td align="left" style="font-size:0px;padding:0;word-break:break-word;">
312
+ <div style="font-family:'Acumin', 'Helvetica', Arial;font-size:14px;line-height:20px;text-align:left;color:black;">Version: <%= app_version %> (<%= build_number %>)</div>
313
+ </td>
314
+ </tr>
315
+ <tr>
316
+ <td align="left" style="font-size:0px;padding:0;word-break:break-word;">
317
+ <div style="font-family:'Acumin', 'Helvetica', Arial;font-size:14px;line-height:20px;text-align:left;color:black;">Release date: <%= release_date %></div>
318
+ </td>
319
+ </tr>
320
+ <tr>
321
+ <td align="left" style="font-size:0px;padding:0;word-break:break-word;">
322
+ <div style="font-family:'Acumin', 'Helvetica', Arial;font-size:14px;line-height:20px;text-align:left;color:black;">Size: <%= binary_size %> MB</div>
323
+ </td>
324
+ </tr>
325
+ </table>
326
+ </td>
327
+ </tr>
328
+ </tbody>
329
+ </table>
330
+ </div>
331
+ <!--[if mso | IE]>
332
+ </td>
333
+
334
+ </tr>
335
+
336
+ </table>
337
+ <![endif]-->
338
+ </td>
339
+ </tr>
340
+ </tbody>
341
+ </table>
342
+ </div>
343
+ <!--[if mso | IE]>
344
+ </td>
345
+ </tr>
346
+ </table>
347
+ <![endif]-->
348
+ <!-- Projectr -->
349
+ <!--[if mso | IE]>
350
+ <table
351
+ align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
352
+ >
353
+ <tr>
354
+ <td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
355
+ <![endif]-->
356
+ <div style="margin:0px auto;max-width:600px;">
357
+ <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
358
+ <tbody>
359
+ <tr>
360
+ <td style="direction:ltr;font-size:0px;padding:20px 32px 20px;text-align:center;">
361
+ <!--[if mso | IE]>
362
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
363
+
364
+ <tr>
365
+
366
+ <td
367
+ class="" style="vertical-align:top;width:480px;"
368
+ >
369
+ <![endif]-->
370
+ <div class="mj-column-px-480 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
371
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
372
+ <table>
373
+ <tr>
374
+ <td>
375
+ <a href="https://github.com/Polidea">
376
+ <img src="https://s3.eu-central-1.amazonaws.com/fota.polidea.com/mails/images/social-github.png" width="29" height="29" alt="Github">
377
+ </a>
378
+ </td>
379
+ <td width="16"></td>
380
+ <td>
381
+ <a href="https://www.facebook.com/Polidea.Software">
382
+ <img src="https://s3.eu-central-1.amazonaws.com/fota.polidea.com/mails/images/social-facebook.png" width="29" height="29" alt="Facebook">
383
+ </a>
384
+ </td>
385
+ <td width="16"></td>
386
+ <td>
387
+ <a href="https://twitter.com/polidea">
388
+ <img src="https://s3.eu-central-1.amazonaws.com/fota.polidea.com/mails/images/social-twitter.png" width="29" height="29" alt="Twitter">
389
+ </a>
390
+ </td>
391
+ <td width="16"></td>
392
+ <td>
393
+ <a href="https://www.linkedin.com/company/polidea">
394
+ <img src="https://s3.eu-central-1.amazonaws.com/fota.polidea.com/mails/images/social-linkedin.png" width="29" height="29" alt="Linkedin">
395
+ </a>
396
+ </td>
397
+ <td width="16"></td>
398
+ <td>
399
+ <a href="https://instagram.com/polidea">
400
+ <img src="https://s3.eu-central-1.amazonaws.com/fota.polidea.com/mails/images/social-instagram.png" width="29" height="29" alt="Instagram">
401
+ </a>
402
+ </td>
403
+ </tr>
404
+ </table>
405
+ </table>
406
+ </div>
407
+ <!--[if mso | IE]>
408
+ </td>
409
+
410
+ </tr>
411
+
412
+ </table>
413
+ <![endif]-->
414
+ </td>
415
+ </tr>
416
+ </tbody>
417
+ </table>
418
+ </div>
419
+ <!--[if mso | IE]>
420
+ </td>
421
+ </tr>
422
+ </table>
423
+
424
+ <table
425
+ align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
426
+ >
427
+ <tr>
428
+ <td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
429
+ <![endif]-->
430
+ <div style="margin:0px auto;max-width:600px;">
431
+ <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
432
+ <tbody>
433
+ <tr>
434
+ <td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
435
+ <!--[if mso | IE]>
436
+ <table role="presentation" border="0" cellpadding="0" cellspacing="0">
437
+
438
+ <tr>
439
+
440
+ <td
441
+ class="" style="vertical-align:top;width:600px;"
442
+ >
443
+ <![endif]-->
444
+ <div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
445
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
446
+ <tr>
447
+ <td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
448
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
449
+ <tbody>
450
+ <tr>
451
+ <td style="width:300px;">
452
+ <img height="auto" src="<%= qr_code %>" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:14px;" width="300">
453
+ </td>
454
+ </tr>
455
+ </tbody>
456
+ </table>
457
+ </td>
458
+ </tr>
459
+ </table>
460
+ </div>
461
+ <!--[if mso | IE]>
462
+ </td>
463
+
464
+ </tr>
465
+
466
+ </table>
467
+ <![endif]-->
468
+ </td>
469
+ </tr>
470
+ </tbody>
471
+ </table>
472
+ </div>
473
+ <!--[if mso | IE]>
474
+ </td>
475
+ </tr>
476
+ </table>
477
+ <![endif]-->
478
+ </div>
479
+ </body>
480
+
481
+ </html>