fastlane_core 0.41.0 → 0.41.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/fastlane_core/helper.rb +28 -3
- data/lib/fastlane_core/itunes_transporter.rb +179 -97
- data/lib/fastlane_core/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44801fe477ef2ece37a3950c2183bf38132bdb7a
|
4
|
+
data.tar.gz: 87c3ceb4bd30e0cb8e4f78bd1d457ca26f99fb1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00620ff82620ab06c7e3f0e640538fed09b0678d3e1daa88096cad316330f833cbea8d7884de6c4c6e7c27c08c3c47ed7f272ea48ba1cbefe97b753ee4f4ff68
|
7
|
+
data.tar.gz: 38576f178ff6eb0494774420e14b1ca94bf0bf3ee750e6c9132b1a6981236d1c6773827aa42415c0d4956ef8f24342fe33af2772392b0da979efb5c21c9f0f3c
|
data/lib/fastlane_core/helper.rb
CHANGED
@@ -124,15 +124,40 @@ module FastlaneCore
|
|
124
124
|
@xcode_version
|
125
125
|
end
|
126
126
|
|
127
|
+
def self.transporter_java_executable_path
|
128
|
+
return File.join(self.transporter_java_path, 'bin', 'java')
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.transporter_java_ext_dir
|
132
|
+
return File.join(self.transporter_java_path, 'lib', 'ext')
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.transporter_java_jar_path
|
136
|
+
return File.join(self.itms_path, 'lib', 'itmstransporter-launcher.jar')
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.transporter_user_dir
|
140
|
+
return File.join(self.itms_path, 'bin')
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.transporter_java_path
|
144
|
+
return File.join(self.itms_path, 'java')
|
145
|
+
end
|
146
|
+
|
127
147
|
# @return the full path to the iTMSTransporter executable
|
128
148
|
def self.transporter_path
|
149
|
+
return File.join(self.itms_path, 'bin', 'iTMSTransporter')
|
150
|
+
end
|
151
|
+
|
152
|
+
# @return the full path to the iTMSTransporter executable
|
153
|
+
def self.itms_path
|
129
154
|
return '' unless self.is_mac? # so tests work on Linx too
|
130
155
|
|
131
156
|
[
|
132
|
-
"../Applications/Application Loader.app/Contents/MacOS/itms
|
133
|
-
"../Applications/Application Loader.app/Contents/itms
|
157
|
+
"../Applications/Application Loader.app/Contents/MacOS/itms",
|
158
|
+
"../Applications/Application Loader.app/Contents/itms"
|
134
159
|
].each do |path|
|
135
|
-
result = File.join(self.xcode_path, path)
|
160
|
+
result = File.expand_path(File.join(self.xcode_path, path))
|
136
161
|
return result if File.exist?(result)
|
137
162
|
end
|
138
163
|
UI.user_error!("Could not find transporter at #{self.xcode_path}. Please make sure you set the correct path to your Xcode installation.")
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'pty'
|
2
2
|
require 'shellwords'
|
3
|
+
require 'fileutils'
|
3
4
|
require 'credentials_manager/account_manager'
|
4
5
|
|
5
6
|
module FastlaneCore
|
@@ -11,7 +12,8 @@ module FastlaneCore
|
|
11
12
|
class TransporterTransferError < StandardError
|
12
13
|
end
|
13
14
|
|
14
|
-
class
|
15
|
+
# Base class for executing the iTMSTransporter
|
16
|
+
class TransporterExecutor
|
15
17
|
ERROR_REGEX = />\s*ERROR:\s+(.+)/
|
16
18
|
WARNING_REGEX = />\s*WARN:\s+(.+)/
|
17
19
|
OUTPUT_REGEX = />\s+(.+)/
|
@@ -21,93 +23,13 @@ module FastlaneCore
|
|
21
23
|
|
22
24
|
private_constant :ERROR_REGEX, :WARNING_REGEX, :OUTPUT_REGEX, :RETURN_VALUE_REGEX, :SKIP_ERRORS
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
@hide_transporter_output = true
|
27
|
-
|
28
|
-
@hide_transporter_output = false if $verbose
|
29
|
-
end
|
30
|
-
|
31
|
-
# Returns a new instance of the iTunesTransporter.
|
32
|
-
# If no username or password given, it will be taken from
|
33
|
-
# the #{CredentialsManager::AccountManager}
|
34
|
-
def initialize(user = nil, password = nil)
|
35
|
-
data = CredentialsManager::AccountManager.new(user: user, password: password)
|
36
|
-
@user = data.user
|
37
|
-
@password = data.password
|
38
|
-
end
|
39
|
-
|
40
|
-
# Downloads the latest version of the app metadata package from iTC.
|
41
|
-
# @param app_id [Integer] The unique App ID
|
42
|
-
# @param dir [String] the path in which the package file should be stored
|
43
|
-
# @return (Bool) True if everything worked fine
|
44
|
-
# @raise [Deliver::TransporterTransferError] when something went wrong
|
45
|
-
# when transfering
|
46
|
-
def download(app_id, dir = nil)
|
47
|
-
dir ||= "/tmp"
|
48
|
-
|
49
|
-
UI.message("Going to download app metadata from iTunes Connect")
|
50
|
-
command = build_download_command(@user, @password, app_id, dir)
|
51
|
-
UI.verbose(build_download_command(@user, 'YourPassword', app_id, dir)) if $verbose
|
52
|
-
|
53
|
-
result = execute_transporter(command)
|
54
|
-
|
55
|
-
itmsp_path = File.join(dir, "#{app_id}.itmsp")
|
56
|
-
if result and File.directory? itmsp_path
|
57
|
-
UI.success("Successfully downloaded the latest package from iTunes Connect.")
|
58
|
-
else
|
59
|
-
handle_error(@password)
|
60
|
-
end
|
61
|
-
|
62
|
-
result
|
63
|
-
end
|
64
|
-
|
65
|
-
# Uploads the modified package back to iTunes Connect
|
66
|
-
# @param app_id [Integer] The unique App ID
|
67
|
-
# @param dir [String] the path in which the package file is located
|
68
|
-
# @return (Bool) True if everything worked fine
|
69
|
-
# @raise [Deliver::TransporterTransferError] when something went wrong
|
70
|
-
# when transfering
|
71
|
-
def upload(app_id, dir)
|
72
|
-
dir = File.join(dir, "#{app_id}.itmsp")
|
73
|
-
|
74
|
-
UI.message("Going to upload updated app to iTunes Connect")
|
75
|
-
UI.success("This might take a few minutes, please don't interrupt the script")
|
76
|
-
|
77
|
-
command = build_upload_command(@user, @password, dir)
|
78
|
-
UI.verbose(build_upload_command(@user, 'YourPassword', dir)) if $verbose
|
26
|
+
def execute(command, hide_output)
|
27
|
+
return command if Helper.is_test?
|
79
28
|
|
80
|
-
result = execute_transporter(command)
|
81
|
-
|
82
|
-
if result
|
83
|
-
UI.success("-" * 102)
|
84
|
-
UI.success("Successfully uploaded package to iTunes Connect. It might take a few minutes until it's visible online.")
|
85
|
-
UI.success("-" * 102)
|
86
|
-
|
87
|
-
FileUtils.rm_rf(dir) unless Helper.is_test? # we don't need the package any more, since the upload was successful
|
88
|
-
else
|
89
|
-
handle_error(@password)
|
90
|
-
end
|
91
|
-
|
92
|
-
result
|
93
|
-
end
|
94
|
-
|
95
|
-
private
|
96
|
-
|
97
|
-
def handle_error(password)
|
98
|
-
# rubocop:disable Style/CaseEquality
|
99
|
-
unless /^[0-9a-zA-Z\.\$\_]*$/ === password
|
100
|
-
UI.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.")
|
101
|
-
end
|
102
|
-
# rubocop:enable Style/CaseEquality
|
103
|
-
UI.error("Could not download/upload from iTunes Connect! It's probably related to your password or your internet connection.")
|
104
|
-
end
|
105
|
-
|
106
|
-
def execute_transporter(command)
|
107
29
|
@errors = []
|
108
30
|
@warnings = []
|
109
31
|
|
110
|
-
if
|
32
|
+
if hide_output
|
111
33
|
# Show a one time message instead
|
112
34
|
UI.success("Waiting for iTunes Connect transporter to be finished.")
|
113
35
|
UI.success("iTunes Transporter progress... this might take a few minutes...")
|
@@ -116,7 +38,7 @@ module FastlaneCore
|
|
116
38
|
begin
|
117
39
|
PTY.spawn(command) do |stdin, stdout, pid|
|
118
40
|
stdin.each do |line|
|
119
|
-
parse_line(line) # this is where the parsing happens
|
41
|
+
parse_line(line, hide_output) # this is where the parsing happens
|
120
42
|
end
|
121
43
|
end
|
122
44
|
rescue => ex
|
@@ -136,7 +58,9 @@ module FastlaneCore
|
|
136
58
|
true
|
137
59
|
end
|
138
60
|
|
139
|
-
|
61
|
+
private
|
62
|
+
|
63
|
+
def parse_line(line, hide_output)
|
140
64
|
# Taken from https://github.com/sshaw/itunes_store_transporter/blob/master/lib/itunes/store/transporter/output_parser.rb
|
141
65
|
|
142
66
|
output_done = false
|
@@ -180,38 +104,43 @@ module FastlaneCore
|
|
180
104
|
end
|
181
105
|
end
|
182
106
|
|
183
|
-
if !
|
107
|
+
if !hide_output and line =~ OUTPUT_REGEX
|
184
108
|
# General logging for debug purposes
|
185
109
|
unless output_done
|
186
110
|
UI.verbose("[Transporter]: #{$1}")
|
187
111
|
end
|
188
112
|
end
|
189
113
|
end
|
114
|
+
end
|
190
115
|
|
191
|
-
|
116
|
+
# Generates commands and executes the iTMSTransporter through the shell script it provides by the same name
|
117
|
+
class ShellScriptTransporterExecutor < TransporterExecutor
|
118
|
+
def build_upload_command(username, password, source = "/tmp")
|
192
119
|
[
|
193
120
|
'"' + Helper.transporter_path + '"',
|
194
|
-
"-m
|
121
|
+
"-m upload",
|
195
122
|
"-u \"#{username}\"",
|
196
123
|
"-p #{shell_escaped_password(password)}",
|
197
|
-
"-
|
198
|
-
"
|
124
|
+
"-f '#{source}'",
|
125
|
+
ENV["DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS"], # that's here, because the user might overwrite the -t option
|
126
|
+
"-t 'Signiant'",
|
127
|
+
"-k 100000"
|
199
128
|
].join(' ')
|
200
129
|
end
|
201
130
|
|
202
|
-
def
|
131
|
+
def build_download_command(username, password, apple_id, destination = "/tmp")
|
203
132
|
[
|
204
133
|
'"' + Helper.transporter_path + '"',
|
205
|
-
"-m
|
134
|
+
"-m lookupMetadata",
|
206
135
|
"-u \"#{username}\"",
|
207
136
|
"-p #{shell_escaped_password(password)}",
|
208
|
-
"-
|
209
|
-
|
210
|
-
"-t 'Signiant'",
|
211
|
-
"-k 100000"
|
137
|
+
"-apple_id #{apple_id}",
|
138
|
+
"-destination '#{destination}'"
|
212
139
|
].join(' ')
|
213
140
|
end
|
214
141
|
|
142
|
+
private
|
143
|
+
|
215
144
|
def shell_escaped_password(password)
|
216
145
|
# because the shell handles passwords with single-quotes incorrectly, use gsub to replace ShellEscape'd single-quotes of this form:
|
217
146
|
# \'
|
@@ -227,4 +156,157 @@ module FastlaneCore
|
|
227
156
|
"'" + password + "'"
|
228
157
|
end
|
229
158
|
end
|
159
|
+
|
160
|
+
# Generates commands and executes the iTMSTransporter by invoking its Java app directly, to avoid the crazy parameter
|
161
|
+
# escaping problems in its accompanying shell script.
|
162
|
+
class JavaTransporterExecutor < TransporterExecutor
|
163
|
+
def build_upload_command(username, password, source = "/tmp")
|
164
|
+
[
|
165
|
+
Helper.transporter_java_executable_path.shellescape,
|
166
|
+
"-Djava.ext.dirs=#{Helper.transporter_java_ext_dir.shellescape}",
|
167
|
+
'-XX:NewSize=2m',
|
168
|
+
'-Xms32m',
|
169
|
+
'-Xmx1024m',
|
170
|
+
'-Xms1024m',
|
171
|
+
'-Djava.awt.headless=true',
|
172
|
+
'-Dsun.net.http.retryPost=false',
|
173
|
+
"-classpath #{Helper.transporter_java_jar_path.shellescape}",
|
174
|
+
'com.apple.transporter.Application',
|
175
|
+
'-m upload',
|
176
|
+
"-u #{username.shellescape}",
|
177
|
+
"-p #{password.shellescape}",
|
178
|
+
"-f #{source.shellescape}",
|
179
|
+
ENV["DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS"], # that's here, because the user might overwrite the -t option
|
180
|
+
'-t Signiant',
|
181
|
+
'-k 100000',
|
182
|
+
'2>&1' # cause stderr to be written to stdout
|
183
|
+
].compact.join(' ') # compact gets rid of the possibly nil ENV value
|
184
|
+
end
|
185
|
+
|
186
|
+
def build_download_command(username, password, apple_id, destination = "/tmp")
|
187
|
+
[
|
188
|
+
Helper.transporter_java_executable_path.shellescape,
|
189
|
+
"-Djava.ext.dirs=#{Helper.transporter_java_ext_dir.shellescape}",
|
190
|
+
'-XX:NewSize=2m',
|
191
|
+
'-Xms32m',
|
192
|
+
'-Xmx1024m',
|
193
|
+
'-Xms1024m',
|
194
|
+
'-Djava.awt.headless=true',
|
195
|
+
'-Dsun.net.http.retryPost=false',
|
196
|
+
"-classpath #{Helper.transporter_java_jar_path.shellescape}",
|
197
|
+
'com.apple.transporter.Application',
|
198
|
+
'-m lookupMetadata',
|
199
|
+
"-u #{username.shellescape}",
|
200
|
+
"-p #{password.shellescape}",
|
201
|
+
"-apple_id #{apple_id.shellescape}",
|
202
|
+
"-destination #{destination.shellescape}",
|
203
|
+
'2>&1' # cause stderr to be written to stdout
|
204
|
+
].join(' ')
|
205
|
+
end
|
206
|
+
|
207
|
+
def execute(command, hide_output)
|
208
|
+
# The Java command needs to be run starting in a working directory in the iTMSTransporter
|
209
|
+
# file area. The shell script takes care of changing directories over to there, but we'll
|
210
|
+
# handle it manually here for this strategy.
|
211
|
+
FileUtils.cd(Helper.itms_path) do
|
212
|
+
return super(command, hide_output)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
class ItunesTransporter
|
218
|
+
# This will be called from the Deliverfile, and disables the logging of the transporter output
|
219
|
+
def self.hide_transporter_output
|
220
|
+
@hide_transporter_output = !$verbose
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.hide_transporter_output?
|
224
|
+
@hide_transporter_output
|
225
|
+
end
|
226
|
+
|
227
|
+
# Returns a new instance of the iTunesTransporter.
|
228
|
+
# If no username or password given, it will be taken from
|
229
|
+
# the #{CredentialsManager::AccountManager}
|
230
|
+
def initialize(user = nil, password = nil, avoid_shell_script = false)
|
231
|
+
avoid_shell_script ||= !ENV['FASTLANE_EXPERIMENTAL_TRANSPORTER_AVOID_SHELL_SCRIPT'].nil?
|
232
|
+
data = CredentialsManager::AccountManager.new(user: user, password: password)
|
233
|
+
|
234
|
+
@user = data.user
|
235
|
+
@password = data.password
|
236
|
+
@transporter_executor = avoid_shell_script ? JavaTransporterExecutor.new : ShellScriptTransporterExecutor.new
|
237
|
+
end
|
238
|
+
|
239
|
+
# Downloads the latest version of the app metadata package from iTC.
|
240
|
+
# @param app_id [Integer] The unique App ID
|
241
|
+
# @param dir [String] the path in which the package file should be stored
|
242
|
+
# @return (Bool) True if everything worked fine
|
243
|
+
# @raise [Deliver::TransporterTransferError] when something went wrong
|
244
|
+
# when transfering
|
245
|
+
def download(app_id, dir = nil)
|
246
|
+
dir ||= "/tmp"
|
247
|
+
|
248
|
+
UI.message("Going to download app metadata from iTunes Connect")
|
249
|
+
command = @transporter_executor.build_download_command(@user, @password, app_id, dir)
|
250
|
+
UI.verbose(@transporter_executor.build_download_command(@user, 'YourPassword', app_id, dir))
|
251
|
+
|
252
|
+
result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?)
|
253
|
+
return result if Helper.is_test?
|
254
|
+
|
255
|
+
itmsp_path = File.join(dir, "#{app_id}.itmsp")
|
256
|
+
successful = result && File.directory?(itmsp_path)
|
257
|
+
|
258
|
+
if successful
|
259
|
+
UI.success("✅ Successfully downloaded the latest package from iTunes Connect to #{itmsp_path}")
|
260
|
+
else
|
261
|
+
handle_error(@password)
|
262
|
+
end
|
263
|
+
|
264
|
+
successful
|
265
|
+
end
|
266
|
+
|
267
|
+
# Uploads the modified package back to iTunes Connect
|
268
|
+
# @param app_id [Integer] The unique App ID
|
269
|
+
# @param dir [String] the path in which the package file is located
|
270
|
+
# @return (Bool) True if everything worked fine
|
271
|
+
# @raise [Deliver::TransporterTransferError] when something went wrong
|
272
|
+
# when transfering
|
273
|
+
def upload(app_id, dir)
|
274
|
+
dir = File.join(dir, "#{app_id}.itmsp")
|
275
|
+
|
276
|
+
UI.message("Going to upload updated app to iTunes Connect")
|
277
|
+
UI.success("This might take a few minutes, please don't interrupt the script")
|
278
|
+
|
279
|
+
command = @transporter_executor.build_upload_command(@user, @password, dir)
|
280
|
+
|
281
|
+
UI.verbose(@transporter_executor.build_upload_command(@user, 'YourPassword', dir))
|
282
|
+
|
283
|
+
result = @transporter_executor.execute(command, ItunesTransporter.hide_transporter_output?)
|
284
|
+
|
285
|
+
if result
|
286
|
+
UI.success("-" * 102)
|
287
|
+
UI.success("Successfully uploaded package to iTunes Connect. It might take a few minutes until it's visible online.")
|
288
|
+
UI.success("-" * 102)
|
289
|
+
|
290
|
+
FileUtils.rm_rf(dir) unless Helper.is_test? # we don't need the package any more, since the upload was successful
|
291
|
+
else
|
292
|
+
handle_error(@password)
|
293
|
+
end
|
294
|
+
|
295
|
+
result
|
296
|
+
end
|
297
|
+
|
298
|
+
private
|
299
|
+
|
300
|
+
def handle_error(password)
|
301
|
+
# rubocop:disable Style/CaseEquality
|
302
|
+
unless /^[0-9a-zA-Z\.\$\_]*$/ === password
|
303
|
+
UI.error([
|
304
|
+
"Password contains special characters, which may not be handled properly by iTMSTransporter.",
|
305
|
+
"If you experience problems uploading to iTunes Connect, please consider changing your password to something with only alphanumeric characters."
|
306
|
+
].join(' '))
|
307
|
+
end
|
308
|
+
# rubocop:enable Style/CaseEquality
|
309
|
+
UI.error("Could not download/upload from iTunes Connect! It's probably related to your password or your internet connection.")
|
310
|
+
end
|
311
|
+
end
|
230
312
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.41.
|
4
|
+
version: 0.41.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felix Krause
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -399,7 +399,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
399
399
|
version: '0'
|
400
400
|
requirements: []
|
401
401
|
rubyforge_project:
|
402
|
-
rubygems_version: 2.4.
|
402
|
+
rubygems_version: 2.4.5.1
|
403
403
|
signing_key:
|
404
404
|
specification_version: 4
|
405
405
|
summary: Contains all shared code/dependencies of the fastlane.tools
|