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,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