spaceship 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|

|
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
|