deliver 0.13.5 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -208
  3. data/bin/deliver +1 -1
  4. data/lib/assets/DeliverfileDefault +2 -22
  5. data/lib/assets/summary.html.erb +27 -39
  6. data/lib/deliver.rb +16 -14
  7. data/lib/deliver/app_screenshot.rb +43 -33
  8. data/lib/deliver/commands_generator.rb +30 -108
  9. data/lib/deliver/detect_values.rb +23 -0
  10. data/lib/deliver/download_screenshots.rb +30 -9
  11. data/lib/deliver/html_generator.rb +7 -8
  12. data/lib/deliver/options.rb +126 -0
  13. data/lib/deliver/runner.rb +75 -0
  14. data/lib/deliver/setup.rb +84 -0
  15. data/lib/deliver/submit_for_review.rb +60 -0
  16. data/lib/deliver/upload_assets.rb +22 -0
  17. data/lib/deliver/upload_metadata.rb +100 -0
  18. data/lib/deliver/upload_price_tier.rb +21 -0
  19. data/lib/deliver/upload_screenshots.rb +63 -0
  20. data/lib/deliver/version.rb +2 -1
  21. metadata +37 -62
  22. data/lib/assets/DeliverLanguageMapping.json +0 -187
  23. data/lib/assets/DeliverfileExample +0 -38
  24. data/lib/deliver/app.rb +0 -167
  25. data/lib/deliver/app_metadata.rb +0 -419
  26. data/lib/deliver/app_metadata_screenshots.rb +0 -189
  27. data/lib/deliver/deliver_process.rb +0 -426
  28. data/lib/deliver/deliverer.rb +0 -138
  29. data/lib/deliver/deliverfile/deliverfile.rb +0 -35
  30. data/lib/deliver/deliverfile/deliverfile_creator.rb +0 -135
  31. data/lib/deliver/deliverfile/dsl.rb +0 -142
  32. data/lib/deliver/dependency_checker.rb +0 -19
  33. data/lib/deliver/ipa_file_analyser.rb +0 -44
  34. data/lib/deliver/ipa_uploader.rb +0 -148
  35. data/lib/deliver/itunes_connect/itunes_connect.rb +0 -12
  36. data/lib/deliver/itunes_connect/itunes_connect_additional.rb +0 -105
  37. data/lib/deliver/itunes_connect/itunes_connect_app_icon.rb +0 -41
  38. data/lib/deliver/itunes_connect/itunes_connect_app_rating.rb +0 -90
  39. data/lib/deliver/itunes_connect/itunes_connect_apple_watch_app_icon.rb +0 -42
  40. data/lib/deliver/itunes_connect/itunes_connect_information.rb +0 -34
  41. data/lib/deliver/itunes_connect/itunes_connect_new_version.rb +0 -67
  42. data/lib/deliver/itunes_connect/itunes_connect_reader.rb +0 -46
  43. data/lib/deliver/itunes_connect/itunes_connect_screenshot_fetcher.rb +0 -54
  44. data/lib/deliver/itunes_connect/itunes_connect_submission.rb +0 -282
  45. data/lib/deliver/itunes_transporter.rb +0 -221
  46. data/lib/deliver/metadata_item.rb +0 -94
  47. data/lib/deliver/testflight.rb +0 -27
@@ -1,19 +0,0 @@
1
- module Deliver
2
- class DependencyChecker
3
- def self.check_dependencies
4
- return if Helper.is_test?
5
- self.check_xcode_select
6
- end
7
-
8
- def self.check_xcode_select
9
- unless `xcode-select -v`.include?"xcode-select version "
10
- Helper.log.fatal '#############################################################'
11
- Helper.log.fatal "# You have to install the Xcode commdand line tools to use deliver"
12
- Helper.log.fatal "# Install the latest version of Xcode from the AppStore"
13
- Helper.log.fatal "# Run xcode-select --install to install the developer tools"
14
- Helper.log.fatal '#############################################################'
15
- raise "Run 'xcode-select --install' and start deliver again"
16
- end
17
- end
18
- end
19
- end
@@ -1,44 +0,0 @@
1
- module Deliver
2
- class IpaFileAnalyser
3
-
4
- # Fetches the app identifier (e.g. com.facebook.Facebook) from the given ipa file.
5
- def self.fetch_app_identifier(path)
6
- plist = IpaFileAnalyser.fetch_info_plist_file(path)
7
- return plist['CFBundleIdentifier'] if plist
8
- return nil
9
- end
10
-
11
- # Fetches the app version from the given ipa file.
12
- def self.fetch_app_version(path)
13
- plist = IpaFileAnalyser.fetch_info_plist_file(path)
14
- return plist['CFBundleShortVersionString'] if plist
15
- return nil
16
- end
17
-
18
- def self.fetch_info_plist_file(path)
19
- Zip::File.open(path) do |zipfile|
20
- zipfile.each do |file|
21
- if file.name.include?'.plist' and not ['.bundle', '.framework'].any? { |a| file.name.include?a }
22
- # We can not be completely sure, that's the correct plist file, so we have to try
23
- begin
24
- # The XML file has to be properly unpacked first
25
- tmp_path = "/tmp/deploytmp.plist"
26
- File.write(tmp_path, zipfile.read(file))
27
- system("plutil -convert xml1 #{tmp_path}")
28
- result = Plist::parse_xml(tmp_path)
29
- File.delete(tmp_path)
30
-
31
- if result['CFBundleIdentifier'] or result['CFBundleVersion']
32
- return result
33
- end
34
- rescue
35
- # We don't really care, look for another XML file
36
- end
37
- end
38
- end
39
- end
40
-
41
- nil
42
- end
43
- end
44
- end
@@ -1,148 +0,0 @@
1
- require 'zip'
2
- require 'plist'
3
-
4
- module Deliver
5
- class IpaUploaderError < StandardError
6
- end
7
-
8
- IPA_UPLOAD_STRATEGY_APP_STORE = 1
9
- IPA_UPLOAD_STRATEGY_BETA_BUILD = 2
10
- IPA_UPLOAD_STRATEGY_JUST_UPLOAD = 3
11
-
12
- # This class takes care of preparing and uploading the given ipa file
13
- # Metadata + IPA file can not be handled in one file
14
- class IpaUploader < AppMetadata
15
- attr_accessor :app
16
- attr_accessor :publish_strategy
17
-
18
- # Create a new uploader for one ipa file. This will only upload the ipa and no
19
- # other app metadata.
20
- # @param app (Deliver::App) The app for which the ipa should be uploaded for
21
- # @param dir (String) The path to where we can store (copy) the ipa file. Usually /tmp/
22
- # @param ipa_path (String) The path to the IPA file which should be uploaded
23
- # @param publish_strategy (Int) If it's a beta build, it will be released to the testers.
24
- # If it's a production build it will be released into production. Otherwise no action.
25
- # @raise (IpaUploaderError) Is thrown when the ipa file was not found or is not valid
26
- def initialize(app, dir, ipa_path, publish_strategy)
27
- ipa_path.dup.strip! # remove unused white spaces
28
- raise IpaUploaderError.new("IPA on path '#{ipa_path}' not found") unless File.exists?(ipa_path)
29
- raise IpaUploaderError.new("IPA on path '#{ipa_path}' is not a valid IPA file") unless ipa_path.include?".ipa"
30
-
31
- super(app, dir, false)
32
-
33
- @ipa_file = Deliver::MetadataItem.new(ipa_path)
34
- @publish_strategy = publish_strategy
35
- end
36
-
37
- # Fetches the app identifier (e.g. com.facebook.Facebook) from the given ipa file.
38
- def fetch_app_identifier
39
- return IpaFileAnalyser.fetch_app_identifier(@ipa_file.path)
40
- end
41
-
42
- # Fetches the app version from the given ipa file.
43
- def fetch_app_version
44
- return IpaFileAnalyser.fetch_app_version(@ipa_file.path)
45
- end
46
-
47
-
48
- #####################################################
49
- # @!group Uploading the ipa file
50
- #####################################################
51
-
52
- # Actually upload the ipa file to Apple
53
- # @param submit_information (Hash) A hash containing submit information (export, content rights)
54
- def upload!(submit_information = nil)
55
- Helper.log.info "Uploading ipa file to iTunesConnect"
56
- build_document
57
-
58
- # Write the current XML state to disk
59
- folder_name = "#{@app.apple_id}.itmsp"
60
- path = "#{@metadata_dir}/#{folder_name}/"
61
- FileUtils.mkdir_p path
62
-
63
- File.write("#{path}/#{METADATA_FILE_NAME}", @data.to_xml)
64
-
65
- @ipa_file.store_file_inside_package(path)
66
-
67
- is_okay = true
68
- begin
69
- is_okay = transporter.upload(@app, @metadata_dir)
70
- rescue => ex
71
- Helper.log.debug ex
72
- is_okay = ex.to_s.include?"ready exists a binary upload with build" # this just means, the ipa is already online
73
- end
74
-
75
- if is_okay
76
- unless Helper.is_test?
77
- return publish_on_itunes_connect(submit_information)
78
- end
79
- end
80
-
81
- return is_okay
82
- end
83
-
84
-
85
-
86
- private
87
- # This method will trigger the iTunesConnect class to choose the latest build
88
- # @return Was it successful?
89
- def publish_on_itunes_connect(submit_information = nil)
90
- if @publish_strategy == IPA_UPLOAD_STRATEGY_APP_STORE
91
- return publish_production_build(submit_information)
92
- elsif @publish_strategy == IPA_UPLOAD_STRATEGY_BETA_BUILD
93
- return publish_beta_build
94
- else
95
- Helper.log.info "deliver will **not** submit the app for Review or for TestFlight distribution".yellow
96
- Helper.log.info "If you want to distribute the binary, don't define `skip_deploy` ".yellow
97
-
98
- if ENV["DELIVER_WHAT_TO_TEST"] or ENV["DELIVER_BETA_DESCRIPTION"] or ENV["DELIVER_BETA_FEEDBACK_EMAIL"]
99
- Helper.log.warn "---------------------------------------------------".yellow
100
- Helper.log.warn "You provided beta version metadata, but used the ".yellow
101
- Helper.log.warn "`skip_deploy` option when running deliver.".yellow
102
- Helper.log.warn "You have to remove `skip_deploy` to set a changelog".yellow
103
- Helper.log.warn "for TestFlight builds".yellow
104
- Helper.log.warn "---------------------------------------------------".yellow
105
- end
106
- end
107
- return true
108
- end
109
-
110
- def publish_beta_build
111
- # Distribute to beta testers
112
- Helper.log.info "Distributing the latest build to Beta Testers."
113
- if self.app.itc.put_build_into_beta_testing!(self.app, self.fetch_app_version)
114
- return true
115
- end
116
- return false
117
- end
118
-
119
- def publish_production_build(submit_information)
120
- # Publish onto Production
121
- Helper.log.info "Putting the latest build onto production."
122
- if self.app.itc.put_build_into_production!(self.app, self.fetch_app_version)
123
- if self.app.itc.submit_for_review!(self.app, submit_information)
124
- Helper.log.info "Successfully deployed a new update of your app. You can now enjoy a good cold Club Mate.".green
125
- return true
126
- end
127
- end
128
- return false
129
- end
130
-
131
-
132
- def build_document
133
- builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
134
- xml.package(xmlns: "http://apple.com/itunes/importer", version: "software4.7") {
135
- xml.software_assets(apple_id: @app.apple_id) {
136
- xml.asset(type: 'bundle') {
137
-
138
- }
139
- }
140
- }
141
- end
142
-
143
- @data = builder.doc
144
- asset = @data.xpath('//x:asset', "x" => Deliver::AppMetadata::ITUNES_NAMESPACE).first
145
- asset << @ipa_file.create_xml_node(@data)
146
- end
147
- end
148
- end
@@ -1,12 +0,0 @@
1
- require 'fastlane_core/itunes_connect/itunes_connect'
2
-
3
- # Import all the actions
4
- require 'deliver/itunes_connect/itunes_connect_submission'
5
- require 'deliver/itunes_connect/itunes_connect_reader'
6
- require 'deliver/itunes_connect/itunes_connect_new_version'
7
- require 'deliver/itunes_connect/itunes_connect_app_icon'
8
- require 'deliver/itunes_connect/itunes_connect_apple_watch_app_icon'
9
- require 'deliver/itunes_connect/itunes_connect_app_rating'
10
- require 'deliver/itunes_connect/itunes_connect_additional'
11
- require 'deliver/itunes_connect/itunes_connect_screenshot_fetcher'
12
- require 'deliver/itunes_connect/itunes_connect_information'
@@ -1,105 +0,0 @@
1
- module Deliver
2
- class ItunesConnect < FastlaneCore::ItunesConnect
3
- # This file sets additional information like copyright and age rating
4
-
5
- def set_copyright!(app, text)
6
- verify_app(app)
7
- open_app_page(app)
8
-
9
- Helper.log.info "Setting copyright to '#{text}'".green
10
-
11
- first("input[ng-model='versionInfo.copyright.value']").set text
12
-
13
- (click_on "Save" rescue nil) # if nothing has changed, there is no back button and we don't care
14
- rescue => ex
15
- error_occured(ex)
16
- end
17
-
18
- def set_app_review_information!(app, hash)
19
- verify_app(app)
20
- open_app_page(app)
21
-
22
- Helper.log.info "Setting review information: #{hash}"
23
-
24
- first("input[ng-model='versionInfo.appReviewInfo.firstName.value']").set hash[:first_name]
25
- first("input[ng-model='versionInfo.appReviewInfo.lastName.value']").set hash[:last_name]
26
- first("input[ng-model='versionInfo.appReviewInfo.phoneNumber.value']").set hash[:phone_number]
27
- first("input[ng-model='versionInfo.appReviewInfo.emailAddress.value']").set hash[:email_address]
28
- first("input[ng-model='versionInfo.appReviewInfo.userName.value']").set hash[:demo_user]
29
- first("input[ng-model='versionInfo.appReviewInfo.password.value']").set hash[:demo_password]
30
- first("span[ng-show='versionInfo.appReviewInfo.reviewNotes.isEditable'] > * > textarea").set hash[:notes]
31
-
32
- (click_on "Save" rescue nil) # if nothing has changed, there is no back button and we don't care
33
-
34
- Helper.log.info "Successfully saved review information".green
35
- rescue => ex
36
- error_occured(ex)
37
- end
38
-
39
- def set_release_after_approval!(app, automatic_release)
40
- verify_app(app)
41
- open_app_page(app)
42
-
43
- Helper.log.info "Setting automatic release to '#{automatic_release}'".green
44
-
45
- radio_value = automatic_release ? "true" : "false"
46
-
47
- # Find the correct radio button
48
- first("div[itc-radio='versionInfo.releaseOnApproval.value'][radio-value='#{radio_value}'] > * > a").click
49
-
50
- (click_on "Save" rescue nil) # if nothing has changed, there is no back button and we don't care
51
- rescue => ex
52
- error_occured(ex)
53
- end
54
-
55
- def set_categories!(app, primary, secondary, primarySubs, secondarySubs)
56
- verify_app(app)
57
- open_app_page(app)
58
-
59
- Helper.log.info "Setting primary/secondary category.'".green
60
-
61
- set_category_dropdown(primary, "primaryCategory")
62
-
63
- set_category_dropdown(secondary, "secondaryCategory")
64
-
65
- if primarySubs
66
- if primarySubs.length > 0
67
- set_category_dropdown(primarySubs[0], "primaryFirstSubCategory")
68
- end
69
- if primarySubs.length > 1
70
- set_category_dropdown(primarySubs[1], "primarySecondSubCategory")
71
- end
72
- end
73
-
74
- if secondarySubs
75
- if secondarySubs.length > 0
76
- set_category_dropdown(secondarySubs[0], "secondaryFirstSubCategory")
77
- end
78
- if secondarySubs.length > 1
79
- set_category_dropdown(secondarySubs[1], "secondarySecondSubCategory")
80
- end
81
- end
82
-
83
- (click_on "Save" rescue nil) # if nothing has changed, there is no back button and we don't care
84
- rescue => ex
85
- error_occured(ex)
86
- end
87
-
88
- private
89
-
90
- def set_category_dropdown(value, catId)
91
- if value
92
- all("select[ng-model='versionInfo.#{catId}.value'] > option").each do |category|
93
- if category.text.to_s == value.to_s
94
- category.select_option
95
- value = nil
96
- break
97
- end
98
- end
99
- if value
100
- Helper.log.info "Could not find #{catId} '#{value}'. Make sure it's available on iTC".red
101
- end
102
- end
103
- end
104
- end
105
- end
@@ -1,41 +0,0 @@
1
- require 'fastimage'
2
-
3
- module Deliver
4
- class ItunesConnect < FastlaneCore::ItunesConnect
5
- # Uploading a new full size app icon
6
-
7
- def upload_app_icon!(app, path)
8
- path = File.expand_path(path)
9
- raise "Could not find app icon at path '#{path}'".red unless File.exists?path
10
-
11
- size = FastImage.size(path)
12
- raise "App icon must have the resolution of 1024x1024px".red unless (size[0] == 1024 and size[1] == 1024)
13
-
14
- # Remove alpha channel
15
- Helper.log.info "Removing alpha channel from provided App Icon (iTunes Connect requirement)".green
16
-
17
- `sips -s format bmp '#{path}' &> /dev/null ` # &> /dev/null since there is warning because of the extension
18
- `sips -s format png '#{path}'`
19
-
20
- begin
21
- verify_app(app)
22
- open_app_page(app)
23
-
24
- Helper.log.info "Starting upload of new app icon".green
25
-
26
- evaluate_script("$('.appversionicon:not(.watchIcon) > .ios7-style-icon').prev().click()") # delete button
27
- evaluate_script("$('[style-class=\"appversionicon rounded\"] [itc-launch-filechooser] + input').attr('id', 'deliverFileUploadInput')") # set div
28
- evaluate_script("URL = webkitURL; URL.createObjectURL = function(){return 'blob:abc'}"); # shim URL
29
- page.attach_file("deliverFileUploadInput", path) # add file
30
-
31
- sleep 10
32
-
33
- click_on "Save"
34
-
35
- Helper.log.info "Finished uploading the new app icon".green
36
- rescue => ex
37
- error_occured(ex)
38
- end
39
- end
40
- end
41
- end
@@ -1,90 +0,0 @@
1
- require 'json'
2
-
3
- module Deliver
4
- class ItunesConnect < FastlaneCore::ItunesConnect
5
- # Setting the app's age restrictions
6
-
7
- def set_app_rating!(app, path_to_json)
8
- path_to_json = File.expand_path(path_to_json)
9
- raise "Could not find app rating JSON file" unless File.exists?(path_to_json)
10
-
11
- config = JSON.parse(File.read(path_to_json))
12
-
13
- verify_app(app)
14
- open_app_page(app)
15
-
16
- Helper.log.info "Updating the app's rating".green
17
-
18
- first("a[ng-show='versionInfo.ratings.isEditable']").click # open the ratings screen
19
-
20
- rows = wait_for_elements(".defaultTable.ratingsTable > tbody > tr.ng-scope") # .ng-scope, since there is one empty row
21
-
22
- if rows.count != (config.count - 1) # -1 the kids
23
- raise "The number of options passed in the config file does not match the number of options available on iTC! Make sure to use the latest template from https://github.com/KrauseFx/deliver/blob/master/assets/example_rating_config.json".red
24
- end
25
-
26
-
27
- # Setting all the values based on config file
28
- rows.each_with_index do |row, index|
29
- current = config[index]
30
-
31
- level = name_for_level(current['level'], current['type'] == 'boolean')
32
-
33
- Helper.log.info "Setting '#{current['comment']}' to #{level}.".green
34
-
35
- radio_value = "ITC.apps.ratings.level.#{level}"
36
-
37
- row.first("td > div[radio-value='#{radio_value}']").click
38
- end
39
-
40
-
41
- # Apple, doing some extra thingy for the kids section
42
- begin
43
- val = config.last['level'].to_i
44
- currently_enabled = (all("div[itc-checkbox='tempPageContent.ratingDialog.madeForKidsChecked'] > * > input").last.value != "")
45
- Helper.log.info "Setting kids mode to #{val}".green
46
- if val > 0
47
- if not currently_enabled
48
- all("div[itc-checkbox='tempPageContent.ratingDialog.madeForKidsChecked'] > * > a").last.click
49
- end
50
-
51
- # Kids is enabled, check mode
52
- first("select[ng-model='tempRatings.ageBand'] > option[value='#{val - 1}']").select_option # -1 since 0 is no kids mode
53
- else
54
- if currently_enabled
55
- # disable kids mode
56
- all("div[itc-checkbox='tempPageContent.ratingDialog.madeForKidsChecked'] > * > a").last.click
57
- end
58
- end
59
- rescue
60
- Helper.log.warn "Couldn't set kids mode because of other options."
61
- end
62
-
63
- # Check if there is a warning or error message because of this rating
64
- error_message = first("p[ng-show='tempPageContent.ratingDialog.showErrorMessage']")
65
- Helper.log.error error_message.text if error_message
66
-
67
- Helper.log.info "Finished setting updated app rating"
68
-
69
- (click_on "Done" rescue nil)
70
-
71
- (click_on "Save" rescue nil) # if nothing has changed, there is no back button and we don't care
72
- rescue => ex
73
- error_occured(ex)
74
- end
75
-
76
- private
77
- def name_for_level(level, is_boolean)
78
- if is_boolean
79
- return "NO" if level == 0
80
- return "YES" if level == 1
81
- else
82
- return "NONE" if level == 0
83
- return "INFREQUENT_MILD" if level == 1
84
- return "FREQUENT_INTENSE" if level == 2
85
- end
86
-
87
- raise "Unknown level '#{level}' - must be 0, 1 or 2".red
88
- end
89
- end
90
- end