fastlane 2.241.0 → 2.241.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +37 -37
- data/deliver/lib/deliver/app_screenshot.rb +8 -0
- data/fastlane/lib/fastlane/commands_generator.rb +7 -1
- data/fastlane/lib/fastlane/server/socket_server.rb +57 -7
- data/fastlane/lib/fastlane/swift_lane_manager.rb +32 -11
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane/swift/ArgumentProcessor.swift +10 -2
- data/fastlane/swift/Deliverfile.swift +1 -1
- data/fastlane/swift/DeliverfileProtocol.swift +1 -1
- data/fastlane/swift/Fastlane.swift +2 -18
- data/fastlane/swift/Gymfile.swift +1 -1
- data/fastlane/swift/GymfileProtocol.swift +1 -1
- data/fastlane/swift/MainProcess.swift +27 -0
- data/fastlane/swift/Matchfile.swift +1 -1
- data/fastlane/swift/MatchfileProtocol.swift +1 -1
- data/fastlane/swift/Precheckfile.swift +1 -1
- data/fastlane/swift/PrecheckfileProtocol.swift +1 -1
- data/fastlane/swift/RubyCommand.swift +1 -9
- data/fastlane/swift/RubyCommandable.swift +3 -2
- data/fastlane/swift/Scanfile.swift +1 -1
- data/fastlane/swift/ScanfileProtocol.swift +1 -1
- data/fastlane/swift/Screengrabfile.swift +1 -1
- data/fastlane/swift/ScreengrabfileProtocol.swift +1 -1
- data/fastlane/swift/Snapshotfile.swift +1 -1
- data/fastlane/swift/SnapshotfileProtocol.swift +1 -1
- data/fastlane/swift/main.swift +1 -1
- data/fastlane_core/lib/fastlane_core/itunes_transporter.rb +19 -5
- data/match/lib/match/runner.rb +2 -0
- data/match/lib/match/storage/gitlab/secure_file.rb +2 -1
- data/match/lib/match/storage/google_cloud_storage.rb +1 -1
- data/match/lib/match/storage/interface.rb +10 -0
- data/match/lib/match/storage/s3_storage.rb +1 -1
- data/produce/lib/produce/developer_center.rb +2 -49
- data/produce/lib/produce/module.rb +0 -21
- data/produce/lib/produce/options.rb +0 -17
- data/produce/lib/produce/service.rb +4 -3
- data/sigh/lib/sigh/manager.rb +5 -1
- data/snapshot/lib/snapshot/reports_generator.rb +2 -0
- data/spaceship/lib/spaceship/client.rb +7 -1
- data/spaceship/lib/spaceship/connect_api/models/app_screenshot_set.rb +2 -0
- data/spaceship/lib/spaceship/connect_api/models/bundle_id.rb +2 -13
- data/spaceship/lib/spaceship/connect_api/models/bundle_id_capability.rb +0 -6
- data/spaceship/lib/spaceship/connect_api/provisioning/provisioning.rb +0 -16
- metadata +19 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de93b89428a3de2726024a5c924723c7fcb3f3096402884e4139749e8d22cb39
|
|
4
|
+
data.tar.gz: 25aa9615e76bb0f0e503ebf6cad2170178938f249027994b8b463ce45bd94403
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a3ae511471c7729e7854ac3aa91ea06d2ceb306e87b7e968aab9be77ce05d0ba6e3b127e3e720598380084686158c2b40b2e587cf78d402b5ec1a6aebcf4e42c
|
|
7
|
+
data.tar.gz: 84016a1687602afdde1ab12f6c6dacb1f22550c6ab3c3724e79638435dea908cdcf58735519523ba670074190caddb6bb4b0fb792c359b56b181751acb0347eb
|
data/README.md
CHANGED
|
@@ -36,17 +36,23 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
36
36
|
</a>
|
|
37
37
|
<h4 align='center'><a href='https://twitter.com/Sujan'>Jan Piotrowski</a></h4>
|
|
38
38
|
</td>
|
|
39
|
-
<td id='
|
|
40
|
-
<a href='https://github.com/
|
|
41
|
-
<img src='https://github.com/
|
|
39
|
+
<td id='connor-tumbleson'>
|
|
40
|
+
<a href='https://github.com/ibotpeaches'>
|
|
41
|
+
<img src='https://github.com/ibotpeaches.png?size=200' width='140'>
|
|
42
42
|
</a>
|
|
43
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
43
|
+
<h4 align='center'><a href='https://twitter.com/ibotpeaches'>Connor Tumbleson</a></h4>
|
|
44
44
|
</td>
|
|
45
|
-
<td id='
|
|
46
|
-
<a href='https://github.com/
|
|
47
|
-
<img src='https://github.com/
|
|
45
|
+
<td id='olivier-halligon'>
|
|
46
|
+
<a href='https://github.com/AliSoftware'>
|
|
47
|
+
<img src='https://github.com/AliSoftware.png?size=200' width='140'>
|
|
48
48
|
</a>
|
|
49
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
49
|
+
<h4 align='center'><a href='https://twitter.com/aligatr'>Olivier Halligon</a></h4>
|
|
50
|
+
</td>
|
|
51
|
+
<td id='felix-krause'>
|
|
52
|
+
<a href='https://github.com/KrauseFx'>
|
|
53
|
+
<img src='https://github.com/KrauseFx.png?size=200' width='140'>
|
|
54
|
+
</a>
|
|
55
|
+
<h4 align='center'><a href='https://twitter.com/KrauseFx'>Felix Krause</a></h4>
|
|
50
56
|
</td>
|
|
51
57
|
<td id='josh-holtz'>
|
|
52
58
|
<a href='https://github.com/joshdholtz'>
|
|
@@ -54,31 +60,25 @@ If the above doesn't help, please [submit an issue](https://github.com/fastlane/
|
|
|
54
60
|
</a>
|
|
55
61
|
<h4 align='center'><a href='https://twitter.com/joshdholtz'>Josh Holtz</a></h4>
|
|
56
62
|
</td>
|
|
57
|
-
<td id='stefan-natchev'>
|
|
58
|
-
<a href='https://github.com/snatchev'>
|
|
59
|
-
<img src='https://github.com/snatchev.png?size=200' width='140'>
|
|
60
|
-
</a>
|
|
61
|
-
<h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
|
|
62
|
-
</td>
|
|
63
63
|
</tr>
|
|
64
64
|
<tr>
|
|
65
|
-
<td id='
|
|
66
|
-
<a href='https://github.com/
|
|
67
|
-
<img src='https://github.com/
|
|
65
|
+
<td id='joshua-liebowitz'>
|
|
66
|
+
<a href='https://github.com/taquitos'>
|
|
67
|
+
<img src='https://github.com/taquitos.png?size=200' width='140'>
|
|
68
68
|
</a>
|
|
69
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
69
|
+
<h4 align='center'><a href='https://twitter.com/taquitos'>Joshua Liebowitz</a></h4>
|
|
70
70
|
</td>
|
|
71
|
-
<td id='
|
|
72
|
-
<a href='https://github.com/
|
|
73
|
-
<img src='https://github.com/
|
|
71
|
+
<td id='jérôme-lacoste'>
|
|
72
|
+
<a href='https://github.com/lacostej'>
|
|
73
|
+
<img src='https://github.com/lacostej.png?size=200' width='140'>
|
|
74
74
|
</a>
|
|
75
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
75
|
+
<h4 align='center'><a href='https://twitter.com/lacostej'>Jérôme Lacoste</a></h4>
|
|
76
76
|
</td>
|
|
77
|
-
<td id='
|
|
78
|
-
<a href='https://github.com/
|
|
79
|
-
<img src='https://github.com/
|
|
77
|
+
<td id='stefan-natchev'>
|
|
78
|
+
<a href='https://github.com/snatchev'>
|
|
79
|
+
<img src='https://github.com/snatchev.png?size=200' width='140'>
|
|
80
80
|
</a>
|
|
81
|
-
<h4 align='center'><a href='https://twitter.com/
|
|
81
|
+
<h4 align='center'><a href='https://twitter.com/snatchev'>Stefan Natchev</a></h4>
|
|
82
82
|
</td>
|
|
83
83
|
<td id='roger-oba'>
|
|
84
84
|
<a href='https://github.com/rogerluan'>
|
|
@@ -95,23 +95,23 @@ _fastlane_ wouldn't be what it is today without the countless hours these former
|
|
|
95
95
|
|
|
96
96
|
<p id='alumni'>
|
|
97
97
|
<a href='https://github.com/endocrimes'>Danielle Tomlinson</a>,
|
|
98
|
+
<a href='https://github.com/max-ott'>Max Ott</a>,
|
|
98
99
|
<a href='https://github.com/crazymanish'>Manish Rathi</a>,
|
|
100
|
+
<a href='https://github.com/lucgrabowski'>Łukasz Grabowski</a>,
|
|
101
|
+
<a href='https://github.com/mollyIV'>Daniel Jankowski</a>,
|
|
99
102
|
<a href='https://github.com/ainame'>Satoshi Namai</a>,
|
|
100
|
-
<a href='https://github.com/
|
|
103
|
+
<a href='https://github.com/getaaron'>Aaron Brager</a>,
|
|
104
|
+
<a href='https://github.com/jdee'>Jimmy Dee</a>,
|
|
105
|
+
<a href='https://github.com/revolter'>Iulian Onofrei</a>,
|
|
106
|
+
<a href='https://github.com/mgrebenets'>Maksym Grebenets</a>,
|
|
107
|
+
<a href='https://github.com/armcburney'>Andrew McBurney</a>,
|
|
101
108
|
<a href='https://github.com/lmirosevic'>Luka Mirosevic</a>,
|
|
109
|
+
<a href='https://github.com/milch'>Manu Wallner</a>,
|
|
102
110
|
<a href='https://github.com/minuscorp'>Jorge Revuelta H</a>,
|
|
103
|
-
<a href='https://github.com/mollyIV'>Daniel Jankowski</a>,
|
|
104
|
-
<a href='https://github.com/armcburney'>Andrew McBurney</a>,
|
|
105
|
-
<a href='https://github.com/max-ott'>Max Ott</a>,
|
|
106
|
-
<a href='https://github.com/lucgrabowski'>Łukasz Grabowski</a>,
|
|
107
111
|
<a href='https://github.com/matthewellis'>Matthew Ellis</a>,
|
|
108
|
-
<a href='https://github.com/jdee'>Jimmy Dee</a>,
|
|
109
|
-
<a href='https://github.com/nafu'>Fumiya Nakamura</a>,
|
|
110
112
|
<a href='https://github.com/hjanuschka'>Helmut Januschka</a>,
|
|
111
|
-
<a href='https://github.com/
|
|
112
|
-
<a href='https://github.com/
|
|
113
|
-
<a href='https://github.com/mgrebenets'>Maksym Grebenets</a>,
|
|
114
|
-
<a href='https://github.com/revolter'>Iulian Onofrei</a>
|
|
113
|
+
<a href='https://github.com/nafu'>Fumiya Nakamura</a>,
|
|
114
|
+
<a href='https://github.com/giginet'>Kohki Miki</a>
|
|
115
115
|
</p>
|
|
116
116
|
<!-- team:end -->
|
|
117
117
|
|
|
@@ -19,6 +19,7 @@ module Deliver
|
|
|
19
19
|
DisplayType::APP_IPHONE_61 => "iPhone 14 Pro",
|
|
20
20
|
DisplayType::APP_IPHONE_65 => "iPhone XS Max",
|
|
21
21
|
DisplayType::APP_IPHONE_67 => "iPhone 14 Pro Max",
|
|
22
|
+
DisplayType::APP_IPHONE_DUO => "iPhone Duo",
|
|
22
23
|
DisplayType::APP_IPAD_97 => "iPad",
|
|
23
24
|
DisplayType::APP_IPAD_105 => "iPad 10.5",
|
|
24
25
|
DisplayType::APP_IPAD_PRO_3GEN_11 => "iPad 11",
|
|
@@ -100,6 +101,13 @@ module Deliver
|
|
|
100
101
|
[960, 600],
|
|
101
102
|
[960, 640]
|
|
102
103
|
],
|
|
104
|
+
# iPhone Duo outer and inner displays
|
|
105
|
+
DisplayType::APP_IPHONE_DUO => [
|
|
106
|
+
[1398, 2034],
|
|
107
|
+
[2034, 1398],
|
|
108
|
+
[2007, 2853],
|
|
109
|
+
[2853, 2007]
|
|
110
|
+
],
|
|
103
111
|
DisplayType::APP_IPAD_97 => [
|
|
104
112
|
[1024, 748],
|
|
105
113
|
[1024, 768],
|
|
@@ -144,11 +144,16 @@ module Fastlane
|
|
|
144
144
|
c.option('-s', '--stay_alive', 'Keeps socket server up even after error or disconnects, requires CTRL-C to kill.')
|
|
145
145
|
c.option('-c seconds', '--connection_timeout', 'Sets connection established timeout')
|
|
146
146
|
c.option('-p port', '--port', "Sets the port on localhost for the socket connection")
|
|
147
|
+
c.option('--token_file path', "File holding the token every command must carry (default: FASTLANE_SOCKET_SERVER_TOKEN_FILE)")
|
|
147
148
|
c.action do |args, options|
|
|
148
149
|
default_connection_timeout = 5
|
|
149
150
|
stay_alive = options.stay_alive || false
|
|
150
151
|
connection_timeout = options.connection_timeout || default_connection_timeout
|
|
151
152
|
port = options.port || 2000
|
|
153
|
+
token_file = options.token_file || ENV["FASTLANE_SOCKET_SERVER_TOKEN_FILE"]
|
|
154
|
+
token = File.read(token_file).strip if token_file
|
|
155
|
+
UI.user_error!("The token file #{token_file} is empty") if token_file && token.empty?
|
|
156
|
+
UI.important("No --token_file given: any local process that connects first can run actions") unless token
|
|
152
157
|
|
|
153
158
|
if stay_alive && options.connection_timeout.nil?
|
|
154
159
|
UI.important("stay_alive is set, but the connection timeout is not, this will give you #{default_connection_timeout} seconds to (re)connect")
|
|
@@ -162,7 +167,8 @@ module Fastlane
|
|
|
162
167
|
command_executor: command_executor,
|
|
163
168
|
connection_timeout: connection_timeout,
|
|
164
169
|
stay_alive: stay_alive,
|
|
165
|
-
port: port
|
|
170
|
+
port: port,
|
|
171
|
+
token: token
|
|
166
172
|
)
|
|
167
173
|
result = server.start
|
|
168
174
|
UI.success("Result: #{result}") if result
|
|
@@ -3,6 +3,7 @@ require 'fastlane/server/command_parser.rb'
|
|
|
3
3
|
require 'fastlane/server/json_return_value_processor.rb'
|
|
4
4
|
require 'socket'
|
|
5
5
|
require 'json'
|
|
6
|
+
require 'openssl'
|
|
6
7
|
|
|
7
8
|
module Fastlane
|
|
8
9
|
class SocketServer
|
|
@@ -20,7 +21,8 @@ module Fastlane
|
|
|
20
21
|
return_value_processor: nil,
|
|
21
22
|
connection_timeout: 5,
|
|
22
23
|
stay_alive: false,
|
|
23
|
-
port: 2000
|
|
24
|
+
port: 2000,
|
|
25
|
+
token: nil
|
|
24
26
|
)
|
|
25
27
|
if return_value_processor.nil?
|
|
26
28
|
return_value_processor = JSONReturnValueProcessor.new
|
|
@@ -31,6 +33,8 @@ module Fastlane
|
|
|
31
33
|
@connection_timeout = connection_timeout.to_i
|
|
32
34
|
@stay_alive = stay_alive
|
|
33
35
|
@port = port.to_i
|
|
36
|
+
# When set, every command must carry it: the runner reads it from a file only this user can read
|
|
37
|
+
@token = token
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
# this is the public API, don't call anything else
|
|
@@ -78,6 +82,11 @@ module Fastlane
|
|
|
78
82
|
end
|
|
79
83
|
|
|
80
84
|
def parse_and_execute_command(command_string: nil)
|
|
85
|
+
unless token_matches?(command_string)
|
|
86
|
+
UI.error("Refusing a command without this run's token: it does not come from the FastlaneRunner fastlane started")
|
|
87
|
+
return COMMAND_EXECUTION_STATE[:error]
|
|
88
|
+
end
|
|
89
|
+
|
|
81
90
|
command = CommandParser.parse(json: command_string)
|
|
82
91
|
case command
|
|
83
92
|
when ControlCommand
|
|
@@ -137,15 +146,17 @@ module Fastlane
|
|
|
137
146
|
end
|
|
138
147
|
|
|
139
148
|
def listen
|
|
140
|
-
@
|
|
141
|
-
|
|
149
|
+
@servers = listening_sockets
|
|
150
|
+
@port = @servers.first.local_address.ip_port
|
|
151
|
+
UI.verbose("Waiting for #{@connection_timeout} seconds for a connection from FastlaneRunner on port #{@port}")
|
|
142
152
|
|
|
143
|
-
# set thread
|
|
153
|
+
# set thread locals so the caller can tell the runner where to connect
|
|
154
|
+
Thread.current[:port] = @port
|
|
144
155
|
Thread.current[:ready] = true
|
|
145
156
|
@client = nil
|
|
146
157
|
begin
|
|
147
158
|
Timeout.timeout(@connection_timeout) do
|
|
148
|
-
@client =
|
|
159
|
+
@client = accept_first_connection
|
|
149
160
|
end
|
|
150
161
|
rescue Timeout::Error
|
|
151
162
|
UI.user_error!("fastlane failed to receive a connection from the FastlaneRunner binary after #{@connection_timeout} seconds, shutting down")
|
|
@@ -166,8 +177,47 @@ module Fastlane
|
|
|
166
177
|
@client.close
|
|
167
178
|
@client = nil
|
|
168
179
|
|
|
169
|
-
|
|
170
|
-
|
|
180
|
+
close_servers
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# A socket on every address localhost resolves to. The runner connects to
|
|
184
|
+
# `localhost` and tries those addresses in turn, so another process listening
|
|
185
|
+
# on one this server did not take would receive its commands.
|
|
186
|
+
def listening_sockets
|
|
187
|
+
# Port 0: a free port, the same on every address
|
|
188
|
+
return Socket.tcp_server_sockets('localhost', 0) if @port.zero?
|
|
189
|
+
|
|
190
|
+
sockets = []
|
|
191
|
+
Addrinfo.getaddrinfo('localhost', @port, nil, :STREAM).uniq(&:ip_address).each do |address|
|
|
192
|
+
sockets << address.listen
|
|
193
|
+
rescue SystemCallError => e
|
|
194
|
+
sockets.each(&:close)
|
|
195
|
+
UI.user_error!("fastlane could not listen on #{address.inspect_sockaddr} for the FastlaneRunner binary: #{e.message}")
|
|
196
|
+
end
|
|
197
|
+
sockets
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# The first connection on any of the sockets. They are closed once it is
|
|
201
|
+
# accepted, so nothing else can connect while the runner is talking to us.
|
|
202
|
+
def accept_first_connection
|
|
203
|
+
ready, = IO.select(@servers)
|
|
204
|
+
ready.first.accept.first
|
|
205
|
+
ensure
|
|
206
|
+
close_servers
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def token_matches?(command_string)
|
|
210
|
+
return true if @token.nil?
|
|
211
|
+
|
|
212
|
+
token = JSON.parse(command_string)['token']
|
|
213
|
+
token.kind_of?(String) && OpenSSL.secure_compare(token, @token)
|
|
214
|
+
rescue JSON::ParserError, TypeError
|
|
215
|
+
false
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def close_servers
|
|
219
|
+
(@servers || []).each { |server| server.close unless server.closed? }
|
|
220
|
+
@servers = nil
|
|
171
221
|
end
|
|
172
222
|
|
|
173
223
|
# record fastlane action command and then execute it
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
require_relative 'lane_manager_base.rb'
|
|
2
2
|
require_relative 'swift_runner_upgrader.rb'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'tmpdir'
|
|
3
5
|
|
|
4
6
|
module Fastlane
|
|
5
7
|
class SwiftLaneManager < LaneManagerBase
|
|
6
8
|
# The runner protocol version this fastlane ships, see main.swift
|
|
7
|
-
RUNNER_PROTOCOL_VERSION =
|
|
9
|
+
RUNNER_PROTOCOL_VERSION = 2
|
|
8
10
|
RUNNER_PROTOCOL_VERSION_REGEX = /FastlaneRunnerProtocolVersion \[(\d+)\]/
|
|
9
11
|
|
|
10
12
|
# @param lane_name The name of the lane to execute
|
|
@@ -31,15 +33,25 @@ module Fastlane
|
|
|
31
33
|
|
|
32
34
|
self.ensure_runner_built!
|
|
33
35
|
self.warn_if_runner_outdated
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
# A secret only this user can read, which the runner sends back with every command
|
|
37
|
+
token_dir = Dir.mktmpdir('fastlane-runner')
|
|
38
|
+
token_file = File.join(token_dir, 'token')
|
|
39
|
+
token = SecureRandom.hex(32)
|
|
40
|
+
File.write(token_file, token, perm: 0o600)
|
|
41
|
+
token = nil unless self.runner_sends_token?
|
|
42
|
+
# Port 0 lets the server pick a free port, which no other process can take beforehand
|
|
43
|
+
socket_thread = self.start_socket_thread(port: swift_server_port || 0, token: token)
|
|
44
|
+
sleep(0.250) while socket_thread[:ready].nil? && socket_thread.alive?
|
|
45
|
+
# The thread ended without listening: join raises its error rather than waiting forever
|
|
46
|
+
socket_thread.join unless socket_thread[:ready]
|
|
37
47
|
# wait on socket_thread to be in ready state, then start the runner thread
|
|
38
|
-
self.cruise_swift_lane_in_thread(lane, parameters,
|
|
48
|
+
self.cruise_swift_lane_in_thread(lane, parameters, socket_thread[:port], token_file)
|
|
39
49
|
|
|
40
50
|
socket_thread.value
|
|
41
51
|
rescue Exception => ex # rubocop:disable Lint/RescueException
|
|
42
52
|
e = ex
|
|
53
|
+
ensure
|
|
54
|
+
FileUtils.remove_entry(token_dir) if token_dir
|
|
43
55
|
end
|
|
44
56
|
# If we have a thread exception, drop that in the exception
|
|
45
57
|
# won't ever have a situation where e is non-nil, and socket_thread[:exception] is also non-nil
|
|
@@ -84,14 +96,14 @@ module Fastlane
|
|
|
84
96
|
end
|
|
85
97
|
end
|
|
86
98
|
|
|
87
|
-
def self.cruise_swift_lane_in_thread(lane, parameters = nil, swift_server_port)
|
|
99
|
+
def self.cruise_swift_lane_in_thread(lane, parameters = nil, swift_server_port, token_file)
|
|
88
100
|
if parameters.nil?
|
|
89
101
|
parameters = {}
|
|
90
102
|
end
|
|
91
103
|
|
|
92
104
|
parameter_string = ""
|
|
93
105
|
parameters.each do |key, value|
|
|
94
|
-
parameter_string += " #{key} #{value}"
|
|
106
|
+
parameter_string += " #{key.to_s.shellescape} #{value.to_s.shellescape}"
|
|
95
107
|
end
|
|
96
108
|
|
|
97
109
|
if FastlaneCore::Globals.verbose?
|
|
@@ -99,13 +111,16 @@ module Fastlane
|
|
|
99
111
|
end
|
|
100
112
|
|
|
101
113
|
parameter_string += " swiftServerPort #{swift_server_port}"
|
|
114
|
+
parameter_string += " swiftServerTokenFile #{token_file.shellescape}"
|
|
115
|
+
|
|
116
|
+
command = "#{FastlaneCore::FastlaneFolder.swift_runner_path.shellescape} lane #{lane.to_s.shellescape}#{parameter_string}"
|
|
102
117
|
|
|
103
118
|
return Thread.new do
|
|
104
119
|
if FastlaneCore::Globals.verbose?
|
|
105
|
-
return_value = Actions.sh(
|
|
120
|
+
return_value = Actions.sh(command)
|
|
106
121
|
UI.message("runner output: ".yellow + return_value)
|
|
107
122
|
else
|
|
108
|
-
Actions.sh(
|
|
123
|
+
Actions.sh("#{command} > /dev/null")
|
|
109
124
|
end
|
|
110
125
|
end
|
|
111
126
|
end
|
|
@@ -223,13 +238,13 @@ module Fastlane
|
|
|
223
238
|
return project_modified
|
|
224
239
|
end
|
|
225
240
|
|
|
226
|
-
def self.start_socket_thread(port: nil)
|
|
241
|
+
def self.start_socket_thread(port: nil, token: nil)
|
|
227
242
|
require 'fastlane/server/socket_server'
|
|
228
243
|
require 'fastlane/server/socket_server_action_command_executor'
|
|
229
244
|
|
|
230
245
|
return Thread.new do
|
|
231
246
|
command_executor = SocketServerActionCommandExecutor.new
|
|
232
|
-
server = Fastlane::SocketServer.new(command_executor: command_executor, port: port)
|
|
247
|
+
server = Fastlane::SocketServer.new(command_executor: command_executor, port: port, token: token)
|
|
233
248
|
server.start
|
|
234
249
|
end
|
|
235
250
|
end
|
|
@@ -245,6 +260,12 @@ module Fastlane
|
|
|
245
260
|
UI.important("Your FastlaneRunner is outdated for this version of fastlane. Run a lane outside CI, without `disable_runner_upgrades`, to update it, then commit the changes.")
|
|
246
261
|
end
|
|
247
262
|
|
|
263
|
+
# Runners from protocol version 2 send the run's token with every command; older ones get warn_if_runner_outdated.
|
|
264
|
+
# Version 1 was released before the token existed.
|
|
265
|
+
def self.runner_sends_token?
|
|
266
|
+
runner_protocol_version >= 2
|
|
267
|
+
end
|
|
268
|
+
|
|
248
269
|
def self.ensure_runner_built!
|
|
249
270
|
UI.verbose("Checking for new user-provided tool configuration files")
|
|
250
271
|
# if self.link_user_configs_to_project returns true, that means we need to rebuild the runner
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module Fastlane
|
|
2
|
-
VERSION = '2.241.
|
|
2
|
+
VERSION = '2.241.2'.freeze
|
|
3
3
|
SUMMARY = "The easiest way to build and release mobile apps.".freeze
|
|
4
4
|
DESCRIPTION = "The easiest way to automate beta deployments and releases for your iOS and Android apps".freeze
|
|
5
5
|
MINIMUM_XCODE_RELEASE = "7.0".freeze
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
import Foundation
|
|
12
12
|
|
|
13
13
|
struct ArgumentProcessor {
|
|
14
|
+
/// The secret fastlane wrote to a file only this user can read: sent with every command so that
|
|
15
|
+
/// fastlane can tell this runner from another process that connected to its port
|
|
16
|
+
static var serverToken: String?
|
|
17
|
+
|
|
14
18
|
let args: [RunnerArgument]
|
|
15
19
|
let currentLane: String
|
|
16
20
|
let commandTimeout: Int
|
|
@@ -33,6 +37,10 @@ struct ArgumentProcessor {
|
|
|
33
37
|
|
|
34
38
|
port = UInt32(fastlaneArgsMinusLanes.first(where: { $0.name == "swiftServerPort" })?.value ?? "") ?? 2000
|
|
35
39
|
|
|
40
|
+
if let tokenFile = fastlaneArgsMinusLanes.first(where: { $0.name == "swiftServerTokenFile" })?.value {
|
|
41
|
+
ArgumentProcessor.serverToken = (try? String(contentsOfFile: tokenFile, encoding: .utf8))?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
// Configure logMode since we might need to use it before we finish parsing
|
|
37
45
|
if let logModeArg = potentialLogMode.first {
|
|
38
46
|
let logModeString = logModeArg.value
|
|
@@ -74,7 +82,7 @@ struct ArgumentProcessor {
|
|
|
74
82
|
func laneParameters() -> [String: String] {
|
|
75
83
|
let laneParametersArgs = args.filter { arg in
|
|
76
84
|
let lowercasedName = arg.name.lowercased()
|
|
77
|
-
return lowercasedName != "timeoutseconds" && lowercasedName != "lane" && lowercasedName != "logmode"
|
|
85
|
+
return lowercasedName != "timeoutseconds" && lowercasedName != "lane" && lowercasedName != "logmode" && lowercasedName != "swiftservertokenfile"
|
|
78
86
|
}
|
|
79
87
|
var laneParameters = [String: String]()
|
|
80
88
|
for arg in laneParametersArgs {
|
|
@@ -86,4 +94,4 @@ struct ArgumentProcessor {
|
|
|
86
94
|
|
|
87
95
|
// Please don't remove the lines below
|
|
88
96
|
// They are used to detect outdated files
|
|
89
|
-
// FastlaneRunnerAPIVersion [0.9.
|
|
97
|
+
// FastlaneRunnerAPIVersion [0.9.3]
|
|
@@ -3437,8 +3437,6 @@ public func createAppOnManagedPlayStore(jsonKey: OptionalConfigValue<String?> =
|
|
|
3437
3437
|
|
|
3438
3438
|
- parameters:
|
|
3439
3439
|
- username: Your Apple ID Username
|
|
3440
|
-
- apiKeyPath: Path to your App Store Connect API Key JSON file (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-json-file)
|
|
3441
|
-
- apiKey: Your App Store Connect API Key information (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-hash-option)
|
|
3442
3440
|
- appIdentifier: App Identifier (Bundle ID, e.g. com.krausefx.app)
|
|
3443
3441
|
- bundleIdentifierSuffix: App Identifier Suffix (Ignored if App Identifier does not end with .*)
|
|
3444
3442
|
- appName: App Name
|
|
@@ -3463,8 +3461,6 @@ public func createAppOnManagedPlayStore(jsonKey: OptionalConfigValue<String?> =
|
|
|
3463
3461
|
For more information about _produce_, visit its documentation page: [https://docs.fastlane.tools/actions/produce/](https://docs.fastlane.tools/actions/produce/).
|
|
3464
3462
|
*/
|
|
3465
3463
|
public func createAppOnline(username: String,
|
|
3466
|
-
apiKeyPath: OptionalConfigValue<String?> = .fastlaneDefault(nil),
|
|
3467
|
-
apiKey: OptionalConfigValue<[String: Any]?> = .fastlaneDefault(nil),
|
|
3468
3464
|
appIdentifier: String,
|
|
3469
3465
|
bundleIdentifierSuffix: OptionalConfigValue<String?> = .fastlaneDefault(nil),
|
|
3470
3466
|
appName: String,
|
|
@@ -3485,8 +3481,6 @@ public func createAppOnline(username: String,
|
|
|
3485
3481
|
itcTeamName: OptionalConfigValue<String?> = .fastlaneDefault(nil))
|
|
3486
3482
|
{
|
|
3487
3483
|
let usernameArg = RubyCommand.Argument(name: "username", value: username, type: nil)
|
|
3488
|
-
let apiKeyPathArg = apiKeyPath.asRubyArgument(name: "api_key_path", type: nil)
|
|
3489
|
-
let apiKeyArg = apiKey.asRubyArgument(name: "api_key", type: nil)
|
|
3490
3484
|
let appIdentifierArg = RubyCommand.Argument(name: "app_identifier", value: appIdentifier, type: nil)
|
|
3491
3485
|
let bundleIdentifierSuffixArg = bundleIdentifierSuffix.asRubyArgument(name: "bundle_identifier_suffix", type: nil)
|
|
3492
3486
|
let appNameArg = RubyCommand.Argument(name: "app_name", value: appName, type: nil)
|
|
@@ -3506,8 +3500,6 @@ public func createAppOnline(username: String,
|
|
|
3506
3500
|
let itcTeamIdArg = RubyCommand.Argument(name: "itc_team_id", value: itcTeamId, type: nil)
|
|
3507
3501
|
let itcTeamNameArg = itcTeamName.asRubyArgument(name: "itc_team_name", type: nil)
|
|
3508
3502
|
let array: [RubyCommand.Argument?] = [usernameArg,
|
|
3509
|
-
apiKeyPathArg,
|
|
3510
|
-
apiKeyArg,
|
|
3511
3503
|
appIdentifierArg,
|
|
3512
3504
|
bundleIdentifierSuffixArg,
|
|
3513
3505
|
appNameArg,
|
|
@@ -8437,8 +8429,6 @@ public func println(message: OptionalConfigValue<String?> = .fastlaneDefault(nil
|
|
|
8437
8429
|
|
|
8438
8430
|
- parameters:
|
|
8439
8431
|
- username: Your Apple ID Username
|
|
8440
|
-
- apiKeyPath: Path to your App Store Connect API Key JSON file (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-json-file)
|
|
8441
|
-
- apiKey: Your App Store Connect API Key information (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-hash-option)
|
|
8442
8432
|
- appIdentifier: App Identifier (Bundle ID, e.g. com.krausefx.app)
|
|
8443
8433
|
- bundleIdentifierSuffix: App Identifier Suffix (Ignored if App Identifier does not end with .*)
|
|
8444
8434
|
- appName: App Name
|
|
@@ -8463,8 +8453,6 @@ public func println(message: OptionalConfigValue<String?> = .fastlaneDefault(nil
|
|
|
8463
8453
|
For more information about _produce_, visit its documentation page: [https://docs.fastlane.tools/actions/produce/](https://docs.fastlane.tools/actions/produce/).
|
|
8464
8454
|
*/
|
|
8465
8455
|
public func produce(username: String,
|
|
8466
|
-
apiKeyPath: OptionalConfigValue<String?> = .fastlaneDefault(nil),
|
|
8467
|
-
apiKey: OptionalConfigValue<[String: Any]?> = .fastlaneDefault(nil),
|
|
8468
8456
|
appIdentifier: String,
|
|
8469
8457
|
bundleIdentifierSuffix: OptionalConfigValue<String?> = .fastlaneDefault(nil),
|
|
8470
8458
|
appName: String,
|
|
@@ -8485,8 +8473,6 @@ public func produce(username: String,
|
|
|
8485
8473
|
itcTeamName: OptionalConfigValue<String?> = .fastlaneDefault(nil))
|
|
8486
8474
|
{
|
|
8487
8475
|
let usernameArg = RubyCommand.Argument(name: "username", value: username, type: nil)
|
|
8488
|
-
let apiKeyPathArg = apiKeyPath.asRubyArgument(name: "api_key_path", type: nil)
|
|
8489
|
-
let apiKeyArg = apiKey.asRubyArgument(name: "api_key", type: nil)
|
|
8490
8476
|
let appIdentifierArg = RubyCommand.Argument(name: "app_identifier", value: appIdentifier, type: nil)
|
|
8491
8477
|
let bundleIdentifierSuffixArg = bundleIdentifierSuffix.asRubyArgument(name: "bundle_identifier_suffix", type: nil)
|
|
8492
8478
|
let appNameArg = RubyCommand.Argument(name: "app_name", value: appName, type: nil)
|
|
@@ -8506,8 +8492,6 @@ public func produce(username: String,
|
|
|
8506
8492
|
let itcTeamIdArg = RubyCommand.Argument(name: "itc_team_id", value: itcTeamId, type: nil)
|
|
8507
8493
|
let itcTeamNameArg = itcTeamName.asRubyArgument(name: "itc_team_name", type: nil)
|
|
8508
8494
|
let array: [RubyCommand.Argument?] = [usernameArg,
|
|
8509
|
-
apiKeyPathArg,
|
|
8510
|
-
apiKeyArg,
|
|
8511
8495
|
appIdentifierArg,
|
|
8512
8496
|
bundleIdentifierSuffixArg,
|
|
8513
8497
|
appNameArg,
|
|
@@ -12621,7 +12605,7 @@ public func updateProjectProvisioning(xcodeproj: OptionalConfigValue<String?> =
|
|
|
12621
12605
|
targetFilter: OptionalConfigValue<String?> = .fastlaneDefault(nil),
|
|
12622
12606
|
buildConfigurationFilter: OptionalConfigValue<String?> = .fastlaneDefault(nil),
|
|
12623
12607
|
buildConfiguration: OptionalConfigValue<String?> = .fastlaneDefault(nil),
|
|
12624
|
-
certificate: String = "/var/folders/
|
|
12608
|
+
certificate: String = "/var/folders/n6/hn37q6pj7sj_9dmqqc2113800000gn/T/AppleIncRootCertificate.cer",
|
|
12625
12609
|
codeSigningIdentity: OptionalConfigValue<String?> = .fastlaneDefault(nil))
|
|
12626
12610
|
{
|
|
12627
12611
|
let xcodeprojArg = xcodeproj.asRubyArgument(name: "xcodeproj", type: nil)
|
|
@@ -14261,4 +14245,4 @@ public let snapshotfile: Snapshotfile = .init()
|
|
|
14261
14245
|
|
|
14262
14246
|
// Please don't remove the lines below
|
|
14263
14247
|
// They are used to detect outdated files
|
|
14264
|
-
// FastlaneRunnerAPIVersion [0.9.
|
|
14248
|
+
// FastlaneRunnerAPIVersion [0.9.211]
|
|
@@ -42,12 +42,21 @@ class MainProcess {
|
|
|
42
42
|
killExistingSocketServerProcesses(port: argumentProcessor.port)
|
|
43
43
|
abortIfPortIsInUse(argumentProcessor.port)
|
|
44
44
|
|
|
45
|
+
let serverToken = createServerToken()
|
|
46
|
+
ArgumentProcessor.serverToken = serverToken.token
|
|
47
|
+
|
|
45
48
|
rubySocketCommand = Process()
|
|
46
49
|
rubySocketCommand.launchPath = "/usr/bin/env"
|
|
47
50
|
rubySocketCommand.arguments = fastlaneLaunchArguments() + ["socket_server", "-c", String(timeout), "-p", String(argumentProcessor.port)]
|
|
51
|
+
// An environment variable rather than --token_file, so that an older fastlane still starts
|
|
52
|
+
var environment = ProcessInfo.processInfo.environment
|
|
53
|
+
environment["FASTLANE_SOCKET_SERVER_TOKEN_FILE"] = serverToken.file
|
|
54
|
+
rubySocketCommand.environment = environment
|
|
48
55
|
rubySocketCommand.launch()
|
|
49
56
|
|
|
50
57
|
waitUntilSocketServerIsListening(port: argumentProcessor.port, serverProcess: rubySocketCommand)
|
|
58
|
+
// socket_server read the token when it started
|
|
59
|
+
try? FileManager.default.removeItem(atPath: serverToken.directory)
|
|
51
60
|
thread.start()
|
|
52
61
|
#endif
|
|
53
62
|
}
|
|
@@ -78,6 +87,24 @@ class MainProcess {
|
|
|
78
87
|
return ["bundle", "exec", "fastlane"]
|
|
79
88
|
}
|
|
80
89
|
|
|
90
|
+
/// A random token in a file only this user can read, which socket_server requires on every command and the runner sends
|
|
91
|
+
func createServerToken() -> (token: String, directory: String, file: String) {
|
|
92
|
+
var template = Array((NSTemporaryDirectory() as NSString).appendingPathComponent("fastlane-runner.XXXXXX").utf8CString)
|
|
93
|
+
guard let created = mkdtemp(&template), let random = FileHandle(forReadingAtPath: "/dev/urandom") else {
|
|
94
|
+
log(message: "Could not create the token for fastlane socket_server")
|
|
95
|
+
exit(1)
|
|
96
|
+
}
|
|
97
|
+
let directory = String(cString: created)
|
|
98
|
+
let token = random.readData(ofLength: 32).map { String(format: "%02x", $0) }.joined()
|
|
99
|
+
random.closeFile()
|
|
100
|
+
let file = directory + "/token"
|
|
101
|
+
guard token.count == 64, FileManager.default.createFile(atPath: file, contents: token.data(using: .utf8), attributes: [.posixPermissions: 0o600]) else {
|
|
102
|
+
log(message: "Could not create the token for fastlane socket_server")
|
|
103
|
+
exit(1)
|
|
104
|
+
}
|
|
105
|
+
return (token, directory, file)
|
|
106
|
+
}
|
|
107
|
+
|
|
81
108
|
/// The directory of the Gemfile Bundler would use: BUNDLE_GEMFILE, else the
|
|
82
109
|
/// nearest Gemfile or gems.rb from the working directory up.
|
|
83
110
|
func bundleRootDirectory() -> String? {
|
|
@@ -81,15 +81,7 @@ struct RubyCommand: RubyCommandable {
|
|
|
81
81
|
|
|
82
82
|
var closure: ((String) -> Void)? {
|
|
83
83
|
let callbacks = args.filter { ($0.type != nil) && $0.type == .stringClosure }
|
|
84
|
-
guard let callback = callbacks.first else {
|
|
85
|
-
return nil
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
guard let callbackArgValue = callback.value else {
|
|
89
|
-
return nil
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
guard let callbackClosure = callbackArgValue as? ((String) -> Void) else {
|
|
84
|
+
guard let callback = callbacks.first, let callbackArgValue = callback.value, let callbackClosure = callbackArgValue as? ((String) -> Void) else {
|
|
93
85
|
return nil
|
|
94
86
|
}
|
|
95
87
|
return callbackClosure
|
|
@@ -32,12 +32,13 @@ protocol RubyCommandable {
|
|
|
32
32
|
|
|
33
33
|
extension RubyCommandable {
|
|
34
34
|
var json: String {
|
|
35
|
+
let token = ArgumentProcessor.serverToken.map { ", \"token\": \"\($0)\"" } ?? ""
|
|
35
36
|
return """
|
|
36
|
-
{ "commandType": "\(type.token)", "command": \(commandJson) }
|
|
37
|
+
{ "commandType": "\(type.token)", "command": \(commandJson)\(token) }
|
|
37
38
|
"""
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// Please don't remove the lines below
|
|
42
43
|
// They are used to detect outdated files
|
|
43
|
-
// FastlaneRunnerAPIVersion [0.9.
|
|
44
|
+
// FastlaneRunnerAPIVersion [0.9.3]
|
data/fastlane/swift/main.swift
CHANGED
|
@@ -12,7 +12,7 @@ import Foundation
|
|
|
12
12
|
|
|
13
13
|
let argumentProcessor = ArgumentProcessor(args: CommandLine.arguments)
|
|
14
14
|
/// fastlane reads this from the built runner, to tell whether it is outdated. Increase it when the runner changes how it talks to fastlane.
|
|
15
|
-
let runnerProtocolVersion = "FastlaneRunnerProtocolVersion [
|
|
15
|
+
let runnerProtocolVersion = "FastlaneRunnerProtocolVersion [2]"
|
|
16
16
|
verbose(message: runnerProtocolVersion)
|
|
17
17
|
let timeout = argumentProcessor.commandTimeout
|
|
18
18
|
|
|
@@ -58,30 +58,44 @@ module FastlaneCore
|
|
|
58
58
|
# Runs preparations before executing any command from the executor.
|
|
59
59
|
#
|
|
60
60
|
# @param original_api_key [Hash] api key containing the issuer id and private key
|
|
61
|
-
# @return [Hash] copy of `api_key` which includes an extra `key_dir` with the location of the .p8 file on disk
|
|
61
|
+
# @return [Hash] copy of `api_key` which includes an extra `key_dir` with the location of the .p8 file on disk,
|
|
62
|
+
# and `cleanup_path`: what #cleanup removes afterwards
|
|
62
63
|
def prepare(original_api_key:)
|
|
63
64
|
return if original_api_key.nil?
|
|
64
65
|
# Create .p8 file from api_key and provide api key info which contains .p8 file path
|
|
65
66
|
api_key = original_api_key.dup
|
|
67
|
+
p8_name = "AuthKey_#{api_key[:key_id]}.p8"
|
|
66
68
|
if self.kind_of?(ShellScriptTransporterExecutor)
|
|
67
69
|
# as of Transporter v3.3.0, the app is unable to detect the private keys under the 'private_keys' folder in current directory
|
|
68
70
|
# so we must rely on the other search paths in the Home dir:
|
|
69
71
|
# https://help.apple.com/itc/transporteruserguide/en.lproj/static.html#itc803b7be80
|
|
70
72
|
private_keys_dir = File.join(Dir.home, ".appstoreconnect/private_keys")
|
|
71
73
|
unless Dir.exist?(private_keys_dir)
|
|
72
|
-
FileUtils.mkdir_p(private_keys_dir)
|
|
74
|
+
FileUtils.mkdir_p(private_keys_dir, mode: 0o700)
|
|
73
75
|
end
|
|
74
76
|
api_key[:key_dir] = private_keys_dir
|
|
77
|
+
# Keys already there belong to the user: only remove the one written below
|
|
78
|
+
p8_path = File.join(private_keys_dir, p8_name)
|
|
79
|
+
api_key[:cleanup_path] = p8_path unless File.exist?(p8_path)
|
|
75
80
|
else
|
|
76
81
|
api_key[:key_dir] = Dir.mktmpdir("deliver-")
|
|
82
|
+
api_key[:cleanup_path] = api_key[:key_dir]
|
|
77
83
|
end
|
|
78
84
|
# Specified p8 needs to be generated to call altool or iTMSTransporter
|
|
79
|
-
File.
|
|
85
|
+
p8_path = File.join(api_key[:key_dir], p8_name)
|
|
86
|
+
File.open(p8_path, "wb", 0o600) do |p8|
|
|
80
87
|
p8.write(api_key[:key])
|
|
81
88
|
end
|
|
89
|
+
File.chmod(0o600, p8_path)
|
|
82
90
|
api_key
|
|
83
91
|
end
|
|
84
92
|
|
|
93
|
+
# Removes what #prepare wrote: its temporary directory, or the key file it added to ~/.appstoreconnect/private_keys
|
|
94
|
+
def cleanup(api_key)
|
|
95
|
+
return if api_key.nil? || api_key[:cleanup_path].nil?
|
|
96
|
+
FileUtils.rm_rf(api_key[:cleanup_path])
|
|
97
|
+
end
|
|
98
|
+
|
|
85
99
|
def execute(command, hide_output)
|
|
86
100
|
if Helper.test?
|
|
87
101
|
yield(nil) if block_given?
|
|
@@ -869,7 +883,7 @@ module FastlaneCore
|
|
|
869
883
|
return upload(app_id, dir, package_path: package_path, asset_path: asset_path)
|
|
870
884
|
ensure
|
|
871
885
|
if use_api_key
|
|
872
|
-
|
|
886
|
+
@transporter_executor.cleanup(api_key)
|
|
873
887
|
end
|
|
874
888
|
end
|
|
875
889
|
|
|
@@ -967,7 +981,7 @@ module FastlaneCore
|
|
|
967
981
|
return provider_ids
|
|
968
982
|
ensure
|
|
969
983
|
if use_api_key
|
|
970
|
-
|
|
984
|
+
@transporter_executor.cleanup(api_key)
|
|
971
985
|
end
|
|
972
986
|
end
|
|
973
987
|
|
data/match/lib/match/runner.rb
CHANGED
|
@@ -387,6 +387,8 @@ module Match
|
|
|
387
387
|
|
|
388
388
|
if Helper.mac?
|
|
389
389
|
installed_profile = FastlaneCore::ProvisioningProfile.install(stored_profile_path, keychain_path)
|
|
390
|
+
else
|
|
391
|
+
UI.message("Skipping installation of provisioning profile as it would not work on this operating system.")
|
|
390
392
|
end
|
|
391
393
|
|
|
392
394
|
if params[:output_path]
|
|
@@ -2,6 +2,7 @@ require 'open-uri'
|
|
|
2
2
|
require 'ostruct'
|
|
3
3
|
|
|
4
4
|
require_relative '../../module'
|
|
5
|
+
require_relative '../interface'
|
|
5
6
|
|
|
6
7
|
module Match
|
|
7
8
|
module Storage
|
|
@@ -36,7 +37,7 @@ module Match
|
|
|
36
37
|
url = URI("#{file_url}/download")
|
|
37
38
|
|
|
38
39
|
begin
|
|
39
|
-
destination_file =
|
|
40
|
+
destination_file = Storage.download_path(working_directory, @file.name)
|
|
40
41
|
|
|
41
42
|
create_subfolders(working_directory)
|
|
42
43
|
File.open(destination_file, "wb") do |saved_file|
|
|
@@ -158,7 +158,7 @@ module Match
|
|
|
158
158
|
|
|
159
159
|
bucket.files.each do |current_file|
|
|
160
160
|
file_path = current_file.name # e.g. "N8X438SEU2/certs/distribution/XD9G7QCACF.cer"
|
|
161
|
-
download_path =
|
|
161
|
+
download_path = Storage.download_path(self.working_directory, file_path)
|
|
162
162
|
|
|
163
163
|
FileUtils.mkdir_p(File.expand_path("..", download_path))
|
|
164
164
|
UI.verbose("Downloading file from Google Cloud Storage '#{file_path}' on bucket #{self.bucket_name}")
|
|
@@ -2,6 +2,16 @@ require_relative '../module'
|
|
|
2
2
|
|
|
3
3
|
module Match
|
|
4
4
|
module Storage
|
|
5
|
+
# Where a file named `remote_name` by the storage is written in `working_directory`.
|
|
6
|
+
# Fails when the name would place it outside, e.g. with `..` segments.
|
|
7
|
+
def self.download_path(working_directory, remote_name)
|
|
8
|
+
root = File.expand_path(working_directory)
|
|
9
|
+
path = File.expand_path(File.join(root, remote_name))
|
|
10
|
+
return path if path.start_with?(root + File::SEPARATOR)
|
|
11
|
+
|
|
12
|
+
UI.user_error!("Refusing to download #{remote_name.inspect}: it would be written outside #{root}")
|
|
13
|
+
end
|
|
14
|
+
|
|
5
15
|
class Interface
|
|
6
16
|
MATCH_VERSION_FILE_NAME = "match_version.txt"
|
|
7
17
|
# The working directory in which we download all the profiles
|
|
@@ -114,7 +114,7 @@ module Match
|
|
|
114
114
|
file_path = strip_s3_object_prefix(object.key) # :s3_object_prefix:team_id/path/to/file
|
|
115
115
|
|
|
116
116
|
# strip s3_prefix from file_path
|
|
117
|
-
download_path =
|
|
117
|
+
download_path = Storage.download_path(self.working_directory, file_path)
|
|
118
118
|
|
|
119
119
|
FileUtils.mkdir_p(File.expand_path("..", download_path))
|
|
120
120
|
UI.verbose("Downloading file from S3 '#{file_path}' on bucket #{self.s3_bucket}")
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
require 'ostruct'
|
|
2
1
|
require 'spaceship'
|
|
3
2
|
require_relative 'module'
|
|
4
|
-
require_relative 'service'
|
|
5
3
|
|
|
6
4
|
module Produce
|
|
7
5
|
class DeveloperCenter
|
|
@@ -88,8 +86,6 @@ module Produce
|
|
|
88
86
|
end
|
|
89
87
|
|
|
90
88
|
def create_new_app
|
|
91
|
-
return create_new_app_via_connect_api if using_connect_api?
|
|
92
|
-
|
|
93
89
|
ENV["CREATED_NEW_APP_ID"] = Time.now.to_i.to_s
|
|
94
90
|
if app_exists?
|
|
95
91
|
UI.success("[DevCenter] App '#{Produce.config[:app_identifier]}' already exists, nothing to do on the Dev Center")
|
|
@@ -122,31 +118,6 @@ module Produce
|
|
|
122
118
|
return true
|
|
123
119
|
end
|
|
124
120
|
|
|
125
|
-
# App Store Connect API key path — used instead of Spaceship.login.
|
|
126
|
-
def create_new_app_via_connect_api
|
|
127
|
-
config_enabled_services = Produce.config[:enable_services] || Produce.config[:enabled_features]
|
|
128
|
-
|
|
129
|
-
existing = Spaceship::ConnectAPI::BundleId.find(app_identifier)
|
|
130
|
-
if existing
|
|
131
|
-
UI.success("[DevCenter] App '#{Produce.config[:app_identifier]}' already exists, nothing to do on the Dev Center")
|
|
132
|
-
return true
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
app_name = Produce.config[:app_name]
|
|
136
|
-
UI.message("Creating new app '#{app_name}' on the Apple Dev Center")
|
|
137
|
-
|
|
138
|
-
connect_api_platform = Spaceship::ConnectAPI::BundleIdPlatform.map(platform)
|
|
139
|
-
bundle_id = Spaceship::ConnectAPI::BundleId.create(name: app_name, platform: connect_api_platform, identifier: app_identifier)
|
|
140
|
-
|
|
141
|
-
UI.crash!("Something went wrong when creating the new app - it's not listed in the apps list") unless bundle_id
|
|
142
|
-
|
|
143
|
-
UI.message("Created app #{bundle_id.id}")
|
|
144
|
-
enable_inline_services(config_enabled_services) if config_enabled_services && !config_enabled_services.empty?
|
|
145
|
-
UI.success("Finished creating new app '#{app_name}' on the Dev Center")
|
|
146
|
-
|
|
147
|
-
return true
|
|
148
|
-
end
|
|
149
|
-
|
|
150
121
|
def enable_services
|
|
151
122
|
app_service = Spaceship.app_service
|
|
152
123
|
enabled_clean_options = {}
|
|
@@ -190,17 +161,6 @@ module Produce
|
|
|
190
161
|
|
|
191
162
|
private
|
|
192
163
|
|
|
193
|
-
# Delegates to Service#update instead of duplicating its ~40-capability mapping.
|
|
194
|
-
# Skips any capability explicitly marked "off" — meaningless on a brand-new app.
|
|
195
|
-
def enable_inline_services(config_enabled_services)
|
|
196
|
-
to_enable = config_enabled_services.reject { |_, v| v == SERVICE_OFF }
|
|
197
|
-
return if to_enable.empty?
|
|
198
|
-
|
|
199
|
-
options = OpenStruct.new(to_enable)
|
|
200
|
-
options.define_singleton_method(:__hash__) { to_enable }
|
|
201
|
-
Produce::Service.new.update(true, options)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
164
|
def platform
|
|
205
165
|
# This was added to support creation of multiple platforms
|
|
206
166
|
# Produce::ItunesConnect can take an array of platforms to create for App Store Connect
|
|
@@ -214,16 +174,9 @@ module Produce
|
|
|
214
174
|
Spaceship.app.find(app_identifier, mac: platform == "osx") != nil
|
|
215
175
|
end
|
|
216
176
|
|
|
217
|
-
def using_connect_api?
|
|
218
|
-
!connect_api_token.nil?
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
def connect_api_token
|
|
222
|
-
Spaceship::ConnectAPI::Token.from(hash: Produce.config[:api_key], filepath: Produce.config[:api_key_path])
|
|
223
|
-
end
|
|
224
|
-
|
|
225
177
|
def login
|
|
226
|
-
Produce.
|
|
178
|
+
Spaceship.login(Produce.config[:username], nil)
|
|
179
|
+
Spaceship.select_team
|
|
227
180
|
end
|
|
228
181
|
end
|
|
229
182
|
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'fastlane_core/helper'
|
|
2
|
-
require 'spaceship'
|
|
3
2
|
|
|
4
3
|
module Produce
|
|
5
4
|
class << self
|
|
@@ -12,24 +11,4 @@ module Produce
|
|
|
12
11
|
|
|
13
12
|
ENV['FASTLANE_TEAM_ID'] ||= ENV["PRODUCE_TEAM_ID"]
|
|
14
13
|
ENV['DELIVER_USER'] ||= ENV["PRODUCE_USERNAME"]
|
|
15
|
-
|
|
16
|
-
# Shared by DeveloperCenter#login and Service#bundle_id. Prefers a configured API key, falls
|
|
17
|
-
# back to a token already set by another lane (e.g. app_store_connect_api_key), then falls
|
|
18
|
-
# back to username-based session login.
|
|
19
|
-
def self.authenticate_connect_api!
|
|
20
|
-
if (token = Spaceship::ConnectAPI::Token.from(hash: Produce.config[:api_key], filepath: Produce.config[:api_key_path]))
|
|
21
|
-
UI.message("Authenticating with App Store Connect API Key")
|
|
22
|
-
|
|
23
|
-
# `current_team_id:` avoids a live, session-based team_id lookup elsewhere in
|
|
24
|
-
# ConnectAPI's provisioning client, which has no session to use here.
|
|
25
|
-
Spaceship::ConnectAPI.client = Spaceship::ConnectAPI::Client.new(token: token, current_team_id: Produce.config[:team_id])
|
|
26
|
-
elsif !Spaceship::ConnectAPI.token.nil?
|
|
27
|
-
UI.message("Using existing authorization token for App Store Connect API")
|
|
28
|
-
else
|
|
29
|
-
UI.message("Starting login with user '#{Produce.config[:username]}'")
|
|
30
|
-
Spaceship.login(Produce.config[:username], nil)
|
|
31
|
-
Spaceship.select_team
|
|
32
|
-
UI.message("Successfully logged in")
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
14
|
end
|
|
@@ -17,23 +17,6 @@ module Produce
|
|
|
17
17
|
code_gen_sensitive: true,
|
|
18
18
|
default_value: user,
|
|
19
19
|
default_value_dynamic: true),
|
|
20
|
-
|
|
21
|
-
# App Store Connect API
|
|
22
|
-
FastlaneCore::ConfigItem.new(key: :api_key_path,
|
|
23
|
-
env_names: ["PRODUCE_API_KEY_PATH", "APP_STORE_CONNECT_API_KEY_PATH"],
|
|
24
|
-
description: "Path to your App Store Connect API Key JSON file (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-json-file)",
|
|
25
|
-
optional: true,
|
|
26
|
-
conflicting_options: [:api_key],
|
|
27
|
-
verify_block: proc do |value|
|
|
28
|
-
UI.user_error!("Couldn't find API key JSON file at path '#{value}'") unless File.exist?(value)
|
|
29
|
-
end),
|
|
30
|
-
FastlaneCore::ConfigItem.new(key: :api_key,
|
|
31
|
-
env_names: ["PRODUCE_API_KEY", "APP_STORE_CONNECT_API_KEY"],
|
|
32
|
-
description: "Your App Store Connect API Key information (https://docs.fastlane.tools/app-store-connect-api/#using-fastlane-api-key-hash-option)",
|
|
33
|
-
type: Hash,
|
|
34
|
-
optional: true,
|
|
35
|
-
sensitive: true,
|
|
36
|
-
conflicting_options: [:api_key_path]),
|
|
37
20
|
FastlaneCore::ConfigItem.new(key: :app_identifier,
|
|
38
21
|
env_name: "PRODUCE_APP_IDENTIFIER",
|
|
39
22
|
short_option: "-a",
|
|
@@ -419,9 +419,10 @@ module Produce
|
|
|
419
419
|
|
|
420
420
|
def bundle_id
|
|
421
421
|
return @bundle_id if @bundle_id
|
|
422
|
-
|
|
423
|
-
Produce.
|
|
424
|
-
|
|
422
|
+
UI.message("Starting login with user '#{Produce.config[:username]}'")
|
|
423
|
+
Spaceship.login(Produce.config[:username], nil)
|
|
424
|
+
Spaceship.select_team
|
|
425
|
+
UI.message("Successfully logged in")
|
|
425
426
|
@bundle_id ||= Spaceship::ConnectAPI::BundleId.find(Produce.config[:app_identifier].to_s)
|
|
426
427
|
end
|
|
427
428
|
end
|
data/sigh/lib/sigh/manager.rb
CHANGED
|
@@ -41,7 +41,11 @@ module Sigh
|
|
|
41
41
|
ENV["SIGH_UDID"] = ENV["SIGH_UUID"] = uuid if uuid
|
|
42
42
|
ENV["SIGH_NAME"] = name if name
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
if Helper.mac?
|
|
45
|
+
FastlaneCore::ProvisioningProfile.install(profile, keychain_path)
|
|
46
|
+
else
|
|
47
|
+
UI.message("Skipping installation of provisioning profile as it would not work on this operating system.")
|
|
48
|
+
end
|
|
45
49
|
end
|
|
46
50
|
end
|
|
47
51
|
end
|
|
@@ -84,6 +84,8 @@ module Snapshot
|
|
|
84
84
|
{
|
|
85
85
|
# snapshot in Xcode 9 saves screenshots with the SIMULATOR_DEVICE_NAME
|
|
86
86
|
# which includes spaces
|
|
87
|
+
'iPhone 18 Pro Max' => "iPhone 18 Pro Max",
|
|
88
|
+
'iPhone 18 Pro' => "iPhone 18 Pro",
|
|
87
89
|
'iPhone 17 Pro Max' => "iPhone 17 Pro Max",
|
|
88
90
|
'iPhone 17 Pro' => "iPhone 17 Pro",
|
|
89
91
|
'iPhone 17' => "iPhone 17",
|
|
@@ -252,6 +252,8 @@ module Spaceship
|
|
|
252
252
|
else
|
|
253
253
|
# Log to file by default
|
|
254
254
|
path = File.join(Dir.tmpdir, "spaceship#{Time.now.to_i}_#{Process.pid}_#{Thread.current.object_id}.log")
|
|
255
|
+
# The log holds every request and response body: create it readable by its owner only
|
|
256
|
+
File.open(path, File::WRONLY | File::CREAT | File::APPEND, 0o600) {}
|
|
255
257
|
@logger = Logger.new(path)
|
|
256
258
|
end
|
|
257
259
|
|
|
@@ -279,10 +281,12 @@ module Spaceship
|
|
|
279
281
|
def store_cookie(path: nil)
|
|
280
282
|
path ||= persistent_cookie_path
|
|
281
283
|
FileUtils.mkdir_p(File.expand_path("..", path))
|
|
284
|
+
File.chmod(0o700, File.expand_path("..", path))
|
|
282
285
|
|
|
283
286
|
# really important to specify the session to true
|
|
284
287
|
# otherwise myacinfo and more won't be stored
|
|
285
288
|
@cookie.save(path, :yaml, session: true)
|
|
289
|
+
File.chmod(0o600, path)
|
|
286
290
|
return File.read(path)
|
|
287
291
|
end
|
|
288
292
|
|
|
@@ -880,6 +884,8 @@ module Spaceship
|
|
|
880
884
|
begin
|
|
881
885
|
if File.exist?(persistent_cookie_path)
|
|
882
886
|
puts("Loading session from '#{persistent_cookie_path}'") if Spaceship::Globals.verbose?
|
|
887
|
+
File.chmod(0o700, File.expand_path("..", persistent_cookie_path))
|
|
888
|
+
File.chmod(0o600, persistent_cookie_path)
|
|
883
889
|
@cookie.load(persistent_cookie_path)
|
|
884
890
|
return true
|
|
885
891
|
end
|
|
@@ -1144,7 +1150,7 @@ module Spaceship
|
|
|
1144
1150
|
|
|
1145
1151
|
def log_response(method, url, response, headers = nil, &block)
|
|
1146
1152
|
url ||= extract_key_from_block('url', &block)
|
|
1147
|
-
body = response.body.kind_of?(String) ? response.body.force_encoding(Encoding::UTF_8) : response.body
|
|
1153
|
+
body = response.body.kind_of?(String) ? response.body.dup.force_encoding(Encoding::UTF_8) : response.body
|
|
1148
1154
|
logger.debug("<< #{method.upcase} #{url}: #{response.status} #{body}")
|
|
1149
1155
|
end
|
|
1150
1156
|
|
|
@@ -19,6 +19,7 @@ module Spaceship
|
|
|
19
19
|
APP_IPHONE_61 = "APP_IPHONE_61"
|
|
20
20
|
APP_IPHONE_65 = "APP_IPHONE_65"
|
|
21
21
|
APP_IPHONE_67 = "APP_IPHONE_67"
|
|
22
|
+
APP_IPHONE_DUO = "APP_IPHONE_DUO"
|
|
22
23
|
|
|
23
24
|
APP_IPAD_97 = "APP_IPAD_97"
|
|
24
25
|
APP_IPAD_105 = "APP_IPAD_105"
|
|
@@ -77,6 +78,7 @@ module Spaceship
|
|
|
77
78
|
APP_IPHONE_61,
|
|
78
79
|
APP_IPHONE_65,
|
|
79
80
|
APP_IPHONE_67,
|
|
81
|
+
APP_IPHONE_DUO,
|
|
80
82
|
|
|
81
83
|
APP_IPAD_97,
|
|
82
84
|
APP_IPAD_105,
|
|
@@ -81,23 +81,12 @@ module Spaceship
|
|
|
81
81
|
return resp.to_models.first
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
# `patch_bundle_id_capability` patches the wrong resource (bundleIds, with
|
|
85
|
-
# attributes nested inside a relationship's `data` entry) — invalid JSON:API,
|
|
86
|
-
# rejected by Apple. This uses the real per-capability endpoints instead:
|
|
87
|
-
# create to enable, PATCH to update settings, delete to disable.
|
|
88
84
|
def update_capability(capability_type, enabled: false, settings: [], client: nil)
|
|
89
85
|
raise "capability_type is required " if capability_type.nil?
|
|
90
86
|
|
|
91
87
|
client ||= Spaceship::ConnectAPI
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if enabled
|
|
95
|
-
return create_capability(capability_type, settings: settings, client: client) unless existing
|
|
96
|
-
return existing.update!(client: client, settings: settings)
|
|
97
|
-
else
|
|
98
|
-
existing&.delete!(client: client)
|
|
99
|
-
return nil
|
|
100
|
-
end
|
|
88
|
+
resp = client.patch_bundle_id_capability(bundle_id_id: id, seed_id: seed_id, enabled: enabled, capability_type: capability_type, settings: settings)
|
|
89
|
+
return resp.to_models.first
|
|
101
90
|
end
|
|
102
91
|
end
|
|
103
92
|
end
|
|
@@ -133,12 +133,6 @@ module Spaceship
|
|
|
133
133
|
client ||= Spaceship::ConnectAPI
|
|
134
134
|
client.delete_bundle_id_capability(bundle_id_capability_id: id)
|
|
135
135
|
end
|
|
136
|
-
|
|
137
|
-
def update!(client: nil, enabled: true, settings: [])
|
|
138
|
-
client ||= Spaceship::ConnectAPI
|
|
139
|
-
resp = client.patch_bundle_id_capability_configuration(bundle_id_capability_id: id, enabled: enabled, settings: settings)
|
|
140
|
-
return resp.to_models.first
|
|
141
|
-
end
|
|
142
136
|
end
|
|
143
137
|
end
|
|
144
138
|
end
|
|
@@ -89,22 +89,6 @@ module Spaceship
|
|
|
89
89
|
provisioning_request_client.post("#{Version::V1}/bundleIdCapabilities", body)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
# Modify a Capability Configuration — patches the bundleIdCapability resource
|
|
93
|
-
# directly (attributes at the top level), not the bundleId it belongs to.
|
|
94
|
-
def patch_bundle_id_capability_configuration(bundle_id_capability_id:, enabled: false, settings: [])
|
|
95
|
-
body = {
|
|
96
|
-
data: {
|
|
97
|
-
type: "bundleIdCapabilities",
|
|
98
|
-
id: bundle_id_capability_id,
|
|
99
|
-
attributes: {
|
|
100
|
-
enabled: enabled,
|
|
101
|
-
settings: settings
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
provisioning_request_client.patch("#{Version::V1}/bundleIdCapabilities/#{bundle_id_capability_id}", body)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
92
|
def patch_bundle_id_capability(bundle_id_id:, seed_id:, enabled: false, capability_type:, settings: [])
|
|
109
93
|
body = {
|
|
110
94
|
data: {
|
metadata
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fastlane
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.241.
|
|
4
|
+
version: 2.241.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
8
|
-
- Manu Wallner
|
|
9
|
-
- Jan Piotrowski
|
|
10
|
-
- Fumiya Nakamura
|
|
11
|
-
- Satoshi Namai
|
|
7
|
+
- Josh Holtz
|
|
12
8
|
- Jimmy Dee
|
|
13
|
-
-
|
|
9
|
+
- Joshua Liebowitz
|
|
14
10
|
- Max Ott
|
|
15
|
-
- Łukasz Grabowski
|
|
16
|
-
- Iulian Onofrei
|
|
17
11
|
- Olivier Halligon
|
|
18
|
-
- Jérôme Lacoste
|
|
19
12
|
- Helmut Januschka
|
|
20
|
-
-
|
|
21
|
-
-
|
|
13
|
+
- Satoshi Namai
|
|
14
|
+
- Danielle Tomlinson
|
|
15
|
+
- Manish Rathi
|
|
16
|
+
- Iulian Onofrei
|
|
17
|
+
- Kohki Miki
|
|
22
18
|
- Jorge Revuelta H
|
|
23
|
-
-
|
|
24
|
-
- Josh Holtz
|
|
19
|
+
- Andrew McBurney
|
|
25
20
|
- Connor Tumbleson
|
|
21
|
+
- Jan Piotrowski
|
|
22
|
+
- Maksym Grebenets
|
|
23
|
+
- Manu Wallner
|
|
26
24
|
- Stefan Natchev
|
|
27
|
-
-
|
|
28
|
-
-
|
|
25
|
+
- Łukasz Grabowski
|
|
26
|
+
- Matthew Ellis
|
|
29
27
|
- Roger Oba
|
|
30
|
-
-
|
|
28
|
+
- Luka Mirosevic
|
|
29
|
+
- Aaron Brager
|
|
30
|
+
- Fumiya Nakamura
|
|
31
|
+
- Jérôme Lacoste
|
|
32
|
+
- Daniel Jankowski
|
|
31
33
|
- Felix Krause
|
|
32
|
-
- Joshua Liebowitz
|
|
33
|
-
- Danielle Tomlinson
|
|
34
34
|
bindir: bin
|
|
35
35
|
cert_chain: []
|
|
36
36
|
date: 1980-01-02 00:00:00.000000000 Z
|