fastlane-plugin-shuttle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 602fcbb57374635eb54413662526bb1cbb5eb92e
4
+ data.tar.gz: 1337002082164db30a5e3dfe6a1edbd6d32497b7
5
+ SHA512:
6
+ metadata.gz: ac11b3760899ffb20832691a6e1b61967b93e74ad07d950a219298fadfd1542134eaf0bc15629630c059544c53c94f7f301ded0488ce5f8cf75d5721ff8fdfc9
7
+ data.tar.gz: cce1ed5253fbae930f1b660403f6e6b6325eaec4b71ab2846641d09dde957eade498f239c62f124ab7552069f54f1a43a70ff8a3b1ae025c91a709f421f89680
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Frédéric Ruaudel <fred@h2g.io>
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.
@@ -0,0 +1,72 @@
1
+ # Shuttle `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-shuttle) [![Gem Version](https://badge.fury.io/rb/fastlane-plugin-shuttle.svg)](https://badge.fury.io/rb/fastlane-plugin-shuttle)
4
+
5
+ ## Getting Started
6
+
7
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-shuttle`, add it to your project by running:
8
+
9
+ ```bash
10
+ fastlane add_plugin shuttle
11
+ ```
12
+
13
+ ## About Shuttle
14
+
15
+ Publish your builds on your [Shuttle.tools](https://shuttle.tools) instance
16
+
17
+ This plugin provides a `shuttle` action which allows you to upload and distribute your apps to your testers via your Shuttle instance interface.
18
+
19
+ ## Usage
20
+
21
+ To get started, first, [obtain an API access token](https://docs.shuttle.tools/api-access-token/) in your Shuttle instance admin section. The API Access Token is used to authenticate with the Shuttle API in each call.
22
+
23
+ ```ruby
24
+ url = shuttle(
25
+ access_token: <shuttle access token>,
26
+ package_path: <path to your IPA or APK binary file>,
27
+ release_name: <release name displayed in shuttle>,
28
+ release_notes: <release notes>,
29
+ base_url: "https://<your instance name>.shuttle.tools/")
30
+ ```
31
+
32
+ The action parameters `access_token` can be omitted when its value is [set as environment variables](https://docs.fastlane.tools/advanced/#environment-variables). Below a list of all available environment variables:
33
+
34
+ - `SHUTTLE_ACCESS_TOKEN` - API Access Token for Shuttle API
35
+ - `SHUTTLE_BASE_URL` - Shuttle instance URL (eg. https://<your instance name>.shuttle.tools/)
36
+ - `SHUTTLE_RELEASE_NAME` - The name of the release (eg. MyApp v3)
37
+ - `SHUTTLE_PACKAGE_PATH` - Build release path for android or ios build (if not provided, it'll check in shared values `GRADLE_APK_OUTPUT_PATH` or `IPA_OUTPUT_PATH`)
38
+ - `SHUTTLE_ENV_ID` - The uniq ID of the app's environment you want to publish the build to (if not provided, it will try to guess it or ask to select/create it interactively then display the value so you can set it definitively)
39
+ - `SHUTTLE_RELEASE_NOTES` - Release notes
40
+
41
+ ## Example
42
+
43
+ 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`.
44
+
45
+ ## Run tests for this plugin
46
+
47
+ To run both the tests, and code style validation, run
48
+
49
+ ```
50
+ rake
51
+ ```
52
+
53
+ To automatically fix many of the styling issues, use
54
+ ```
55
+ rubocop -a
56
+ ```
57
+
58
+ ## Issues and Feedback
59
+
60
+ For any other issues and feedback about this plugin, please submit it to this repository.
61
+
62
+ ## Troubleshooting
63
+
64
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
65
+
66
+ ## Using _fastlane_ Plugins
67
+
68
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
69
+
70
+ ## About _fastlane_
71
+
72
+ _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/shuttle/version'
2
+
3
+ module Fastlane
4
+ module Shuttle
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::Shuttle.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,138 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/shuttle_helper'
3
+ require_relative '../helper/app_environment_selector'
4
+ require 'faraday'
5
+ require 'json'
6
+ require 'app-info'
7
+ require 'terminal-table'
8
+
9
+ ShuttleInstance = Struct.new(:base_url, :access_token)
10
+ ShuttleApp = Struct.new(:id, :name, :platform_id, :path)
11
+ ShuttleEnvironment = Struct.new(:id, :name, :package_id, :app_id, :versioning_id, :path)
12
+ ShuttleBuild = Struct.new(:id)
13
+ AppEnvironment = Struct.new(:shuttle_app, :shuttle_environment)
14
+ PackageInfo = Struct.new(:id, :name, :path, :platform_id, :release_version, :build_version)
15
+ ReleaseInfo = Struct.new(:name, :notes, :build, :environment, :commit_id)
16
+
17
+ module Fastlane
18
+ module Actions
19
+ module SharedValues
20
+ SHUTTLE_DOWNLOAD_LINK = :SHUTTLE_DOWNLOAD_LINK
21
+ end
22
+
23
+ class ShuttleAction < Action
24
+ def self.run(params)
25
+ helper = Helper::ShuttleHelper
26
+ selector = Helper::AppEnvironmentSelector
27
+ shuttle_instance = helper.get_shuttle_instance(params)
28
+ package_info = helper.get_app_info(params)
29
+
30
+ UI.message("Uploading #{package_info.platform_id} package #{package_info.path} with ID #{package_info.id}…")
31
+
32
+ app_environment = selector.get_app_environment(shuttle_instance, package_info, params)
33
+
34
+ release = helper.get_release_info(params, app_environment, package_info)
35
+
36
+ helper.print_summary_table(shuttle_instance, app_environment, package_info, release)
37
+
38
+ release.build = helper.upload_build(shuttle_instance, package_info, app_environment.shuttle_app.id)
39
+
40
+ helper.create_release(shuttle_instance, release)
41
+
42
+ download_url = helper.download_url(shuttle_instance, app_environment, package_info)
43
+ Actions.lane_context[SharedValues::SHUTTLE_DOWNLOAD_LINK] = download_url
44
+
45
+ return download_url
46
+ end
47
+
48
+ def self.description
49
+ "Publish your builds on [Shuttle.tools](https://www.shuttle.tools)"
50
+ end
51
+
52
+ def self.authors
53
+ ["Frédéric Ruaudel <fred@h2g.io>"]
54
+ end
55
+
56
+ def self.return_value
57
+ # If your method provides a return value, you can describe here what it does
58
+ "Shuttle download link"
59
+ end
60
+
61
+ def self.details
62
+ # Optional:
63
+ [
64
+ "If you don't know which `env_id` to set, just run the action interactively without `env_id` parameter to force the plugin to fetch available info from your instance or give you the opportunity to create any needed app and environment that would be missing.",
65
+ "Once done, you will get the associated `env_id` in the _Shuttle upload info summary_ table at the end of the script execution. Just add it in your action parameter to make it works reliably next time including in your CI non-interactive environment"
66
+ ].join("\n")
67
+ end
68
+
69
+ def self.output
70
+ [
71
+ ['SHUTTLE_DOWNLOAD_LINK', 'The newly generated download link for this build']
72
+ ]
73
+ end
74
+
75
+ def self.available_options
76
+ [
77
+ FastlaneCore::ConfigItem.new(key: :package_path,
78
+ env_name: "SHUTTLE_PACKAGE_PATH",
79
+ description: "The path to the new app you want to upload to Shuttle ( if not provided, it'll check in shared values GRADLE_APK_OUTPUT_PATH or IPA_OUTPUT_PATH)",
80
+ optional: true,
81
+ type: String),
82
+ FastlaneCore::ConfigItem.new(key: :base_url,
83
+ env_name: "SHUTTLE_BASE_URL",
84
+ description: "The base url of your Shuttle instance",
85
+ optional: false,
86
+ type: String),
87
+ FastlaneCore::ConfigItem.new(key: :access_token,
88
+ env_name: "SHUTTLE_ACCESS_TOKEN",
89
+ description: "The access token of your account on Shuttle",
90
+ optional: false,
91
+ type: String),
92
+ FastlaneCore::ConfigItem.new(key: :release_name,
93
+ env_name: "SHUTTLE_RELEASE_NAME",
94
+ description: "The name of the release (eg. MyApp v3)",
95
+ optional: true,
96
+ type: String),
97
+ FastlaneCore::ConfigItem.new(key: :release_notes,
98
+ env_name: "SHUTTLE_RELEASE_NOTES",
99
+ description: "The release notes of the release (eg. Bug fixes)",
100
+ optional: true,
101
+ default_value: "Bug fixes and improvements",
102
+ type: String),
103
+ FastlaneCore::ConfigItem.new(key: :env_id,
104
+ env_name: "SHUTTLE_ENV_ID",
105
+ description: "The uniq ID of the app's environment you want to publish the build to (if not provided, it will try to guess it or ask to select/create it interactively then display the value so you can set it definitively)",
106
+ optional: true,
107
+ type: String),
108
+ ]
109
+ end
110
+
111
+ def self.example_code
112
+ [
113
+ 'download_url = shuttle(
114
+ access_token: "...",
115
+ base_url: "https://myInstance.shuttle.tools",
116
+ package_path: "./app.ipa"
117
+ )',
118
+ 'shuttle(
119
+ access_token: "...",
120
+ base_url: "https://myInstance.shuttle.tools",
121
+ package_path: "./app.ipa",
122
+ env_id: "UD6VCR-2X7TME-XSMZW6-MNXIR7",
123
+ release_name: "My App v5.0-1",
124
+ release_notes: "Changelog"
125
+ )'
126
+ ]
127
+ end
128
+
129
+ def self.is_supported?(platform)
130
+ # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
131
+ # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
132
+ #
133
+ # [:ios, :mac, :android].include?(platform)
134
+ true
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,118 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class AppEnvironmentSelector
8
+ # class methods that you define here become available in your action
9
+ # as `Helper::ShuttleHelper.your_method`
10
+ #
11
+ def self.get_app_environment(shuttle_instance, package_info, params)
12
+ helper = Helper::ShuttleHelper
13
+
14
+ app_environment = self.get_app_env_from_params(shuttle_instance, package_info, params, helper)
15
+ return app_environment unless app_environment.nil?
16
+
17
+ all_environments = helper.get_environments(shuttle_instance)
18
+
19
+ app_environment = nil
20
+ environments = all_environments.select do |env|
21
+ env.package_id == package_info.id
22
+ end
23
+
24
+ app_environments = helper.get_app_environments(shuttle_instance, environments).select do |app_env|
25
+ app_env.shuttle_app.platform_id == package_info.platform_id
26
+ end
27
+
28
+ if app_environments.empty?
29
+ app = self.get_app_interactive(shuttle_instance, package_info, helper)
30
+ env = self.get_env_interactive(shuttle_instance, app, package_info, helper)
31
+ app_environment = AppEnvironment.new(app, env)
32
+ else
33
+ if app_environments.count == 1
34
+ app_environment = app_environments[0]
35
+ else
36
+ app_environment = self.desambiguate_app_environment(app_environments, package_info, helper)
37
+ end
38
+ end
39
+
40
+ return app_environment
41
+ end
42
+
43
+ def self.get_app_env_from_params(shuttle_instance, package_info, params, helper)
44
+ env_id = params[:env_id]
45
+ return nil if env_id.to_s.empty?
46
+ environment = helper.get_environment(shuttle_instance, env_id)
47
+ app = helper.get_app(shuttle_instance, environment.app_id)
48
+
49
+ return AppEnvironment.new(app, environment) if environment.package_id == package_info.id && app.platform_id == package_info.platform_id
50
+
51
+ UI.important("App #{app.name} doesn't match the given build platform #{package_info.platform_id}, please check that #{env_id} is the correct environment ID.") unless app.platform_id == package_info.platform_id
52
+ UI.important("Environement #{environment.name} with ID #{env_id} doesn't match the build's package ID #{package_info.id}, please check that you set the correct environment ID") unless environment.package_id == package_info.id
53
+ return nil
54
+ end
55
+
56
+ def self.desambiguate_app_environment(app_environments, package_info, helper)
57
+ options = app_environments.map do |app_env|
58
+ "#{app_env.shuttle_app.name} (#{app_env.shuttle_environment.name})"
59
+ end
60
+ choice_index = helper.prompt_choices(
61
+ "Can't guess which app and environment to use, please choose the correct one:",
62
+ options,
63
+ "Too many environments with package id #{package_info.id} for #{package_info.platform_id}"
64
+ )
65
+ app_environment = app_environments[choice_index]
66
+ end
67
+
68
+ def self.get_app_interactive(shuttle_instance, package_info, helper)
69
+ apps = helper.get_apps(shuttle_instance).select do |app|
70
+ app.platform_id == package_info.platform_id
71
+ end
72
+ options = apps.map do |app|
73
+ "#{app.name}"
74
+ end
75
+ create_new_option = "Create a new one…"
76
+ choice_index = helper.prompt_choices(
77
+ "Can't guess which app to use, please choose the correct one:",
78
+ options << create_new_option,
79
+ "No environments configured for package id #{package_info.id}"
80
+ )
81
+ return self.create_app_interactive(shuttle_instance, package_info, helper) if options[choice_index] == create_new_option
82
+ return apps[choice_index]
83
+ end
84
+
85
+ def self.create_app_interactive(shuttle_instance, package_info, helper)
86
+ app_name = UI.input("app name (default: #{package_info.name}): ")
87
+ app_name = package_info.name if app_name.to_s.empty?
88
+ helper.create_app(shuttle_instance, app_name, package_info.platform_id)
89
+ end
90
+
91
+ def self.get_env_interactive(shuttle_instance, app, package_info, helper)
92
+ environments = helper.get_environments_for_app(shuttle_instance, app).select do |env|
93
+ env.package_id == package_info.id
94
+ end
95
+ options = environments.map do |env|
96
+ "#{env.name}"
97
+ end
98
+ create_new_option = "Create a new one…"
99
+ choice_index = helper.prompt_choices(
100
+ "Can't guess which #{app.name}'s environment to use, please choose the correct one:",
101
+ options << create_new_option,
102
+ "No environments configured for package id #{package_info.id}"
103
+ )
104
+ return self.create_environment_interactive(shuttle_instance, app, package_info, helper) if options[choice_index] == create_new_option
105
+ return environments[choice_index]
106
+ end
107
+
108
+ def self.create_environment_interactive(shuttle_instance, app, package_info, helper)
109
+ env_name = UI.input("environment name: ")
110
+ versioning_id_choices = ["version_only", "version_and_build"]
111
+ choice_index = helper.prompt_choices("environment version scheme:", versioning_id_choices, "interactive mode needed")
112
+ app_name = package_info.name if app_name.to_s.empty?
113
+ helper.create_environment(shuttle_instance, env_name, versioning_id_choices[choice_index], app.id, package_info.id)
114
+ end
115
+
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,300 @@
1
+ require 'fastlane_core/ui/ui'
2
+ require 'fastlane/action'
3
+ require 'uri'
4
+
5
+ module Fastlane
6
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
7
+
8
+ module Helper
9
+ class ShuttleHelper
10
+ # class methods that you define here become available in your action
11
+ # as `Helper::ShuttleHelper.your_method`
12
+ #
13
+ def self.get_shuttle_instance(params)
14
+ shuttle_base_url = params[:base_url]
15
+ shuttle_access_token = params[:access_token]
16
+ ShuttleInstance.new(shuttle_base_url, shuttle_access_token)
17
+ end
18
+
19
+ def self.get_app_info(params)
20
+ package_path = params[:package_path] unless params[:package_path].to_s.empty?
21
+ package_path = Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::IPA_OUTPUT_PATH] if package_path.to_s.empty?
22
+ package_path = Fastlane::Actions.lane_context[Fastlane::Actions::SharedValues::GRADLE_APK_OUTPUT_PATH] if package_path.to_s.empty?
23
+ UI.abort_with_message!("No Package file found") if package_path.to_s.empty?
24
+ UI.abort_with_message!("Package at path #{package_path} does not exist") unless File.exist?(package_path)
25
+ app_info = ::AppInfo.parse(package_path)
26
+ PackageInfo.new(app_info.identifier, app_info.name, package_path, app_info.os.downcase, app_info.release_version, app_info.build_version)
27
+ end
28
+
29
+ def self.get_release_name(params, app_environment, package_info)
30
+ return params[:release_name] unless params[:release_name].to_s.empty?
31
+ release_name = "#{app_environment.shuttle_app.name} v#{package_info.release_version}"
32
+ if app_environment.shuttle_environment.versioning_id == "version_and_build"
33
+ return "#{release_name}-#{package_info.build_version}"
34
+ end
35
+ return release_name
36
+ end
37
+
38
+ def self.get_release_info(params, app_environment, package_info)
39
+ release_name = self.get_release_name(params, app_environment, package_info)
40
+ commit_id = Helper.backticks("git show --format='%H' --quiet").chomp
41
+ ReleaseInfo.new(release_name, params[:release_notes], nil, app_environment.shuttle_environment, commit_id)
42
+ end
43
+
44
+ def self.connection(shuttle_instance, endpoint, is_multipart = false)
45
+ return Faraday.new(url: "#{shuttle_instance.base_url}/api#{endpoint}") do |builder|
46
+ # builder.response :logger, Logger.new(STDOUT), bodies: true
47
+ builder.headers["Authorization"] = "Bearer #{shuttle_instance.access_token}"
48
+ builder.headers["Accept"] = "application/vnd.api+json"
49
+ if is_multipart
50
+ builder.request :multipart
51
+ else
52
+ builder.headers["Content-Type"] = "application/vnd.api+json"
53
+ end
54
+ builder.adapter :net_http
55
+ end
56
+ end
57
+
58
+ def self.get(shuttle_instance, endpoint, debug=false)
59
+ connection = self.connection(shuttle_instance, endpoint)
60
+ response = connection.get()
61
+ case response.status
62
+ when 200...300
63
+ data = JSON.parse(response.body)
64
+ UI.message("Debug: #{JSON.pretty_generate(data["data"])}\n") if debug == true
65
+ data["data"]
66
+ else
67
+ UI.abort_with_message!("Error #{response.status.to_s} occured while calling endpoint #{endpoint}")
68
+ nil
69
+ end
70
+ end
71
+
72
+ def self.environment_from_json(json_env)
73
+ attrb = json_env["attributes"]
74
+ ShuttleEnvironment.new(
75
+ json_env["id"],
76
+ attrb["name"],
77
+ attrb["package_id"],
78
+ json_env["relationships"]["app"]["data"]["id"],
79
+ attrb["versioning_id"],
80
+ attrb["path"]
81
+ )
82
+ end
83
+
84
+ def self.get_environments(shuttle_instance)
85
+ self.get(shuttle_instance, '/environments').map do |json_env|
86
+ self.environment_from_json(json_env)
87
+ end
88
+ end
89
+
90
+ def self.get_environment(shuttle_instance, env_id)
91
+ json_env = self.get(shuttle_instance, "/environments/#{env_id}")
92
+ self.environment_from_json(json_env)
93
+ end
94
+
95
+ def self.get_environments_for_app(shuttle_instance, app)
96
+ self.get(shuttle_instance, "/apps/#{app.id}/environments").map do |json_env|
97
+ self.environment_from_json(json_env)
98
+ end
99
+ end
100
+
101
+ def self.create_environment(shuttle_instance, name, versioning_id, app_id, package_id)
102
+ body = JSON.generate({
103
+ data: {
104
+ type: "environments",
105
+ attributes: {
106
+ name: name,
107
+ path: name.downcase,
108
+ package_id: package_id,
109
+ versioning_id: versioning_id
110
+ },
111
+ relationships: {
112
+ app: {
113
+ data: {
114
+ id: app_id,
115
+ type: "apps"
116
+ }
117
+ }
118
+ }
119
+ }
120
+ })
121
+ json_env = self.post(shuttle_instance, "/environments", body)
122
+ self.environment_from_json(json_env)
123
+ end
124
+
125
+ def self.app_from_json(json_app)
126
+ json_app_attrb = json_app["attributes"]
127
+ ShuttleApp.new(
128
+ json_app["id"],
129
+ json_app_attrb["name"],
130
+ json_app_attrb["platform_id"],
131
+ json_app_attrb["path"]
132
+ )
133
+ end
134
+
135
+ def self.get_apps(shuttle_instance)
136
+ self.get(shuttle_instance, "/apps/").map do |json_app|
137
+ self.app_from_json(json_app)
138
+ end
139
+ end
140
+
141
+ def self.get_app(shuttle_instance, app_id)
142
+ json_app = self.get(shuttle_instance, "/apps/#{app_id}")
143
+ self.app_from_json(json_app)
144
+ end
145
+
146
+ def self.create_app(shuttle_instance, app_name, app_platform)
147
+ app_path = "#{app_name.downcase}-#{app_platform}"
148
+ body = JSON.generate({
149
+ data: {
150
+ type: "apps",
151
+ attributes: {
152
+ name: app_name,
153
+ path: app_path,
154
+ platform_id: app_platform
155
+ }
156
+ }
157
+ })
158
+ json_app = self.post(shuttle_instance, "/apps", body)
159
+ self.app_from_json(json_app)
160
+ end
161
+
162
+ def self.get_app_environments(shuttle_instance, environments)
163
+ apps = environments.map do |env|
164
+ self.get_app(shuttle_instance, env.app_id)
165
+ end
166
+
167
+ apps.zip(environments).map do |app_env|
168
+ AppEnvironment.new(
169
+ app_env[0],
170
+ app_env[1]
171
+ )
172
+ end
173
+ end
174
+
175
+ def self.post(shuttle_instance, endpoint, body, is_multipart: false, debug: false)
176
+ connection = self.connection(shuttle_instance, endpoint, is_multipart)
177
+ response = connection.post do |req|
178
+ req.body = body
179
+ end
180
+ case response.status
181
+ when 200...300
182
+ data = JSON.parse response.body
183
+ UI.message(JSON.pretty_generate(data)) if debug == true
184
+ data["data"]
185
+ else
186
+ self.abort(endpoint, body, response)
187
+ nil
188
+ end
189
+ end
190
+
191
+ def self.abort(endpoint, body, response)
192
+ reqBody = JSON.parse body
193
+ errorBody = JSON.parse response.body
194
+ case endpoint
195
+ when "/releases"
196
+ UI.abort_with_message!("💥 Can't create release for #{reqBody["data"]["attributes"]["title"]}: #{errorBody["errors"][0]["detail"]}")
197
+ else
198
+ UI.abort_with_message!("Error #{response.status.to_s} occured while calling endpoint #{endpoint} with body #{body} => #{errorBody["errors"][0]["detail"]}")
199
+ end
200
+ end
201
+
202
+ def self.upload_build(shuttle_instance, package_info, app_id)
203
+ body = {
204
+ "build[app_id]": app_id,
205
+ "build[package]": Faraday::UploadIO.new(package_info.path, 'application/octet-stream')
206
+ }
207
+ json_build = self.post(shuttle_instance, '/builds', body, is_multipart: true)
208
+ ShuttleBuild.new(json_build["id"])
209
+ end
210
+
211
+ def self.create_release(shuttle_instance, release)
212
+ body = JSON.generate({
213
+ data: {
214
+ type: "releases",
215
+ attributes: {
216
+ title: release.name,
217
+ notes: release.notes,
218
+ commit_id: release.commit_id
219
+ },
220
+ relationships: {
221
+ build: {
222
+ data: {
223
+ id: release.build.id,
224
+ type: "builds"
225
+ }
226
+ },
227
+ environment: {
228
+ data: {
229
+ id: release.environment.id,
230
+ type: "environments"
231
+ }
232
+ }
233
+ }
234
+ }
235
+ })
236
+ json_release = self.post(shuttle_instance, "/releases", body)
237
+ end
238
+
239
+ def self.prompt_choices(question, options, nonInteractiveErrorMessage)
240
+ UI.abort_with_message!(nonInteractiveErrorMessage) unless UI.interactive?
241
+ abort_option = "None match, abort"
242
+ user_choice = UI.select question, options << abort_option
243
+ case user_choice
244
+ when abort_option
245
+ UI.user_error!("Aborting…")
246
+ else
247
+ choice_index = options.find_index(user_choice)
248
+ end
249
+ end
250
+
251
+ def self.download_url(shuttle_instance, app_environment, package_info)
252
+ app = app_environment.shuttle_app
253
+ env = app_environment.shuttle_environment
254
+ url_path = File.join(
255
+ app.path,
256
+ env.path,
257
+ package_info.release_version)
258
+ url_path = File.join(url_path, package_info.build_version) if env.versioning_id == "version_and_build"
259
+ return URI.join(
260
+ shuttle_instance.base_url, url_path).to_s
261
+ end
262
+
263
+ def self.print_summary_table(shuttle_instance, app_environment, package_info, release)
264
+ rows = [
265
+ 'Shuttle Base URL',
266
+ 'Shuttle app name',
267
+ 'Shuttle env name',
268
+ 'Shuttle env ID',
269
+ 'Package path',
270
+ 'Platform',
271
+ 'Package Id',
272
+ 'Release name',
273
+ 'Release version',
274
+ 'Build version',
275
+ 'Release notes',
276
+ 'Commit hash',
277
+ 'Shuttle release URL'
278
+ ].zip([
279
+ shuttle_instance.base_url,
280
+ app_environment.shuttle_app.name,
281
+ app_environment.shuttle_environment.name,
282
+ app_environment.shuttle_environment.id,
283
+ package_info.path,
284
+ package_info.platform_id,
285
+ package_info.id,
286
+ release.name,
287
+ package_info.release_version,
288
+ package_info.build_version,
289
+ release.notes,
290
+ release.commit_id,
291
+ self.download_url(shuttle_instance, app_environment, package_info)
292
+ ])
293
+ table = Terminal::Table.new :rows => rows, :title => "Shuttle upload info summary".green
294
+ puts
295
+ puts table
296
+ puts
297
+ end
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module Shuttle
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-shuttle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Shuttle Project
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: app-info
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
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: rspec
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_junit_formatter
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: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 0.49.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 0.49.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-require_tools
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: fastlane
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 2.97.0
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 2.97.0
153
+ description:
154
+ email: dev@shuttle.tools
155
+ executables: []
156
+ extensions: []
157
+ extra_rdoc_files: []
158
+ files:
159
+ - LICENSE
160
+ - README.md
161
+ - lib/fastlane/plugin/shuttle.rb
162
+ - lib/fastlane/plugin/shuttle/actions/shuttle_action.rb
163
+ - lib/fastlane/plugin/shuttle/helper/app_environment_selector.rb
164
+ - lib/fastlane/plugin/shuttle/helper/shuttle_helper.rb
165
+ - lib/fastlane/plugin/shuttle/version.rb
166
+ homepage: https://github.com/ShuttleProject/fastlane-plugin-shuttle
167
+ licenses:
168
+ - MIT
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.6.13
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: Publish your builds on Shuttle.tools
190
+ test_files: []