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.
- checksums.yaml +4 -4
- data/README.md +9 -208
- data/bin/deliver +1 -1
- data/lib/assets/DeliverfileDefault +2 -22
- data/lib/assets/summary.html.erb +27 -39
- data/lib/deliver.rb +16 -14
- data/lib/deliver/app_screenshot.rb +43 -33
- data/lib/deliver/commands_generator.rb +30 -108
- data/lib/deliver/detect_values.rb +23 -0
- data/lib/deliver/download_screenshots.rb +30 -9
- data/lib/deliver/html_generator.rb +7 -8
- data/lib/deliver/options.rb +126 -0
- data/lib/deliver/runner.rb +75 -0
- data/lib/deliver/setup.rb +84 -0
- data/lib/deliver/submit_for_review.rb +60 -0
- data/lib/deliver/upload_assets.rb +22 -0
- data/lib/deliver/upload_metadata.rb +100 -0
- data/lib/deliver/upload_price_tier.rb +21 -0
- data/lib/deliver/upload_screenshots.rb +63 -0
- data/lib/deliver/version.rb +2 -1
- metadata +37 -62
- data/lib/assets/DeliverLanguageMapping.json +0 -187
- data/lib/assets/DeliverfileExample +0 -38
- data/lib/deliver/app.rb +0 -167
- data/lib/deliver/app_metadata.rb +0 -419
- data/lib/deliver/app_metadata_screenshots.rb +0 -189
- data/lib/deliver/deliver_process.rb +0 -426
- data/lib/deliver/deliverer.rb +0 -138
- data/lib/deliver/deliverfile/deliverfile.rb +0 -35
- data/lib/deliver/deliverfile/deliverfile_creator.rb +0 -135
- data/lib/deliver/deliverfile/dsl.rb +0 -142
- data/lib/deliver/dependency_checker.rb +0 -19
- data/lib/deliver/ipa_file_analyser.rb +0 -44
- data/lib/deliver/ipa_uploader.rb +0 -148
- data/lib/deliver/itunes_connect/itunes_connect.rb +0 -12
- data/lib/deliver/itunes_connect/itunes_connect_additional.rb +0 -105
- data/lib/deliver/itunes_connect/itunes_connect_app_icon.rb +0 -41
- data/lib/deliver/itunes_connect/itunes_connect_app_rating.rb +0 -90
- data/lib/deliver/itunes_connect/itunes_connect_apple_watch_app_icon.rb +0 -42
- data/lib/deliver/itunes_connect/itunes_connect_information.rb +0 -34
- data/lib/deliver/itunes_connect/itunes_connect_new_version.rb +0 -67
- data/lib/deliver/itunes_connect/itunes_connect_reader.rb +0 -46
- data/lib/deliver/itunes_connect/itunes_connect_screenshot_fetcher.rb +0 -54
- data/lib/deliver/itunes_connect/itunes_connect_submission.rb +0 -282
- data/lib/deliver/itunes_transporter.rb +0 -221
- data/lib/deliver/metadata_item.rb +0 -94
- data/lib/deliver/testflight.rb +0 -27
@@ -1,221 +0,0 @@
|
|
1
|
-
require 'pty'
|
2
|
-
require 'shellwords'
|
3
|
-
require 'credentials_manager/password_manager'
|
4
|
-
|
5
|
-
|
6
|
-
module Deliver
|
7
|
-
# The TransporterInputError occurs when you passed wrong inputs to the {Deliver::ItunesTransporter}
|
8
|
-
class TransporterInputError < StandardError
|
9
|
-
end
|
10
|
-
# The TransporterTransferError occurs when some error happens
|
11
|
-
# while uploading or downloading something from/to iTC
|
12
|
-
class TransporterTransferError < StandardError
|
13
|
-
end
|
14
|
-
|
15
|
-
class ItunesTransporter
|
16
|
-
ERROR_REGEX = />\s*ERROR:\s+(.+)/
|
17
|
-
WARNING_REGEX = />\s*WARN:\s+(.+)/
|
18
|
-
OUTPUT_REGEX = />\s+(.+)/
|
19
|
-
RETURN_VALUE_REGEX = />\sDBG-X:\sReturning\s+(\d+)/
|
20
|
-
|
21
|
-
SKIP_ERRORS = [ "ERROR: An exception has occurred: Scheduling automatic restart in 1 minute" ]
|
22
|
-
|
23
|
-
private_constant :ERROR_REGEX, :WARNING_REGEX, :OUTPUT_REGEX, :RETURN_VALUE_REGEX, :SKIP_ERRORS
|
24
|
-
|
25
|
-
# This will be called from the Deliverfile, and disables the logging of the transporter output
|
26
|
-
def self.hide_transporter_output
|
27
|
-
@@hide_transporter_output = true
|
28
|
-
end
|
29
|
-
|
30
|
-
# Returns a new instance of the iTunesTransporter.
|
31
|
-
# If no username or password given, it will be taken from
|
32
|
-
# the #{CredentialsManager::PasswordManager}
|
33
|
-
def initialize(user = nil, password = nil)
|
34
|
-
@user = (user || CredentialsManager::PasswordManager.shared_manager.username)
|
35
|
-
@password = (password || CredentialsManager::PasswordManager.shared_manager.password)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Downloads the latest version of the app metadata package from iTC.
|
39
|
-
# @param app [Deliver::App] The app you want to download the data for
|
40
|
-
# @param dir [String] the path to the package file
|
41
|
-
# @return (Bool) True if everything worked fine
|
42
|
-
# @raise [Deliver::TransporterTransferError] when something went wrong
|
43
|
-
# when transfering
|
44
|
-
# @raise [Deliver::TransporterInputError] when passing wrong inputs
|
45
|
-
def download(app, dir = nil)
|
46
|
-
dir ||= "/tmp"
|
47
|
-
raise TransporterInputError.new("No valid Deliver::App given") unless app.kind_of?Deliver::App
|
48
|
-
|
49
|
-
Helper.log.info "Going to download app metadata from iTunesConnect"
|
50
|
-
dir ||= app.get_metadata_directory
|
51
|
-
command = build_download_command(@user, @password, app.apple_id, dir)
|
52
|
-
|
53
|
-
result = execute_transporter(command)
|
54
|
-
|
55
|
-
itmsp_path = File.join(dir, "#{app.apple_id}.itmsp")
|
56
|
-
if result and File.directory?itmsp_path
|
57
|
-
Helper.log.info "Successfully downloaded the latest package from iTunesConnect.".green
|
58
|
-
else
|
59
|
-
unless /^[0-9a-zA-Z\.\$\_]*$/ === @password
|
60
|
-
Helper.log.error "Password contains special characters, which may not be handled properly by iTMSTransporter. If you experience problems uploading to iTunes Connect, please consider changing your password to something with only alphanumeric characters."
|
61
|
-
end
|
62
|
-
Helper.log.fatal "Could not download metadata from iTunes Connect! It's probably related to your password or your internet connection."
|
63
|
-
end
|
64
|
-
|
65
|
-
result
|
66
|
-
end
|
67
|
-
|
68
|
-
# Uploads the modified package back to iTunesConnect
|
69
|
-
# @param app [Deliver::App] The app you want to download the data for
|
70
|
-
# @param dir [String] the path in which the package file is located
|
71
|
-
# @return (Bool) True if everything worked fine
|
72
|
-
# @raise [Deliver::TransporterTransferError] when something went wrong
|
73
|
-
# when transfering
|
74
|
-
# @raise [Deliver::TransporterInputError] when passing wrong inputs
|
75
|
-
def upload(app, dir)
|
76
|
-
raise TransporterInputError.new("No valid Deliver::App given") unless app.kind_of?Deliver::App
|
77
|
-
|
78
|
-
dir ||= app.get_metadata_directory
|
79
|
-
dir += "/#{app.apple_id}.itmsp"
|
80
|
-
|
81
|
-
Helper.log.info "Going to upload updated app to iTunesConnect"
|
82
|
-
Helper.log.info "This might take a few minutes, please don't interrupt the script".green
|
83
|
-
|
84
|
-
command = build_upload_command(@user, @password, dir)
|
85
|
-
result = execute_transporter(command)
|
86
|
-
|
87
|
-
if result
|
88
|
-
Helper.log.info(("-" * 102).green)
|
89
|
-
Helper.log.info("Successfully uploaded package to iTunesConnect. It might take a few minutes until it's visible online.".green)
|
90
|
-
Helper.log.info(("-" * 102).green)
|
91
|
-
|
92
|
-
FileUtils.rm_rf(dir) unless Helper.is_test? # we don't need the package any more, since the upload was successful
|
93
|
-
end
|
94
|
-
|
95
|
-
result
|
96
|
-
end
|
97
|
-
|
98
|
-
private
|
99
|
-
def execute_transporter(command)
|
100
|
-
@errors = []
|
101
|
-
@warnings = []
|
102
|
-
|
103
|
-
if defined?@@hide_transporter_output
|
104
|
-
# Show a one time message instead
|
105
|
-
Helper.log.info "Waiting for iTunes Connect transporter to be finished.".green
|
106
|
-
Helper.log.info "iTunes Transporter progress... this might take a few minutes...".green
|
107
|
-
end
|
108
|
-
|
109
|
-
begin
|
110
|
-
PTY.spawn(command) do |stdin, stdout, pid|
|
111
|
-
stdin.each do |line|
|
112
|
-
parse_line(line) # this is where the parsing happens
|
113
|
-
end
|
114
|
-
end
|
115
|
-
rescue => ex
|
116
|
-
Helper.log.fatal(ex.to_s)
|
117
|
-
@errors << ex.to_s
|
118
|
-
end
|
119
|
-
|
120
|
-
if @warnings.count > 0
|
121
|
-
Helper.log.warn(@warnings.join("\n"))
|
122
|
-
end
|
123
|
-
|
124
|
-
if @errors.count > 0
|
125
|
-
Helper.log.error(@errors.join("\n"))
|
126
|
-
return false
|
127
|
-
end
|
128
|
-
|
129
|
-
true
|
130
|
-
end
|
131
|
-
|
132
|
-
def parse_line(line)
|
133
|
-
# Taken from https://github.com/sshaw/itunes_store_transporter/blob/master/lib/itunes/store/transporter/output_parser.rb
|
134
|
-
|
135
|
-
output_done = false
|
136
|
-
|
137
|
-
re = Regexp.union(SKIP_ERRORS)
|
138
|
-
if line.match(re)
|
139
|
-
# Those lines will not be handle like errors or warnings
|
140
|
-
|
141
|
-
elsif line =~ ERROR_REGEX
|
142
|
-
@errors << $1
|
143
|
-
Helper.log.error "[Transporter Error Output]: #{$1}".red
|
144
|
-
|
145
|
-
# Check if it's a login error
|
146
|
-
if $1.include?"Your Apple ID or password was entered incorrectly" or
|
147
|
-
$1.include?"This Apple ID has been locked for security reasons"
|
148
|
-
|
149
|
-
CredentialsManager::PasswordManager.shared_manager.password_seems_wrong unless Helper.is_test?
|
150
|
-
elsif $1.include?"Redundant Binary Upload. There already exists a binary upload with build"
|
151
|
-
Helper.log.fatal $1
|
152
|
-
Helper.log.fatal "You have to change the build number of your app to upload your ipa file"
|
153
|
-
end
|
154
|
-
|
155
|
-
output_done = true
|
156
|
-
elsif line =~ WARNING_REGEX
|
157
|
-
@warnings << $1
|
158
|
-
Helper.log.warn "[Transporter Warning Output]: #{$1}".yellow
|
159
|
-
output_done = true
|
160
|
-
end
|
161
|
-
|
162
|
-
if line =~ RETURN_VALUE_REGEX
|
163
|
-
if $1.to_i != 0
|
164
|
-
Helper.log.fatal "Transporter transfer failed.".red
|
165
|
-
Helper.log.warn(@warnings.join("\n").yellow)
|
166
|
-
Helper.log.error(@errors.join("\n").red)
|
167
|
-
raise "Return status of iTunes Transporter was #{$1}: #{@errors.join('\n')}".red
|
168
|
-
else
|
169
|
-
Helper.log.info "iTunes Transporter successfully finished its job".green
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
if not defined?@@hide_transporter_output and line =~ OUTPUT_REGEX
|
174
|
-
# General logging for debug purposes
|
175
|
-
unless output_done
|
176
|
-
Helper.log.debug "[Transporter]: #{$1}"
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def build_download_command(username, password, apple_id, destination = "/tmp")
|
182
|
-
[
|
183
|
-
'"' + Helper.transporter_path + '"',
|
184
|
-
"-m lookupMetadata",
|
185
|
-
"-u \"#{username}\"",
|
186
|
-
"-p '#{escaped_password(password)}'",
|
187
|
-
"-apple_id #{apple_id}",
|
188
|
-
"-destination '#{destination}'",
|
189
|
-
"-subitemtype None"
|
190
|
-
].join(' ')
|
191
|
-
|
192
|
-
# The subitemtype defines that we don't want to download in-app purchases and gamecenter things
|
193
|
-
# Allowed values:
|
194
|
-
# - GameCenterLeaderboard
|
195
|
-
# - GameCenterAchievement
|
196
|
-
# - InAppPurchase
|
197
|
-
# - None
|
198
|
-
end
|
199
|
-
|
200
|
-
def build_upload_command(username, password, source = "/tmp")
|
201
|
-
parts = [
|
202
|
-
'"' + Helper.transporter_path + '"',
|
203
|
-
"-m upload",
|
204
|
-
"-u \"#{username}\"",
|
205
|
-
"-p '#{escaped_password(password)}'",
|
206
|
-
"-f '#{source}'",
|
207
|
-
ENV["DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS"], # that's here, because the user might overwrite the -t option
|
208
|
-
"-k 100000"
|
209
|
-
]
|
210
|
-
|
211
|
-
parts << "-t 'Signiant'" unless parts.join(" ").include?"-t"
|
212
|
-
|
213
|
-
parts.join(' ')
|
214
|
-
end
|
215
|
-
|
216
|
-
def escaped_password(password)
|
217
|
-
Shellwords.escape(password)
|
218
|
-
end
|
219
|
-
|
220
|
-
end
|
221
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'nokogiri'
|
2
|
-
|
3
|
-
module Deliver
|
4
|
-
# This class represents a file, included in the metadata.xml
|
5
|
-
#
|
6
|
-
# It takes care of calculating the file size and md5 value.
|
7
|
-
class MetadataItem
|
8
|
-
# @return [String] The path to this particular asset
|
9
|
-
attr_accessor :path
|
10
|
-
|
11
|
-
# Returns a new instance of MetadataItem
|
12
|
-
# @param path [String] the path to the real world file
|
13
|
-
# @param custom_node_name [String] You can set a custom name
|
14
|
-
# for the newly created node.
|
15
|
-
def initialize(path, custom_node_name = nil)
|
16
|
-
raise "File not found at path '#{path}'" unless File.exists?path
|
17
|
-
|
18
|
-
self.path = path
|
19
|
-
@custom_node_name = custom_node_name
|
20
|
-
end
|
21
|
-
|
22
|
-
# This method is called when storing this item into the metadata.xml file
|
23
|
-
#
|
24
|
-
# This method will calculate the md5 hash and exact file size
|
25
|
-
# Generates XML code that looks something like this
|
26
|
-
# +code+
|
27
|
-
# <data_file>
|
28
|
-
# <size>11463227</size>
|
29
|
-
# <file_name>myapp.54.56.ipa</file_name>
|
30
|
-
# <checksum type="md5">9d6b7b0e20bde9a3c831db89563e949f</checksum>
|
31
|
-
# </data_file>
|
32
|
-
# Take a look at the subclass {Deliver::AppScreenshot#create_xml_node} for a
|
33
|
-
# screenshot specific implementation
|
34
|
-
# @param doc [Nokogiri::XML::Document] The document this node
|
35
|
-
# should be added to
|
36
|
-
# @return [Nokogiri::XML::Node] the resulting XML node
|
37
|
-
def create_xml_node(doc)
|
38
|
-
screenshot = Nokogiri::XML::Node.new(name_for_xml_node, doc)
|
39
|
-
|
40
|
-
node_set = Nokogiri::XML::NodeSet.new(doc)
|
41
|
-
|
42
|
-
# File Size
|
43
|
-
size = Nokogiri::XML::Node.new('size', doc)
|
44
|
-
size.content = File.size(self.path)
|
45
|
-
node_set << size
|
46
|
-
|
47
|
-
# File Name
|
48
|
-
file_name = Nokogiri::XML::Node.new('file_name', doc)
|
49
|
-
file_name.content = resulting_file_name
|
50
|
-
node_set << file_name
|
51
|
-
|
52
|
-
# md5 Checksum
|
53
|
-
checksum = Nokogiri::XML::Node.new('checksum', doc)
|
54
|
-
checksum.content = md5_value
|
55
|
-
checksum['type'] = 'md5'
|
56
|
-
node_set << checksum
|
57
|
-
|
58
|
-
|
59
|
-
screenshot.children = node_set
|
60
|
-
|
61
|
-
return screenshot
|
62
|
-
end
|
63
|
-
|
64
|
-
# We also have to copy the file itself, since it has to be *inside* the package
|
65
|
-
# You don't have to call this method manually.
|
66
|
-
def store_file_inside_package(path_to_package)
|
67
|
-
# This will also rename the resulting file to not have any spaces or other
|
68
|
-
# illegal characters in the file name
|
69
|
-
|
70
|
-
FileUtils.cp(self.path, "#{path_to_package}/#{resulting_file_name}")
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
def name_for_xml_node
|
75
|
-
@custom_node_name || 'data_file'
|
76
|
-
end
|
77
|
-
|
78
|
-
# The file name which is used inside the package
|
79
|
-
def resulting_file_name
|
80
|
-
extension = File.extname(self.path)
|
81
|
-
"#{file_name_for_element}#{extension}"
|
82
|
-
end
|
83
|
-
|
84
|
-
def md5_value
|
85
|
-
Digest::MD5.hexdigest(File.read(self.path))
|
86
|
-
end
|
87
|
-
|
88
|
-
# This method will also take some other things into account to generate a truly unique
|
89
|
-
# file name. This will enable using the same screenshots multiple times
|
90
|
-
def file_name_for_element
|
91
|
-
Digest::MD5.hexdigest([File.read(self.path), self.path].join("-"))
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
data/lib/deliver/testflight.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module Deliver
|
2
|
-
class Testflight
|
3
|
-
|
4
|
-
# Uploads a new build to Apple TestFlight
|
5
|
-
# @param ipa_path (String) a path to the IPA to upload
|
6
|
-
# @param app_id (String) optional, the app ID
|
7
|
-
# @param skip_deploy (boolean) Should the submission be skipped?
|
8
|
-
def self.upload!(ipa_path, app_id, skip_deploy)
|
9
|
-
ItunesTransporter.hide_transporter_output
|
10
|
-
|
11
|
-
app_identifier = IpaFileAnalyser.fetch_app_identifier(ipa_path)
|
12
|
-
app_identifier ||= ENV["TESTFLIGHT_APP_IDENTITIFER"] || ask("Could not automatically find the app identifier, please enter the app's bundle identifier: ")
|
13
|
-
app_id ||= (FastlaneCore::ItunesSearchApi.fetch_by_identifier(app_identifier)['trackId'] rescue nil)
|
14
|
-
app_id ||= (FastlaneCore::ItunesConnect.new.find_apple_id(app_identifier) rescue nil)
|
15
|
-
app_id ||= ENV["TESTFLIGHT_APPLE_ID"] || ask("Could not automatically find the app ID, please enter it here (e.g. 956814360): ")
|
16
|
-
strategy = (skip_deploy ? Deliver::IPA_UPLOAD_STRATEGY_JUST_UPLOAD : Deliver::IPA_UPLOAD_STRATEGY_BETA_BUILD)
|
17
|
-
|
18
|
-
Helper.log.info "Ready to upload new build to TestFlight (#{app_identifier} - #{app_id})".green
|
19
|
-
|
20
|
-
# Got everything to ready to deploy
|
21
|
-
app = App.new(app_identifier: app_identifier, apple_id: app_id)
|
22
|
-
ipa = IpaUploader.new(app, '/tmp/', ipa_path, strategy)
|
23
|
-
result = ipa.upload!
|
24
|
-
raise "Error distributing new beta version!".red unless result == true
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|