deliver 0.13.5 → 1.0.0.beta1

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.
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,187 +0,0 @@
1
- [
2
- {
3
- "locale": "cmn-Hans",
4
- "name": "Simplified Chinese",
5
- "game-center": true
6
-
7
- },
8
- {
9
- "locale": "cmn-Hant",
10
- "name": "Traditional Chinese",
11
- "game-center": true
12
- },
13
- {
14
- "locale": "da-DK",
15
- "name": "Danish",
16
- "game-center": true,
17
- "alternatives": ["da"]
18
- },
19
- {
20
- "locale": "nl-NL",
21
- "name": "Dutch",
22
- "game-center": true,
23
- "alternatives": ["nl"]
24
- },
25
- {
26
- "locale": "en-AU",
27
- "name": "Australian English",
28
- "game-center": false
29
- },
30
- {
31
- "locale": "en-AU",
32
- "name": "English_Australian",
33
- "game-center": false
34
- },
35
- {
36
- "locale": "en-CA",
37
- "name": "Canadian English",
38
- "game-center": false
39
- },
40
- {
41
- "locale": "en-CA",
42
- "name": "English_CA",
43
- "game-center": false
44
- },
45
- {
46
- "locale": "en-GB",
47
- "name": "UK English",
48
- "game-center": true
49
- },
50
- {
51
- "locale": "en-GB",
52
- "name": "English_UK",
53
- "game-center": true
54
- },
55
- {
56
- "locale": "en-US",
57
- "name": "English",
58
- "game-center": true
59
- },
60
- {
61
- "locale": "fi-FI",
62
- "name": "Finnish",
63
- "game-center": true,
64
- "alternatives": ["fi"]
65
- },
66
- {
67
- "locale": "fr-CA",
68
- "name": "Canadian French",
69
- "game-center": false
70
- },
71
- {
72
- "locale": "fr-CA",
73
- "name": "French_CA",
74
- "game-center": false
75
- },
76
- {
77
- "locale": "fr-FR",
78
- "name": "French",
79
- "game-center": true,
80
- "alternatives": ["fr"]
81
- },
82
- {
83
- "locale": "de-DE",
84
- "name": "German",
85
- "game-center": true,
86
- "alternatives": ["de"]
87
- },
88
- {
89
- "locale": "el-GR",
90
- "name": "Greek",
91
- "game-center": true,
92
- "alternatives": ["el"]
93
- },
94
- {
95
- "locale": "id-ID",
96
- "name": "Indonesian",
97
- "game-center": true,
98
- "alternatives": ["id"]
99
- },
100
- {
101
- "locale": "it-IT",
102
- "name": "Italian",
103
- "game-center": true,
104
- "alternatives": ["it"]
105
- },
106
- {
107
- "locale": "ja-JP",
108
- "name": "Japanese",
109
- "game-center": true,
110
- "alternatives": ["ja"]
111
- },
112
- {
113
- "locale": "ko-KR",
114
- "name": "Korean",
115
- "game-center": true,
116
- "alternatives": ["ko"]
117
- },
118
- {
119
- "locale": "ms-MY",
120
- "name": "Malay",
121
- "game-center": true,
122
- "alternatives": ["ms"]
123
- },
124
- {
125
- "locale": "no-NO",
126
- "name": "Norwegian",
127
- "game-center": true,
128
- "alternatives": ["no"]
129
- },
130
- {
131
- "locale": "pt-BR",
132
- "name": "Brazilian Portuguese",
133
- "game-center": true
134
- },
135
- {
136
- "locale": "pt-PT",
137
- "name": "Portuguese",
138
- "game-center": true,
139
- "alternatives": ["pt"]
140
- },
141
- {
142
- "locale": "ru-RU",
143
- "name": "Russian",
144
- "game-center": true,
145
- "alternatives": ["ru"]
146
- },
147
- {
148
- "locale": "es-MX",
149
- "name": "Mexican Spanish",
150
- "game-center": false
151
- },
152
- {
153
- "locale": "es-MX",
154
- "name": "Spanish_MX",
155
- "game-center": false
156
- },
157
- {
158
- "locale": "es-ES",
159
- "name": "Spanish",
160
- "game-center": true,
161
- "alternatives": ["es"]
162
- },
163
- {
164
- "locale": "sv-SE",
165
- "name": "Swedish",
166
- "game-center": true,
167
- "alternatives": ["sv"]
168
- },
169
- {
170
- "locale": "th-TH",
171
- "name": "Thai",
172
- "game-center": true,
173
- "alternatives": ["th"]
174
- },
175
- {
176
- "locale": "tr-TR",
177
- "name": "Turkish",
178
- "game-center": true,
179
- "alternatives": ["tr"]
180
- },
181
- {
182
- "locale": "vi-VI",
183
- "name" : "Vietnamese",
184
- "game-center": true,
185
- "alternatives": ["vi"]
186
- }
187
- ]
@@ -1,38 +0,0 @@
1
- ###################### App Metadata ######################
2
- #
3
- # title(
4
- # "en-US" => "Your App Name"
5
- # )
6
- #
7
- # changelog(
8
- # "en-US" => "iPhone 6 (Plus) Support"
9
- # )
10
- # More options: https://github.com/KrauseFx/deliver/blob/master/Deliverfile.md
11
-
12
- ###################### Screenshots ######################
13
- # Store all screenshots in the ./screenshots folder separated
14
- # by language. If you use snapshot, this happens automatically
15
-
16
- ###################### IPA File ######################
17
- # This part is only relevant, if you want to submit a new binary
18
- # If you don't use fastlane (https://github.com/KrauseFx/fastlane)
19
- # you can set the path to your ipa file using:
20
- # ipa "./app.ipa"
21
-
22
- # to provide an ipa file for TestFlight distribution, use beta_ipa
23
- # beta_ipa "./app.ipa"
24
-
25
- # The version of your app - remove this if you provide an ipa file
26
- version "[[APP_VERSION]]"
27
-
28
- ###################### More Options ######################
29
- # If you want to have even more control, check out the documentation
30
- # https://github.com/KrauseFx/deliver/blob/master/Deliverfile.md
31
-
32
-
33
- ###################### Automatically generated ######################
34
- # Feel free to remove the following lines if you use fastlane
35
-
36
- app_identifier "[[APP_IDENTIFIER]]" # The bundle identifier of your app
37
- apple_id "[[APPLE_ID]]" # This is NOT your Apple login ID, but the App ID of your app
38
- email "[[EMAIL]]" # the login email address
data/lib/deliver/app.rb DELETED
@@ -1,167 +0,0 @@
1
- module Deliver
2
- class App
3
- attr_accessor :apple_id, :app_identifier, :metadata
4
-
5
-
6
- # Defines the different states of the app
7
- #
8
- # As specified by Apple: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/ChangingAppStatus.html
9
- module AppStatus
10
- PREPARE_FOR_SUBMISSION = "Prepare for Submission"
11
- WAITING_FOR_REVIEW = "Waiting For Review"
12
- IN_REVIEW = "In Review"
13
- UPLOAD_RECEIVED = "Upload Received"
14
- PENDING_DEVELOPER_RELEASE = "Pending Developer Release"
15
- PROCESSING_FOR_APP_STORE = "Processing for App Store"
16
- READY_FOR_SALE = "Ready for Sale"
17
- REJECTED = "Rejected"
18
-
19
-
20
- # Unused app states
21
- # PENDING_APPLE_RELASE="Pending Apple Release"
22
- # PENDING_CONTRACT = "Pending Contract"
23
- # WAITING_FOR_EXPORT_COMPLIANCE = "Waiting For Export Compliance"
24
- # METADATA_REJECTED = "Metadata Rejected"
25
- # REMOVED_FROM_SALE = "Removed From Sale"
26
- # DEVELOPER_REJECTED = "Developer Rejected" # equals PREPARE_FOR_SUBMISSION
27
- # DEVELOPER_REMOVED_FROM_SALE = "Developer Removed From Sale"
28
- # INVALID_BINARY = "Invalid Binary"
29
- end
30
-
31
- # @param apple_id The Apple ID of the app you want to modify or update. This ID has usually 9 digits
32
- # @param app_identifier If you don't pass this, it will automatically be fetched from the Apple API
33
- # which means it takes longer. If you **can** pass the app_identifier (e.g. com.facebook.Facebook) do it
34
- def initialize(apple_id: nil, app_identifier: nil)
35
- self.apple_id = apple_id.to_s.gsub('id', '').to_i
36
- self.app_identifier = app_identifier
37
-
38
- if apple_id and not app_identifier
39
- # Fetch the app identifier based on the given Apple ID
40
- self.app_identifier = FastlaneCore::ItunesSearchApi.fetch_bundle_identifier(apple_id)
41
- elsif app_identifier and not apple_id
42
- # Fetch the Apple ID based on the given app identifier
43
- begin
44
- begin
45
- self.apple_id = FastlaneCore::ItunesSearchApi.fetch_by_identifier(app_identifier)['trackId']
46
- rescue
47
- Helper.log.warn "App doesn't seem to be in the App Store yet or is not available in the US App Store. Using the iTC API instead."
48
- # Use the iTunes Connect API instead: make that default in the future
49
- self.apple_id = FastlaneCore::ItunesConnect.new.find_apple_id(app_identifier)
50
- raise "Couldn't find Apple ID" unless self.apple_id
51
- end
52
- rescue
53
- unless Helper.is_test?
54
- Helper.log.info "Could not find Apple ID based on the app identifier in the US App Store. Maybe the app is not yet in the store?".yellow
55
- Helper.log.info "You can provide the Apple ID of your app using `apple_id '974739333'` in your `Deliverfile`".green
56
-
57
- while ((self.apple_id || '').to_s.length == 0) || ((self.apple_id || 0).to_i == 0)
58
- self.apple_id = ask("\nApple ID of your app (e.g. 284882215): ")
59
- end
60
- else
61
- raise "Please pass a valid Apple ID using 'apple_id'".red
62
- end
63
- end
64
- end
65
- end
66
-
67
- def to_s
68
- "#{apple_id} - #{app_identifier}"
69
- end
70
-
71
- #####################################################
72
- # @!group Interacting with iTunesConnect
73
- #####################################################
74
-
75
- # The iTC handler which is used to interact with the iTunesConnect backend
76
- def itc
77
- @itc ||= Deliver::ItunesConnect.new
78
- end
79
-
80
- # This method fetches the current app status from iTunesConnect.
81
- # This method may take some time to execute, since it uses frontend scripting under the hood.
82
- # @return the current App Status defined at {Deliver::App::AppStatus}, like "Waiting For Review"
83
- def get_app_status
84
- itc.get_app_status(self)
85
- end
86
-
87
- # This method fetches the app version of the latest published version
88
- # This method may take some time to execute, since it uses frontend scripting under the hood.
89
- # @return the currently active app version, which in production
90
- def get_live_version
91
- itc.get_live_version(self)
92
- end
93
-
94
-
95
- #####################################################
96
- # @!group Updating the App Metadata
97
- #####################################################
98
-
99
- # Use this method to change the default download location for the metadata packages
100
- def set_metadata_directory(dir)
101
- raise "Can not change metadata directory after accessing metadata of an app" if @metadata
102
- @metadata_dir = dir
103
- end
104
-
105
- # @return the path to the directy in which the itmsp files will be downloaded
106
- def get_metadata_directory
107
- return @metadata_dir if @metadata_dir
108
- return "./spec/fixtures/packages/" if Helper.is_test?
109
- return "./"
110
- end
111
-
112
- # Access to update the metadata of this app
113
- #
114
- # The first time accessing this, will take some time, since it's downloading
115
- # the latest version from iTC.
116
- #
117
- # Don't forget to call {#upload_metadata!} once you are finished
118
- # @return [Deliver::AppMetadata] the latest metadata of this app
119
- def metadata
120
- @metadata ||= Deliver::AppMetadata.new(self, get_metadata_directory)
121
- end
122
-
123
- # Was the app metadata already downloaded?
124
- def metadata_downloaded?
125
- @metadata != nil
126
- end
127
-
128
-
129
- # Uploads a new app icon to iTunesConnect. This uses a headless browser
130
- # which makes this command quite slow.
131
- # @param (path) a path to the new app icon. The image must have the resolution of 1024x1024
132
- def upload_app_icon!(path)
133
- itc.upload_app_icon!(self, path)
134
- end
135
-
136
- # Uploads a new apple watch app icon to iTunesConnect. This uses a headless browser
137
- # which makes this command quite slow.
138
- # @param (path) a path to the new apple watch app icon. The image must have the resolution of 1024x1024
139
- def upload_apple_watch_app_icon!(path)
140
- itc.upload_apple_watch_app_icon!(self, path)
141
- end
142
- #####################################################
143
- # @!group Destructive/Constructive methods
144
- #####################################################
145
-
146
- # This method creates a new version of your app using the
147
- # iTunesConnect frontend. This will happen directly after calling
148
- # this method.
149
- # @param version_number (String) the version number as string for
150
- # the new version that should be created
151
- def create_new_version!(version_number)
152
- itc.create_new_version!(self, version_number)
153
- end
154
-
155
- # This method has to be called, after modifying the values of .metadata.
156
- # It will take care of uploading all changes to Apple.
157
- # This method might take a few minutes to run
158
- # @return [bool] true on success
159
- # @raise [Deliver::TransporterTransferError]
160
- # @raise [Deliver::TransporterInputError]
161
- def upload_metadata!
162
- raise "You first have to modify the metadata using app.metadata.setDescription" unless @metadata
163
-
164
- self.metadata.upload!
165
- end
166
- end
167
- end
@@ -1,419 +0,0 @@
1
- require 'nokogiri'
2
- require 'deliver/app_metadata_screenshots'
3
-
4
- module Deliver
5
- class AppMetadataError < StandardError
6
- end
7
- class AppMetadataParameterError < StandardError
8
- end
9
- class AppMetadataTooManyScreenshotsError < StandardError
10
- end
11
-
12
- class AppMetadata
13
- ITUNES_NAMESPACE = "http://apple.com/itunes/importer"
14
- METADATA_FILE_NAME = "metadata.xml"
15
- MAXIMUM_NUMBER_OF_SCREENSHOTS = 5
16
-
17
- # @return Data contains all information for this app, including the unmodified one
18
- attr_accessor :information
19
- # data = {
20
- # 'en-US' => {
21
- # title: {
22
- # value: "Something",
23
- # modified: false
24
- # },
25
- # version_whats_new: {
26
- # value: "Some text",
27
- # modified: true
28
- # }
29
- # screenshots: {
30
- # '45' => [
31
- # Screenshot1
32
- # ]
33
- # }
34
- # }
35
- # }
36
-
37
- private_constant :METADATA_FILE_NAME, :MAXIMUM_NUMBER_OF_SCREENSHOTS
38
-
39
- INVALID_LANGUAGE_ERROR = "The specified language could not be found. Make sure it is available in FastlaneCore::Languages::ALL_LANGUAGES"
40
-
41
- # You don't have to manually create an AppMetadata object. It will
42
- # be created when you access the app's metadata ({Deliver::App#metadata})
43
- # @param app [Deliver::App] The app this metadata is from/for
44
- # @param dir [String] The app this metadata is from/for
45
- # @param redownload_package [bool] When true
46
- # the current package will be downloaded from iTC before you can
47
- # modify any values. This should only be false for unit tests
48
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct app object
49
- def initialize(app, dir, redownload_package = true)
50
- raise AppMetadataParameterError.new("No valid Deliver::App given") unless app.kind_of?Deliver::App
51
-
52
- @metadata_dir = dir
53
- @app = app
54
-
55
- if self.class == AppMetadata
56
- if redownload_package
57
- # Delete the one that may exists already
58
- unless Helper.is_test?
59
- `rm -rf #{dir}/*.itmsp`
60
- end
61
-
62
- # we want to update the metadata, so first we have to download the existing one
63
- transporter.download(app, dir)
64
-
65
- # Parse the downloaded package
66
- parse_package(dir)
67
- else
68
- # use_data contains the data to be used. This is the case for unit tests
69
- parse_package(dir)
70
- end
71
- end
72
- end
73
-
74
- def information
75
- @information ||= {}
76
- end
77
-
78
- # Verifies the if the version of iTunesConnect matches the one you pass as parameter
79
- def verify_version(version_number)
80
- xml_version = self.fetch_value("//x:version").first['string']
81
- raise "Version mismatch: on iTunesConnect the latest version is '#{xml_version}', you specified '#{version_number}'" if xml_version != version_number
82
- true
83
- end
84
-
85
- # Adds a new locale (language) to the given app
86
- # @param language (FastlaneCore::Languages::ALL_LANGUAGES) the language you want to add
87
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
88
- # @return (Bool) Is true, if the language was created. False, when the language alreade existed
89
- def add_new_locale(language)
90
- unless FastlaneCore::Languages::ALL_LANGUAGES.include?language
91
- raise "Language '#{language}' is invalid. It must be in #{FastlaneCore::Languages::ALL_LANGUAGES}."
92
- end
93
-
94
- if information[language] != nil
95
- Helper.log.info("Locale '#{language}' already exists. Can not create it again.")
96
- return false
97
- end
98
-
99
-
100
- locales = fetch_value("//x:locales").first
101
-
102
- new_locale = @data.create_element('locale')
103
- new_locale['name'] = language
104
- locales << new_locale
105
-
106
- # Title is the only thing which is required by iTC
107
- default_title = information.values.first[:title][:value]
108
-
109
- title = @data.create_element('title')
110
- title.content = default_title
111
- new_locale << title
112
-
113
- Helper.log.info("Successfully created the new locale '#{language}'. The default title '#{default_title}' was set.")
114
- Helper.log.info("You can update the title using 'app.metadata.update_title'")
115
-
116
- information[language] ||= {}
117
- information[language][:title] = { value: default_title, modified: true}
118
-
119
- true
120
- end
121
-
122
-
123
- #####################################################
124
- # @!group Updating metadata information
125
- #####################################################
126
-
127
- # Updates the app title
128
- # @param (Hash) hash The hash should contain the correct language codes ({FastlaneCore::Languages})
129
- # as keys.
130
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
131
- def update_title(hash)
132
- update_metadata_key(:title, hash)
133
- end
134
-
135
- # Updates the app description which is shown in the AppStore
136
- # @param (Hash) hash The hash should contain the correct language codes ({FastlaneCore::Languages})
137
- # as keys.
138
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
139
- def update_description(hash)
140
- update_metadata_key(:description, hash)
141
- end
142
-
143
- # Updates the app changelog of the latest version
144
- # @param (Hash) hash The hash should contain the correct language codes ({FastlaneCore::Languages})
145
- # as keys.
146
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
147
- def update_changelog(hash)
148
- update_metadata_key(:version_whats_new, hash)
149
-
150
-
151
- # The code below doesn't work when the app
152
- # is not in the US App Store
153
- # https://github.com/KrauseFx/deliver/issues/236#issuecomment-111016436
154
- # check if we're allowed to do that
155
- # if FastlaneCore::ItunesSearchApi.fetch(@app.apple_id)
156
- # # App is already in the store
157
- # update_metadata_key(:version_whats_new, hash)
158
- # else
159
- # # App is not in the store, skipping changelog for now
160
- # Helper.log.info "It seems like this it the initial release of your app, which can't contain a changelog. Skipping the changelog for now.".yellow
161
- # end
162
- end
163
-
164
- # Updates the Marketing URL
165
- # @param (Hash) hash The hash should contain the correct language codes ({FastlaneCore::Languages})
166
- # as keys.
167
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
168
- def update_marketing_url(hash)
169
- update_metadata_key(:software_url, hash)
170
- end
171
-
172
- # Updates the Support URL
173
- # @param (Hash) hash The hash should contain the correct language codes ({FastlaneCore::Languages})
174
- # as keys.
175
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
176
- def update_support_url(hash)
177
- update_metadata_key(:support_url, hash)
178
- end
179
-
180
- # Updates the Privacy URL
181
- # @param (Hash) hash The hash should contain the correct language codes ({FastlaneCore::Languages})
182
- # as keys.
183
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
184
- def update_privacy_url(hash)
185
- update_metadata_key(:privacy_url, hash)
186
- end
187
-
188
- # Updates the app keywords
189
- # @param (Hash) hash The hash should contain the correct language codes ({FastlaneCore::Languages})
190
- # as keys. The value should be an array of keywords (each keyword is a string)
191
- # @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
192
- def update_keywords(hash)
193
- update_localized_value('keywords', hash) do |field, keywords, language|
194
- raise AppMetadataParameterError.new("Parameter needs to be a hash (each language) with an array of keywords in it (given: #{hash})") unless keywords.kind_of?Array
195
-
196
- next unless information
197
- next unless information[language]
198
- next unless information[language][:keywords]
199
-
200
- if not information[language][:keywords] or keywords.sort != information[language][:keywords][:value].sort
201
- field.children.remove # remove old keywords
202
-
203
- node_set = Nokogiri::XML::NodeSet.new(@data)
204
- keywords.each do |word|
205
- keyword = Nokogiri::XML::Node.new('keyword', @data)
206
- keyword.content = word
207
- node_set << keyword
208
- end
209
-
210
- field.children = node_set
211
-
212
- information[language][:keywords] = { value: keywords, modified: true }
213
- end
214
- end
215
- end
216
-
217
- # Updates the price tier of the given app
218
- # @param (Integer) tier The tier that should be used from now on
219
- def update_price_tier(tier)
220
- raise "Price Tier '#{tier}' must be of type integer".red unless tier.kind_of?Integer
221
- raise "Invalid price tier '#{tier}' given, must be 0 to 94".red unless (tier.to_i >= 0 and tier.to_i <= 87)
222
-
223
- price = fetch_value("//x:wholesale_price_tier").last
224
- unless price
225
- Helper.log.info "No initial pricing found, setting the first one."
226
-
227
- formatted_date = "2015-01-01"
228
- pricing = Nokogiri.XML("
229
- <products>
230
- <product>
231
- <territory>WW</territory>
232
- <cleared_for_sale>true</cleared_for_sale>
233
- <sales_start_date>#{formatted_date}</sales_start_date>
234
- <intervals>
235
- <interval>
236
- <start_date>#{formatted_date}</start_date>
237
- <wholesale_price_tier>0</wholesale_price_tier>
238
- </interval>
239
- </intervals>
240
- <allow_volume_discount>true</allow_volume_discount>
241
- </product>
242
- </products>")
243
- software_metadata = fetch_value("//x:software_metadata").last
244
- software_metadata << pricing.root
245
-
246
- # We're done here, now fetch the element again and set the real price tier
247
- price = fetch_value("//x:wholesale_price_tier").last
248
- raise "Something went wrong creating the new price tier" unless price
249
- end
250
-
251
- price.content = tier
252
- end
253
-
254
- #####################################################
255
- # @!group Manually fetching elements from the metadata.xml
256
- #####################################################
257
-
258
- # Directly fetch XML nodes from the metadata.xml.
259
- # @example Fetch all keywords
260
- # fetch_value("//x:keyword")
261
- # @example Fetch a specific locale
262
- # fetch_value("//x:locale[@name='de-DE']")
263
- # @example Fetch the node that contains all screenshots for a specific language
264
- # fetch_value("//x:locale[@name='de-DE']/x:software_screenshots")
265
- # @return the requests XML nodes or node set
266
- def fetch_value(xpath)
267
- @data.xpath(xpath, "x" => ITUNES_NAMESPACE)
268
- end
269
-
270
-
271
- #####################################################
272
- # @!group Uploading the updated metadata
273
- #####################################################
274
-
275
- # Actually uploads the updated metadata to Apple.
276
- # This method might take a while.
277
- # @raise (TransporterTransferError) When something goes wrong when uploading
278
- # the metadata/app
279
- def upload!
280
- unless Helper.is_test?
281
- # First: Write the current XML state to disk
282
- File.write("#{@package_path}/#{METADATA_FILE_NAME}", @data.to_xml)
283
- end
284
-
285
- transporter.upload(@app, @metadata_dir)
286
- end
287
-
288
- private
289
- def update_metadata_key(key, hash)
290
- update_localized_value(key, hash) do |field, new_val, language|
291
- raise AppMetadataParameterError.new("Parameter needs to be an hash, containg strings.") unless new_val.kind_of?String
292
- if field.content != new_val
293
- field.content = new_val
294
- information[language][key] = { value: new_val, modified: true }
295
- end
296
- end
297
- end
298
-
299
- # @return (Deliver::ItunesTransporter) The iTunesTransporter which is
300
- # used to upload/download the app metadata.
301
- def transporter
302
- @transporter ||= ItunesTransporter.new
303
- end
304
-
305
- def update_localized_value(xpath_name, new_value)
306
- raise AppMetadataParameterError.new("Please pass a hash of languages to this method") unless new_value.kind_of?Hash
307
- raise AppMetadataParameterError.new("Please pass a block, which updates the resulting node") unless block_given?
308
-
309
- xpath_name = xpath_name.to_s
310
-
311
- # Run through all the locales given by the 'user'
312
- new_value.each do |language, value|
313
- # First, check if this is the legacy locale notation
314
- unless FastlaneCore::Languages::ALL_LANGUAGES.include?language
315
- # Check if we need to support legacy language codes
316
- short_code = language.match(/(\w\w)\-.*/)
317
- if short_code and short_code.length == 2 and FastlaneCore::Languages::ALL_LANGUAGES.include?short_code[1]
318
- language = short_code[1]
319
- else
320
- raise AppMetadataParameterError.new("#{INVALID_LANGUAGE_ERROR} (#{language})")
321
- end
322
- end
323
-
324
-
325
- create_locale_if_not_exists(language)
326
-
327
- locale = fetch_value("//x:locale[@name='#{language}']").first
328
-
329
- field = locale.search(xpath_name).first
330
-
331
- if not field
332
- # This entry does not exist yet, so we have to create it
333
- field = Nokogiri::XML::Node.new(xpath_name, @data)
334
- locale << field
335
- end
336
-
337
- yield(field, value, language)
338
- Helper.log.info "Updated #{xpath_name} for locale #{language}"
339
- end
340
- end
341
-
342
- def create_locale_if_not_exists(locale)
343
- add_new_locale(locale) unless information[locale]
344
- end
345
-
346
- # Parses the metadata using nokogiri
347
- def parse_package(path)
348
- unless path.include?".itmsp"
349
- path += "/#{@app.apple_id}.itmsp/"
350
- end
351
- @package_path = path
352
-
353
- @data ||= Nokogiri::XML(File.read("#{path}/#{METADATA_FILE_NAME}"))
354
- verify_package
355
- clean_package
356
- fill_in_data
357
- end
358
-
359
- # Checks if there is a non live version available
360
- # (a new version, or a new app)
361
- def verify_package
362
- raise AppMetadataError.new("metadata_token is missing. This package seems to be broken") if fetch_value("//x:metadata_token").count != 1
363
- end
364
-
365
- # Cleans up the package of stuff we do not want to modify/upload
366
- def clean_package
367
- # Remove the live version (if it exists)
368
- versions = fetch_value("//x:version")
369
- while versions.count > 1
370
- versions.last.remove
371
- versions = fetch_value("//x:version")
372
- end
373
- Helper.log.info "Modifying version '#{versions.first['string']}' of app #{@app.app_identifier}"
374
-
375
- # Remove all GameCenter related code
376
- fetch_value("//x:game_center").remove
377
-
378
- # Remove all InApp purchases
379
- fetch_value("//x:in_app_purchases").remove
380
-
381
- fetch_value("//x:software_screenshots").remove
382
-
383
- # Change the Chinese locale
384
- # Radar 22548000
385
- # from zh-Hans to cmn-Hans
386
- chinese = []
387
- chinese << fetch_value("//x:locale[@name='zh-Hant']").last
388
- chinese << fetch_value("//x:locale[@name='zh-Hans']").last
389
- chinese.each do |entry|
390
- entry["name"] = entry["name"].gsub("zh", "cmn") if entry && entry["name"]
391
- end
392
- end
393
-
394
- # This will fill in all information we got (from the downloaded metadata.xml file) into self.information
395
- def fill_in_data
396
- locales = fetch_value("//x:locale")
397
- locales.each do |locale|
398
- language = locale['name']
399
- information[language] ||= {}
400
-
401
- all_keys = [:title, :description, :version_whats_new, :software_url, :support_url, :privacy_url]
402
-
403
- all_keys.each do |key|
404
- information[language][key] = {
405
- value: (locale.search(key.to_s).first.content rescue ''),
406
- modified: false
407
- }
408
- end
409
-
410
- information[language][:keywords] = { value: [], modified: false}
411
- locale.search('keyword').each do |current|
412
- information[language][:keywords][:value] << current.content
413
- end
414
-
415
- information[language][:screenshots] = []
416
- end
417
- end
418
- end
419
- end