spaceship 0.0.15 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/assets/languageMapping.json +224 -0
  3. data/lib/spaceship.rb +20 -63
  4. data/lib/spaceship/base.rb +71 -14
  5. data/lib/spaceship/client.rb +9 -274
  6. data/lib/spaceship/launcher.rb +1 -1
  7. data/lib/spaceship/portal/app.rb +125 -0
  8. data/lib/spaceship/portal/certificate.rb +273 -0
  9. data/lib/spaceship/portal/device.rb +102 -0
  10. data/lib/spaceship/portal/portal.rb +6 -0
  11. data/lib/spaceship/portal/portal_base.rb +13 -0
  12. data/lib/spaceship/portal/portal_client.rb +289 -0
  13. data/lib/spaceship/portal/provisioning_profile.rb +369 -0
  14. data/lib/spaceship/portal/spaceship.rb +94 -0
  15. data/lib/spaceship/{ui → portal/ui}/select_team.rb +0 -0
  16. data/lib/spaceship/tunes/app_screenshot.rb +28 -0
  17. data/lib/spaceship/tunes/app_status.rb +63 -0
  18. data/lib/spaceship/tunes/app_submission.rb +149 -0
  19. data/lib/spaceship/tunes/app_version.rb +337 -0
  20. data/lib/spaceship/tunes/application.rb +253 -0
  21. data/lib/spaceship/tunes/build.rb +128 -0
  22. data/lib/spaceship/tunes/build_train.rb +79 -0
  23. data/lib/spaceship/tunes/language_converter.rb +44 -0
  24. data/lib/spaceship/tunes/language_item.rb +54 -0
  25. data/lib/spaceship/tunes/processing_build.rb +30 -0
  26. data/lib/spaceship/tunes/spaceship.rb +26 -0
  27. data/lib/spaceship/tunes/tester.rb +177 -0
  28. data/lib/spaceship/tunes/tunes.rb +12 -0
  29. data/lib/spaceship/tunes/tunes_base.rb +15 -0
  30. data/lib/spaceship/tunes/tunes_client.rb +360 -0
  31. data/lib/spaceship/version.rb +1 -1
  32. metadata +27 -7
  33. data/lib/spaceship/app.rb +0 -125
  34. data/lib/spaceship/certificate.rb +0 -271
  35. data/lib/spaceship/device.rb +0 -100
  36. data/lib/spaceship/provisioning_profile.rb +0 -367
File without changes
@@ -0,0 +1,28 @@
1
+ module Spaceship
2
+ module Tunes
3
+ # Represents a screenshot hosted on iTunes Connect
4
+ class AppScreenshot
5
+
6
+ attr_accessor :thumbnail_url
7
+
8
+ attr_accessor :sort_order
9
+
10
+ attr_accessor :original_file_name
11
+
12
+ attr_accessor :url
13
+
14
+ attr_accessor :device_type
15
+
16
+ attr_accessor :language
17
+
18
+ def initialize(hash)
19
+ self.thumbnail_url = hash[:thumbnail_url]
20
+ self.sort_order = hash[:sort_order]
21
+ self.original_file_name = hash[:original_file_name]
22
+ self.url = hash[:url]
23
+ self.device_type = hash[:device_type]
24
+ self.language = hash[:language]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,63 @@
1
+ module Spaceship
2
+ module Tunes
3
+ # Defines the different states of the app
4
+ #
5
+ # As specified by Apple: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/ChangingAppStatus.html
6
+ module AppStatus
7
+ # You can edit this version, upload new binaries and more
8
+ PREPARE_FOR_SUBMISSION = "Prepare for Submission"
9
+
10
+ # App is currently live in the App Store
11
+ READY_FOR_SALE = "Ready for Sale"
12
+
13
+ # Waiting for Apple's Review
14
+ WAITING_FOR_REVIEW = "Waiting For Review"
15
+
16
+ # Currently in Review
17
+ IN_REVIEW = "In Review"
18
+
19
+ # App rejected for whatever reason
20
+ REJECTED = "Rejected"
21
+
22
+ # The developer took the app from the App Store
23
+ DEVELOPER_REMOVED_FROM_SALE = "Developer Removed From Sale"
24
+
25
+ # Developer rejected this version/binary
26
+ DEVELOPER_REJECTED = "Developer Rejected"
27
+
28
+ # You have to renew your Apple account to keep using iTunes Connect
29
+ PENDING_CONTRACT = "Pending Contract"
30
+
31
+ UPLOAD_RECEIVED = "Upload Received"
32
+ PENDING_DEVELOPER_RELEASE = "Pending Developer Release"
33
+ PROCESSING_FOR_APP_STORE = "Processing for App Store"
34
+
35
+ # Unused app states
36
+ # PENDING_APPLE_RELASE = "Pending Apple Release"
37
+
38
+ # WAITING_FOR_EXPORT_COMPLIANCE = "Waiting For Export Compliance"
39
+ # METADATA_REJECTED = "Metadata Rejected"
40
+ # REMOVED_FROM_SALE = "Removed From Sale"
41
+ # INVALID_BINARY = "Invalid Binary"
42
+
43
+
44
+ # Get the app status matching based on a string (given by iTunes Connect)
45
+ def self.get_from_string(text)
46
+ mapping = {
47
+ 'readyForSale' => READY_FOR_SALE,
48
+ 'prepareForUpload' => PREPARE_FOR_SUBMISSION,
49
+ 'devRejected' => DEVELOPER_REJECTED,
50
+ 'pendingContract' => PENDING_CONTRACT,
51
+ 'developerRemovedFromSale' => DEVELOPER_REMOVED_FROM_SALE,
52
+ 'waitingForReview' => WAITING_FOR_REVIEW
53
+ }
54
+
55
+ mapping.each do |k, v|
56
+ return v if k == text
57
+ end
58
+
59
+ return nil
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,149 @@
1
+ module Spaceship
2
+ module Tunes
3
+ # Represents a submission for review of an iTunes Connect Application
4
+ # This class handles the submission of all review information and documents
5
+ class AppSubmission < TunesBase
6
+ # @return (Spaceship::Tunes::Application) A reference to the application
7
+ # this submission is for
8
+ attr_accessor :application
9
+
10
+ # @return (AppVersion) The version to use for this submission
11
+ attr_accessor :version
12
+
13
+ # @return (String) The stage of this submission (start, complete)
14
+ attr_accessor :stage
15
+
16
+ # @return (Boolean) Submitted for Review
17
+ attr_accessor :submitted_for_review
18
+
19
+ # @return (Boolean) Ad ID Info - Limits ads tracking
20
+ attr_accessor :add_id_info_limits_tracking
21
+
22
+ # @return (Boolean) Ad ID Info - Serves ads
23
+ attr_accessor :add_id_info_serves_ads
24
+
25
+ # @return (Boolean) Ad ID Info - Tracks actions
26
+ attr_accessor :add_id_info_tracks_action
27
+
28
+ # @return (Boolean) Ad ID Info - Tracks installs
29
+ attr_accessor :add_id_info_tracks_install
30
+
31
+ # @return (Boolean) Ad ID Info - Uses idfa
32
+ attr_accessor :add_id_info_uses_idfa
33
+
34
+ # @return (Boolean) Content Rights - Contains third party content
35
+ attr_accessor :content_rights_contains_third_party_content
36
+
37
+ # @return (Boolean) Content Rights - Has rights of content
38
+ attr_accessor :content_rights_has_rights
39
+
40
+ # @return (Boolean) Export Compliance - Available on French Store
41
+ attr_accessor :export_compliance_available_on_french_store
42
+
43
+ # @return (@TODO) Export Compliance - CCAT File
44
+ attr_accessor :export_compliance_ccat_file
45
+
46
+ # @return (Boolean) Export Compliance - Contains proprietary cryptography
47
+ attr_accessor :export_compliance_contains_proprietary_cryptography
48
+
49
+ # @return (Boolean) Export Compliance - Contains third-party cryptography
50
+ attr_accessor :export_compliance_contains_third_party_cryptography
51
+
52
+ # @return (Boolean) Export Compliance - Is exempt
53
+ attr_accessor :export_compliance_is_exempt
54
+
55
+ # @return (Boolean) Export Compliance - Uses encryption
56
+ attr_accessor :export_compliance_uses_encryption
57
+
58
+ # @return (String) Export Compliance - App type
59
+ attr_accessor :export_compliance_app_type
60
+
61
+ # @return (Boolean) Export Compliance - Encryption Updated
62
+ attr_accessor :export_compliance_encryption_updated
63
+
64
+ # @return (Boolean) Export Compliance - Compliance Required
65
+ attr_accessor :export_compliance_compliance_required
66
+
67
+ # @return (String) Export Compliance - Platform
68
+ attr_accessor :export_compliance_platform
69
+
70
+ attr_mapping({
71
+
72
+ # Ad ID Info Section
73
+ 'adIdInfo.limitsTracking.value' => :add_id_info_limits_tracking,
74
+ 'adIdInfo.servesAds.value' => :add_id_info_serves_ads,
75
+ 'adIdInfo.tracksAction.value' => :add_id_info_tracks_action,
76
+ 'adIdInfo.tracksInstall.value' => :add_id_info_tracks_install,
77
+ 'adIdInfo.usesIdfa.value' => :add_id_info_uses_idfa,
78
+
79
+ # Content Rights Section
80
+ 'contentRights.containsThirdPartyContent.value' => :content_rights_contains_third_party_content,
81
+ 'contentRights.hasRights.value' => :content_rights_has_rights,
82
+
83
+ # Export Compliance Section
84
+ 'exportCompliance.availableOnFrenchStore.value' => :export_compliance_available_on_french_store,
85
+ 'exportCompliance.ccatFile.value' => :export_compliance_ccat_file,
86
+ 'exportCompliance.containsProprietaryCryptography.value' => :export_compliance_contains_proprietary_cryptography,
87
+ 'exportCompliance.containsThirdPartyCryptography.value' => :export_compliance_contains_third_party_cryptography,
88
+ 'exportCompliance.isExempt.value' => :export_compliance_is_exempt,
89
+ 'exportCompliance.usesEncryption.value' => :export_compliance_uses_encryption,
90
+ 'exportCompliance.appType' => :export_compliance_app_type,
91
+ 'exportCompliance.encryptionUpdated' => :export_compliance_encryption_updated,
92
+ 'exportCompliance.exportComplianceRequired' => :export_compliance_compliance_required,
93
+ 'exportCompliance.platform' => :export_compliance_platform
94
+ })
95
+
96
+ class << self
97
+ # Create a new object based on a hash.
98
+ # This is used to create a new object based on the server response.
99
+ def factory(attrs)
100
+ orig = attrs.dup
101
+
102
+ # fill content rights section if iTC returns nil
103
+ if attrs["contentRights"].nil?
104
+ attrs.merge!("contentRights" => {
105
+ "containsThirdPartyContent" => {
106
+ "value" => nil
107
+ },
108
+ "hasRights" => {
109
+ "value" => nil
110
+ }
111
+ })
112
+ end
113
+
114
+ obj = self.new(attrs)
115
+ return obj
116
+ end
117
+
118
+ # @param application (Spaceship::Tunes::Application) The app this submission is for
119
+ # @param app_id (String) The unique Apple ID of this app
120
+ def create(application, app_id, version)
121
+ stage = "start"
122
+ attrs = client.send_app_submission(application.apple_id, version.raw_data, stage)
123
+ attrs.merge!(application: application)
124
+ attrs.merge!(version: version)
125
+ attrs.merge!(stage: stage)
126
+
127
+ return self.factory(attrs)
128
+ end
129
+ end
130
+
131
+ # Save and complete the app submission
132
+ def complete!
133
+ @stage = "complete"
134
+ client.send_app_submission(application.apple_id, raw_data, @stage)
135
+ @submitted_for_review = true
136
+ end
137
+
138
+ # @return (String) An URL to this specific resource. You can enter this URL into your browser
139
+ def url
140
+ "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/apps/#{self.application.apple_id}/version/submit/#{self.stage}"
141
+ end
142
+
143
+ def setup
144
+ @submitted_for_review = false
145
+ end
146
+
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,337 @@
1
+ module Spaceship
2
+ module Tunes
3
+ # Represents an editable version of an iTunes Connect Application
4
+ # This can either be the live or the edit version retrieved via the app
5
+ class AppVersion < TunesBase
6
+ # @return (Spaceship::Tunes::Application) A reference to the application
7
+ # this version is for
8
+ attr_accessor :application
9
+
10
+ # @return (String) The version number of this version
11
+ attr_accessor :version
12
+
13
+ # @return (String) The copyright information of this app
14
+ attr_accessor :copyright
15
+
16
+ # @return (Spaceship::Tunes::AppStatus) What's the current status of this app
17
+ # e.g. Waiting for Review, Ready for Sale, ...
18
+ attr_reader :app_status
19
+
20
+ # @return (Bool) Is that the version that's currently available in the App Store?
21
+ attr_accessor :is_live
22
+
23
+ # Categories (e.g. MZGenre.Business)
24
+ attr_accessor :primary_category
25
+
26
+ attr_accessor :primary_first_sub_category
27
+
28
+ attr_accessor :primary_second_sub_category
29
+
30
+ attr_accessor :secondary_category
31
+
32
+ attr_accessor :secondary_first_sub_category
33
+
34
+ attr_accessor :secondary_second_sub_category
35
+
36
+ # @return (String) App Status (e.g. 'readyForSale'). You should use `app_status` instead
37
+ attr_accessor :raw_status
38
+
39
+ # @return (Bool)
40
+ attr_accessor :can_reject_version
41
+
42
+ # @return (Bool)
43
+ attr_accessor :can_prepare_for_upload
44
+
45
+ # @return (Bool)
46
+ attr_accessor :can_send_version_live
47
+
48
+ # @return (Bool) Should the app automatically be released once it's approved?
49
+ attr_accessor :release_on_approval
50
+
51
+ # @return (Bool)
52
+ attr_accessor :can_beta_test
53
+
54
+ # @return (Bool) Does the binary contain a watch binary?
55
+ attr_accessor :supports_apple_watch
56
+
57
+ # @return (String) URL to the full resolution 1024x1024 app icon
58
+ attr_accessor :app_icon_url
59
+
60
+ # @return (String) Name of the original file
61
+ attr_accessor :app_icon_original_name
62
+
63
+ # @return (String) URL to the full resolution 1024x1024 app icon
64
+ attr_accessor :watch_app_icon_url
65
+
66
+ # @return (String) Name of the original file
67
+ attr_accessor :watch_app_icon_original_name
68
+
69
+ # @return (Integer) a unqiue ID for this version generated by iTunes Connect
70
+ attr_accessor :version_id
71
+
72
+ # @return TODO
73
+ attr_accessor :company_information
74
+
75
+ ####
76
+ # App Review Information
77
+ ####
78
+ # @return (String) App Review Information First Name
79
+ attr_accessor :review_first_name
80
+
81
+ # @return (String) App Review Information Last Name
82
+ attr_accessor :review_last_name
83
+
84
+ # @return (String) App Review Information Phone Number
85
+ attr_accessor :review_phone_number
86
+
87
+ # @return (String) App Review Information Email Address
88
+ attr_accessor :review_email
89
+
90
+ # @return (String) App Review Information Demo Account User Name
91
+ attr_accessor :review_demo_user
92
+
93
+ # @return (String) App Review Information Demo Account Password
94
+ attr_accessor :review_demo_password
95
+
96
+ # @return (String) App Review Information Notes
97
+ attr_accessor :review_notes
98
+
99
+ ####
100
+ # Localized values:
101
+ ####
102
+
103
+ # @return (Array) Raw access the all available languages. You shouldn't use it probbaly
104
+ attr_accessor :languages
105
+
106
+ # @return (Hash) A hash representing the app name in all languages
107
+ attr_reader :name
108
+
109
+ # @return (Hash) A hash representing the keywords in all languages
110
+ attr_reader :keywords
111
+
112
+ # @return (Hash) A hash representing the description in all languages
113
+ attr_reader :description
114
+
115
+ # @return (Hash) The changelog
116
+ attr_reader :release_notes
117
+
118
+ # @return (Hash) A hash representing the keywords in all languages
119
+ attr_reader :privacy_url
120
+
121
+ # @return (Hash) A hash representing the keywords in all languages
122
+ attr_reader :support_url
123
+
124
+ # @return (Hash) A hash representing the keywords in all languages
125
+ attr_reader :marketing_url
126
+
127
+ # @return (Hash) Represents the screenshots of this app version (read-only)
128
+ attr_reader :screenshots
129
+
130
+
131
+ attr_mapping({
132
+ 'canBetaTest' => :can_beta_test,
133
+ 'canPrepareForUpload' => :can_prepare_for_upload,
134
+ 'canRejectVersion' => :can_reject_version,
135
+ 'canSendVersionLive' => :can_send_version_live,
136
+ 'copyright.value' => :copyright,
137
+ 'details.value' => :languages,
138
+ 'largeAppIcon.value.originalFileName' => :app_icon_original_name,
139
+ 'largeAppIcon.value.url' => :app_icon_url,
140
+ 'primaryCategory.value' => :primary_category,
141
+ 'primaryFirstSubCategory.value' => :primary_first_sub_category,
142
+ 'primarySecondSubCategory.value' => :primary_second_sub_category,
143
+ 'releaseOnApproval.value' => :release_on_approval,
144
+ 'secondaryCategory.value' => :secondary_category,
145
+ 'secondaryFirstSubCategory.value' => :secondary_first_sub_category,
146
+ 'secondarySecondSubCategory.value' => :secondary_second_sub_category,
147
+ 'status' => :raw_status,
148
+ 'supportsAppleWatch' => :supports_apple_watch,
149
+ 'versionId' => :version_id,
150
+ 'version.value' => :version,
151
+ 'watchAppIcon.value.originalFileName' => :watch_app_icon_original_name,
152
+ 'watchAppIcon.value.url' => :watch_app_icon_url,
153
+
154
+ # App Review Information
155
+ 'appReviewInfo.firstName.value' => :review_first_name,
156
+ 'appReviewInfo.lastName.value' => :review_last_name,
157
+ 'appReviewInfo.phoneNumber.value' => :review_phone_number,
158
+ 'appReviewInfo.emailAddress.value' => :review_email,
159
+ 'appReviewInfo.reviewNotes.value' => :review_notes,
160
+ 'appReviewInfo.userName.value' => :review_demo_user,
161
+ 'appReviewInfo.password.value' => :review_demo_password
162
+ })
163
+
164
+ class << self
165
+ # Create a new object based on a hash.
166
+ # This is used to create a new object based on the server response.
167
+ def factory(attrs)
168
+ orig = attrs.dup
169
+ obj = self.new(attrs)
170
+ obj.unfold_languages
171
+
172
+ return obj
173
+ end
174
+
175
+ # @param application (Spaceship::Tunes::Application) The app this version is for
176
+ # @param app_id (String) The unique Apple ID of this app
177
+ # @param is_live (Boolean) Is that the version that's live in the App Store?
178
+ def find(application, app_id, is_live = false)
179
+ attrs = client.app_version(app_id, is_live)
180
+ attrs.merge!(application: application)
181
+ attrs.merge!(is_live: is_live)
182
+
183
+ return self.factory(attrs)
184
+ end
185
+ end
186
+
187
+ # @return (Bool) Is that version currently available in the App Store?
188
+ def is_live?
189
+ is_live
190
+ end
191
+
192
+ # Call this method to make sure the given languages are available for this app
193
+ # You should call this method before accessing the name, description and other localized values
194
+ # This will create the new language if it's not available yet and do nothing if everything's there
195
+ # def create_languages!(languages)
196
+ # raise "Please pass an array" unless languages.kind_of?Array
197
+
198
+ # copy_from = self.languages.first
199
+ # languages.each do |language|
200
+ # # First, see if it's already available
201
+ # found = self.languages.find do |local|
202
+ # local['language'] == language
203
+ # end
204
+
205
+ # unless found
206
+ # new_language = copy_from.dup
207
+ # new_language['language'] = language
208
+ # [:description, :releaseNotes, :keywords].each do |key|
209
+ # new_language[key.to_s]['value'] = nil
210
+ # end
211
+
212
+ # self.languages << new_language
213
+ # unfold_languages
214
+
215
+ # # Now we need to set a useless `pageLanguageValue` value because iTC says so, after adding a new version
216
+ # self.languages.each do |current|
217
+ # current['pageLanguageValue'] = current['language']
218
+ # end
219
+ # end
220
+ # end
221
+
222
+ # languages
223
+ # end
224
+
225
+ # Push all changes that were made back to iTunes Connect
226
+ def save!
227
+ client.update_app_version!(application.apple_id, is_live?, raw_data)
228
+ end
229
+
230
+ # @return (String) An URL to this specific resource. You can enter this URL into your browser
231
+ def url
232
+ "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/#{self.application.apple_id}/" + (self.is_live? ? "cur" : "")
233
+ end
234
+
235
+
236
+ # Private methods
237
+ def setup
238
+ # Properly parse the AppStatus
239
+ status = raw_data['status']
240
+ @app_status = Tunes::AppStatus.get_from_string(status)
241
+
242
+ # Setup the screenshots
243
+ @screenshots = {}
244
+ raw_data['details']['value'].each do |row|
245
+ # Now that's one language right here
246
+ @screenshots[row['language']] = setup_screenshots(row)
247
+ end
248
+ end
249
+
250
+
251
+ # Prefill name, keywords, etc...
252
+ def unfold_languages
253
+ {
254
+ name: :name,
255
+ keywords: :keywords,
256
+ description: :description,
257
+ privacyURL: :privacy_url,
258
+ supportURL: :support_url,
259
+ marketingURL: :marketing_url,
260
+ releaseNotes: :release_notes
261
+ }.each do |json, attribute|
262
+ instance_variable_set("@#{attribute}".to_sym, LanguageItem.new(json, languages))
263
+ end
264
+ end
265
+
266
+ # These methods takes care of properly parsing values that
267
+ # are not returned in the right format, e.g. boolean as string
268
+ def release_on_approval
269
+ super == 'true'
270
+ end
271
+
272
+ def supports_apple_watch
273
+ (super != nil)
274
+ end
275
+
276
+ def primary_category=(value)
277
+ value = "MZGenre.#{value}" unless value.include?"MZGenre"
278
+ super(value)
279
+ end
280
+
281
+ def primary_category=(value)
282
+ value = "MZGenre.#{value}" unless value.include?"MZGenre"
283
+ super(value)
284
+ end
285
+
286
+ def primary_first_sub_cate=(value)gory
287
+ value = "MZGenre.#{value}" unless value.include?"MZGenre"
288
+ super(value)
289
+ end
290
+
291
+ def primary_second_sub_cat=(value)egory
292
+ value = "MZGenre.#{value}" unless value.include?"MZGenre"
293
+ super(value)
294
+ end
295
+
296
+ def secondary_category=(value)
297
+ value = "MZGenre.#{value}" unless value.include?"MZGenre"
298
+ super(value)
299
+ end
300
+
301
+ def secondary_first_sub_ca=(value)tegory
302
+ value = "MZGenre.#{value}" unless value.include?"MZGenre"
303
+ super(value)
304
+ end
305
+
306
+ def secondary_second_sub_c=(value)ategory
307
+ value = "MZGenre.#{value}" unless value.include?"MZGenre"
308
+ super(value)
309
+ end
310
+
311
+ private
312
+ # generates the nested data structure to represent screenshots
313
+ def setup_screenshots(row)
314
+ screenshots = row.fetch('screenshots', {}).fetch('value', nil)
315
+ return [] unless screenshots
316
+
317
+ result = []
318
+
319
+ screenshots.each do |device_type, value|
320
+ value['value'].each do |screenshot|
321
+ screenshot = screenshot['value']
322
+ result << Tunes::AppScreenshot.new({
323
+ url: screenshot['url'],
324
+ thumbnail_url: screenshot['thumbNailUrl'],
325
+ sort_order: screenshot['sortOrder'],
326
+ original_file_name: screenshot['originalFileName'],
327
+ device_type: device_type,
328
+ language: row['language']
329
+ })
330
+ end
331
+ end
332
+
333
+ return result
334
+ end
335
+ end
336
+ end
337
+ end