deliver 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ module Deliver
2
+ # Login code
3
+ class ItunesConnect
4
+ # Loggs in a user with the given login data on the iTC Frontend.
5
+ # You don't need to pass a username and password. It will
6
+ # Automatically be fetched using the {CredentialsManager::PasswordManager}.
7
+ # This method will also automatically be called when triggering other
8
+ # actions like {#open_app_page}
9
+ # @param user (String) (optional) The username/email address
10
+ # @param password (String) (optional) The password
11
+ # @return (bool) true if everything worked fine
12
+ # @raise [ItunesConnectGeneralError] General error while executing
13
+ # this action
14
+ # @raise [ItunesConnectLoginError] Login data is wrong
15
+ def login(user = nil, password = nil)
16
+ Helper.log.info "Logging into iTunesConnect"
17
+
18
+ user ||= CredentialsManager::PasswordManager.shared_manager.username
19
+ password ||= CredentialsManager::PasswordManager.shared_manager.password
20
+
21
+ result = visit ITUNESCONNECT_URL
22
+ raise "Could not open iTunesConnect" unless result['status'] == 'success'
23
+
24
+ sleep 3
25
+
26
+ if page.has_content?"My Apps"
27
+ # Already logged in
28
+ return true
29
+ end
30
+
31
+ begin
32
+ wait_for_elements('#accountpassword')
33
+ rescue => ex
34
+ # when the user is already logged in, this will raise an exception
35
+ end
36
+
37
+ fill_in "accountname", with: user
38
+ fill_in "accountpassword", with: password
39
+
40
+ begin
41
+ (wait_for_elements(".enabled").first.click rescue nil) # Login Button
42
+ wait_for_elements('.homepageWrapper.ng-scope')
43
+
44
+ if page.has_content?"My Apps"
45
+ # Everything looks good
46
+ else
47
+ raise ItunesConnectLoginError.new("Looks like your login data was correct, but you do not have access to the apps.")
48
+ end
49
+ rescue => ex
50
+ Helper.log.debug(ex)
51
+ raise ItunesConnectLoginError.new("Error logging in user #{user} with the given password. Make sure you entered them correctly.")
52
+ end
53
+
54
+ Helper.log.info "Successfully logged into iTunesConnect"
55
+
56
+ true
57
+ rescue => ex
58
+ error_occured(ex)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,57 @@
1
+ module Deliver
2
+ class ItunesConnect
3
+ # This method creates a new version of your app using the
4
+ # iTunesConnect frontend. This will happen directly after calling
5
+ # this method.
6
+ # @param app (Deliver::App) the app you want to modify
7
+ # @param version_number (String) the version number as string for
8
+ # the new version that should be created
9
+ def create_new_version!(app, version_number)
10
+ begin
11
+ current_version = get_live_version(app)
12
+
13
+ verify_app(app)
14
+ open_app_page(app)
15
+
16
+ if page.has_content?BUTTON_STRING_NEW_VERSION
17
+
18
+ if current_version == version_number
19
+ # This means, this version is already live on the App Store
20
+ raise "Version #{version_number} is already created, submitted and released on iTC. Please verify you're using a new version number."
21
+ end
22
+
23
+ click_on BUTTON_STRING_NEW_VERSION
24
+
25
+ Helper.log.info "Creating a new version (#{version_number})"
26
+
27
+ all(".fullWidth.nobottom.ng-isolate-scope.ng-pristine").last.set(version_number.to_s)
28
+ click_on "Create"
29
+
30
+ while not page.has_content?"Prepare for Submission"
31
+ sleep 1
32
+ Helper.log.debug("Waiting for 'Prepare for Submission'")
33
+ end
34
+ else
35
+ Helper.log.warn "Can not create version #{version_number} on iTunesConnect. Maybe it was already created."
36
+ Helper.log.info "Check out '#{current_url}' what's the latest version."
37
+
38
+ begin
39
+ created_version = first(".status.waiting").text.split(" ").first
40
+ if created_version != version_number
41
+ raise "Some other version ('#{created_version}') was created instead of the one you defined ('#{version_number}')"
42
+ end
43
+ rescue => ex
44
+ # Can not fetch the version number of the new version (this happens, when it's e.g. 'Developer Rejected')
45
+ unless page.has_content?version_number
46
+ raise "Some other version was created instead of the one you defined ('#{version_number}')."
47
+ end
48
+ end
49
+ end
50
+
51
+ true
52
+ rescue => ex
53
+ error_occured(ex)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,60 @@
1
+ module Deliver
2
+ # For all the information reading (e.g. version number)
3
+ class ItunesConnect
4
+ # This method will fetch the current status ({Deliver::App::AppStatus})
5
+ # of your app and return it. This method uses a headless browser
6
+ # under the hood, so it might take some time until you get the result
7
+ # @param app (Deliver::App) the app you want this information from
8
+ # @raise [ItunesConnectGeneralError] General error while executing
9
+ # this action
10
+ # @raise [ItunesConnectLoginError] Login data is wrong
11
+ def get_app_status(app)
12
+ begin
13
+ verify_app(app)
14
+
15
+ open_app_page(app)
16
+
17
+ if page.has_content?WAITING_FOR_REVIEW
18
+ # That's either Upload Received or Waiting for Review
19
+ if page.has_content?"To submit a new build, you must remove this version from review"
20
+ return App::AppStatus::WAITING_FOR_REVIEW
21
+ else
22
+ return App::AppStatus::UPLOAD_RECEIVED
23
+ end
24
+ elsif page.has_content?BUTTON_STRING_NEW_VERSION
25
+ return App::AppStatus::READY_FOR_SALE
26
+ elsif page.has_content?BUTTON_STRING_SUBMIT_FOR_REVIEW
27
+ return App::AppStatus::PREPARE_FOR_SUBMISSION
28
+ else
29
+ raise "App status not yet implemented"
30
+ end
31
+ rescue Exception => ex
32
+ error_occured(ex)
33
+ end
34
+ end
35
+
36
+ # This method will fetch the version number of the currently live version
37
+ # of your app and return it. This method uses a headless browser
38
+ # under the hood, so it might take some time until you get the result
39
+ # @param app (Deliver::App) the app you want this information from
40
+ # @raise [ItunesConnectGeneralError] General error while executing
41
+ # this action
42
+ # @raise [ItunesConnectLoginError] Login data is wrong
43
+ def get_live_version(app)
44
+ begin
45
+ verify_app(app)
46
+
47
+ open_app_page(app)
48
+
49
+ begin
50
+ return first(".status.ready").text.split(" ").first
51
+ rescue
52
+ Helper.log.debug "Could not fetch version number of the live version for app #{app}."
53
+ return nil
54
+ end
55
+ rescue => ex
56
+ error_occured(ex)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,222 @@
1
+ module Deliver
2
+ # Everything related to submitting the app
3
+ class ItunesConnect
4
+
5
+ # This will put the latest uploaded build as a new beta build
6
+ def put_build_into_beta_testing!(app, version_number)
7
+ begin
8
+ verify_app(app)
9
+ open_app_page(app)
10
+
11
+ Helper.log.info("Choosing the latest build on iTunesConnect for beta distribution")
12
+
13
+ click_on "Prerelease"
14
+
15
+ wait_for_preprocessing
16
+
17
+ if all(".switcher.ng-binding").count == 0
18
+ raise "Could not find beta build on '#{current_url}'. Make sure it is available there"
19
+ end
20
+
21
+ if first(".switcher.ng-binding")['class'].include?"checked"
22
+ Helper.log.warn("Beta Build seems to be already active. Take a look at '#{current_url}'")
23
+ return true
24
+ end
25
+
26
+ first(".switcher.ng-binding").click
27
+ if page.has_content?"Are you sure you want to start testing"
28
+ click_on "Start"
29
+ end
30
+
31
+
32
+ return true
33
+ rescue => ex
34
+ error_occured(ex)
35
+ end
36
+ end
37
+
38
+ # This will choose the latest uploaded build on iTunesConnect as the production one
39
+ # After this method, you still have to call submit_for_review to actually submit the
40
+ # whole update
41
+ # @param app (Deliver::App) the app you want to choose the build for
42
+ # @param version_number (String) the version number as string for
43
+ def put_build_into_production!(app, version_number)
44
+ begin
45
+ verify_app(app)
46
+ open_app_page(app)
47
+
48
+ Helper.log.info("Choosing the latest build on iTunesConnect for release")
49
+
50
+ click_on "Prerelease"
51
+
52
+ wait_for_preprocessing
53
+
54
+ ################# Apple is finished processing the ipa file #################
55
+
56
+ Helper.log.info("Apple finally finished processing the ipa file")
57
+ open_app_page(app)
58
+
59
+ begin
60
+ first('a', :text => BUTTON_ADD_NEW_BUILD).click
61
+ wait_for_elements(".buildModalList")
62
+ sleep 5
63
+ rescue
64
+ if page.has_content?"Upload Date"
65
+ # That's fine, the ipa was already selected
66
+ return true
67
+ else
68
+ raise "Could not find Build Button. It looks like the ipa file was not properly uploaded."
69
+ end
70
+ end
71
+
72
+ if page.all('td', :text => version_number).count > 1
73
+ Helper.log.fatal "There were multiple submitted builds found. Don't know which one to choose. Just choosing the top one for now"
74
+ end
75
+
76
+ result = page.first('td', :text => version_number).first(:xpath,"./..").first(:css, ".small").click
77
+ click_on "Done" # Save the modal dialog
78
+ click_on "Save" # on the top right to save everything else
79
+
80
+ error = page.has_content?BUTTON_ADD_NEW_BUILD
81
+ raise "Could not put build itself onto production. Try opening '#{current_url}'" if error
82
+
83
+ return true
84
+ rescue => ex
85
+ error_occured(ex)
86
+ end
87
+ end
88
+
89
+ # Submits the update itself to Apple, this includes the app metadata and the ipa file
90
+ # This can easily cause exceptions, which will be shown on iTC.
91
+ # @param app (Deliver::App) the app you want to submit
92
+ # @param perms (Hash) information about content rights, ...
93
+ def submit_for_review!(app, perms = nil)
94
+ begin
95
+ verify_app(app)
96
+ open_app_page(app)
97
+
98
+ Helper.log.info("Submitting app for Review")
99
+
100
+ if not page.has_content?BUTTON_STRING_SUBMIT_FOR_REVIEW
101
+ if page.has_content?WAITING_FOR_REVIEW
102
+ Helper.log.info("App is already Waiting For Review")
103
+ return true
104
+ else
105
+ raise "Couldn't find button with name '#{BUTTON_STRING_SUBMIT_FOR_REVIEW}'"
106
+ end
107
+ end
108
+
109
+ click_on BUTTON_STRING_SUBMIT_FOR_REVIEW
110
+ sleep 4
111
+
112
+ errors = (all(".pagemessage.error") || []).count > 0
113
+ raise "Some error occured when submitting the app for review: '#{current_url}'" if errors
114
+
115
+ wait_for_elements(".savingWrapper.ng-scope.ng-pristine")
116
+ wait_for_elements(".radiostyle")
117
+ sleep 3
118
+
119
+ if page.has_content?"Content Rights"
120
+ # Looks good.. just a few more steps
121
+
122
+ perms ||= {
123
+ export_compliance: {
124
+ encryption_updated: false,
125
+ cryptography_enabled: false,
126
+ is_exempt: false
127
+ },
128
+ third_party_content: {
129
+ contains_third_party_content: false,
130
+ has_rights: false
131
+ },
132
+ advertising_identifier: {
133
+ use_idfa: false,
134
+ serve_advertisement: false,
135
+ attribute_advertisement: false,
136
+ attribute_actions: false,
137
+ limit_ad_tracking: false
138
+ }
139
+ }
140
+
141
+ basic = "//*[@itc-radio='submitForReviewAnswers"
142
+ checkbox = "//*[@itc-checkbox='submitForReviewAnswers"
143
+
144
+ #####################
145
+ # Export Compliance #
146
+ #####################
147
+ if page.has_content?"Export"
148
+
149
+ if not perms[:export_compliance][:encryption_updated] and perms[:export_compliance][:cryptography_enabled]
150
+ raise "encryption_updated must be enabled if cryptography_enabled is enabled!"
151
+ end
152
+
153
+ begin
154
+ encryption_updated_control = all(:xpath, "#{basic}.exportCompliance.encryptionUpdated.value' and @radio-value='#{perms[:export_compliance][:encryption_updated]}']//input")
155
+ encryption_updated_control[0].trigger('click') if encryption_updated_control.count > 0
156
+ first(:xpath, "#{basic}.exportCompliance.usesEncryption.value' and @radio-value='#{perms[:export_compliance][:cryptography_enabled]}']//input").trigger('click')
157
+ first(:xpath, "#{basic}.exportCompliance.isExempt.value' and @radio-value='#{perms[:export_compliance][:is_exempt]}']//input").trigger('click')
158
+ rescue
159
+ end
160
+ end
161
+
162
+ ##################
163
+ # Content Rights #
164
+ ##################
165
+ if page.has_content?"Content Rights"
166
+ if not perms[:third_party_content][:contains_third_party_content] and perms[:third_party_content][:has_rights]
167
+ raise "contains_third_party_content must be enabled if has_rights is enabled".red
168
+ end
169
+
170
+ begin
171
+ first(:xpath, "#{basic}.contentRights.containsThirdPartyContent.value' and @radio-value='#{perms[:third_party_content][:contains_third_party_content]}']//input").trigger('click')
172
+ first(:xpath, "#{basic}.contentRights.hasRights.value' and @radio-value='#{perms[:third_party_content][:has_rights]}']//input").trigger('click')
173
+ rescue
174
+ end
175
+ end
176
+
177
+ ##########################
178
+ # Advertising Identifier #
179
+ ##########################
180
+ if page.has_content?"Advertising Identifier"
181
+ first(:xpath, "#{basic}.adIdInfo.usesIdfa.value' and @radio-value='#{perms[:advertising_identifier][:use_idfa]}']//a").click rescue nil
182
+
183
+ if perms[:advertising_identifier][:use_idfa]
184
+ if perms[:advertising_identifier][:serve_advertisement]
185
+ first(:xpath, "#{checkbox}.adIdInfo.servesAds.value']//a").click
186
+ end
187
+ if perms[:advertising_identifier][:attribute_advertisement]
188
+ first(:xpath, "#{checkbox}.adIdInfo.tracksInstall.value']//a").click
189
+ end
190
+ if perms[:advertising_identifier][:attribute_actions]
191
+ first(:xpath, "#{checkbox}.adIdInfo.tracksAction.value']//a").click
192
+ end
193
+ if perms[:advertising_identifier][:limit_ad_tracking]
194
+ first(:xpath, "#{checkbox}.adIdInfo.limitsTracking.value']//a").click
195
+ end
196
+ end
197
+ end
198
+
199
+
200
+ Helper.log.info("Filled out the export compliance and other information on iTC".green)
201
+
202
+ click_on "Submit"
203
+ sleep 5
204
+
205
+ if page.has_content?WAITING_FOR_REVIEW
206
+ # Everything worked :)
207
+ Helper.log.info("Successfully submitted App for Review".green)
208
+ return true
209
+ else
210
+ raise "So close, it looks like there went something wrong with the actual deployment. Checkout '#{current_url}'".red
211
+ end
212
+ else
213
+ raise "Something is missing here.".red
214
+ end
215
+ return false
216
+ rescue => ex
217
+ error_occured(ex)
218
+ end
219
+ end
220
+
221
+ end
222
+ end
@@ -1,3 +1,3 @@
1
1
  module Deliver
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deliver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Krause
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-15 00:00:00.000000000 Z
11
+ date: 2015-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -295,10 +295,9 @@ files:
295
295
  - lib/deliver.rb
296
296
  - lib/deliver/app.rb
297
297
  - lib/deliver/app_metadata.rb
298
+ - lib/deliver/app_metadata_screenshots.rb
298
299
  - lib/deliver/app_screenshot.rb
299
300
  - lib/deliver/commands.rb
300
- - lib/deliver/commands/init.rb
301
- - lib/deliver/commands/run.rb
302
301
  - lib/deliver/deliver_process.rb
303
302
  - lib/deliver/deliverer.rb
304
303
  - lib/deliver/deliverfile/deliverfile.rb
@@ -307,7 +306,15 @@ files:
307
306
  - lib/deliver/dependency_checker.rb
308
307
  - lib/deliver/helper.rb
309
308
  - lib/deliver/ipa_uploader.rb
310
- - lib/deliver/itunes_connect.rb
309
+ - lib/deliver/itunes_connect/itunes_connect.rb
310
+ - lib/deliver/itunes_connect/itunes_connect_additional.rb
311
+ - lib/deliver/itunes_connect/itunes_connect_app_icon.rb
312
+ - lib/deliver/itunes_connect/itunes_connect_app_rating.rb
313
+ - lib/deliver/itunes_connect/itunes_connect_helper.rb
314
+ - lib/deliver/itunes_connect/itunes_connect_login.rb
315
+ - lib/deliver/itunes_connect/itunes_connect_new_version.rb
316
+ - lib/deliver/itunes_connect/itunes_connect_reader.rb
317
+ - lib/deliver/itunes_connect/itunes_connect_submission.rb
311
318
  - lib/deliver/itunes_search_api.rb
312
319
  - lib/deliver/itunes_transporter.rb
313
320
  - lib/deliver/languages.rb