deliver 0.5.0 → 0.6.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 +5 -3
- data/bin/deliver +58 -13
- data/lib/assets/DeliverfileDefault +0 -5
- data/lib/deliver.rb +1 -1
- data/lib/deliver/app.rb +7 -6
- data/lib/deliver/app_metadata.rb +8 -148
- data/lib/deliver/app_metadata_screenshots.rb +153 -0
- data/lib/deliver/deliver_process.rb +24 -0
- data/lib/deliver/deliverer.rb +19 -0
- data/lib/deliver/deliverfile/deliverfile_creator.rb +1 -1
- data/lib/deliver/deliverfile/dsl.rb +7 -3
- data/lib/deliver/itunes_connect/itunes_connect.rb +72 -0
- data/lib/deliver/itunes_connect/itunes_connect_additional.rb +90 -0
- data/lib/deliver/itunes_connect/itunes_connect_app_icon.rb +35 -0
- data/lib/deliver/itunes_connect/itunes_connect_app_rating.rb +68 -0
- data/lib/deliver/itunes_connect/itunes_connect_helper.rb +83 -0
- data/lib/deliver/itunes_connect/itunes_connect_login.rb +61 -0
- data/lib/deliver/itunes_connect/itunes_connect_new_version.rb +57 -0
- data/lib/deliver/itunes_connect/itunes_connect_reader.rb +60 -0
- data/lib/deliver/itunes_connect/itunes_connect_submission.rb +222 -0
- data/lib/deliver/version.rb +1 -1
- metadata +12 -5
- data/lib/deliver/commands/init.rb +0 -10
- data/lib/deliver/commands/run.rb +0 -24
- data/lib/deliver/itunes_connect.rb +0 -552
@@ -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
|
data/lib/deliver/version.rb
CHANGED
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.
|
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-
|
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
|