deliver 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +37 -10
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/.yardopts +1 -0
- data/Gemfile.lock +106 -0
- data/LICENSE +21 -0
- data/README.md +268 -17
- data/Rakefile +3 -0
- data/assets/PDFExample.png +0 -0
- data/assets/SubmitForReviewInformation.png +0 -0
- data/assets/deliver.png +0 -0
- data/assets/deliver.pxm +0 -0
- data/assets/deliverFullSize.png +0 -0
- data/bin/deliver +32 -0
- data/deliver.gemspec +15 -9
- data/lib/assets/DeliverfileDefault +47 -0
- data/lib/assets/DeliverfileExample +62 -0
- data/lib/assets/ScreenshotsHelp +4 -0
- data/lib/deliver.rb +20 -1
- data/lib/deliver/app.rb +146 -0
- data/lib/deliver/app_metadata.rb +487 -0
- data/lib/deliver/app_screenshot.rb +119 -0
- data/lib/deliver/commands.rb +4 -0
- data/lib/deliver/commands/init.rb +12 -0
- data/lib/deliver/commands/run.rb +20 -0
- data/lib/deliver/deliver_process.rb +241 -0
- data/lib/deliver/deliverer.rb +112 -0
- data/lib/deliver/deliverfile/deliverfile.rb +35 -0
- data/lib/deliver/deliverfile/deliverfile_creator.rb +115 -0
- data/lib/deliver/deliverfile/dsl.rb +124 -0
- data/lib/deliver/dependency_checker.rb +32 -0
- data/lib/deliver/helper.rb +55 -0
- data/lib/deliver/ipa_uploader.rb +160 -0
- data/lib/deliver/itunes_connect.rb +514 -0
- data/lib/deliver/itunes_search_api.rb +48 -0
- data/lib/deliver/itunes_transporter.rb +154 -0
- data/lib/deliver/languages.rb +6 -0
- data/lib/deliver/metadata_item.rb +94 -0
- data/lib/deliver/password_manager.rb +86 -0
- data/lib/deliver/pdf_generator.rb +131 -0
- data/lib/deliver/version.rb +1 -1
- data/spec/app_metadata_spec.rb +350 -0
- data/spec/app_screenshot_spec.rb +88 -0
- data/spec/app_spec.rb +85 -0
- data/spec/deliverer_spec.rb +48 -0
- data/spec/deliverfile_creator_spec.rb +73 -0
- data/spec/example_deliver_files_spec.rb +227 -0
- data/spec/fixtures/Deliverfiles/DeliverfileCallbacks +14 -0
- data/spec/fixtures/Deliverfiles/DeliverfileCallbacksFailingTests +14 -0
- data/spec/fixtures/Deliverfiles/DeliverfileCallbacksNoErrorBlock +6 -0
- data/spec/fixtures/Deliverfiles/DeliverfileDefaultLanguageNotOnTop +8 -0
- data/spec/fixtures/Deliverfiles/DeliverfileDuplicateIpa +2 -0
- data/spec/fixtures/Deliverfiles/DeliverfileLocales +16 -0
- data/spec/fixtures/Deliverfiles/DeliverfileMetadataJson +6 -0
- data/spec/fixtures/Deliverfiles/DeliverfileMissingAppVersion +1 -0
- data/spec/fixtures/Deliverfiles/DeliverfileMissingBlockForTests +7 -0
- data/spec/fixtures/Deliverfiles/DeliverfileMissingIdentifier +1 -0
- data/spec/fixtures/Deliverfiles/DeliverfileMissingLanguage +1 -0
- data/spec/fixtures/Deliverfiles/DeliverfileMissingValue +3 -0
- data/spec/fixtures/Deliverfiles/DeliverfileMixed +37 -0
- data/spec/fixtures/Deliverfiles/DeliverfileNoVersion +3 -0
- data/spec/fixtures/Deliverfiles/DeliverfileScreenshots +6 -0
- data/spec/fixtures/Deliverfiles/DeliverfileScreenshotsFallbackDefaultLanguage +7 -0
- data/spec/fixtures/Deliverfiles/DeliverfileSimple +8 -0
- data/spec/fixtures/Deliverfiles/DeliverfileVersionMismatchPackage +8 -0
- data/spec/fixtures/Deliverfiles/DeliverfileWrongIdentifier +5 -0
- data/spec/fixtures/Deliverfiles/DeliverfileWrongVersion +5 -0
- data/spec/fixtures/Deliverfiles/metadata.json +24 -0
- data/spec/fixtures/example1.itmsp/metadata.xml +121 -0
- data/spec/fixtures/example2.itmsp/metadata.xml +54 -0
- data/spec/fixtures/ipas/Example1.ipa +0 -0
- data/spec/fixtures/metadata/ipa_result.xml +12 -0
- data/spec/fixtures/metadata/ipa_result2.xml +12 -0
- data/spec/fixtures/packages/464686641.itmsp/metadata.xml +104 -0
- data/spec/fixtures/packages/794902327.itmsp/metadata.xml +107 -0
- data/spec/fixtures/packages/878567776.itmsp/metadata.xml +104 -0
- data/spec/fixtures/screenshots/de-DE/iPhone4.png +0 -0
- data/spec/fixtures/screenshots/de-DE/iPhone6.png +0 -0
- data/spec/fixtures/screenshots/de-DE/iPhone6Plus1.png +0 -0
- data/spec/fixtures/screenshots/de-DE/iPhone6Plus2.png +0 -0
- data/spec/fixtures/screenshots/de-DE/screenshot1.png +0 -0
- data/spec/fixtures/screenshots/de-DE/screenshot2.png +0 -0
- data/spec/fixtures/screenshots/de-DE/screenshot3.png +0 -0
- data/spec/fixtures/screenshots/de-DE/screenshot5.png +0 -0
- data/spec/fixtures/screenshots/en-US/english.png +0 -0
- data/spec/fixtures/screenshots/iPhone4.png +0 -0
- data/spec/fixtures/screenshots/invalidImage.png +0 -0
- data/spec/fixtures/screenshots/screenshot1.png +0 -0
- data/spec/fixtures/screenshots/tooMany/de-DE/iPhone4 2.png +0 -0
- data/spec/fixtures/screenshots/tooMany/de-DE/iPhone4 copy 2.png +0 -0
- data/spec/fixtures/screenshots/tooMany/de-DE/iPhone4 copy 3.png +0 -0
- data/spec/fixtures/screenshots/tooMany/de-DE/iPhone4 copy 4.png +0 -0
- data/spec/fixtures/screenshots/tooMany/de-DE/iPhone4 copy 5.png +0 -0
- data/spec/fixtures/screenshots/tooMany/de-DE/iPhone4 copy 6.png +0 -0
- data/spec/fixtures/screenshots/tooMany/de-DE/iPhone4 copy.png +0 -0
- data/spec/fixtures/screenshots/tooMany/de-DE/iPhone4.png +0 -0
- data/spec/helper_spec.rb +16 -0
- data/spec/ipa_uploader_spec.rb +61 -0
- data/spec/itunes_connect_spec.rb +12 -0
- data/spec/itunes_search_api_spec.rb +24 -0
- data/spec/itunes_transporter_spec.rb +52 -0
- data/spec/languages_spec.rb +7 -0
- data/spec/metadata_item_spec.rb +36 -0
- data/spec/mocking/transporter_mocking.rb +40 -0
- data/spec/mocking/webmocking.rb +31 -0
- data/spec/password_manager_spec.rb +27 -0
- data/spec/responses/itunesLookup-.json +4 -0
- data/spec/responses/itunesLookup-0.json +4 -0
- data/spec/responses/itunesLookup-284882215.json +106 -0
- data/spec/responses/itunesLookup-at.felixkrause.iTanky.json +8 -0
- data/spec/responses/itunesLookup-com.facebook.Facebook.json +106 -0
- data/spec/responses/itunesLookup-invalid.json +4 -0
- data/spec/responses/itunesLookup-net.sunapps.invalid.json +4 -0
- data/spec/responses/transporter/download_invalid_apple_id.txt +35 -0
- data/spec/responses/transporter/download_valid_apple_id.txt +32 -0
- data/spec/responses/transporter/upload_invalid.txt +174 -0
- data/spec/responses/transporter/upload_valid.txt +290 -0
- data/spec/spec_helper.rb +23 -0
- data/tasks/rspec.rake +3 -0
- metadata +242 -8
- data/LICENSE.txt +0 -22
data/Rakefile
CHANGED
Binary file
|
Binary file
|
data/assets/deliver.png
ADDED
Binary file
|
data/assets/deliver.pxm
ADDED
Binary file
|
Binary file
|
data/bin/deliver
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.push File.expand_path("../../lib", __FILE__)
|
4
|
+
|
5
|
+
require 'deliver'
|
6
|
+
require 'commander/import'
|
7
|
+
|
8
|
+
HighLine.track_eof = false
|
9
|
+
|
10
|
+
|
11
|
+
# Commander
|
12
|
+
program :version, Deliver::VERSION
|
13
|
+
program :description, 'CLI for \'Deliver\' - Automate uploading of app metadata, screenshots and app updates to Apple'
|
14
|
+
program :help, 'Author', 'Felix Krause <krausefx@gmail.com>'
|
15
|
+
program :help, 'Website', 'http://felixkrause.at'
|
16
|
+
program :help, 'GitHub', 'https://github.com/krausefx/deliver'
|
17
|
+
program :help_formatter, :compact
|
18
|
+
|
19
|
+
global_option('--verbose') { $verbose = true }
|
20
|
+
|
21
|
+
default_command :run
|
22
|
+
|
23
|
+
require 'deliver/commands'
|
24
|
+
|
25
|
+
def deliver_path
|
26
|
+
[enclosed_directory, Deliver::Deliverfile::Deliverfile::FILE_NAME].join('/')
|
27
|
+
end
|
28
|
+
|
29
|
+
# The directoy in which the Deliverfile and metadata should be created
|
30
|
+
def enclosed_directory
|
31
|
+
"."
|
32
|
+
end
|
data/deliver.gemspec
CHANGED
@@ -12,10 +12,10 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = %q{Using Deliver you can easily integrate a real continuous delivery
|
13
13
|
solution for iOS applications. You can update the app metadata, upload screenshots
|
14
14
|
in all languages for different screensizes to iTunesConnect and even publish a new
|
15
|
-
ipa file to iTunesConnect. You define
|
15
|
+
ipa file to iTunesConnect. You define your prefered deployment information once in a so called
|
16
16
|
Deliverfile and store it in git, to easily deploy from every machine, even your Continuos Integration server}
|
17
17
|
spec.homepage = ""
|
18
|
-
|
18
|
+
spec.license = "MIT"
|
19
19
|
|
20
20
|
spec.required_ruby_version = '>= 1.9.3'
|
21
21
|
|
@@ -30,17 +30,23 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_dependency 'fastimage', '~> 1.6.3' # fetch the image sizes from the screenshots
|
31
31
|
spec.add_dependency 'rubyzip', '~> 1.1.6' # needed for extracting the ipa file
|
32
32
|
spec.add_dependency 'plist', '~> 3.1.0' # for reading the Info.plist of the ipa file
|
33
|
-
spec.add_dependency 'colored'
|
33
|
+
spec.add_dependency 'colored' # coloured terminal output
|
34
|
+
spec.add_dependency 'commander', '~> 4.2.0' # CLI parser
|
35
|
+
spec.add_dependency 'prawn' # generating PDF file for the applied changes
|
34
36
|
|
35
37
|
# Frontend Scripting
|
36
38
|
spec.add_dependency 'capybara', '~> 2.4.3' # for controlling iTC
|
37
39
|
spec.add_dependency 'poltergeist', '~> 1.5.1' # headless Javascript browser for controlling iTC
|
38
40
|
|
39
41
|
# Development only
|
40
|
-
spec.add_development_dependency
|
41
|
-
spec.add_development_dependency
|
42
|
-
spec.add_development_dependency
|
43
|
-
spec.add_development_dependency
|
44
|
-
spec.add_development_dependency
|
45
|
-
spec.add_development_dependency
|
42
|
+
spec.add_development_dependency 'bundler'
|
43
|
+
spec.add_development_dependency 'rake'
|
44
|
+
spec.add_development_dependency 'rspec', '~> 3.1.0'
|
45
|
+
spec.add_development_dependency 'pry'
|
46
|
+
spec.add_development_dependency 'yard', '~> 0.8.7.4'
|
47
|
+
spec.add_development_dependency 'webmock', '~> 1.19.0'
|
48
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
49
|
+
|
50
|
+
|
51
|
+
spec.post_install_message = "This gem requires phantomjs. Install it using 'brew update && brew install phantomjs'"
|
46
52
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# For more information about each property, visit the GitHub documentation: https://github.com/krausefx/deliver
|
2
|
+
# Everything next to a # is a comment and will be ignored
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
########################################
|
7
|
+
# App Metadata
|
8
|
+
########################################
|
9
|
+
|
10
|
+
# The app identifier is required
|
11
|
+
app_identifier "[[APP_IDENTIFIER]]"
|
12
|
+
|
13
|
+
|
14
|
+
# This folder has to include one folder for each language
|
15
|
+
# More information about automatic screenshot upload:
|
16
|
+
screenshots_path "./deliver/screenshots/"
|
17
|
+
|
18
|
+
|
19
|
+
# version '1.2' # you can pass this if you want to verify the version number with the ipa file
|
20
|
+
|
21
|
+
config_json_folder './deliver'
|
22
|
+
|
23
|
+
|
24
|
+
########################################
|
25
|
+
# Building and Testing
|
26
|
+
########################################
|
27
|
+
|
28
|
+
# Dynamic generation of the ipa file
|
29
|
+
# I'm using Shenzhen by Mattt, but you can use any build tool you want
|
30
|
+
# Remove the whole block if you do not want to upload an ipa file
|
31
|
+
ipa do
|
32
|
+
# Add any code you want, like incrementing the build
|
33
|
+
# number or changing the app identifier
|
34
|
+
|
35
|
+
# system("ipa build") # build your project using Shenzhen
|
36
|
+
"./[[APP_NAME]].ipa" # Tell 'Deliver' where it can find the finished ipa file
|
37
|
+
end
|
38
|
+
|
39
|
+
# ipa "./latest.ipa" # this can be used, if you prefer manually building the ipa file
|
40
|
+
|
41
|
+
# unit_tests do
|
42
|
+
# system("xctool test")
|
43
|
+
# end
|
44
|
+
|
45
|
+
success do
|
46
|
+
system("say 'Successfully deployed a new version.'")
|
47
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# This is the example Deliverfile
|
2
|
+
# For more information about each property, visit the GitHub documentation: https://github.com/krausefx/deliver
|
3
|
+
#
|
4
|
+
# You can remove those parts you don't need
|
5
|
+
#
|
6
|
+
# A list of available language codes can be found here: https://github.com/krausefx/deliver#available-language-codes
|
7
|
+
#
|
8
|
+
# Everything next to a # is a comment and will be ignored
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
########################################
|
13
|
+
# App Metadata
|
14
|
+
########################################
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
# The app identifier is required
|
19
|
+
app_identifier "[Your App Identifier, e.g. at.felixkrause.app_name]"
|
20
|
+
|
21
|
+
|
22
|
+
# This folder has to include one folder for each language
|
23
|
+
# More information about automatic screenshot upload:
|
24
|
+
screenshots_path "./screenshots"
|
25
|
+
|
26
|
+
|
27
|
+
# version '1.2' # you can pass this if you want to verify the version number with the ipa file
|
28
|
+
#
|
29
|
+
# title({
|
30
|
+
# "en-US" => "Your App Name"
|
31
|
+
# })
|
32
|
+
#
|
33
|
+
# changelog({
|
34
|
+
# "en-US" => "iPhone 6 (Plus) Support"
|
35
|
+
# })
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
########################################
|
40
|
+
# Building and Testing
|
41
|
+
########################################
|
42
|
+
|
43
|
+
# Dynamic generation of the ipa file
|
44
|
+
# I'm using Shenzhen by Mattt, but you can use any build tool you want
|
45
|
+
# Remove the whole block if you do not want to upload an ipa file
|
46
|
+
ipa do
|
47
|
+
# Add any code you want, like incrementing the build
|
48
|
+
# number or changing the app identifier
|
49
|
+
|
50
|
+
# system("ipa build") # build your project using Shenzhen
|
51
|
+
"./[[APP_NAME]].ipa" # Tell 'Deliver' where it can find the finished ipa file
|
52
|
+
end
|
53
|
+
|
54
|
+
# ipa "./latest.ipa" # this can be used, if you prefer manually building the ipa file
|
55
|
+
|
56
|
+
# unit_tests do
|
57
|
+
# system("xctool test")
|
58
|
+
# end
|
59
|
+
|
60
|
+
success do
|
61
|
+
system("say 'Successfully deployed a new version.'")
|
62
|
+
end
|
data/lib/deliver.rb
CHANGED
@@ -1,4 +1,23 @@
|
|
1
|
-
require
|
1
|
+
require 'deliver/version'
|
2
|
+
require 'deliver/helper'
|
3
|
+
require 'deliver/app'
|
4
|
+
require 'deliver/app_metadata'
|
5
|
+
require 'deliver/metadata_item'
|
6
|
+
require 'deliver/app_screenshot'
|
7
|
+
require 'deliver/itunes_connect'
|
8
|
+
require 'deliver/itunes_search_api'
|
9
|
+
require 'deliver/itunes_transporter'
|
10
|
+
require 'deliver/deliverfile/deliverfile'
|
11
|
+
require 'deliver/deliverfile/deliverfile_creator'
|
12
|
+
require 'deliver/deliverer'
|
13
|
+
require 'deliver/ipa_uploader'
|
14
|
+
require 'deliver/languages'
|
15
|
+
require 'deliver/pdf_generator'
|
16
|
+
require 'deliver/dependency_checker'
|
17
|
+
require 'deliver/deliver_process'
|
18
|
+
|
19
|
+
# Third Party code
|
20
|
+
require 'colored'
|
2
21
|
|
3
22
|
module Deliver
|
4
23
|
# Your code goes here...
|
data/lib/deliver/app.rb
ADDED
@@ -0,0 +1,146 @@
|
|
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 = Deliver::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
|
+
self.apple_id = Deliver::ItunesSearchApi.fetch_by_identifier(app_identifier)['trackId']
|
45
|
+
rescue
|
46
|
+
Helper.log.fatal "Could not find Apple ID based on the app identifier '#{app_identifier}'. Maybe the app is not in the AppStore yet?"
|
47
|
+
raise "Please pass a valid Apple ID using 'apple_id'".red
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"#{apple_id} - #{app_identifier}"
|
54
|
+
end
|
55
|
+
|
56
|
+
#####################################################
|
57
|
+
# @!group Interacting with iTunesConnect
|
58
|
+
#####################################################
|
59
|
+
|
60
|
+
# The iTC handler which is used to interact with the iTunesConnect backend
|
61
|
+
def itc
|
62
|
+
@itc ||= Deliver::ItunesConnect.new
|
63
|
+
end
|
64
|
+
|
65
|
+
# This method fetches the current app status from iTunesConnect.
|
66
|
+
# This method may take some time to execute, since it uses frontend scripting under the hood.
|
67
|
+
# @return the current App Status defined at {Deliver::App::AppStatus}, like "Waiting For Review"
|
68
|
+
def get_app_status
|
69
|
+
itc.get_app_status(self)
|
70
|
+
end
|
71
|
+
|
72
|
+
# This method fetches the app version of the latest published version
|
73
|
+
# This method may take some time to execute, since it uses frontend scripting under the hood.
|
74
|
+
# @return the currently active app version, which in production
|
75
|
+
def get_live_version
|
76
|
+
itc.get_live_version(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
#####################################################
|
81
|
+
# @!group Updating the App Metadata
|
82
|
+
#####################################################
|
83
|
+
|
84
|
+
# Use this method to change the default download location for the metadata packages
|
85
|
+
def set_metadata_directory(dir)
|
86
|
+
raise "Can not change metadata directory after accessing metadata of an app" if @metadata
|
87
|
+
@metadata_dir = dir
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return the path to the directy in which the itmsp files will be downloaded
|
91
|
+
def get_metadata_directory
|
92
|
+
return @metadata_dir if @metadata_dir
|
93
|
+
return "./spec/fixtures/packages/" if Helper.is_test?
|
94
|
+
return "./"
|
95
|
+
end
|
96
|
+
|
97
|
+
# Access to update the metadata of this app
|
98
|
+
#
|
99
|
+
# The first time accessing this, will take some time, since it's downloading
|
100
|
+
# the latest version from iTC.
|
101
|
+
#
|
102
|
+
# Don't forget to call {#upload_metadata!} once you are finished
|
103
|
+
# @return [Deliver::AppMetadata] the latest metadata of this app
|
104
|
+
def metadata
|
105
|
+
@metadata ||= Deliver::AppMetadata.new(self, get_metadata_directory)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Was the app metadata already downloaded?
|
109
|
+
def metadata_downloaded?
|
110
|
+
@metadata != nil
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
# # Uploads a new app icon to iTunesConnect. This uses a headless browser
|
115
|
+
# # which makes this command quite slow.
|
116
|
+
# # @param (path) a path to the new app icon. The image must have the resolution of 1024x1024
|
117
|
+
# def update_app_icon!(path)
|
118
|
+
# itc.update_app_icon!(self, path)
|
119
|
+
# end
|
120
|
+
|
121
|
+
#####################################################
|
122
|
+
# @!group Destructive/Constructive methods
|
123
|
+
#####################################################
|
124
|
+
|
125
|
+
# This method creates a new version of your app using the
|
126
|
+
# iTunesConnect frontend. This will happen directly after calling
|
127
|
+
# this method.
|
128
|
+
# @param version_number (String) the version number as string for
|
129
|
+
# the new version that should be created
|
130
|
+
def create_new_version!(version_number)
|
131
|
+
itc.create_new_version!(self, version_number)
|
132
|
+
end
|
133
|
+
|
134
|
+
# This method has to be called, after modifying the values of .metadata.
|
135
|
+
# It will take care of uploading all changes to Apple.
|
136
|
+
# This method might take a few minutes to run
|
137
|
+
# @return [bool] true on success
|
138
|
+
# @raise [Deliver::TransporterTransferError]
|
139
|
+
# @raise [Deliver::TransporterInputError]
|
140
|
+
def upload_metadata!
|
141
|
+
raise "You first have to modify the metadata using app.metadata.setDescription" unless @metadata
|
142
|
+
|
143
|
+
self.metadata.upload!
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,487 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Deliver
|
4
|
+
class AppMetadataError < StandardError
|
5
|
+
end
|
6
|
+
class AppMetadataParameterError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class AppMetadata
|
10
|
+
ITUNES_NAMESPACE = "http://apple.com/itunes/importer"
|
11
|
+
METADATA_FILE_NAME = "metadata.xml"
|
12
|
+
MAXIMUM_NUMBER_OF_SCREENSHOTS = 5
|
13
|
+
|
14
|
+
# @return Data contains all information for this app, including the unmodified one
|
15
|
+
attr_accessor :information
|
16
|
+
# data = {
|
17
|
+
# 'en-US' => {
|
18
|
+
# title: {
|
19
|
+
# value: "Something",
|
20
|
+
# modified: false
|
21
|
+
# },
|
22
|
+
# version_whats_new: {
|
23
|
+
# value: "Some text",
|
24
|
+
# modified: true
|
25
|
+
# }
|
26
|
+
# screenshots: {
|
27
|
+
# '45' => [
|
28
|
+
# Screenshot1
|
29
|
+
# ]
|
30
|
+
# }
|
31
|
+
# }
|
32
|
+
# }
|
33
|
+
|
34
|
+
private_constant :METADATA_FILE_NAME, :MAXIMUM_NUMBER_OF_SCREENSHOTS
|
35
|
+
|
36
|
+
INVALID_LANGUAGE_ERROR = "The specified language could not be found. Make sure it is available in Deliver::Languages::ALL_LANGUAGES"
|
37
|
+
|
38
|
+
# You don't have to manually create an AppMetadata object. It will
|
39
|
+
# be created when you access the app's metadata ({Deliver::App#metadata})
|
40
|
+
# @param app [Deliver::App] The app this metadata is from/for
|
41
|
+
# @param dir [String] The app this metadata is from/for
|
42
|
+
# @param redownload_package [bool] When true
|
43
|
+
# the current package will be downloaded from iTC before you can
|
44
|
+
# modify any values. This should only be false for unit tests
|
45
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct app object
|
46
|
+
def initialize(app, dir, redownload_package = true)
|
47
|
+
raise AppMetadataParameterError.new("No valid Deliver::App given") unless app.kind_of?Deliver::App
|
48
|
+
|
49
|
+
@metadata_dir = dir
|
50
|
+
@app = app
|
51
|
+
|
52
|
+
if self.class == AppMetadata
|
53
|
+
if redownload_package
|
54
|
+
# Delete the one that may exists already
|
55
|
+
unless Helper.is_test?
|
56
|
+
`rm -fr #{dir}/*.itmsp`
|
57
|
+
end
|
58
|
+
|
59
|
+
# we want to update the metadata, so first we have to download the existing one
|
60
|
+
transporter.download(app, dir)
|
61
|
+
|
62
|
+
# Parse the downloaded package
|
63
|
+
parse_package(dir)
|
64
|
+
else
|
65
|
+
# use_data contains the data to be used. This is the case for unit tests
|
66
|
+
parse_package(dir)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def information
|
72
|
+
@information ||= {}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Verifies the if the version of iTunesConnect matches the one you pass as parameter
|
76
|
+
def verify_version(version_number)
|
77
|
+
xml_version = self.fetch_value("//x:version").first['string']
|
78
|
+
raise "Version mismatch: on iTunesConnect the latest version is '#{xml_version}', you specified '#{version_number}'" if xml_version != version_number
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
# Adds a new locale (language) to the given app
|
83
|
+
# @param language (Deliver::Languages::ALL_LANGUAGES) the language you want to
|
84
|
+
# this app
|
85
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
|
86
|
+
# @return (Bool) Is true, if the language was created. False, when the language alreade existed
|
87
|
+
def add_new_locale(language)
|
88
|
+
unless Deliver::Languages::ALL_LANGUAGES.include?language
|
89
|
+
raise "Language '#{language}' is invalid. It must be in #{Deliver::Languages::ALL_LANGUAGES}."
|
90
|
+
end
|
91
|
+
|
92
|
+
if information[language] != nil
|
93
|
+
Helper.log.info("Locale '#{language}' already exists. Can not create it again.")
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
locales = fetch_value("//x:locales").first
|
99
|
+
|
100
|
+
new_locale = @data.create_element('locale')
|
101
|
+
new_locale['name'] = language
|
102
|
+
locales << new_locale
|
103
|
+
|
104
|
+
# Title is the only thing which is required by iTC
|
105
|
+
default_title = information.values.first[:title][:value]
|
106
|
+
|
107
|
+
title = @data.create_element('title')
|
108
|
+
title.content = default_title
|
109
|
+
new_locale << title
|
110
|
+
|
111
|
+
Helper.log.info("Successfully created the new locale '#{language}'. The default title '#{default_title}' was set, since it's required by iTunesConnect.")
|
112
|
+
Helper.log.info("You can update the title using 'app.metadata.update_title'")
|
113
|
+
|
114
|
+
information[language] ||= {}
|
115
|
+
information[language][:title] = { value: default_title, modified: true}
|
116
|
+
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
#####################################################
|
122
|
+
# @!group Updating metadata information
|
123
|
+
#####################################################
|
124
|
+
|
125
|
+
# Updates the app title
|
126
|
+
# @param (Hash) hash The hash should contain the correct language codes ({Deliver::Languages})
|
127
|
+
# as keys.
|
128
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
|
129
|
+
def update_title(hash)
|
130
|
+
update_metadata_key(:title, hash)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Updates the app description which is shown in the AppStore
|
134
|
+
# @param (Hash) hash The hash should contain the correct language codes ({Deliver::Languages})
|
135
|
+
# as keys.
|
136
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
|
137
|
+
def update_description(hash)
|
138
|
+
update_metadata_key(:description, hash)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Updates the app changelog of the latest version
|
142
|
+
# @param (Hash) hash The hash should contain the correct language codes ({Deliver::Languages})
|
143
|
+
# as keys.
|
144
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
|
145
|
+
def update_changelog(hash)
|
146
|
+
update_metadata_key(:version_whats_new, hash)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Updates the Marketing URL
|
150
|
+
# @param (Hash) hash The hash should contain the correct language codes ({Deliver::Languages})
|
151
|
+
# as keys.
|
152
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
|
153
|
+
def update_marketing_url(hash)
|
154
|
+
update_metadata_key(:software_url, hash)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Updates the Support URL
|
158
|
+
# @param (Hash) hash The hash should contain the correct language codes ({Deliver::Languages})
|
159
|
+
# as keys.
|
160
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
|
161
|
+
def update_support_url(hash)
|
162
|
+
update_metadata_key(:support_url, hash)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Updates the Privacy URL
|
166
|
+
# @param (Hash) hash The hash should contain the correct language codes ({Deliver::Languages})
|
167
|
+
# as keys.
|
168
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
|
169
|
+
def update_privacy_url(hash)
|
170
|
+
update_metadata_key(:privacy_url, hash)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Updates the app keywords
|
174
|
+
# @param (Hash) hash The hash should contain the correct language codes ({Deliver::Languages})
|
175
|
+
# as keys. The value should be an array of keywords (each keyword is a string)
|
176
|
+
# @raise (AppMetadataParameterError) Is thrown when don't pass a correct hash with correct language codes.
|
177
|
+
def update_keywords(hash)
|
178
|
+
update_localized_value('keywords', hash) do |field, keywords, language|
|
179
|
+
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
|
180
|
+
|
181
|
+
if keywords.sort != information[language][:keywords][:value].sort
|
182
|
+
field.children.remove # remove old keywords
|
183
|
+
|
184
|
+
node_set = Nokogiri::XML::NodeSet.new(@data)
|
185
|
+
keywords.each do |word|
|
186
|
+
keyword = Nokogiri::XML::Node.new('keyword', @data)
|
187
|
+
keyword.content = word
|
188
|
+
node_set << keyword
|
189
|
+
end
|
190
|
+
|
191
|
+
field.children = node_set
|
192
|
+
|
193
|
+
information[language][:keywords] = { value: keywords, modified: true }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
#####################################################
|
199
|
+
# @!group Screenshot related
|
200
|
+
#####################################################
|
201
|
+
|
202
|
+
# Removes all currently enabled screenshots for the given language.
|
203
|
+
# @param (String) language The language, which has to be in this list: {Deliver::Languages}.
|
204
|
+
def clear_all_screenshots(language)
|
205
|
+
raise AppMetadataParameterError.new(INVALID_LANGUAGE_ERROR) unless Languages::ALL_LANGUAGES.include?language
|
206
|
+
|
207
|
+
update_localized_value('software_screenshots', {language => {}}) do |field, useless, language|
|
208
|
+
field.children.remove # remove all the screenshots
|
209
|
+
end
|
210
|
+
information[language][:screenshots] = []
|
211
|
+
true
|
212
|
+
end
|
213
|
+
|
214
|
+
# Appends another screenshot to the already existing ones
|
215
|
+
# @param (String) language The language, which has to be in this list: {Deliver::Languages}.
|
216
|
+
# @param (Deliver::AppScreenshot) app_screenshot The screenshot you want to add to the app metadata.
|
217
|
+
# @raise (AppMetadataParameterError) When there are already 5 screenshots (MAXIMUM_NUMBER_OF_SCREENSHOTS).
|
218
|
+
|
219
|
+
def add_screenshot(language, app_screenshot)
|
220
|
+
raise AppMetadataParameterError.new(INVALID_LANGUAGE_ERROR) unless Languages::ALL_LANGUAGES.include?language
|
221
|
+
|
222
|
+
create_locale_if_not_exists(language)
|
223
|
+
|
224
|
+
# Fetch the 'software_screenshots' node (array) for the specific locale
|
225
|
+
locales = self.fetch_value("//x:locale[@name='#{language}']")
|
226
|
+
|
227
|
+
screenshots = self.fetch_value("//x:locale[@name='#{language}']/x:software_screenshots").first
|
228
|
+
|
229
|
+
if not screenshots or screenshots.children.count == 0
|
230
|
+
screenshots.remove if screenshots
|
231
|
+
|
232
|
+
# First screenshot ever
|
233
|
+
screenshots = Nokogiri::XML::Node.new('software_screenshots', @data)
|
234
|
+
locales.first << screenshots
|
235
|
+
|
236
|
+
node_set = Nokogiri::XML::NodeSet.new(@data)
|
237
|
+
node_set << app_screenshot.create_xml_node(@data, 1)
|
238
|
+
screenshots.children = node_set
|
239
|
+
else
|
240
|
+
# There is already at least one screenshot
|
241
|
+
next_index = 1
|
242
|
+
screenshots.children.each do |screen|
|
243
|
+
if screen['display_target'] == app_screenshot.screen_size
|
244
|
+
next_index += 1
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
if next_index > MAXIMUM_NUMBER_OF_SCREENSHOTS
|
249
|
+
raise AppMetadataParameterError.new("Only #{MAXIMUM_NUMBER_OF_SCREENSHOTS} screenshots are allowed per language per device type (#{app_screenshot.screen_size})")
|
250
|
+
end
|
251
|
+
|
252
|
+
# Ready for storing the screenshot into the metadata.xml now
|
253
|
+
screenshots.children.after(app_screenshot.create_xml_node(@data, next_index))
|
254
|
+
end
|
255
|
+
|
256
|
+
information[language][:screenshots] << app_screenshot
|
257
|
+
|
258
|
+
app_screenshot.store_file_inside_package(@package_path)
|
259
|
+
end
|
260
|
+
|
261
|
+
# This method will clear all screenshots and set the new ones you pass
|
262
|
+
# @param new_screenshots
|
263
|
+
# +code+
|
264
|
+
# {
|
265
|
+
# 'de-DE' => [
|
266
|
+
# AppScreenshot.new('path/screenshot1.png', Deliver::ScreenSize::IOS_35),
|
267
|
+
# AppScreenshot.new('path/screenshot2.png', Deliver::ScreenSize::IOS_40),
|
268
|
+
# AppScreenshot.new('path/screenshot3.png', Deliver::ScreenSize::IOS_IPAD)
|
269
|
+
# ]
|
270
|
+
# }
|
271
|
+
# This method uses {#clear_all_screenshots} and {#add_screenshot} under the hood.
|
272
|
+
# @return [bool] true if everything was successful
|
273
|
+
# @raise [AppMetadataParameterError] error is raised when parameters are invalid
|
274
|
+
def set_all_screenshots(new_screenshots)
|
275
|
+
error_text = "Please pass a hash, containing an array of AppScreenshot objects"
|
276
|
+
raise AppMetadataParameterError.new(error_text) unless new_screenshots.kind_of?Hash
|
277
|
+
|
278
|
+
new_screenshots.each do |key, value|
|
279
|
+
if key.kind_of?String and value.kind_of?Array and value.count > 0 and value.first.kind_of?AppScreenshot
|
280
|
+
|
281
|
+
self.clear_all_screenshots(key)
|
282
|
+
|
283
|
+
value.each do |screen|
|
284
|
+
add_screenshot(key, screen)
|
285
|
+
end
|
286
|
+
else
|
287
|
+
raise AppMetadataParameterError.new(error_text)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
true
|
291
|
+
end
|
292
|
+
|
293
|
+
# Automatically add all screenshots contained in the given directory to the app.
|
294
|
+
#
|
295
|
+
# This method will automatically detect which device type each screenshot is.
|
296
|
+
#
|
297
|
+
# This will also clear all existing screenshots before setting the new ones.
|
298
|
+
# @param (Hash) hash A hash containing a different path for each locale ({Deliver::Languages::ALL_LANGUAGES})
|
299
|
+
def set_screenshots_for_each_language(hash)
|
300
|
+
raise AppMetadataParameterError.new("Parameter needs to be an hash, containg strings with the new description") unless hash.kind_of?Hash
|
301
|
+
|
302
|
+
hash.each do |language, current_path|
|
303
|
+
resulting_path = "#{current_path}/*.png"
|
304
|
+
|
305
|
+
raise AppMetadataParameterError.new(INVALID_LANGUAGE_ERROR) unless Languages::ALL_LANGUAGES.include?language
|
306
|
+
|
307
|
+
if Dir[resulting_path].count == 0
|
308
|
+
Helper.log.error("No screenshots found at the given path '#{resulting_path}'")
|
309
|
+
else
|
310
|
+
self.clear_all_screenshots(language)
|
311
|
+
|
312
|
+
Dir[resulting_path].sort.each do |path|
|
313
|
+
add_screenshot(language, Deliver::AppScreenshot.new(path))
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
true
|
319
|
+
end
|
320
|
+
|
321
|
+
# This method will run through all the available locales, check if there is
|
322
|
+
# a folder for this language (e.g. 'en-US') and use all screenshots in there
|
323
|
+
# @param (String) path A path to the folder, which contains a folder for each locale
|
324
|
+
def set_all_screenshots_from_path(path)
|
325
|
+
raise AppMetadataParameterError.new("Parameter needs to be a path (string)") unless path.kind_of?String
|
326
|
+
|
327
|
+
found = false
|
328
|
+
Deliver::Languages::ALL_LANGUAGES.each do |language|
|
329
|
+
full_path = path + "/#{language}"
|
330
|
+
if File.directory?(full_path)
|
331
|
+
found = true
|
332
|
+
set_screenshots_for_each_language({
|
333
|
+
language => full_path
|
334
|
+
})
|
335
|
+
end
|
336
|
+
end
|
337
|
+
return found
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
#####################################################
|
342
|
+
# @!group Manually fetching elements from the metadata.xml
|
343
|
+
#####################################################
|
344
|
+
|
345
|
+
# Directly fetch XML nodes from the metadata.xml.
|
346
|
+
# @example Fetch all keywords
|
347
|
+
# fetch_value("//x:keyword")
|
348
|
+
# @example Fetch a specific locale
|
349
|
+
# fetch_value("//x:locale[@name='de-DE']")
|
350
|
+
# @example Fetch the node that contains all screenshots for a specific language
|
351
|
+
# fetch_value("//x:locale[@name='de-DE']/x:software_screenshots")
|
352
|
+
# @return the requests XML nodes or node set
|
353
|
+
def fetch_value(xpath)
|
354
|
+
@data.xpath(xpath, "x" => ITUNES_NAMESPACE)
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
#####################################################
|
359
|
+
# @!group Uploading the updated metadata
|
360
|
+
#####################################################
|
361
|
+
|
362
|
+
# Actually uploads the updated metadata to Apple.
|
363
|
+
# This method might take a while.
|
364
|
+
# @raise (TransporterTransferError) When something goes wrong when uploading
|
365
|
+
# the metadata/app
|
366
|
+
def upload!
|
367
|
+
unless Helper.is_test?
|
368
|
+
# First: Write the current XML state to disk
|
369
|
+
File.write("#{@package_path}/#{METADATA_FILE_NAME}", @data.to_xml)
|
370
|
+
end
|
371
|
+
|
372
|
+
transporter.upload(@app, @metadata_dir)
|
373
|
+
end
|
374
|
+
|
375
|
+
private
|
376
|
+
def update_metadata_key(key, hash)
|
377
|
+
update_localized_value(key, hash) do |field, new_val, language|
|
378
|
+
raise AppMetadataParameterError.new("Parameter needs to be an hash, containg strings.") unless new_val.kind_of?String
|
379
|
+
if field.content != new_val
|
380
|
+
field.content = new_val
|
381
|
+
information[language][key] = { value: new_val, modified: true }
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# @return (Deliver::ItunesTransporter) The iTunesTranspoter which is
|
387
|
+
# used to upload/download the app metadata.
|
388
|
+
def transporter
|
389
|
+
@transporter ||= ItunesTransporter.new
|
390
|
+
end
|
391
|
+
|
392
|
+
def update_localized_value(xpath_name, new_value)
|
393
|
+
raise AppMetadataParameterError.new("Please pass a hash of languages to this method") unless new_value.kind_of?Hash
|
394
|
+
raise AppMetadataParameterError.new("Please pass a block, which updates the resulting node") unless block_given?
|
395
|
+
|
396
|
+
xpath_name = xpath_name.to_s
|
397
|
+
|
398
|
+
# Run through all the locales given by the 'user'
|
399
|
+
new_value.each do |language, value|
|
400
|
+
create_locale_if_not_exists(language)
|
401
|
+
|
402
|
+
locale = fetch_value("//x:locale[@name='#{language}']").first
|
403
|
+
|
404
|
+
raise AppMetadataParameterError.new("#{INVALID_LANGUAGE_ERROR} (#{language})") unless Languages::ALL_LANGUAGES.include?language
|
405
|
+
|
406
|
+
|
407
|
+
field = locale.search(xpath_name).first
|
408
|
+
|
409
|
+
if not field
|
410
|
+
# This entry does not exist yet, so we have to create it
|
411
|
+
field = Nokogiri::XML::Node.new(xpath_name, @data)
|
412
|
+
locale << field
|
413
|
+
end
|
414
|
+
|
415
|
+
yield(field, value, language)
|
416
|
+
Helper.log.info "Updated #{xpath_name} for locale #{language}"
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
def create_locale_if_not_exists(locale)
|
421
|
+
add_new_locale(locale) unless information[locale]
|
422
|
+
end
|
423
|
+
|
424
|
+
# Parses the metadata using nokogiri
|
425
|
+
def parse_package(path)
|
426
|
+
unless path.include?".itmsp"
|
427
|
+
path += "/#{@app.apple_id}.itmsp/"
|
428
|
+
end
|
429
|
+
@package_path = path
|
430
|
+
|
431
|
+
@data ||= Nokogiri::XML(File.read("#{path}/#{METADATA_FILE_NAME}"))
|
432
|
+
verify_package
|
433
|
+
clean_package
|
434
|
+
fill_in_data
|
435
|
+
end
|
436
|
+
|
437
|
+
# Checks if there is a non live version available
|
438
|
+
# (a new version, or a new app)
|
439
|
+
def verify_package
|
440
|
+
versions = fetch_value("//x:version")
|
441
|
+
|
442
|
+
raise AppMetadataError.new("metadata_token is missing. This package seems to be broken") if fetch_value("//x:metadata_token").count != 1
|
443
|
+
end
|
444
|
+
|
445
|
+
# Cleans up the package of stuff we do not want to modify/upload
|
446
|
+
def clean_package
|
447
|
+
|
448
|
+
# Remove the live version (if it exists)
|
449
|
+
versions = fetch_value("//x:version")
|
450
|
+
while versions.count > 1
|
451
|
+
versions.last.remove
|
452
|
+
versions = fetch_value("//x:version")
|
453
|
+
end
|
454
|
+
Helper.log.info "Modifying version '#{versions.first['string']}' of app #{@app.app_identifier}"
|
455
|
+
|
456
|
+
# Remove all GameCenter related code
|
457
|
+
fetch_value("//x:game_center").remove
|
458
|
+
|
459
|
+
fetch_value("//x:software_screenshots").remove
|
460
|
+
end
|
461
|
+
|
462
|
+
# This will fill in all information we got (from the downloaded metadata.xml file) into self.information
|
463
|
+
def fill_in_data
|
464
|
+
locales = fetch_value("//x:locale")
|
465
|
+
locales.each do |locale|
|
466
|
+
language = locale['name']
|
467
|
+
information[language] ||= {}
|
468
|
+
|
469
|
+
all_keys = [:title, :description, :version_whats_new, :software_url, :support_url, :privacy_url]
|
470
|
+
|
471
|
+
all_keys.each do |key|
|
472
|
+
information[language][key] = {
|
473
|
+
value: (locale.search(key.to_s).first.content rescue ''),
|
474
|
+
modified: false
|
475
|
+
}
|
476
|
+
end
|
477
|
+
|
478
|
+
information[language][:keywords] = { value: [], modified: false}
|
479
|
+
locale.search('keyword').each do |current|
|
480
|
+
information[language][:keywords][:value] << current.content
|
481
|
+
end
|
482
|
+
|
483
|
+
information[language][:screenshots] = []
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|