fastlane-plugin-polidea 0.1.0

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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +52 -0
  4. data/lib/fastlane/plugin/polidea.rb +16 -0
  5. data/lib/fastlane/plugin/polidea/actions/extract_app_icon.rb +155 -0
  6. data/lib/fastlane/plugin/polidea/actions/extract_app_name.rb +91 -0
  7. data/lib/fastlane/plugin/polidea/actions/extract_version.rb +96 -0
  8. data/lib/fastlane/plugin/polidea/actions/get_binary_size.rb +53 -0
  9. data/lib/fastlane/plugin/polidea/actions/mailgun.rb +173 -0
  10. data/lib/fastlane/plugin/polidea/actions/polidea_store_action.rb +248 -0
  11. data/lib/fastlane/plugin/polidea/actions/qr_generator.rb +76 -0
  12. data/lib/fastlane/plugin/polidea/actions/s3.rb +551 -0
  13. data/lib/fastlane/plugin/polidea/helper/polidea_helper.rb +12 -0
  14. data/lib/fastlane/plugin/polidea/templates/images/icon-placeholder.png +0 -0
  15. data/lib/fastlane/plugin/polidea/templates/images/logo.png +0 -0
  16. data/lib/fastlane/plugin/polidea/templates/images/polidea-facebook-icon.png +0 -0
  17. data/lib/fastlane/plugin/polidea/templates/images/polidea-github-icon.png +0 -0
  18. data/lib/fastlane/plugin/polidea/templates/images/polidea-logo.png +0 -0
  19. data/lib/fastlane/plugin/polidea/templates/images/polidea-twitter-icon.png +0 -0
  20. data/lib/fastlane/plugin/polidea/templates/installation-page/css/installation-page.css +3142 -0
  21. data/lib/fastlane/plugin/polidea/templates/installation-page/css/installation-page.css.map +7 -0
  22. data/lib/fastlane/plugin/polidea/templates/installation-page/img/bg.png +0 -0
  23. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg.jpg +0 -0
  24. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg_1024.jpg +0 -0
  25. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg_1280.jpg +0 -0
  26. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg_1360.jpg +0 -0
  27. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg_1440.jpg +0 -0
  28. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg_1680.jpg +0 -0
  29. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg_768.jpg +0 -0
  30. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg_ipad_1024.jpg +0 -0
  31. data/lib/fastlane/plugin/polidea/templates/installation-page/img/jenkins_bg_ipad_768.jpg +0 -0
  32. data/lib/fastlane/plugin/polidea/templates/installation-page/img/logo.svg +32 -0
  33. data/lib/fastlane/plugin/polidea/templates/installation-page/img/logo_square.svg +12 -0
  34. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation.min.js +10 -0
  35. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.abide.js +294 -0
  36. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.accordion.js +65 -0
  37. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.alert.js +43 -0
  38. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.clearing.js +558 -0
  39. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.dropdown.js +313 -0
  40. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.equalizer.js +74 -0
  41. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.interchange.js +344 -0
  42. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.joyride.js +915 -0
  43. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.js +625 -0
  44. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.magellan.js +189 -0
  45. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.offcanvas.js +139 -0
  46. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.orbit.js +472 -0
  47. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.reveal.js +443 -0
  48. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.slider.js +231 -0
  49. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.tab.js +167 -0
  50. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.tooltip.js +298 -0
  51. data/lib/fastlane/plugin/polidea/templates/installation-page/js/foundation/foundation.topbar.js +436 -0
  52. data/lib/fastlane/plugin/polidea/templates/installation-page/js/vendor/aes.js +35 -0
  53. data/lib/fastlane/plugin/polidea/templates/installation-page/js/vendor/enc-base64-min.js +8 -0
  54. data/lib/fastlane/plugin/polidea/templates/installation-page/js/vendor/fastclick.js +9 -0
  55. data/lib/fastlane/plugin/polidea/templates/installation-page/js/vendor/jquery.cookie.js +8 -0
  56. data/lib/fastlane/plugin/polidea/templates/installation-page/js/vendor/jquery.js +26 -0
  57. data/lib/fastlane/plugin/polidea/templates/installation-page/js/vendor/modernizr.js +8 -0
  58. data/lib/fastlane/plugin/polidea/templates/installation-page/js/vendor/pbkdf2.js +19 -0
  59. data/lib/fastlane/plugin/polidea/templates/installation-page/js/vendor/placeholder.js +2 -0
  60. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/fonts.scss +64 -0
  61. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation.scss +45 -0
  62. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/_functions.scss +101 -0
  63. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/_settings.scss +1441 -0
  64. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_accordion.scss +156 -0
  65. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_alert-boxes.scss +126 -0
  66. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_block-grid.scss +132 -0
  67. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_breadcrumbs.scss +132 -0
  68. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_button-groups.scss +200 -0
  69. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_buttons.scss +252 -0
  70. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_clearing.scss +247 -0
  71. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_dropdown-buttons.scss +129 -0
  72. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_dropdown.scss +248 -0
  73. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_flex-video.scss +51 -0
  74. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_forms.scss +570 -0
  75. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_global.scss +447 -0
  76. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_grid.scss +275 -0
  77. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_icon-bar.scss +293 -0
  78. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_inline-lists.scss +57 -0
  79. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_joyride.scss +222 -0
  80. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_keystrokes.scss +61 -0
  81. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_labels.scss +105 -0
  82. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_magellan.scss +34 -0
  83. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_offcanvas.scss +510 -0
  84. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_orbit.scss +368 -0
  85. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_pagination.scss +162 -0
  86. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_panels.scss +93 -0
  87. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_pricing-tables.scss +150 -0
  88. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_progress-bars.scss +79 -0
  89. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_range-slider.scss +168 -0
  90. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_reveal.scss +220 -0
  91. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_side-nav.scss +116 -0
  92. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_split-buttons.scss +191 -0
  93. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_sub-nav.scss +125 -0
  94. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_switches.scss +226 -0
  95. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_tables.scss +135 -0
  96. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_tabs.scss +123 -0
  97. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_thumbs.scss +66 -0
  98. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_toolbar.scss +70 -0
  99. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_tooltips.scss +142 -0
  100. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_top-bar.scss +676 -0
  101. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_type.scss +523 -0
  102. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/foundation/components/_visibility.scss +408 -0
  103. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/installation-page.scss +171 -0
  104. data/lib/fastlane/plugin/polidea/templates/installation-page/sass/normalize.scss +425 -0
  105. data/lib/fastlane/plugin/polidea/templates/installation_template.erb +58 -0
  106. data/lib/fastlane/plugin/polidea/templates/mailgun_template.erb +1225 -0
  107. data/lib/fastlane/plugin/polidea/version.rb +5 -0
  108. metadata +323 -0
@@ -0,0 +1,76 @@
1
+ require 'rqrcode_png'
2
+
3
+ module Fastlane
4
+ class QRGenerator
5
+ def initialize(data, auto_awesome = false, dark_color = ::ChunkyPNG::Color::BLACK)
6
+ @qr_code = RQRCode::QRCode.new(data, size: 10, level: :m)
7
+ @auto_awesome = auto_awesome
8
+ @dark_color = dark_color
9
+
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)
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)
15
+ end
16
+
17
+ def generate(path)
18
+ white = ::ChunkyPNG::Color::WHITE
19
+ blue = ::ChunkyPNG::Color.rgb(26, 159, 217)
20
+
21
+ if !@auto_awesome
22
+ dark_color = @dark_color
23
+ else
24
+ dark_color = blue
25
+ end
26
+
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))
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))
33
+ end
34
+ end
35
+ end
36
+ @image.save(path)
37
+ end
38
+
39
+ private
40
+
41
+ def print_symbol(color, column, row, interpolate)
42
+ (0..4).each do |i|
43
+ if i < 4 && i > 0
44
+ print_symbol_row(interpolated_color(color, 5 * column + i, 5 * row, true), 5 * column + i, row, interpolate)
45
+ else
46
+ print_symbol_row(interpolated_color(color, 5 * column + i, 5 * row, interpolate), 5 * column + i, row, interpolate)
47
+ end
48
+ end
49
+ end
50
+
51
+ def interpolated_color(color, column, row, interpolate)
52
+ if !@auto_awesome
53
+ interpolated_color = color
54
+ elsif interpolate
55
+ interpolated_color = ::ChunkyPNG::Color.interpolate_quick(color, @logo[@margin + column, @margin + row], 255)
56
+ else
57
+ interpolated_color = ::ChunkyPNG::Color.interpolate_quick(color, @logo[@margin + column, @margin + row], 40)
58
+ end
59
+ interpolated_color
60
+ end
61
+
62
+ def print_symbol_row(color, column, row, interpolate)
63
+ (0..4).each do |i|
64
+ if i < 4 && i > 0
65
+ @image[@margin + column, @margin + 5 * row + i] = interpolated_color(color, column, 5 * row + i, true)
66
+ else
67
+ @image[@margin + column, @margin + 5 * row + i] = interpolated_color(color, column, 5 * row + i, interpolate)
68
+ end
69
+ end
70
+ end
71
+
72
+ def path_to_resources
73
+ File.expand_path "#{__dir__}/../templates/images"
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,551 @@
1
+ # rubocop:disable Metrics/AbcSize
2
+ # rubocop:disable Metrics/MethodLength
3
+ # rubocop:disable Metrics/ClassLength
4
+ require 'fastlane/erb_template_helper'
5
+ require 'ostruct'
6
+
7
+ module Fastlane
8
+ module Actions
9
+ module SharedValues
10
+ S3_IPA_OUTPUT_PATH = :S3_IPA_OUTPUT_PATH
11
+ S3_DSYM_OUTPUT_PATH = :S3_DSYM_OUTPUT_PATH
12
+ S3_PLIST_OUTPUT_PATH = :S3_PLIST_OUTPUT_PATH
13
+ S3_APK_OUTPUT_PATH = :S3_APK_OUTPUT_PATH
14
+ S3_HTML_OUTPUT_PATH = :S3_HTML_OUTPUT_PATH
15
+ S3_VERSION_OUTPUT_PATH = :S3_VERSION_OUTPUT_PATH
16
+ S3_ICON_OUTPUT_PATH = :S3_ICON_OUTPUT_PATH
17
+ end
18
+
19
+ S3_ARGS_MAP = {
20
+ ipa: '-f',
21
+ dsym: '-d',
22
+ access_key: '-a',
23
+ secret_access_key: '-s',
24
+ bucket: '-b',
25
+ region: '-r',
26
+ acl: '--acl',
27
+ source: '--source-dir',
28
+ path: '-P'
29
+ }
30
+
31
+ class S3Action < Action
32
+ def self.run(config)
33
+ platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME].to_sym
34
+
35
+ # Calling fetch on config so that default values will be used
36
+ params = {}
37
+ params[:ipa] = config[:ipa]
38
+ params[:apk] = config[:apk]
39
+ params[:icon] = config[:icon]
40
+ params[:dsym] = config[:dsym]
41
+ params[:access_key] = config[:access_key]
42
+ params[:secret_access_key] = config[:secret_access_key]
43
+ params[:bucket] = config[:bucket]
44
+ params[:region] = config[:region]
45
+ params[:acl] = config[:acl]
46
+ params[:source] = config[:source]
47
+ params[:path] = config[:path]
48
+ params[:upload_metadata] = config[:upload_metadata]
49
+ params[:plist_template_path] = config[:plist_template_path]
50
+ params[:html_template_path] = config[:html_template_path]
51
+ params[:html_file_name] = config[:html_file_name]
52
+ params[:version_template_path] = config[:version_template_path]
53
+ params[:version_file_name] = config[:version_file_name]
54
+ params[:acl] = config[:acl]
55
+
56
+ case platform
57
+ when :ios
58
+ upload_ios(params)
59
+ when :android
60
+ upload_android(params)
61
+ end
62
+
63
+ return true
64
+ end
65
+
66
+ def self.upload_ios(params)
67
+ # Pulling parameters for other uses
68
+ s3_region = params[:region]
69
+ s3_subdomain = params[:region] ? "s3-#{params[:region]}" : "s3"
70
+ s3_access_key = params[:access_key]
71
+ s3_secret_access_key = params[:secret_access_key]
72
+ s3_bucket = params[:bucket]
73
+ ipa_file = params[:ipa]
74
+ icon_file = params[:icon]
75
+ dsym_file = params[:dsym]
76
+ acl = params[:acl]
77
+
78
+ validate(params)
79
+ UI.user_error!("No IPA file path given, pass using `ipa: 'ipa path'`") unless ipa_file.to_s.length > 0
80
+
81
+ bucket = get_bucket(s3_access_key, s3_secret_access_key, s3_region, s3_bucket)
82
+
83
+ # Gets info used for the plist
84
+ info = FastlaneCore::IpaFileAnalyser.fetch_info_plist_file(ipa_file)
85
+
86
+ build_number = info['CFBundleVersion']
87
+ bundle_id = info['CFBundleIdentifier']
88
+ bundle_version = info['CFBundleShortVersionString']
89
+ app_name = info['CFBundleName']
90
+ full_version = "#{bundle_version}.#{build_number}"
91
+ url_part = get_url_part(app_name, "ios", bundle_version, build_number)
92
+
93
+ plist_template_path = params[:plist_template_path]
94
+ html_template_path = params[:html_template_path]
95
+ html_file_name = params[:html_file_name]
96
+ version_template_path = params[:version_template_path]
97
+ version_file_name = params[:version_file_name]
98
+
99
+ ipa_file_basename = File.basename(ipa_file)
100
+ ipa_file_name = "#{url_part}#{ipa_file_basename}"
101
+ ipa_file_data = File.open(ipa_file, 'rb')
102
+
103
+ ipa_url = self.upload_file(bucket, ipa_file_name, ipa_file_data, acl)
104
+
105
+ # Setting action and environment variables
106
+ Actions.lane_context[SharedValues::S3_IPA_OUTPUT_PATH] = ipa_url
107
+ ENV[SharedValues::S3_IPA_OUTPUT_PATH.to_s] = ipa_url
108
+
109
+ if dsym_file
110
+ dsym_file_basename = File.basename(dsym_file)
111
+ dsym_file_name = "#{url_part}#{dsym_file_basename}"
112
+ dsym_file_data = File.open(dsym_file, 'rb')
113
+
114
+ dsym_url = self.upload_file(bucket, dsym_file_name, dsym_file_data, acl)
115
+
116
+ # Setting action and environment variables
117
+ Actions.lane_context[SharedValues::S3_DSYM_OUTPUT_PATH] = dsym_url
118
+ ENV[SharedValues::S3_DSYM_OUTPUT_PATH.to_s] = dsym_url
119
+
120
+ end
121
+
122
+ if params[:upload_metadata] == false
123
+ return true
124
+ end
125
+
126
+ #####################################
127
+ #
128
+ # html and plist building
129
+ #
130
+ #####################################
131
+
132
+ # Creating plist and html names
133
+ plist_file_name = "#{url_part}#{app_name.delete(' ')}.plist"
134
+ plist_url = "https://#{s3_subdomain}.amazonaws.com/#{s3_bucket}/#{plist_file_name}"
135
+
136
+ html_file_name ||= "#{url_part}index.html"
137
+ html_resources_name = "#{url_part}installation-page"
138
+
139
+ version_file_name ||= "#{url_part}version.json"
140
+
141
+ # grabs module
142
+ eth = Fastlane::ErbTemplateHelper
143
+
144
+ # Creates plist from template
145
+ if plist_template_path && File.exist?(plist_template_path)
146
+ plist_template = eth.load_from_path(plist_template_path)
147
+ else
148
+ plist_template = eth.load("s3_plist_template")
149
+ end
150
+ plist_render = eth.render(plist_template, {
151
+ url: ipa_url,
152
+ ipa_url: ipa_url,
153
+ build_number: build_number,
154
+ bundle_id: bundle_id,
155
+ bundle_version: bundle_version,
156
+ title: app_name
157
+ })
158
+
159
+ # Gets icon from ipa and uploads it
160
+ icon_url = self.upload_icon(icon_file, url_part, bucket, acl)
161
+
162
+ # Creates html from template
163
+ if html_template_path && File.exist?(html_template_path)
164
+ html_template = eth.load_from_path(html_template_path)
165
+ else
166
+ html_template = eth.load_from_path("#{__dir__}/../templates/installation_template.erb")
167
+ end
168
+ html_render = eth.render(html_template, {
169
+ url: "itms-services://?action=download-manifest&url=#{plist_url}",
170
+ app_version: bundle_version,
171
+ build_number: build_number,
172
+ title: app_name,
173
+ app_icon: icon_url,
174
+ platform: "ios"
175
+ })
176
+
177
+ # Creates version from template
178
+ if version_template_path && File.exist?(version_template_path)
179
+ version_template = eth.load_from_path(version_template_path)
180
+ else
181
+ version_template = eth.load("s3_version_template")
182
+ end
183
+ version_render = eth.render(version_template, {
184
+ url: plist_url,
185
+ plist_url: plist_url,
186
+ ipa_url: ipa_url,
187
+ build_number: build_number,
188
+ bundle_version: bundle_version,
189
+ full_version: full_version
190
+ })
191
+
192
+ #####################################
193
+ #
194
+ # html and plist uploading
195
+ #
196
+ #####################################
197
+
198
+ plist_url = self.upload_file(bucket, plist_file_name, plist_render, acl)
199
+ html_url = self.upload_file(bucket, html_file_name, html_render, acl)
200
+ self.upload_directory(bucket, html_resources_name, "#{__dir__}/../templates/installation-page", acl)
201
+ version_url = self.upload_file(bucket, version_file_name, version_render, acl)
202
+
203
+ # Setting action and environment variables
204
+ Actions.lane_context[SharedValues::S3_PLIST_OUTPUT_PATH] = plist_url
205
+ ENV[SharedValues::S3_PLIST_OUTPUT_PATH.to_s] = plist_url
206
+
207
+ Actions.lane_context[SharedValues::S3_HTML_OUTPUT_PATH] = html_url
208
+ ENV[SharedValues::S3_HTML_OUTPUT_PATH.to_s] = html_url
209
+
210
+ Actions.lane_context[SharedValues::S3_VERSION_OUTPUT_PATH] = version_url
211
+ ENV[SharedValues::S3_VERSION_OUTPUT_PATH.to_s] = version_url
212
+
213
+ Actions.lane_context[SharedValues::S3_ICON_OUTPUT_PATH] = icon_url
214
+ ENV[SharedValues::S3_ICON_OUTPUT_PATH.to_s] = icon_url
215
+
216
+ UI.success("Successfully uploaded ipa file to '#{Actions.lane_context[SharedValues::S3_IPA_OUTPUT_PATH]}'")
217
+ UI.success("Successfully uploaded plist file to '#{Actions.lane_context[SharedValues::S3_PLIST_OUTPUT_PATH]}'")
218
+ UI.success("Successfully uploaded html file to '#{Actions.lane_context[SharedValues::S3_HTML_OUTPUT_PATH]}'")
219
+ UI.success("Successfully uploaded version file to '#{Actions.lane_context[SharedValues::S3_VERSION_OUTPUT_PATH]}'")
220
+ UI.success("Successfully uploaded icon file to '#{Actions.lane_context[SharedValues::S3_ICON_OUTPUT_PATH]}'")
221
+ end
222
+
223
+ def self.upload_android(params)
224
+ # Pulling parameters for other uses
225
+ s3_region = params[:region]
226
+ s3_access_key = params[:access_key]
227
+ s3_secret_access_key = params[:secret_access_key]
228
+ s3_bucket = params[:bucket]
229
+ apk_file = params[:apk]
230
+ icon_file = params[:icon]
231
+ acl = params[:acl]
232
+
233
+ validate(params)
234
+ UI.user_error!("No APK file path given, pass using `apk: 'apk path'`") unless apk_file.to_s.length > 0
235
+
236
+ bucket = get_bucket(s3_access_key, s3_secret_access_key, s3_region, s3_bucket)
237
+
238
+ # Gets info used from the apk manifest
239
+ manifest = Android::Apk.new(apk_file).manifest
240
+
241
+ app_name = manifest.label
242
+ build_number = manifest.version_code
243
+ app_version = manifest.version_name
244
+ url_part = get_url_part(app_name, "android", app_version, build_number)
245
+
246
+ html_template_path = params[:html_template_path]
247
+ html_file_name = params[:html_file_name]
248
+
249
+ apk_file_basename = File.basename(apk_file)
250
+ apk_file_name = "#{url_part}#{apk_file_basename}"
251
+ apk_file_data = File.open(apk_file, 'rb')
252
+
253
+ apk_url = self.upload_file(bucket, apk_file_name, apk_file_data, acl)
254
+
255
+ # Setting action and environment variables
256
+ Actions.lane_context[SharedValues::S3_APK_OUTPUT_PATH] = apk_url
257
+ ENV[SharedValues::S3_APK_OUTPUT_PATH.to_s] = apk_url
258
+
259
+ #####################################
260
+ #
261
+ # html and plist building
262
+ #
263
+ #####################################
264
+
265
+ # Creating html names
266
+
267
+ html_file_name ||= "#{url_part}index.html"
268
+ html_resources_name = "#{url_part}installation-page"
269
+
270
+ # grabs module
271
+ eth = Fastlane::ErbTemplateHelper
272
+
273
+ # Gets icon from ipa and uploads it
274
+ icon_url = self.upload_icon(icon_file, url_part, bucket, acl)
275
+
276
+ # Creates html from template
277
+ if html_template_path && File.exist?(html_template_path)
278
+ html_template = eth.load_from_path(html_template_path)
279
+ else
280
+ html_template = eth.load_from_path("#{__dir__}/../templates/installation_template.erb")
281
+ end
282
+ html_render = eth.render(html_template, {
283
+ url: apk_url,
284
+ app_version: app_version,
285
+ build_number: build_number,
286
+ title: app_name,
287
+ app_icon: icon_url,
288
+ platform: "android"
289
+ })
290
+
291
+ html_url = self.upload_file(bucket, html_file_name, html_render, acl)
292
+ self.upload_directory(bucket, html_resources_name, "#{__dir__}/../templates/installation-page", acl)
293
+
294
+ Actions.lane_context[SharedValues::S3_HTML_OUTPUT_PATH] = html_url
295
+ ENV[SharedValues::S3_HTML_OUTPUT_PATH.to_s] = html_url
296
+
297
+ Actions.lane_context[SharedValues::S3_ICON_OUTPUT_PATH] = icon_url
298
+ ENV[SharedValues::S3_ICON_OUTPUT_PATH.to_s] = icon_url
299
+
300
+ UI.success("Successfully uploaded apk file to '#{Actions.lane_context[SharedValues::S3_APK_OUTPUT_PATH]}'")
301
+ UI.success("Successfully uploaded html file to '#{Actions.lane_context[SharedValues::S3_HTML_OUTPUT_PATH]}'")
302
+ UI.success("Successfully uploaded icon file to '#{Actions.lane_context[SharedValues::S3_ICON_OUTPUT_PATH]}'")
303
+ end
304
+
305
+ def self.validate(params)
306
+ s3_access_key = params[:access_key]
307
+ s3_secret_access_key = params[:secret_access_key]
308
+ s3_bucket = params[:bucket]
309
+
310
+ UI.user_error!("No S3 access key given, pass using `access_key: 'key'`") unless s3_access_key.to_s.length > 0
311
+ UI.user_error!("No S3 secret access key given, pass using `secret_access_key: 'secret key'`") unless s3_secret_access_key.to_s.length > 0
312
+ UI.user_error!("No S3 bucket given, pass using `bucket: 'bucket'`") unless s3_bucket.to_s.length > 0
313
+ end
314
+
315
+ def self.get_url_part(app_name, platform, app_version, build_number)
316
+ "#{app_name}/#{platform}/#{app_version}_#{build_number}/"
317
+ end
318
+
319
+ def self.get_bucket(s3_access_key, s3_secret_access_key, s3_region, s3_bucket)
320
+ self.s3_client(s3_access_key, s3_secret_access_key, s3_region).buckets[s3_bucket]
321
+ end
322
+
323
+ def self.s3_client(s3_access_key, s3_secret_access_key, s3_region)
324
+ Actions.verify_gem!('aws-sdk')
325
+ require 'aws-sdk'
326
+ if s3_region
327
+ s3_client = AWS::S3.new(
328
+ access_key_id: s3_access_key,
329
+ secret_access_key: s3_secret_access_key,
330
+ region: s3_region
331
+ )
332
+ else
333
+ s3_client = AWS::S3.new(
334
+ access_key_id: s3_access_key,
335
+ secret_access_key: s3_secret_access_key
336
+ )
337
+ end
338
+ s3_client
339
+ end
340
+
341
+ def self.upload_file(bucket, file_name, file_data, acl)
342
+ obj = bucket.objects.create(file_name, file_data, acl: acl)
343
+
344
+ # When you enable versioning on a S3 bucket,
345
+ # writing to an object will create an object version
346
+ # instead of replacing the existing object.
347
+ # http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/ObjectVersion.html
348
+ if obj.kind_of? AWS::S3::ObjectVersion
349
+ obj = obj.object
350
+ end
351
+
352
+ # Return public url
353
+ obj.public_url.to_s
354
+ end
355
+
356
+ def self.upload_directory(bucket, directory_name, directory_path, acl)
357
+ files = files_at_path(directory_path)
358
+
359
+ files.each do |file|
360
+ local_path = directory_path + file
361
+ s3_path = directory_name + file
362
+ obj = bucket.objects[s3_path]
363
+ obj.write(file: local_path, content_type: content_type_for_file(local_path), acl: "public_read")
364
+ if obj.exists?
365
+ next
366
+ end
367
+
368
+ result = "Error while uploading file #{local_path}"
369
+ return result
370
+ end
371
+ end
372
+
373
+ def self.files_at_path(path)
374
+ files = Dir.glob(path + "/**/*")
375
+ to_remove = []
376
+ files.each do |file|
377
+ if File.directory?(file)
378
+ to_remove.push file
379
+ else
380
+ file.slice! path
381
+ end
382
+ end
383
+ to_remove.each do |file|
384
+ files.delete file
385
+ end
386
+ return files
387
+ end
388
+
389
+ def self.content_type_for_file(file)
390
+ file_extension = File.extname(file)
391
+ extensions_to_type = {
392
+ ".html" => "text/html",
393
+ ".png" => "image/png",
394
+ ".jpg" => "text/jpeg",
395
+ ".gif" => "image/gif",
396
+ ".svg" => "image/svg",
397
+ ".log" => "text/plain",
398
+ ".css" => "text/css",
399
+ ".js" => "application/javascript"
400
+ }
401
+ if extensions_to_type[file_extension].nil?
402
+ "application/octet-stream"
403
+ else
404
+ extensions_to_type[file_extension]
405
+ end
406
+ end
407
+
408
+ #
409
+ # NOT a fan of this as this was taken straight from Shenzhen
410
+ # https://github.com/nomad/shenzhen/blob/986792db5d4d16a80c865a2748ee96ba63644821/lib/shenzhen/plugins/s3.rb#L32
411
+ #
412
+ # Need to find a way to not use this copied method
413
+ #
414
+ # AGAIN, I am not happy about this right now.
415
+ # Using this for prototype reasons.
416
+ #
417
+ def self.expand_path_with_substitutions_from_ipa_plist(ipa, path)
418
+ substitutions = path.scan(/\{CFBundle[^}]+\}/)
419
+ return path if substitutions.empty?
420
+ info = FastlaneCore::IpaFileAnalyser.fetch_info_plist_file(ipa) or return path
421
+
422
+ substitutions.uniq.each do |substitution|
423
+ key = substitution[1...-1]
424
+ value = info[key]
425
+ path.gsub!(Regexp.new(substitution), value) if value
426
+ end
427
+
428
+ return path
429
+ end
430
+
431
+ def self.upload_icon(icon_path, url_part, bucket, acl)
432
+ icon_file_basename = File.basename(icon_path)
433
+ icon_file = File.open(icon_path)
434
+ icon_file_name = "#{url_part}#{icon_file_basename}"
435
+ self.upload_file(bucket, icon_file_name, icon_file, acl)
436
+ end
437
+
438
+ def self.description
439
+ "Generates a plist file and uploads all to AWS S3"
440
+ end
441
+
442
+ def self.available_options
443
+ [
444
+ FastlaneCore::ConfigItem.new(key: :ipa,
445
+ env_name: "",
446
+ description: ".ipa file for the build ",
447
+ optional: true,
448
+ default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]),
449
+ FastlaneCore::ConfigItem.new(key: :dsym,
450
+ env_name: "",
451
+ description: "zipped .dsym package for the build ",
452
+ optional: true,
453
+ default_value: Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH]),
454
+ FastlaneCore::ConfigItem.new(key: :apk,
455
+ env_name: "",
456
+ description: ".apk file for the build ",
457
+ optional: true,
458
+ default_value: Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]),
459
+ FastlaneCore::ConfigItem.new(key: :icon,
460
+ env_name: "",
461
+ description: "app icon file to upload",
462
+ optional: true,
463
+ default_value: Actions.lane_context[SharedValues::ICON_OUTPUT_PATH]),
464
+ FastlaneCore::ConfigItem.new(key: :upload_metadata,
465
+ env_name: "",
466
+ description: "Upload relevant metadata for this build",
467
+ optional: true,
468
+ default_value: true,
469
+ is_string: false),
470
+ FastlaneCore::ConfigItem.new(key: :plist_template_path,
471
+ env_name: "",
472
+ description: "plist template path",
473
+ optional: true),
474
+ FastlaneCore::ConfigItem.new(key: :html_template_path,
475
+ env_name: "",
476
+ description: "html erb template path",
477
+ optional: true),
478
+ FastlaneCore::ConfigItem.new(key: :html_file_name,
479
+ env_name: "",
480
+ description: "uploaded html filename",
481
+ optional: true),
482
+ FastlaneCore::ConfigItem.new(key: :version_template_path,
483
+ env_name: "",
484
+ description: "version erb template path",
485
+ optional: true),
486
+ FastlaneCore::ConfigItem.new(key: :version_file_name,
487
+ env_name: "",
488
+ description: "uploaded version filename",
489
+ optional: true),
490
+ FastlaneCore::ConfigItem.new(key: :access_key,
491
+ env_name: "S3_ACCESS_KEY",
492
+ description: "AWS Access Key ID ",
493
+ optional: true,
494
+ default_value: ENV['AWS_ACCESS_KEY_ID']),
495
+ FastlaneCore::ConfigItem.new(key: :secret_access_key,
496
+ env_name: "S3_SECRET_ACCESS_KEY",
497
+ description: "AWS Secret Access Key ",
498
+ optional: true,
499
+ default_value: ENV['AWS_SECRET_ACCESS_KEY']),
500
+ FastlaneCore::ConfigItem.new(key: :bucket,
501
+ env_name: "S3_BUCKET",
502
+ description: "AWS bucket name",
503
+ optional: true,
504
+ default_value: ENV['AWS_BUCKET_NAME']),
505
+ FastlaneCore::ConfigItem.new(key: :region,
506
+ env_name: "S3_REGION",
507
+ description: "AWS region (for bucket creation) ",
508
+ optional: true,
509
+ default_value: ENV['AWS_REGION']),
510
+ FastlaneCore::ConfigItem.new(key: :path,
511
+ env_name: "S3_PATH",
512
+ description: "S3 'path'. Values from Info.plist will be substituded for keys wrapped in {} ",
513
+ optional: true,
514
+ default_value: '{CFBundleName}/ios/{CFBundleShortVersionString}_{CFBundleVersion}/'),
515
+ FastlaneCore::ConfigItem.new(key: :source,
516
+ env_name: "S3_SOURCE",
517
+ description: "Optional source directory e.g. ./build ",
518
+ optional: true),
519
+ FastlaneCore::ConfigItem.new(key: :acl,
520
+ env_name: "S3_ACL",
521
+ description: "Uploaded object permissions e.g public_read (default), private, public_read_write, authenticated_read ",
522
+ optional: true,
523
+ default_value: "public_read")
524
+ ]
525
+ end
526
+
527
+ def self.output
528
+ [
529
+ ['S3_IPA_OUTPUT_PATH', 'Direct HTTP link to the uploaded ipa file'],
530
+ ['S3_DSYM_OUTPUT_PATH', 'Direct HTTP link to the uploaded dsym file'],
531
+ ['S3_PLIST_OUTPUT_PATH', 'Direct HTTP link to the uploaded plist file'],
532
+ ['S3_APK_OUTPUT_PATH', 'Direct HTTP link to the uploaded apk file'],
533
+ ['S3_HTML_OUTPUT_PATH', 'Direct HTTP link to the uploaded HTML file'],
534
+ ['S3_VERSION_OUTPUT_PATH', 'Direct HTTP link to the uploaded Version file'],
535
+ ['S3_ICON_OUTPUT_PATH', 'Direct HTTP link to the uploaded icon file']
536
+ ]
537
+ end
538
+
539
+ def self.author
540
+ "joshdholtz"
541
+ end
542
+
543
+ def self.is_supported?(platform)
544
+ [:ios, :android].include? platform
545
+ end
546
+ end
547
+ end
548
+ end
549
+ # rubocop:enable Metrics/AbcSize
550
+ # rubocop:enable Metrics/MethodLength
551
+ # rubocop:enable Metrics/ClassLength