spaceship 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -15
- data/lib/spaceship.rb +2 -1
- data/lib/spaceship/base.rb +0 -1
- data/lib/spaceship/client.rb +71 -69
- data/lib/spaceship/helper/net_http_generic_request.rb +1 -1
- data/lib/spaceship/launcher.rb +19 -14
- data/lib/spaceship/portal/app.rb +23 -9
- data/lib/spaceship/portal/app_group.rb +77 -0
- data/lib/spaceship/portal/app_service.rb +222 -0
- data/lib/spaceship/portal/certificate.rb +2 -2
- data/lib/spaceship/portal/device.rb +7 -7
- data/lib/spaceship/portal/portal.rb +3 -1
- data/lib/spaceship/portal/portal_base.rb +1 -1
- data/lib/spaceship/portal/portal_client.rb +64 -10
- data/lib/spaceship/portal/provisioning_profile.rb +28 -54
- data/lib/spaceship/portal/spaceship.rb +27 -9
- data/lib/spaceship/portal/ui/select_team.rb +6 -8
- data/lib/spaceship/tunes/app_screenshot.rb +1 -1
- data/lib/spaceship/tunes/app_status.rb +5 -6
- data/lib/spaceship/tunes/app_submission.rb +24 -23
- data/lib/spaceship/tunes/app_version.rb +27 -34
- data/lib/spaceship/tunes/application.rb +24 -25
- data/lib/spaceship/tunes/build.rb +6 -7
- data/lib/spaceship/tunes/build_train.rb +2 -2
- data/lib/spaceship/tunes/language_converter.rb +14 -13
- data/lib/spaceship/tunes/language_item.rb +1 -1
- data/lib/spaceship/tunes/processing_build.rb +1 -1
- data/lib/spaceship/tunes/spaceship.rb +5 -5
- data/lib/spaceship/tunes/tester.rb +14 -15
- data/lib/spaceship/tunes/tunes_client.rb +61 -61
- data/lib/spaceship/ui.rb +1 -2
- data/lib/spaceship/version.rb +2 -2
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bee636c5de7df79ede26deda935649fb5a738e3f
|
4
|
+
data.tar.gz: 0e846d25300e1c0658e036f208991f841d55b5d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a81882cdb7b1169c78d456e4a9c531da3888632ad6d9ecd9cb36d3b546a1c95db5e98a88369d3996847a5a61b5f6b80663fb2db4b4950d612c417e84989d80b
|
7
|
+
data.tar.gz: c894e87a4e5819716c5959cfeba5b8de19b2b82c91ef46c4ab506225e1fabfedd51058a46b1ca545eaabd7ae48bf8bbad3499a276f453ca930759d0fe7cd9197
|
data/README.md
CHANGED
@@ -66,17 +66,17 @@ Enough words, here is some code:
|
|
66
66
|
|
67
67
|
```ruby
|
68
68
|
Spaceship.login
|
69
|
-
|
69
|
+
|
70
70
|
# Create a new app
|
71
71
|
app = Spaceship.app.create!(bundle_id: "com.krausefx.app", name: "Spaceship App")
|
72
|
-
|
72
|
+
|
73
73
|
# Use an existing certificate
|
74
74
|
cert = Spaceship.certificate.production.all.first
|
75
|
-
|
75
|
+
|
76
76
|
# Create a new provisioning profile
|
77
77
|
profile = Spaceship.provisioning_profile.app_store.create!(bundle_id: app.bundle_id,
|
78
78
|
certificate: cert)
|
79
|
-
|
79
|
+
|
80
80
|
# Print the name and download the new profile
|
81
81
|
puts "Created Profile " + profile.name
|
82
82
|
profile.download
|
@@ -84,7 +84,7 @@ profile.download
|
|
84
84
|
|
85
85
|
## Speed
|
86
86
|
|
87
|
-
How fast are tools using `spaceship` compared to web scraping?
|
87
|
+
How fast are tools using `spaceship` compared to web scraping?
|
88
88
|
|
89
89
|
![assets/SpaceshipRecording.gif](assets/SpaceshipRecording.gif)
|
90
90
|
|
@@ -116,9 +116,9 @@ You can find the log file here `/tmp/spaceship[time].log`.
|
|
116
116
|
|
117
117
|
## HTTP Client
|
118
118
|
|
119
|
-
Up until now all [fastlane tools](https://fastlane.tools) used web scraping to interact with Apple's web services. `spaceship` uses a simple HTTP client only, resulting in much less overhead and extremely improved speed.
|
119
|
+
Up until now all [fastlane tools](https://fastlane.tools) used web scraping to interact with Apple's web services. `spaceship` uses a simple HTTP client only, resulting in much less overhead and extremely improved speed.
|
120
120
|
|
121
|
-
Advantages of `spaceship` (HTTP client) over web scraping:
|
121
|
+
Advantages of `spaceship` (HTTP client) over web scraping:
|
122
122
|
|
123
123
|
- Blazing fast :rocket: 90% faster than previous methods
|
124
124
|
- No more overhead by loading images, HTML, JS and CSS files on each page load
|
@@ -131,18 +131,19 @@ Advantages of `spaceship` (HTTP client) over web scraping:
|
|
131
131
|
I won't go into too much technical details about the various API endpoints, but just to give you an idea:
|
132
132
|
|
133
133
|
- `https://idmsa.apple.com`: Used to authenticate to get a valid session
|
134
|
-
- `https://developerservices2.apple.com`:
|
134
|
+
- `https://developerservices2.apple.com`:
|
135
135
|
- Get a detailed list of all available provisioning profiles
|
136
136
|
- This API returns the devices, certificates and app for each of the profiles
|
137
137
|
- Register new devices
|
138
|
-
- `https://developer.apple.com`:
|
139
|
-
- List all devices, certificates and
|
138
|
+
- `https://developer.apple.com`:
|
139
|
+
- List all devices, certificates, apps and app groups
|
140
140
|
- Create new certificates, provisioning profiles and apps
|
141
|
+
- Disable/enable services on apps and assign them to app groups
|
141
142
|
- Delete certificates and apps
|
142
143
|
- Repair provisioning profiles
|
143
144
|
- Download provisioning profiles
|
144
145
|
- Team selection
|
145
|
-
- `https://itunesconnect.apple.com`:
|
146
|
+
- `https://itunesconnect.apple.com`:
|
146
147
|
- Managing apps
|
147
148
|
- Managing beta testers
|
148
149
|
- Submitting updates to review
|
@@ -152,11 +153,11 @@ I won't go into too much technical details about the various API endpoints, but
|
|
152
153
|
|
153
154
|
## Magic involved
|
154
155
|
|
155
|
-
`spaceship` does a lot of magic to get everything working so neatly:
|
156
|
+
`spaceship` does a lot of magic to get everything working so neatly:
|
156
157
|
|
157
158
|
- **Sensible Defaults**: You only have to provide the mandatory information (e.g. new provisioning profiles contain all devices by default)
|
158
159
|
- **Local Validation**: When pushing changes back to the Apple Dev Portal `spaceship` will make sure only valid data is sent to Apple (e.g. automatic repairing of provisioning profiles)
|
159
|
-
- **Various request/response types**: When working with the different API endpoints, `spaceship` has to deal with `JSON`, `XML`, `txt`, `plist` and sometimes even `HTML` responses and requests.
|
160
|
+
- **Various request/response types**: When working with the different API endpoints, `spaceship` has to deal with `JSON`, `XML`, `txt`, `plist` and sometimes even `HTML` responses and requests.
|
160
161
|
- **Automatic Pagination**: Even if you have thousands of apps, profiles or certificates, `spaceship` **can** handle your scale. It was heavily tested by first using `spaceship` to create hundreds of profiles and then accessing them using `spaceship`.
|
161
162
|
- **Session, Cookie and CSRF token**: All the security aspects are handled by `spaceship`.
|
162
163
|
- **Profile Magic**: Create and upload code signing requests, all managed by `spaceship`
|
@@ -164,9 +165,9 @@ I won't go into too much technical details about the various API endpoints, but
|
|
164
165
|
|
165
166
|
# Credits
|
166
167
|
|
167
|
-
The initial release was sponsored by [ZeroPush](https://zeropush.com).
|
168
|
+
The initial release was sponsored by [ZeroPush](https://zeropush.com).
|
168
169
|
|
169
|
-
`spaceship` was developed by
|
170
|
+
`spaceship` was developed by
|
170
171
|
- [@KrauseFx](https://twitter.com/KrauseFx).
|
171
172
|
- [@snatchev](https://twitter.com/snatchev/)
|
172
173
|
- [@mathcarignani](https://twitter.com/mathcarignani/)
|
data/lib/spaceship.rb
CHANGED
@@ -19,10 +19,11 @@ module Spaceship
|
|
19
19
|
ProvisioningProfile = Spaceship::Portal::ProvisioningProfile
|
20
20
|
Device = Spaceship::Portal::Device
|
21
21
|
App = Spaceship::Portal::App
|
22
|
+
AppGroup = Spaceship::Portal::AppGroup
|
23
|
+
AppService = Spaceship::Portal::AppService
|
22
24
|
|
23
25
|
# iTunes Connect
|
24
26
|
AppVersion = Spaceship::Tunes::AppVersion
|
25
27
|
AppSubmission = Spaceship::Tunes::AppSubmission
|
26
28
|
Application = Spaceship::Tunes::Application
|
27
29
|
end
|
28
|
-
|
data/lib/spaceship/base.rb
CHANGED
data/lib/spaceship/client.rb
CHANGED
@@ -5,7 +5,6 @@ require 'spaceship/ui'
|
|
5
5
|
require 'spaceship/helper/plist_middleware'
|
6
6
|
require 'spaceship/helper/net_http_generic_request'
|
7
7
|
|
8
|
-
|
9
8
|
if ENV["DEBUG"]
|
10
9
|
require 'openssl'
|
11
10
|
# this has to be on top of this file, since the value can't be changed later
|
@@ -29,7 +28,6 @@ module Spaceship
|
|
29
28
|
# Raised when no user credentials were passed at all
|
30
29
|
class NoUserCredentialsError < StandardError; end
|
31
30
|
|
32
|
-
|
33
31
|
class UnexpectedResponse < StandardError; end
|
34
32
|
|
35
33
|
# Authenticates with Apple's web services. This method has to be called once
|
@@ -157,94 +155,98 @@ module Spaceship
|
|
157
155
|
!!@cookie
|
158
156
|
end
|
159
157
|
|
158
|
+
def with_retry(tries = 5, &block)
|
159
|
+
return block.call
|
160
|
+
rescue Faraday::Error::TimeoutError => ex # New Faraday version: Faraday::TimeoutError => ex
|
161
|
+
unless (tries -= 1).zero?
|
162
|
+
sleep 3
|
163
|
+
retry
|
164
|
+
end
|
165
|
+
|
166
|
+
raise ex # re-raise the exception
|
167
|
+
end
|
168
|
+
|
160
169
|
private
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
170
|
+
|
171
|
+
# Is called from `parse_response` to store the latest csrf_token (if available)
|
172
|
+
def store_csrf_tokens(response)
|
173
|
+
if response and response.headers
|
174
|
+
tokens = response.headers.select { |k, v| %w[csrf csrf_ts].include?(k) }
|
175
|
+
if tokens and not tokens.empty?
|
176
|
+
@csrf_tokens = tokens
|
168
177
|
end
|
169
178
|
end
|
179
|
+
end
|
170
180
|
|
171
181
|
# memoize the last csrf tokens from responses
|
172
|
-
|
173
|
-
|
174
|
-
|
182
|
+
def csrf_tokens
|
183
|
+
@csrf_tokens || {}
|
184
|
+
end
|
175
185
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
186
|
+
def request(method, url_or_path = nil, params = nil, headers = {}, &block)
|
187
|
+
if session?
|
188
|
+
headers.merge!({'Cookie' => cookie})
|
189
|
+
headers.merge!(csrf_tokens)
|
190
|
+
end
|
191
|
+
headers.merge!({'User-Agent' => 'spaceship'})
|
182
192
|
|
183
|
-
|
184
|
-
|
193
|
+
# Before encoding the parameters, log them
|
194
|
+
log_request(method, url_or_path, params)
|
185
195
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
196
|
+
# form-encode the params only if there are params, and the block is not supplied.
|
197
|
+
# this is so that certain requests can be made using the block for more control
|
198
|
+
if method == :post && params && !block_given?
|
199
|
+
params, headers = encode_params(params, headers)
|
200
|
+
end
|
191
201
|
|
192
|
-
|
202
|
+
response = send_request(method, url_or_path, params, headers, &block)
|
193
203
|
|
194
|
-
|
204
|
+
log_response(method, url_or_path, response)
|
195
205
|
|
196
|
-
|
197
|
-
|
206
|
+
return response
|
207
|
+
end
|
198
208
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
end
|
206
|
-
logger.info("#{method.upcase}: #{url} #{params_to_log.join(', ')}")
|
209
|
+
def log_request(method, url, params)
|
210
|
+
params_to_log = Hash(params).dup # to also work with nil
|
211
|
+
params_to_log.delete(:accountPassword) # Dev Portal
|
212
|
+
params_to_log.delete(:theAccountPW) # iTC
|
213
|
+
params_to_log = params_to_log.collect do |key, value|
|
214
|
+
"{#{key}: #{value}}"
|
207
215
|
end
|
216
|
+
logger.info("#{method.upcase}: #{url} #{params_to_log.join(', ')}")
|
217
|
+
end
|
208
218
|
|
209
|
-
|
210
|
-
|
211
|
-
|
219
|
+
def log_response(method, url, response)
|
220
|
+
logger.debug("#{method.upcase}: #{url}: #{response.body}")
|
221
|
+
end
|
212
222
|
|
213
223
|
# Actually sends the request to the remote server
|
214
224
|
# Automatically retries the request up to 3 times if something goes wrong
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
return @client.send(method, url_or_path, params, headers, &block)
|
219
|
-
|
220
|
-
rescue Faraday::Error::TimeoutError => ex # New Faraday version: Faraday::TimeoutError => ex
|
221
|
-
unless (tries -= 1).zero?
|
222
|
-
sleep 3
|
223
|
-
retry
|
224
|
-
end
|
225
|
-
|
226
|
-
raise ex # re-raise the exception
|
225
|
+
def send_request(method, url_or_path, params, headers, &block)
|
226
|
+
with_retry do
|
227
|
+
@client.send(method, url_or_path, params, headers, &block)
|
227
228
|
end
|
229
|
+
end
|
228
230
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
end
|
235
|
-
|
236
|
-
if content == nil
|
237
|
-
raise UnexpectedResponse.new(response.body)
|
238
|
-
else
|
239
|
-
store_csrf_tokens(response)
|
240
|
-
content
|
241
|
-
end
|
231
|
+
def parse_response(response, expected_key = nil)
|
232
|
+
if expected_key
|
233
|
+
content = response.body[expected_key]
|
234
|
+
else
|
235
|
+
content = response.body
|
242
236
|
end
|
243
237
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
238
|
+
if content == nil
|
239
|
+
raise UnexpectedResponse.new(response.body)
|
240
|
+
else
|
241
|
+
store_csrf_tokens(response)
|
242
|
+
content
|
248
243
|
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def encode_params(params, headers)
|
247
|
+
params = Faraday::Utils::ParamsHash[params].to_query
|
248
|
+
headers = {'Content-Type' => 'application/x-www-form-urlencoded'}.merge(headers)
|
249
|
+
return params, headers
|
250
|
+
end
|
249
251
|
end
|
250
252
|
end
|
data/lib/spaceship/launcher.rb
CHANGED
@@ -6,17 +6,17 @@ module Spaceship
|
|
6
6
|
# spaceship. You can call `.new` without any parameters, but you'll have to call
|
7
7
|
# `.login` at a later point. If you prefer, you can pass the login credentials
|
8
8
|
# here already.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# Authenticates with Apple's web services. This method has to be called once
|
11
11
|
# to generate a valid session. The session will automatically be used from then
|
12
12
|
# on.
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# This method will automatically use the username from the Appfile (if available)
|
15
15
|
# and fetch the password from the Keychain (if available)
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# @param user (String) (optional): The username (usually the email address)
|
18
18
|
# @param password (String) (optional): The password
|
19
|
-
#
|
19
|
+
#
|
20
20
|
# @raise InvalidUserCredentialsError: raised if authentication failed
|
21
21
|
def initialize(user = nil, password = nil)
|
22
22
|
@client = PortalClient.new
|
@@ -33,30 +33,30 @@ module Spaceship
|
|
33
33
|
# Authenticates with Apple's web services. This method has to be called once
|
34
34
|
# to generate a valid session. The session will automatically be used from then
|
35
35
|
# on.
|
36
|
-
#
|
36
|
+
#
|
37
37
|
# This method will automatically use the username from the Appfile (if available)
|
38
38
|
# and fetch the password from the Keychain (if available)
|
39
|
-
#
|
39
|
+
#
|
40
40
|
# @param user (String) (optional): The username (usually the email address)
|
41
41
|
# @param password (String) (optional): The password
|
42
|
-
#
|
42
|
+
#
|
43
43
|
# @raise InvalidUserCredentialsError: raised if authentication failed
|
44
|
-
#
|
44
|
+
#
|
45
45
|
# @return (Spaceship::Client) The client the login method was called for
|
46
|
-
def login(user, password)
|
46
|
+
def login(user, password)
|
47
47
|
@client.login(user, password)
|
48
48
|
end
|
49
49
|
|
50
50
|
# Open up the team selection for the user (if necessary).
|
51
|
-
#
|
51
|
+
#
|
52
52
|
# If the user is in multiple teams, a team selection is shown.
|
53
53
|
# The user can then select a team by entering the number
|
54
|
-
#
|
54
|
+
#
|
55
55
|
# Additionally, the team ID is shown next to each team name
|
56
56
|
# so that the user can use the environment variable `FASTLANE_TEAM_ID`
|
57
57
|
# for future user.
|
58
|
-
#
|
59
|
-
# @return (String) The ID of the select team. You also get the value if
|
58
|
+
#
|
59
|
+
# @return (String) The ID of the select team. You also get the value if
|
60
60
|
# the user is only in one team.
|
61
61
|
def select_team
|
62
62
|
@client.select_team
|
@@ -71,6 +71,11 @@ module Spaceship
|
|
71
71
|
Spaceship::App.set_client(@client)
|
72
72
|
end
|
73
73
|
|
74
|
+
# @return (Class) Access the app groups for this spaceship
|
75
|
+
def app_group
|
76
|
+
Spaceship::AppGroup.set_client(@client)
|
77
|
+
end
|
78
|
+
|
74
79
|
# @return (Class) Access the devices for this spaceship
|
75
80
|
def device
|
76
81
|
Spaceship::Device.set_client(@client)
|
@@ -86,4 +91,4 @@ module Spaceship
|
|
86
91
|
Spaceship::ProvisioningProfile.set_client(@client)
|
87
92
|
end
|
88
93
|
end
|
89
|
-
end
|
94
|
+
end
|
data/lib/spaceship/portal/app.rb
CHANGED
@@ -4,7 +4,7 @@ module Spaceship
|
|
4
4
|
class App < PortalBase
|
5
5
|
|
6
6
|
# @return (String) The identifier of this app, provided by the Dev Portal
|
7
|
-
# @example
|
7
|
+
# @example
|
8
8
|
# "RGAWZGXSAA"
|
9
9
|
attr_accessor :app_id
|
10
10
|
|
@@ -14,17 +14,17 @@ module Spaceship
|
|
14
14
|
attr_accessor :name
|
15
15
|
|
16
16
|
# @return (String) the supported platform of this app
|
17
|
-
# @example
|
17
|
+
# @example
|
18
18
|
# "ios"
|
19
19
|
attr_accessor :platform
|
20
20
|
|
21
21
|
# Prefix provided by the Dev Portal
|
22
|
-
# @example
|
22
|
+
# @example
|
23
23
|
# "5A997XSHK2"
|
24
24
|
attr_accessor :prefix
|
25
25
|
|
26
26
|
# @return (String) The bundle_id (app identifier) of your app
|
27
|
-
# @example
|
27
|
+
# @example
|
28
28
|
# "com.krausefx.app"
|
29
29
|
attr_accessor :bundle_id
|
30
30
|
|
@@ -33,7 +33,7 @@ module Spaceship
|
|
33
33
|
|
34
34
|
# @return (Hash) Feature details
|
35
35
|
attr_accessor :features
|
36
|
-
|
36
|
+
|
37
37
|
# @return (Array) List of enabled features
|
38
38
|
attr_accessor :enabled_features
|
39
39
|
|
@@ -45,10 +45,10 @@ module Spaceship
|
|
45
45
|
|
46
46
|
# @return (Fixnum) Number of associated app groups
|
47
47
|
attr_accessor :app_groups_count
|
48
|
-
|
48
|
+
|
49
49
|
# @return (Fixnum) Number of associated cloud containers
|
50
50
|
attr_accessor :cloud_containers_count
|
51
|
-
|
51
|
+
|
52
52
|
# @return (Fixnum) Number of associated identifiers
|
53
53
|
attr_accessor :identifiers_count
|
54
54
|
|
@@ -81,7 +81,7 @@ module Spaceship
|
|
81
81
|
end
|
82
82
|
|
83
83
|
# Creates a new App ID on the Apple Dev Portal
|
84
|
-
#
|
84
|
+
#
|
85
85
|
# if bundle_id ends with '*' then it is a wildcard id otherwise, it is an explicit id
|
86
86
|
# @param bundle_id [String] the bundle id (app_identifier) of the app associated with this provisioning profile
|
87
87
|
# @param name [String] the name of the App
|
@@ -113,13 +113,27 @@ module Spaceship
|
|
113
113
|
client.delete_app!(app_id)
|
114
114
|
self
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
# Fetch a specific App ID details based on the bundle_id
|
118
118
|
# @return (App) The app you're looking for. This is nil if the app can't be found.
|
119
119
|
def details
|
120
120
|
app = client.details_for_app(self)
|
121
121
|
self.class.factory(app)
|
122
122
|
end
|
123
|
+
|
124
|
+
# Associate specific groups with this app
|
125
|
+
# @return (App) The updated detailed app. This is nil if the app couldn't be found
|
126
|
+
def associate_groups(groups)
|
127
|
+
app = client.associate_groups_with_app(self, groups)
|
128
|
+
self.class.factory(app)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Update a service for the app with given AppService object
|
132
|
+
# @return (App) The updated detailed app. This is nil if the app couldn't be found
|
133
|
+
def update_service(service)
|
134
|
+
app = client.update_service_for_app(self, service)
|
135
|
+
self.class.factory(app)
|
136
|
+
end
|
123
137
|
end
|
124
138
|
end
|
125
139
|
end
|