fastlane-plugin-match_keystore 0.1.9 → 0.1.10
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1e26b0b531512693d08eedc46159379bded6984aa802a32f6d6296d94a0116c3
|
|
4
|
+
data.tar.gz: ae4ba30e68e58c3c85bfa15a6a57ec0ab93fd98fc90bf23c597124e57235d4c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8c5983fd39c9d8d92b9981d153e59e21121eb992bb52f04d882b99918e199d1a6edf96588a8a905d3f17b26856a3aa66da7f8f63a587be4d969c29370fbbc3a5
|
|
7
|
+
data.tar.gz: 961c71d337aed1417bf10cbe1b682974fb2c262a830ff04efa99f9a063f32c7ad15345b5a8344a170cded7bdd53eb1ae56839a43e3f571b59a273f385feeec18
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require 'fastlane/action'
|
|
2
2
|
require 'fileutils'
|
|
3
3
|
require 'os'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'digest'
|
|
4
6
|
require_relative '../helper/match_keystore_helper'
|
|
5
7
|
|
|
6
8
|
module Fastlane
|
|
@@ -13,6 +15,17 @@ module Fastlane
|
|
|
13
15
|
|
|
14
16
|
class MatchKeystoreAction < Action
|
|
15
17
|
|
|
18
|
+
def self.to_md5(value)
|
|
19
|
+
hash_value = Digest::MD5.hexdigest value
|
|
20
|
+
hash_value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.load_json(json_path)
|
|
24
|
+
file = File.read(json_path)
|
|
25
|
+
data_hash = JSON.parse(file)
|
|
26
|
+
data_hash
|
|
27
|
+
end
|
|
28
|
+
|
|
16
29
|
def self.load_properties(properties_filename)
|
|
17
30
|
properties = {}
|
|
18
31
|
File.open(properties_filename, 'r') do |properties_file|
|
|
@@ -88,12 +101,14 @@ module Fastlane
|
|
|
88
101
|
else
|
|
89
102
|
apk_path_aligned = apk_path
|
|
90
103
|
end
|
|
104
|
+
apk_path_signed = apk_path.gsub(".apk", "-signed.apk")
|
|
105
|
+
apk_path_signed = apk_path_signed.gsub("unsigned", "")
|
|
106
|
+
apk_path_signed = apk_path_signed.gsub("--", "-")
|
|
91
107
|
|
|
92
108
|
# https://developer.android.com/studio/command-line/apksigner
|
|
93
|
-
apk_path_signed = apk_path.gsub(".apk", "-signed.apk")
|
|
94
109
|
`rm -f #{apk_path_signed}`
|
|
95
|
-
`#{build_tools_path}apksigner sign --ks #{keystore_path} --ks-key-alias '#{alias_name}' --ks-pass pass:'#{
|
|
96
|
-
|
|
110
|
+
`#{build_tools_path}apksigner sign --ks #{keystore_path} --ks-key-alias '#{alias_name}' --ks-pass pass:'#{key_password}' --key-pass pass:'#{alias_password}' --v1-signing-enabled true --v2-signing-enabled true --out #{apk_path_signed} #{apk_path_aligned}`
|
|
111
|
+
|
|
97
112
|
`#{build_tools_path}apksigner verify #{apk_path_signed}`
|
|
98
113
|
`rm -f #{apk_path_aligned}`
|
|
99
114
|
|
|
@@ -107,6 +122,11 @@ module Fastlane
|
|
|
107
122
|
|
|
108
123
|
def self.resolve_apk_path(apk_path)
|
|
109
124
|
|
|
125
|
+
# Set default APK path if not set:
|
|
126
|
+
if apk_path.to_s.strip.empty?
|
|
127
|
+
apk_path = '/app/build/outputs/apk/'
|
|
128
|
+
end
|
|
129
|
+
|
|
110
130
|
if !apk_path.to_s.end_with?(".apk")
|
|
111
131
|
|
|
112
132
|
if !File.directory?(apk_path)
|
|
@@ -134,15 +154,28 @@ module Fastlane
|
|
|
134
154
|
apk_path
|
|
135
155
|
end
|
|
136
156
|
|
|
157
|
+
def self.prompt2(params)
|
|
158
|
+
# UI.message("prompt2: #{params[:value]}")
|
|
159
|
+
if params[:value].to_s.empty?
|
|
160
|
+
return_value = other_action.prompt(text: params[:text], secure_text: params[:secure_text], ci_input: params[:ci_input])
|
|
161
|
+
else
|
|
162
|
+
return_value = params[:value]
|
|
163
|
+
end
|
|
164
|
+
return_value
|
|
165
|
+
end
|
|
166
|
+
|
|
137
167
|
def self.run(params)
|
|
138
168
|
|
|
169
|
+
# Get input parameters:
|
|
139
170
|
git_url = params[:git_url]
|
|
140
171
|
package_name = params[:package_name]
|
|
141
172
|
apk_path = params[:apk_path]
|
|
142
173
|
existing_keystore = params[:existing_keystore]
|
|
143
|
-
|
|
174
|
+
match_secret = params[:match_secret]
|
|
144
175
|
override_keystore = params[:override_keystore]
|
|
176
|
+
keystore_data = params[:keystore_data]
|
|
145
177
|
|
|
178
|
+
# Init constants:
|
|
146
179
|
keystore_name = 'keystore.jks'
|
|
147
180
|
properties_name = 'keystore.properties'
|
|
148
181
|
keystore_info_name = 'keystore.txt'
|
|
@@ -158,35 +191,43 @@ module Fastlane
|
|
|
158
191
|
# Check OpenSSL:
|
|
159
192
|
self.check_openssl_version
|
|
160
193
|
|
|
194
|
+
# Init workign local directory:
|
|
161
195
|
dir_name = ENV['HOME'] + '/.match_keystore'
|
|
162
196
|
unless File.directory?(dir_name)
|
|
163
197
|
UI.message("Creating '.match_keystore' working directory...")
|
|
164
198
|
FileUtils.mkdir_p(dir_name)
|
|
165
199
|
end
|
|
166
200
|
|
|
167
|
-
|
|
201
|
+
# Init 'security password' for AES encryption:
|
|
202
|
+
key_name = "#{self.to_md5(git_url)}.hex"
|
|
203
|
+
key_path = File.join(dir_name, key_name)
|
|
204
|
+
# UI.message(key_path)
|
|
168
205
|
if !File.file?(key_path)
|
|
169
|
-
security_password =
|
|
206
|
+
security_password = self.prompt2(text: "Security password: ", secure_text: true, value: match_secret)
|
|
170
207
|
if security_password.to_s.strip.empty?
|
|
171
|
-
raise "Security password is not defined! Please use '
|
|
208
|
+
raise "Security password is not defined! Please use 'match_secret' parameter for CI."
|
|
172
209
|
end
|
|
173
|
-
UI.message "Generating security key..."
|
|
210
|
+
UI.message "Generating security key '#{key_name}'..."
|
|
174
211
|
self.gen_key(key_path, security_password)
|
|
175
212
|
end
|
|
176
213
|
|
|
214
|
+
# Check is 'security password' is well initialized:
|
|
177
215
|
tmpkey = self.get_file_content(key_path).strip
|
|
178
216
|
if tmpkey.length == 128
|
|
179
|
-
UI.message "Security key initialized"
|
|
217
|
+
UI.message "Security key '#{key_name}' initialized"
|
|
180
218
|
else
|
|
181
|
-
raise "The security key
|
|
219
|
+
raise "The security key '#{key_name}' is malformed, or not initialized!"
|
|
182
220
|
end
|
|
183
221
|
|
|
184
|
-
|
|
222
|
+
# Create repo directory to sync remote Keystores repository:
|
|
223
|
+
repo_dir = File.join(dir_name, self.to_md5(git_url))
|
|
224
|
+
# UI.message(repo_dir)
|
|
185
225
|
unless File.directory?(repo_dir)
|
|
186
226
|
UI.message("Creating 'repo' directory...")
|
|
187
227
|
FileUtils.mkdir_p(repo_dir)
|
|
188
228
|
end
|
|
189
229
|
|
|
230
|
+
# Cloning GIT remote repository:
|
|
190
231
|
gitDir = repo_dir + '/.git'
|
|
191
232
|
unless File.directory?(gitDir)
|
|
192
233
|
UI.message("Cloning remote Keystores repository...")
|
|
@@ -195,6 +236,10 @@ module Fastlane
|
|
|
195
236
|
puts ''
|
|
196
237
|
end
|
|
197
238
|
|
|
239
|
+
# Create sub-directory for Android app:
|
|
240
|
+
if package_name.to_s.strip.empty?
|
|
241
|
+
raise "Package name is not defined!"
|
|
242
|
+
end
|
|
198
243
|
keystoreAppDir = repo_dir + '/' + package_name
|
|
199
244
|
unless File.directory?(keystoreAppDir)
|
|
200
245
|
UI.message("Creating '#{package_name}' keystore directory...")
|
|
@@ -205,6 +250,20 @@ module Fastlane
|
|
|
205
250
|
properties_path = keystoreAppDir + '/' + properties_name
|
|
206
251
|
properties_encrypt_path = keystoreAppDir + '/' + properties_encrypt_name
|
|
207
252
|
|
|
253
|
+
# Load parameters from JSON for CI or Unit Tests:
|
|
254
|
+
if keystore_data != nil && File.file?(keystore_data)
|
|
255
|
+
data_json = self.load_json(keystore_data)
|
|
256
|
+
data_key_password = data_json['key_password']
|
|
257
|
+
data_alias_name = data_json['alias_name']
|
|
258
|
+
data_alias_password = data_json['alias_password']
|
|
259
|
+
data_full_name = data_json['full_name']
|
|
260
|
+
data_org_unit = data_json['org_unit']
|
|
261
|
+
data_org = data_json['org']
|
|
262
|
+
data_city_locality = data_json['city_locality']
|
|
263
|
+
data_state_province = data_json['state_province']
|
|
264
|
+
data_country = data_json['country']
|
|
265
|
+
end
|
|
266
|
+
|
|
208
267
|
# Create keystore with command
|
|
209
268
|
override_keystore = !existing_keystore.to_s.strip.empty? && File.file?(existing_keystore)
|
|
210
269
|
if !File.file?(keystore_path) || override_keystore
|
|
@@ -213,15 +272,15 @@ module Fastlane
|
|
|
213
272
|
FileUtils.remove_dir(keystore_path)
|
|
214
273
|
end
|
|
215
274
|
|
|
216
|
-
key_password =
|
|
275
|
+
key_password = self.prompt2(text: "Keystore Password: ", value: data_key_password)
|
|
217
276
|
if key_password.to_s.strip.empty?
|
|
218
277
|
raise "Keystore Password is not definined!"
|
|
219
278
|
end
|
|
220
|
-
alias_name =
|
|
279
|
+
alias_name = self.prompt2(text: "Keystore Alias name: ", value: data_alias_name)
|
|
221
280
|
if alias_name.to_s.strip.empty?
|
|
222
281
|
raise "Keystore Alias name is not definined!"
|
|
223
282
|
end
|
|
224
|
-
alias_password =
|
|
283
|
+
alias_password = self.prompt2(text: "Keystore Alias password: ", value: data_alias_password)
|
|
225
284
|
if alias_password.to_s.strip.empty?
|
|
226
285
|
raise "Keystore Alias password is not definined!"
|
|
227
286
|
end
|
|
@@ -230,12 +289,12 @@ module Fastlane
|
|
|
230
289
|
if !File.file?(existing_keystore)
|
|
231
290
|
UI.message("Generating Android Keystore...")
|
|
232
291
|
|
|
233
|
-
full_name =
|
|
234
|
-
org_unit =
|
|
235
|
-
org =
|
|
236
|
-
city_locality =
|
|
237
|
-
state_province =
|
|
238
|
-
country =
|
|
292
|
+
full_name = self.prompt2(text: "Certificate First and Last Name: ", value: data_full_name)
|
|
293
|
+
org_unit = self.prompt2(text: "Certificate Organisation Unit: ", value: data_org_unit)
|
|
294
|
+
org = self.prompt2(text: "Certificate Organisation: ", value: data_org)
|
|
295
|
+
city_locality = self.prompt2(text: "Certificate City or Locality: ", value: data_city_locality)
|
|
296
|
+
state_province = self.prompt2(text: "Certificate State or Province: ", value: data_state_province)
|
|
297
|
+
country = self.prompt2(text: "Certificate Country Code (XX): ", value: data_country)
|
|
239
298
|
|
|
240
299
|
keytool_parts = [
|
|
241
300
|
"keytool -genkey -v",
|
|
@@ -272,14 +331,16 @@ module Fastlane
|
|
|
272
331
|
|
|
273
332
|
# Print Keystore data in repo:
|
|
274
333
|
keystore_info_path = keystoreAppDir + '/' + keystore_info_name
|
|
275
|
-
`yes "" | keytool -list -v -keystore #{keystore_path} > #{keystore_info_path}`
|
|
334
|
+
`yes "" | keytool -list -v -keystore #{keystore_path} -storepass #{key_password} > #{keystore_info_path}`
|
|
276
335
|
|
|
277
336
|
UI.message("Upload new Keystore to remote repository...")
|
|
337
|
+
puts ''
|
|
278
338
|
`cd #{repo_dir} && git add .`
|
|
279
339
|
`cd #{repo_dir} && git commit -m "[ADD] Keystore for app '#{package_name}'."`
|
|
280
340
|
`cd #{repo_dir} && git push`
|
|
341
|
+
puts ''
|
|
281
342
|
|
|
282
|
-
else
|
|
343
|
+
else
|
|
283
344
|
UI.message "Keystore file already exists, continue..."
|
|
284
345
|
|
|
285
346
|
self.decrypt_file(properties_encrypt_path, properties_path, key_path)
|
|
@@ -290,37 +351,40 @@ module Fastlane
|
|
|
290
351
|
alias_password = properties['aliasPassword']
|
|
291
352
|
|
|
292
353
|
File.delete(properties_path)
|
|
293
|
-
|
|
294
354
|
end
|
|
295
355
|
|
|
356
|
+
# Resolve path to the APK to sign:
|
|
296
357
|
output_signed_apk = ''
|
|
297
358
|
apk_path = self.resolve_apk_path(apk_path)
|
|
298
359
|
|
|
360
|
+
# Sign APK:
|
|
299
361
|
if File.file?(apk_path)
|
|
300
362
|
UI.message("APK to sign: " + apk_path)
|
|
301
363
|
|
|
302
364
|
if File.file?(keystore_path)
|
|
303
365
|
|
|
304
366
|
UI.message("Signing the APK...")
|
|
367
|
+
puts ''
|
|
305
368
|
output_signed_apk = self.sign_apk(
|
|
306
369
|
apk_path,
|
|
307
370
|
keystore_path,
|
|
308
371
|
key_password,
|
|
309
372
|
alias_name,
|
|
310
373
|
alias_password,
|
|
311
|
-
true
|
|
374
|
+
true # Zip align
|
|
312
375
|
)
|
|
376
|
+
puts ''
|
|
313
377
|
end
|
|
314
378
|
else
|
|
315
379
|
UI.message("No APK file found to sign!")
|
|
316
380
|
end
|
|
317
381
|
|
|
382
|
+
# Prepare contect shared values for next lanes:
|
|
318
383
|
Actions.lane_context[SharedValues::MATCH_KEYSTORE_PATH] = keystore_path
|
|
319
384
|
Actions.lane_context[SharedValues::MATCH_KEYSTORE_ALIAS_NAME] = alias_name
|
|
320
385
|
Actions.lane_context[SharedValues::MATCH_KEYSTORE_APK_SIGNED] = output_signed_apk
|
|
321
386
|
|
|
322
387
|
output_signed_apk
|
|
323
|
-
|
|
324
388
|
end
|
|
325
389
|
|
|
326
390
|
def self.description
|
|
@@ -363,11 +427,11 @@ module Fastlane
|
|
|
363
427
|
FastlaneCore::ConfigItem.new(key: :apk_path,
|
|
364
428
|
env_name: "MATCH_KEYSTORE_APK_PATH",
|
|
365
429
|
description: "Path of the APK file to sign",
|
|
366
|
-
optional:
|
|
430
|
+
optional: true,
|
|
367
431
|
type: String),
|
|
368
|
-
FastlaneCore::ConfigItem.new(key: :
|
|
369
|
-
env_name: "
|
|
370
|
-
description: "
|
|
432
|
+
FastlaneCore::ConfigItem.new(key: :match_secret,
|
|
433
|
+
env_name: "MATCH_KEYSTORE_SECRET",
|
|
434
|
+
description: "Secret to decrypt keystore.properties file (CI)",
|
|
371
435
|
optional: true,
|
|
372
436
|
type: String),
|
|
373
437
|
FastlaneCore::ConfigItem.new(key: :existing_keystore,
|
|
@@ -379,7 +443,12 @@ module Fastlane
|
|
|
379
443
|
env_name: "MATCH_KEYSTORE_OVERRIDE",
|
|
380
444
|
description: "Override an existing Keystore (false by default)",
|
|
381
445
|
optional: true,
|
|
382
|
-
type: Boolean)
|
|
446
|
+
type: Boolean),
|
|
447
|
+
FastlaneCore::ConfigItem.new(key: :keystore_data,
|
|
448
|
+
env_name: "MATCH_KEYSTORE_JSON_PATH",
|
|
449
|
+
description: "Required data to import an existing keystore, or create a new one",
|
|
450
|
+
optional: true,
|
|
451
|
+
type: String)
|
|
383
452
|
]
|
|
384
453
|
end
|
|
385
454
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fastlane-plugin-match_keystore
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Christopher NEY
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-04-
|
|
11
|
+
date: 2020-04-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: pry
|