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 +7 -0
- data/LICENSE +21 -0
- data/README.md +77 -0
- data/lib/assets/s3_android_html_template.erb +88 -0
- data/lib/assets/s3_android_version_template.erb +4 -0
- data/lib/assets/s3_ios_html_template.erb +95 -0
- data/lib/assets/s3_ios_plist_template.erb +31 -0
- data/lib/assets/s3_ios_version_template.erb +4 -0
- data/lib/fastlane/plugin/aws_s3.rb +16 -0
- data/lib/fastlane/plugin/aws_s3/actions/aws_s3_action.rb +477 -0
- data/lib/fastlane/plugin/aws_s3/helper/aws_s3_helper.rb +34 -0
- data/lib/fastlane/plugin/aws_s3/version.rb +5 -0
- data/lib/fastlane/plugin/s3/version.rb +5 -0
- metadata +140 -0
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,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,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
|
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:
|