fastlane-plugin-aws_s3 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cf25b6b9b17c5e1ecab636e6cca8774c1c107f95
4
+ data.tar.gz: a3f657dd480e94d66593c8ea5f2ad1dfdb987360
5
+ SHA512:
6
+ metadata.gz: a796219f0d215c9ad032ae8d49bd01a12b0c7896dcc5b5cb049bc1ff8cf0039cd2525528483e0e91c48799f81af37a2dab5f93d94dae71b384a7099701af6dc3
7
+ data.tar.gz: f48b0e1685a567aa2f9019d77342a639eb8792bd7f04e71b1949e89d1cf0c22338c2c7ee11c3e48576eb5cd81f37522bc90fbfd50e1d5bb0c4426759e3793b24
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Josh Holtz <josh@rokkincat.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # fastlane-plugin-s3 `fastlane` Plugin
2
+
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-s3)
4
+
5
+ ## Getting Started
6
+
7
+ This project is a [fastlane](https://github.com/fastlane/fastlane) plugin. To get started with fastlane-plugin-s3, add it to your project by running:
8
+
9
+ ```bash
10
+ fastlane add_plugin s3
11
+ ```
12
+
13
+ ## About s3
14
+
15
+ Upload a new build to Amazon S3 to distribute the build to beta testers. Works for both Ad Hoc and Enterprise signed applications. This step will generate the necessary HTML, plist, and version files for you.
16
+
17
+ The `s3` action can upload both iOS apps (IPAs) and Android apps (APKs). It is **highly** suggested that you make a separate bucket for each of the apps.
18
+
19
+ Add the `s3` action after the `gym` step:
20
+
21
+ ```ruby
22
+ s3
23
+ ```
24
+
25
+ You can also customize a lot of options:
26
+ ```ruby
27
+ s3(
28
+ # All of these are used to make Shenzhen's `ipa distribute:s3` command
29
+ access_key: ENV['S3_ACCESS_KEY'], # Required from user.
30
+ secret_access_key: ENV['S3_SECRET_ACCESS_KEY'], # Required from user.
31
+ bucket: ENV['S3_BUCKET'], # Required from user.
32
+ region: ENV['S3_REGION'], # Required from user.
33
+
34
+ ipa: 'AppName.ipa', # Required (if not uploading an APK).
35
+ dsym: 'AppName.app.dSYM.zip', # Optional is you use `ipa` to build.
36
+
37
+ apk: 'AppName.apk' # Required (if not uploading an IPA).
38
+
39
+ path: 'v{CFBundleShortVersionString}_b{CFBundleVersion}/', # This is actually the default.
40
+ upload_metadata: true, # Upload version.json, plist and HTML. Set to false to skip uploading of these files.
41
+ version_file_name: 'app_version.json', # Name of the file to upload to S3. Defaults to 'version.json'
42
+ version_template_path: 'path/to/erb' # Path to an ERB to configure the structure of the version JSON file
43
+ )
44
+ ```
45
+
46
+ It is recommended to **not** store the AWS access keys in the `Fastfile`.
47
+
48
+ The uploaded `version.json` file provides an easy way for apps to poll if a new update is available. The JSON looks like:
49
+
50
+ ```json
51
+ {
52
+ "latestVersion": "<%= full_version %>",
53
+ "updateUrl": "itms-services://?action=download-manifest&url=<%= url %>"
54
+ }
55
+ ```
56
+
57
+ ## Example
58
+
59
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
60
+
61
+ **Note to author:** Please set up a sample project to make it easy for users to explore what your plugin does. Provide everything that is necessary to try out the plugin in this project (including a sample Xcode/Android project if necessary)
62
+
63
+ ## Issues and Feedback
64
+
65
+ For any other issues and feedback about this plugin, please submit it to this repository.
66
+
67
+ ## Troubleshooting
68
+
69
+ For some more detailed help with plugins problems, check out the [Plugins Troubleshooting](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/PluginsTroubleshooting.md) doc in the main `fastlane` repo.
70
+
71
+ ## Using `fastlane` Plugins
72
+
73
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Plugins.md) in the main `fastlane` repo.
74
+
75
+ ## About `fastlane`
76
+
77
+ `fastlane` automates building, testing, and releasing your app for beta and app store distributions. To learn more about `fastlane`, check out [fastlane.tools](https://fastlane.tools).
@@ -0,0 +1,88 @@
1
+ <html>
2
+ <head>
3
+ <meta charset="utf-8">
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
5
+ <title>Install <%= title %></title>
6
+ </head>
7
+ <body>
8
+ <style type="text/css">
9
+ * {
10
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
11
+ text-align: center;
12
+ background-color: #f5f5f5;
13
+ }
14
+ .oneRow {
15
+ width: 100%;
16
+ overflow: auto;
17
+ overflow-y: hidden;
18
+ white-space: nowrap;
19
+ text-align: center;
20
+ }
21
+ .download {
22
+ margin: 30px;
23
+ font-size: 130%;
24
+ }
25
+ #appIcon {
26
+ -webkit-border-radius: 22.544%;
27
+ -moz-border-radius: 22.544%;
28
+ -ms-border-radius: 22.544%;
29
+ border-radius: 22.544%;
30
+
31
+ margin-bottom: 30px;
32
+ }
33
+ a {
34
+ text-decoration: none;
35
+ color: blue;
36
+ }
37
+ a:hover {
38
+ text-decoration: underline;
39
+ }
40
+ #footnote {
41
+ color: #737373;
42
+ font-size: 14px;
43
+ }
44
+ #finished { display: none; }
45
+ #fastlaneLogo {
46
+ text-align: center;
47
+ max-width: 150px;
48
+ margin-top: 10px;
49
+ }
50
+ </style>
51
+
52
+ <h1 style="text-align: center;"><%= title %></h1>
53
+ <!-- <img src="app_icon.png" id="appIcon"> -->
54
+
55
+ <div class="oneRow">
56
+ <span class="download" id="android">
57
+ <a href="<%= apk_url %>" id="text" class="btn btn-lg btn-default" onclick="document.getElementById('finished').id = '';">
58
+ Install <%= title %> <%= version_name %>
59
+ </a>
60
+ </span>
61
+
62
+ <!-- <span class="download" id="android">
63
+ </span> -->
64
+ </div>
65
+
66
+ <h3 id="desktop">Please open this page on your Android device!</h3>
67
+
68
+ <p id="finished">
69
+ App is being installed. Close Safari using the home button.
70
+ </p>
71
+
72
+ <p id="footnote">
73
+ This is a beta version and is not meant to share with the public.
74
+ </p>
75
+ <img src="https://s3-eu-west-1.amazonaws.com/fastlane.tools/fastlane_medium.png" id="fastlaneLogo" />
76
+ </body>
77
+
78
+ <script type='text/javascript'>
79
+ if (/Android/i.test(navigator.userAgent))
80
+ {
81
+ document.getElementById("desktop").remove()
82
+ }
83
+ else
84
+ {
85
+ document.getElementById("android").remove()
86
+ }
87
+ </script>
88
+ </html>
@@ -0,0 +1,4 @@
1
+ {
2
+ "latestVersion": "<%= full_version %>",
3
+ "updateUrl": "<%= apk_url %>"
4
+ }
@@ -0,0 +1,95 @@
1
+ <html>
2
+ <head>
3
+ <meta charset="utf-8">
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
5
+ <title>Install <%= title %></title>
6
+ </head>
7
+ <body>
8
+ <style type="text/css">
9
+ * {
10
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
11
+ text-align: center;
12
+ background-color: #f5f5f5;
13
+ }
14
+ .oneRow {
15
+ width: 100%;
16
+ overflow: auto;
17
+ overflow-y: hidden;
18
+ white-space: nowrap;
19
+ text-align: center;
20
+ }
21
+ .download {
22
+ margin: 30px;
23
+ font-size: 130%;
24
+ }
25
+ #appIcon {
26
+ -webkit-border-radius: 22.544%;
27
+ -moz-border-radius: 22.544%;
28
+ -ms-border-radius: 22.544%;
29
+ border-radius: 22.544%;
30
+
31
+ margin-bottom: 30px;
32
+ }
33
+ a {
34
+ text-decoration: none;
35
+ color: blue;
36
+ }
37
+ a:hover {
38
+ text-decoration: underline;
39
+ }
40
+ #footnote {
41
+ color: #737373;
42
+ font-size: 14px;
43
+ }
44
+ #finished { display: none; }
45
+ #fastlaneLogo {
46
+ text-align: center;
47
+ max-width: 150px;
48
+ margin-top: 10px;
49
+ }
50
+ </style>
51
+
52
+ <h1 style="text-align: center;"><%= title %></h1>
53
+ <!-- <img src="app_icon.png" id="appIcon"> -->
54
+
55
+ <div class="oneRow">
56
+ <span class="download" id="ios">
57
+ <a href="itms-services://?action=download-manifest&url=itms-services://?action=download-manifest&url=<%= plist_url %>" id="text" class="btn btn-lg btn-default" onclick="document.getElementById('finished').id = '';">
58
+ Install <%= title %> <%= bundle_version %>
59
+ </a>
60
+ </span>
61
+
62
+ <!-- <span class="download" id="android">
63
+ </span> -->
64
+ </div>
65
+
66
+ <h3 id="desktop">Please open this page on your iPhone!</h3>
67
+
68
+ <p id="finished">
69
+ App is being installed. Close Safari using the home button.
70
+ </p>
71
+
72
+ <p id="footnote">
73
+ This is a beta version and is not meant to share with the public.
74
+ </p>
75
+ <img src="https://s3-eu-west-1.amazonaws.com/fastlane.tools/fastlane_medium.png" id="fastlaneLogo" />
76
+ </body>
77
+
78
+ <script type='text/javascript'>
79
+ // if (/Android/i.test(navigator.userAgent))
80
+ // {
81
+ // document.getElementById("ios").remove()
82
+ // document.getElementById("desktop").remove()
83
+ // }
84
+ if (/iPhone|iPad|iPod/i.test(navigator.userAgent))
85
+ {
86
+ // document.getElementById("android").remove()
87
+ document.getElementById("desktop").remove()
88
+ }
89
+ else
90
+ {
91
+ document.getElementById("ios").remove()
92
+ // document.getElementById("android").remove()
93
+ }
94
+ </script>
95
+ </html>
@@ -0,0 +1,31 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>items</key>
6
+ <array>
7
+ <dict>
8
+ <key>assets</key>
9
+ <array>
10
+ <dict>
11
+ <key>kind</key>
12
+ <string>software-package</string>
13
+ <key>url</key>
14
+ <string><%= ipa_url %></string>
15
+ </dict>
16
+ </array>
17
+ <key>metadata</key>
18
+ <dict>
19
+ <key>bundle-identifier</key>
20
+ <string><%= bundle_id %></string>
21
+ <key>bundle-version</key>
22
+ <string><%= bundle_version %></string>
23
+ <key>kind</key>
24
+ <string>software</string>
25
+ <key>title</key>
26
+ <string><%= title %></string>
27
+ </dict>
28
+ </dict>
29
+ </array>
30
+ </dict>
31
+ </plist>
@@ -0,0 +1,4 @@
1
+ {
2
+ "latestVersion": "<%= full_version %>",
3
+ "updateUrl": "itms-services://?action=download-manifest&url=<%= plist_url %>"
4
+ }
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/aws_s3/version'
2
+
3
+ module Fastlane
4
+ module AwsS3
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::AwsS3.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,477 @@
1
+ # rubocop:disable Metrics/AbcSize
2
+ require 'fastlane/erb_template_helper'
3
+ require 'ostruct'
4
+
5
+ module Fastlane
6
+ module Actions
7
+ module SharedValues
8
+ S3_APK_OUTPUT_PATH = :S3_APK_OUTPUT_PATH
9
+ S3_IPA_OUTPUT_PATH = :S3_IPA_OUTPUT_PATH
10
+ S3_DSYM_OUTPUT_PATH = :S3_DSYM_OUTPUT_PATH
11
+ S3_PLIST_OUTPUT_PATH = :S3_PLIST_OUTPUT_PATH
12
+ S3_HTML_OUTPUT_PATH = :S3_HTML_OUTPUT_PATH
13
+ S3_VERSION_OUTPUT_PATH = :S3_VERSION_OUTPUT_PATH
14
+ end
15
+
16
+ class AwsS3Action < Action
17
+ def self.run(config)
18
+ # Calling fetch on config so that default values will be used
19
+ params = {}
20
+ params[:apk] = config[:apk]
21
+ params[:ipa] = config[:ipa]
22
+ params[:dsym] = config[:dsym]
23
+ params[:access_key] = config[:access_key]
24
+ params[:secret_access_key] = config[:secret_access_key]
25
+ params[:bucket] = config[:bucket]
26
+ params[:region] = config[:region]
27
+ params[:acl] = config[:acl]
28
+ params[:source] = config[:source]
29
+ params[:path] = config[:path]
30
+ params[:upload_metadata] = config[:upload_metadata]
31
+ params[:plist_template_path] = config[:plist_template_path]
32
+ params[:html_template_path] = config[:html_template_path]
33
+ params[:html_file_name] = config[:html_file_name]
34
+ params[:version_template_path] = config[:version_template_path]
35
+ params[:version_file_name] = config[:version_file_name]
36
+
37
+ # Pulling parameters for other uses
38
+ s3_region = params[:region]
39
+ s3_subdomain = params[:region] ? "s3-#{params[:region]}" : "s3"
40
+ s3_access_key = params[:access_key]
41
+ s3_secret_access_key = params[:secret_access_key]
42
+ s3_bucket = params[:bucket]
43
+ apk_file = params[:apk]
44
+ ipa_file = params[:ipa]
45
+ dsym_file = params[:dsym]
46
+ s3_path = params[:path]
47
+ acl = params[:acl].to_sym
48
+
49
+ UI.user_error!("No S3 access key given, pass using `access_key: 'key'`") unless s3_access_key.to_s.length > 0
50
+ 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
51
+ UI.user_error!("No S3 bucket given, pass using `bucket: 'bucket'`") unless s3_bucket.to_s.length > 0
52
+ UI.user_error!("No IPA or APK file path given, pass using `ipa: 'ipa path'` or `apk: 'apk path'`") if ipa_file.to_s.length == 0 && apk_file.to_s.length == 0
53
+ UI.user_error!("Please only give IPA path or APK path (not both)") if ipa_file.to_s.length > 0 && apk_file.to_s.length > 0
54
+
55
+ require 'aws-sdk'
56
+ Aws.config.update({
57
+ region: s3_region,
58
+ credentials: Aws::Credentials.new(s3_access_key, s3_secret_access_key)
59
+ })
60
+
61
+ s3_client = Aws::S3::Client.new
62
+
63
+ upload_ipa(s3_client, params, s3_region, s3_subdomain, s3_access_key, s3_secret_access_key, s3_bucket, ipa_file, dsym_file, s3_path, acl) if ipa_file.to_s.length > 0
64
+ upload_apk(s3_client, params, s3_region, s3_subdomain, s3_access_key, s3_secret_access_key, s3_bucket, apk_file, s3_path, acl) if apk_file.to_s.length > 0
65
+
66
+ return true
67
+ end
68
+
69
+ def self.upload_ipa(s3_client, params, s3_region, s3_subdomain, s3_access_key, s3_secret_access_key, s3_bucket, ipa_file, dsym_file, s3_path, acl)
70
+
71
+ s3_path = "v{CFBundleShortVersionString}_b{CFBundleVersion}/" unless s3_path
72
+
73
+ plist_template_path = params[:plist_template_path]
74
+ html_template_path = params[:html_template_path]
75
+ html_file_name = params[:html_file_name]
76
+ version_template_path = params[:version_template_path]
77
+ version_file_name = params[:version_file_name]
78
+
79
+ url_part = self.expand_path_with_substitutions_from_ipa_plist(ipa_file, s3_path)
80
+
81
+ ipa_file_basename = File.basename(ipa_file)
82
+ ipa_file_name = "#{url_part}#{ipa_file_basename}"
83
+ ipa_file_data = File.open(ipa_file, 'rb')
84
+
85
+ ipa_url = self.upload_file(s3_client, s3_bucket, ipa_file_name, ipa_file_data, acl)
86
+
87
+ # Setting action and environment variables
88
+ Actions.lane_context[SharedValues::S3_IPA_OUTPUT_PATH] = ipa_url
89
+ ENV[SharedValues::S3_IPA_OUTPUT_PATH.to_s] = ipa_url
90
+
91
+ if dsym_file
92
+ dsym_file_basename = File.basename(dsym_file)
93
+ dsym_file_name = "#{url_part}#{dsym_file_basename}"
94
+ dsym_file_data = File.open(dsym_file, 'rb')
95
+
96
+ dsym_url = self.upload_file(s3_client, s3_bucket, dsym_file_name, dsym_file_data, acl)
97
+
98
+ # Setting action and environment variables
99
+ Actions.lane_context[SharedValues::S3_DSYM_OUTPUT_PATH] = dsym_url
100
+ ENV[SharedValues::S3_DSYM_OUTPUT_PATH.to_s] = dsym_url
101
+
102
+ end
103
+
104
+ if params[:upload_metadata] == false
105
+ return true
106
+ end
107
+
108
+ #####################################
109
+ #
110
+ # html and plist building
111
+ #
112
+ #####################################
113
+
114
+ # Gets info used for the plist
115
+ info = FastlaneCore::IpaFileAnalyser.fetch_info_plist_file(ipa_file)
116
+
117
+ build_num = info['CFBundleVersion']
118
+ bundle_id = info['CFBundleIdentifier']
119
+ bundle_version = info['CFBundleShortVersionString']
120
+ title = info['CFBundleName']
121
+ full_version = "#{bundle_version}.#{build_num}"
122
+
123
+ # Creating plist and html names
124
+ plist_file_name = "#{url_part}#{title.delete(' ')}.plist"
125
+ plist_url = "https://#{s3_subdomain}.amazonaws.com/#{s3_bucket}/#{plist_file_name}"
126
+
127
+ html_file_name ||= "index.html"
128
+
129
+ version_file_name ||= "version.json"
130
+
131
+ # grabs module
132
+ eth = Fastlane::Helper::AwsS3Helper
133
+
134
+ # Creates plist from template
135
+ if plist_template_path && File.exist?(plist_template_path)
136
+ puts "1 - #{plist_template_path}"
137
+ plist_template = eth.load_from_path(plist_template_path)
138
+ else
139
+ puts "2 - #{Helper.gem_path('fastlane-plugin-aws_s3')}"
140
+ plist_template = eth.load("s3_ios_plist_template")
141
+ end
142
+ plist_render = eth.render(plist_template, {
143
+ url: ipa_url,
144
+ ipa_url: ipa_url,
145
+ build_num: build_num,
146
+ bundle_id: bundle_id,
147
+ bundle_version: bundle_version,
148
+ title: title
149
+ })
150
+
151
+ # Creates html from template
152
+ if html_template_path && File.exist?(html_template_path)
153
+ html_template = eth.load_from_path(html_template_path)
154
+ else
155
+ html_template = eth.load("s3_ios_html_template")
156
+ end
157
+ html_render = eth.render(html_template, {
158
+ url: plist_url,
159
+ plist_url: plist_url,
160
+ ipa_url: ipa_url,
161
+ build_num: build_num,
162
+ bundle_id: bundle_id,
163
+ bundle_version: bundle_version,
164
+ title: title
165
+ })
166
+
167
+ # Creates version from template
168
+ if version_template_path && File.exist?(version_template_path)
169
+ version_template = eth.load_from_path(version_template_path)
170
+ else
171
+ version_template = eth.load("s3_ios_version_template")
172
+ end
173
+ version_render = eth.render(version_template, {
174
+ url: plist_url,
175
+ plist_url: plist_url,
176
+ ipa_url: ipa_url,
177
+ build_num: build_num,
178
+ bundle_version: bundle_version,
179
+ full_version: full_version
180
+ })
181
+
182
+ #####################################
183
+ #
184
+ # html and plist uploading
185
+ #
186
+ #####################################
187
+
188
+ plist_url = self.upload_file(s3_client, s3_bucket, plist_file_name, plist_render, acl)
189
+ html_url = self.upload_file(s3_client, s3_bucket, html_file_name, html_render, acl)
190
+ version_url = self.upload_file(s3_client, s3_bucket, version_file_name, version_render, acl)
191
+
192
+ # Setting action and environment variables
193
+ Actions.lane_context[SharedValues::S3_PLIST_OUTPUT_PATH] = plist_url
194
+ ENV[SharedValues::S3_PLIST_OUTPUT_PATH.to_s] = plist_url
195
+
196
+ Actions.lane_context[SharedValues::S3_HTML_OUTPUT_PATH] = html_url
197
+ ENV[SharedValues::S3_HTML_OUTPUT_PATH.to_s] = html_url
198
+
199
+ Actions.lane_context[SharedValues::S3_VERSION_OUTPUT_PATH] = version_url
200
+ ENV[SharedValues::S3_VERSION_OUTPUT_PATH.to_s] = version_url
201
+
202
+ UI.success("Successfully uploaded ipa file to '#{Actions.lane_context[SharedValues::S3_IPA_OUTPUT_PATH]}'")
203
+ end
204
+
205
+ def self.upload_apk(s3_client, params, s3_region, s3_subdomain, s3_access_key, s3_secret_access_key, s3_bucket, apk_file, s3_path, acl)
206
+ version = get_apk_version(apk_file)
207
+
208
+ version_code = version[0]
209
+ version_name = version[1]
210
+ title = version[2]
211
+
212
+ s3_path = "#{version_code}_#{version_name}/" unless s3_path
213
+
214
+ html_template_path = params[:html_template_path]
215
+ html_file_name = params[:html_file_name]
216
+ version_template_path = params[:version_template_path]
217
+ version_file_name = params[:version_file_name]
218
+
219
+ url_part = s3_path
220
+
221
+ apk_file_basename = File.basename(apk_file)
222
+ apk_file_name = "#{url_part}#{apk_file_basename}"
223
+ apk_file_data = File.open(apk_file, 'rb')
224
+
225
+ apk_url = self.upload_file(s3_client, s3_bucket, apk_file_name, apk_file_data, acl)
226
+
227
+ # Setting action and environment variables
228
+ Actions.lane_context[SharedValues::S3_APK_OUTPUT_PATH] = apk_url
229
+ ENV[SharedValues::S3_APK_OUTPUT_PATH.to_s] = apk_url
230
+
231
+ if params[:upload_metadata] == false
232
+ return true
233
+ end
234
+
235
+ #####################################
236
+ #
237
+ # html and plist building
238
+ #
239
+ #####################################
240
+
241
+ html_file_name ||= "index.html"
242
+
243
+ version_file_name ||= "version.json"
244
+
245
+ # grabs module
246
+ eth = Fastlane::Helper::AwsS3Helper
247
+
248
+ # Creates html from template
249
+ if html_template_path && File.exist?(html_template_path)
250
+ html_template = eth.load_from_path(html_template_path)
251
+ else
252
+ html_template = eth.load("s3_android_html_template")
253
+ end
254
+ html_render = eth.render(html_template, {
255
+ apk_url: apk_url,
256
+ version_code: version_code,
257
+ version_name: version_name,
258
+ title: title
259
+ })
260
+
261
+ # Creates version from template
262
+ if version_template_path && File.exist?(version_template_path)
263
+ version_template = eth.load_from_path(version_template_path)
264
+ else
265
+ version_template = eth.load("s3_android_version_template")
266
+ end
267
+ version_render = eth.render(version_template, {
268
+ apk_url: apk_url,
269
+ version_code: version_code,
270
+ version_name: version_name,
271
+ full_version: "#{version_code}_#{version_name}"
272
+ })
273
+
274
+ #####################################
275
+ #
276
+ # html and plist uploading
277
+ #
278
+ #####################################
279
+
280
+ html_url = self.upload_file(s3_client, s3_bucket, html_file_name, html_render, acl)
281
+ version_url = self.upload_file(s3_client, s3_bucket, version_file_name, version_render, acl)
282
+
283
+ Actions.lane_context[SharedValues::S3_HTML_OUTPUT_PATH] = html_url
284
+ ENV[SharedValues::S3_HTML_OUTPUT_PATH.to_s] = html_url
285
+
286
+ Actions.lane_context[SharedValues::S3_VERSION_OUTPUT_PATH] = version_url
287
+ ENV[SharedValues::S3_VERSION_OUTPUT_PATH.to_s] = version_url
288
+
289
+ UI.success("Successfully uploaded apk file to '#{Actions.lane_context[SharedValues::S3_APK_OUTPUT_PATH]}'")
290
+ end
291
+
292
+ def self.get_apk_version(apk_file)
293
+ require 'apktools/apkxml'
294
+
295
+ # Load the XML data
296
+ parser = ApkXml.new(apk_file)
297
+ parser.parse_xml("AndroidManifest.xml", false, true)
298
+
299
+ elements = parser.xml_elements
300
+
301
+ versionCode = nil
302
+ versionName = nil
303
+ name = nil
304
+
305
+ elements.each do |element|
306
+ if element.name == "manifest"
307
+ element.attributes.each do |attr|
308
+ if attr.name == "versionCode"
309
+ versionCode = attr.value
310
+ elsif attr.name == "versionName"
311
+ versionName = attr.value
312
+ end
313
+ end
314
+ elsif element.name == "application"
315
+ element.attributes.each do |attr|
316
+ if attr.name == "label"
317
+ name = attr.value
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ if versionCode =~ /^0x[0-9A-Fa-f]+$/ #if is hex
324
+ versionCode = versionCode.to_i(16)
325
+ end
326
+
327
+ [versionCode, versionName, name]
328
+ end
329
+
330
+ def self.upload_file(s3_client, bucket_name, file_name, file_data, acl)
331
+ bucket = Aws::S3::Bucket.new(bucket_name, client: s3_client)
332
+ obj = bucket.put_object({
333
+ acl: acl,
334
+ key: file_name,
335
+ body: file_data
336
+ })
337
+
338
+ # When you enable versioning on a S3 bucket,
339
+ # writing to an object will create an object version
340
+ # instead of replacing the existing object.
341
+ # http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/ObjectVersion.html
342
+ if obj.kind_of? Aws::S3::ObjectVersion
343
+ obj = obj.object
344
+ end
345
+
346
+ # Return public url
347
+ obj.public_url.to_s
348
+ end
349
+
350
+ #
351
+ # NOT a fan of this as this was taken straight from Shenzhen
352
+ # https://github.com/nomad/shenzhen/blob/986792db5d4d16a80c865a2748ee96ba63644821/lib/shenzhen/plugins/s3.rb#L32
353
+ #
354
+ # Need to find a way to not use this copied method
355
+ #
356
+ # AGAIN, I am not happy about this right now.
357
+ # Using this for prototype reasons.
358
+ #
359
+ def self.expand_path_with_substitutions_from_ipa_plist(ipa, path)
360
+ substitutions = path.scan(/\{CFBundle[^}]+\}/)
361
+ return path if substitutions.empty?
362
+ info = FastlaneCore::IpaFileAnalyser.fetch_info_plist_file(ipa) or return path
363
+
364
+ substitutions.uniq.each do |substitution|
365
+ key = substitution[1...-1]
366
+ value = info[key]
367
+ path.gsub!(Regexp.new(substitution), value) if value
368
+ end
369
+
370
+ return path
371
+ end
372
+
373
+ def self.description
374
+ "Generates a plist file and uploads all to AWS S3"
375
+ end
376
+
377
+ def self.available_options
378
+ [
379
+ FastlaneCore::ConfigItem.new(key: :apk,
380
+ env_name: "",
381
+ description: ".apk file for the build ",
382
+ optional: true,
383
+ default_value: Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]),
384
+ FastlaneCore::ConfigItem.new(key: :ipa,
385
+ env_name: "",
386
+ description: ".ipa file for the build ",
387
+ optional: true,
388
+ default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]),
389
+ FastlaneCore::ConfigItem.new(key: :dsym,
390
+ env_name: "",
391
+ description: "zipped .dsym package for the build ",
392
+ optional: true,
393
+ default_value: Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH]),
394
+ FastlaneCore::ConfigItem.new(key: :upload_metadata,
395
+ env_name: "",
396
+ description: "Upload relevant metadata for this build",
397
+ optional: true,
398
+ default_value: true,
399
+ is_string: false),
400
+ FastlaneCore::ConfigItem.new(key: :plist_template_path,
401
+ env_name: "",
402
+ description: "plist template path",
403
+ optional: true),
404
+ FastlaneCore::ConfigItem.new(key: :html_template_path,
405
+ env_name: "",
406
+ description: "html erb template path",
407
+ optional: true),
408
+ FastlaneCore::ConfigItem.new(key: :html_file_name,
409
+ env_name: "",
410
+ description: "uploaded html filename",
411
+ optional: true),
412
+ FastlaneCore::ConfigItem.new(key: :version_template_path,
413
+ env_name: "",
414
+ description: "version erb template path",
415
+ optional: true),
416
+ FastlaneCore::ConfigItem.new(key: :version_file_name,
417
+ env_name: "",
418
+ description: "uploaded version filename",
419
+ optional: true),
420
+ FastlaneCore::ConfigItem.new(key: :access_key,
421
+ env_name: "S3_ACCESS_KEY",
422
+ description: "AWS Access Key ID ",
423
+ optional: true,
424
+ default_value: ENV['AWS_ACCESS_KEY_ID']),
425
+ FastlaneCore::ConfigItem.new(key: :secret_access_key,
426
+ env_name: "S3_SECRET_ACCESS_KEY",
427
+ description: "AWS Secret Access Key ",
428
+ optional: true,
429
+ default_value: ENV['AWS_SECRET_ACCESS_KEY']),
430
+ FastlaneCore::ConfigItem.new(key: :bucket,
431
+ env_name: "S3_BUCKET",
432
+ description: "AWS bucket name",
433
+ optional: true,
434
+ default_value: ENV['AWS_BUCKET_NAME']),
435
+ FastlaneCore::ConfigItem.new(key: :region,
436
+ env_name: "S3_REGION",
437
+ description: "AWS region (for bucket creation) ",
438
+ optional: true,
439
+ default_value: ENV['AWS_REGION']),
440
+ FastlaneCore::ConfigItem.new(key: :path,
441
+ env_name: "S3_PATH",
442
+ description: "S3 'path'. Values from Info.plist will be substituded for keys wrapped in {} ",
443
+ optional: true),
444
+ FastlaneCore::ConfigItem.new(key: :source,
445
+ env_name: "S3_SOURCE",
446
+ description: "Optional source directory e.g. ./build ",
447
+ optional: true),
448
+ FastlaneCore::ConfigItem.new(key: :acl,
449
+ env_name: "S3_ACL",
450
+ description: "Uploaded object permissions e.g public_read (default), private, public_read_write, authenticated_read ",
451
+ optional: true,
452
+ default_value: "public-read"
453
+ )
454
+ ]
455
+ end
456
+
457
+ def self.output
458
+ [
459
+ ['S3_IPA_OUTPUT_PATH', 'Direct HTTP link to the uploaded ipa file'],
460
+ ['S3_DSYM_OUTPUT_PATH', 'Direct HTTP link to the uploaded dsym file'],
461
+ ['S3_PLIST_OUTPUT_PATH', 'Direct HTTP link to the uploaded plist file'],
462
+ ['S3_HTML_OUTPUT_PATH', 'Direct HTTP link to the uploaded HTML file'],
463
+ ['S3_VERSION_OUTPUT_PATH', 'Direct HTTP link to the uploaded Version file']
464
+ ]
465
+ end
466
+
467
+ def self.author
468
+ "joshdholtz"
469
+ end
470
+
471
+ def self.is_supported?(platform)
472
+ platform == :ios || platform == :android
473
+ end
474
+ end
475
+ end
476
+ end
477
+ # rubocop:enable Metrics/AbcSize
@@ -0,0 +1,34 @@
1
+ module Fastlane
2
+ module Helper
3
+ class AwsS3Helper
4
+ # class methods that you define here become available in your action
5
+ # as `Helper::S3Helper.your_method`
6
+ #
7
+ def self.show_message
8
+ UI.message("Hello from the s3 plugin helper!")
9
+ end
10
+
11
+ #
12
+ # Taken from https://github.com/fastlane/fastlane/blob/9c0494ef5e7d71afc51c73fe0b141b02e8991d9c/fastlane/lib/fastlane/erb_template_helper.rb
13
+ # Because I need to load from my plugin gem (not main fastlane gem)
14
+ #
15
+ require "erb"
16
+ def self.load(template_name)
17
+ path = "#{Helper.gem_path('fastlane-plugin-aws_s3')}/lib/assets/#{template_name}.erb"
18
+ puts "path #{path}"
19
+ load_from_path(path)
20
+ end
21
+
22
+ def self.load_from_path(template_filepath)
23
+ unless File.exist?(template_filepath)
24
+ UI.user_error!("Could not find Template at path '#{template_filepath}'")
25
+ end
26
+ File.read(template_filepath)
27
+ end
28
+
29
+ def self.render(template, template_vars_hash)
30
+ Fastlane::ErbalT.new(template_vars_hash).render(template)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module AwsS3
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module AwsS3
3
+ VERSION = "0.1.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-aws_s3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Holtz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: apktools
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: fastlane
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 1.93.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 1.93.1
97
+ description:
98
+ email: josh@rokkincat.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - LICENSE
104
+ - README.md
105
+ - lib/assets/s3_android_html_template.erb
106
+ - lib/assets/s3_android_version_template.erb
107
+ - lib/assets/s3_ios_html_template.erb
108
+ - lib/assets/s3_ios_plist_template.erb
109
+ - lib/assets/s3_ios_version_template.erb
110
+ - lib/fastlane/plugin/aws_s3.rb
111
+ - lib/fastlane/plugin/aws_s3/actions/aws_s3_action.rb
112
+ - lib/fastlane/plugin/aws_s3/helper/aws_s3_helper.rb
113
+ - lib/fastlane/plugin/aws_s3/version.rb
114
+ - lib/fastlane/plugin/s3/version.rb
115
+ homepage: https://github.com/joshdholtz/fastlane-plugin-s3
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.4.5
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Upload IPA and APK to S3
139
+ test_files: []
140
+ has_rdoc: