fastlane-plugin-match_keystore 0.1.14 → 0.2.0
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: 554236b09184bb0b50f9209ae0de3ba469e58bd66a5c0cc28880e8e259f3721a
|
4
|
+
data.tar.gz: b6c33c5ec05a4f72e2cbc04db6e64b2048b361408bedbaa9fe58e52908cbc5b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f99fe0dbb68617919711aaded1054d931a85dbc8730f2ac69fd289341389014fb02a25cc58b833c148991e85bb871cf360450821cff284427c60c6d7000f0f42
|
7
|
+
data.tar.gz: c9de35e3c6cae52e1dca0d78823079f1239bc890df3c5865fb288f1fbe666e181ddba39d5de9136bb82622bdfc380c79b5b045907ca2e5d5df95df87448222b4
|
data/README.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/fastlane-plugin-match_keystore)
|
4
4
|
|
5
|
+
## Machine requirements
|
6
|
+
|
7
|
+
* OpenSSL 1.1.1 min OR LibreSSL 2.9 min installed
|
8
|
+
* Git installed
|
9
|
+
* Android SDK & Build-tools installed
|
10
|
+
* ANDROID_HOME environment variable defined
|
11
|
+
|
5
12
|
## Getting Started
|
6
13
|
|
7
14
|
This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-match_keystore`, add it to your project by running:
|
@@ -2,6 +2,7 @@ require 'fastlane/action'
|
|
2
2
|
require 'fileutils'
|
3
3
|
require 'os'
|
4
4
|
require 'json'
|
5
|
+
require 'pry'
|
5
6
|
require 'digest'
|
6
7
|
require_relative '../helper/match_keystore_helper'
|
7
8
|
|
@@ -15,11 +16,19 @@ module Fastlane
|
|
15
16
|
|
16
17
|
class MatchKeystoreAction < Action
|
17
18
|
|
19
|
+
KEY_VERSION = "2"
|
20
|
+
OPENSSL_BIN_PATH_MAC = "/usr/local/opt/openssl@1.1/bin"
|
21
|
+
|
18
22
|
def self.to_md5(value)
|
19
23
|
hash_value = Digest::MD5.hexdigest value
|
20
24
|
hash_value
|
21
25
|
end
|
22
26
|
|
27
|
+
def self.sha512(value)
|
28
|
+
hash_value = Digest::SHA512.hexdigest value
|
29
|
+
hash_value
|
30
|
+
end
|
31
|
+
|
23
32
|
def self.load_json(json_path)
|
24
33
|
file = File.read(json_path)
|
25
34
|
data_hash = JSON.parse(file)
|
@@ -53,69 +62,259 @@ module Fastlane
|
|
53
62
|
android_home
|
54
63
|
end
|
55
64
|
|
56
|
-
def self.
|
65
|
+
def self.get_build_tools_version(targeted_version)
|
66
|
+
path = self.get_build_tools(targeted_version)
|
67
|
+
version = path.split('/').last
|
68
|
+
version
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.get_build_tools(targeted_version)
|
57
72
|
android_home = self.get_android_home()
|
58
73
|
build_tools_root = File.join(android_home, '/build-tools')
|
59
74
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
build_tools_last_version = sub_dir
|
75
|
+
build_tools_path = ""
|
76
|
+
if !targeted_version.to_s.strip.empty?
|
77
|
+
build_tools_path = File.join(build_tools_root, "/#{targeted_version}/")
|
64
78
|
end
|
65
79
|
|
66
|
-
|
80
|
+
if !File.directory?(build_tools_path)
|
81
|
+
sub_dirs = Dir.glob(File.join(build_tools_root, '*', ''))
|
82
|
+
build_tools_last_version = ''
|
83
|
+
for sub_dir in sub_dirs
|
84
|
+
build_tools_last_version = sub_dir
|
85
|
+
end
|
86
|
+
build_tools_path = build_tools_last_version
|
87
|
+
end
|
88
|
+
|
89
|
+
build_tools_path
|
67
90
|
end
|
68
91
|
|
69
|
-
def self.
|
70
|
-
|
71
|
-
|
72
|
-
|
92
|
+
def self.check_ssl_version(forceOpenSSL)
|
93
|
+
libressl_min = '2.9'
|
94
|
+
openssl_min = '1.1.1'
|
95
|
+
|
96
|
+
openssl = self.openssl(forceOpenSSL)
|
97
|
+
output = `#{openssl} version`
|
98
|
+
if !output.start_with?("LibreSSL") && !output.start_with?("OpenSSL")
|
99
|
+
raise "Please install OpenSSL '#{openssl_min}' at least OR LibreSSL #{libressl_min}' at least"
|
73
100
|
end
|
74
|
-
UI.message("
|
101
|
+
UI.message("SSL/TLS protocol library: '#{output.strip!}'")
|
102
|
+
|
103
|
+
# Check minimum verion:
|
104
|
+
vesion = output.to_str.scan(/[0-9\.]{1,}/).first
|
105
|
+
UI.message("SSL/TLS protocol version: '#{vesion}'")
|
106
|
+
if self.is_libre_ssl(forceOpenSSL)
|
107
|
+
if Gem::Version.new(vesion) < Gem::Version.new(libressl_min)
|
108
|
+
raise "Minimum version for LibreSSL is '#{libressl_min}', please update it. Use homebrew is your are Mac user, and update ~/.bah_profile or ~/.zprofile"
|
109
|
+
end
|
110
|
+
else
|
111
|
+
if Gem::Version.new(vesion) > Gem::Version.new(openssl_min)
|
112
|
+
raise "Minimum version for OpenSSL is '#{openssl_min}' please update it. Use homebrew is your are Mac user, and update ~/.bah_profile or ~/.zprofile"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
output.strip
|
75
117
|
end
|
76
118
|
|
77
|
-
def self.
|
119
|
+
def self.openssl(forceOpenSSL)
|
120
|
+
if forceOpenSSL
|
121
|
+
output = "#{self::OPENSSL_BIN_PATH_MAC}/openssl"
|
122
|
+
else
|
123
|
+
output = "openssl"
|
124
|
+
end
|
125
|
+
output
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.is_libre_ssl(forceOpenSSL)
|
129
|
+
result = false
|
130
|
+
openssl = self.openssl(forceOpenSSL)
|
131
|
+
output = `#{openssl} version`
|
132
|
+
if output.start_with?("LibreSSL")
|
133
|
+
result = true
|
134
|
+
end
|
135
|
+
result
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.gen_key(key_path, password, compat_key)
|
78
139
|
`rm -f '#{key_path}'`
|
79
|
-
|
140
|
+
shaValue = self.sha512(password)
|
141
|
+
# Backward-compatibility
|
142
|
+
if compat_key == "1"
|
143
|
+
`echo "#{password}" | openssl dgst -sha512 | awk '{print $2}' | cut -c1-128 > '#{key_path}'`
|
144
|
+
else
|
145
|
+
`echo "#{shaValue}" > '#{key_path}'`
|
146
|
+
end
|
80
147
|
end
|
81
148
|
|
82
|
-
def self.encrypt_file(clear_file, encrypt_file, key_path)
|
149
|
+
def self.encrypt_file(clear_file, encrypt_file, key_path, forceOpenSSL)
|
83
150
|
`rm -f '#{encrypt_file}'`
|
84
|
-
|
151
|
+
libre_ssl = self.is_libre_ssl(forceOpenSSL)
|
152
|
+
openssl_bin = self.openssl(forceOpenSSL)
|
153
|
+
`#{openssl_bin} enc -aes-256-cbc -salt -pbkdf2 -in '#{clear_file}' -out '#{encrypt_file}' -pass file:'#{key_path}'`
|
85
154
|
end
|
86
155
|
|
87
|
-
def self.decrypt_file(encrypt_file, clear_file, key_path)
|
156
|
+
def self.decrypt_file(encrypt_file, clear_file, key_path, forceOpenSSL)
|
88
157
|
`rm -f '#{clear_file}'`
|
89
|
-
|
158
|
+
libre_ssl = self.is_libre_ssl(forceOpenSSL)
|
159
|
+
openssl_bin = self.openssl(forceOpenSSL)
|
160
|
+
`#{openssl_bin} enc -d -aes-256-cbc -pbkdf2 -in '#{encrypt_file}' -out '#{clear_file}' -pass file:'#{key_path}'`
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.assert_equals(test_name, excepted, value)
|
164
|
+
puts "Unit Test: #{test_name}"
|
165
|
+
if value != excepted
|
166
|
+
puts " - Excepted: #{excepted}"
|
167
|
+
puts " - Returned: #{value}"
|
168
|
+
raise "Unit Test - #{test_name} error!"
|
169
|
+
else
|
170
|
+
puts " - OK"
|
171
|
+
end
|
90
172
|
end
|
91
173
|
|
92
|
-
def self.
|
174
|
+
def self.test_security
|
93
175
|
|
94
|
-
|
176
|
+
self.check_ssl_version(false)
|
177
|
+
|
178
|
+
# Clear temp files
|
179
|
+
temp_dir = File.join(Dir.pwd, '/temp/')
|
180
|
+
FileUtils.rm_rf(temp_dir)
|
181
|
+
Dir.mkdir(temp_dir)
|
182
|
+
|
183
|
+
fakeValue = "4esfsf4dsfds!efs5ZDOJF"
|
184
|
+
# Check MD5
|
185
|
+
md5value = self.to_md5(fakeValue)
|
186
|
+
excepted = "1c815cd208fe08076c9e7b6595d121d1"
|
187
|
+
self.assert_equals("MD5", excepted, md5value)
|
188
|
+
|
189
|
+
# Check SHA-512
|
190
|
+
shaValue = self.sha512(fakeValue)
|
191
|
+
excepted = "cc6a7b0d89cc61c053f7018a305672bdb82bc07e5015f64bb063d9662be4ec81ec8afa819b009de266482b6bd56b7068def2524c32f5b5d4d9db49ee4578499d"
|
192
|
+
self.assert_equals("SHA-512", excepted, shaValue)
|
193
|
+
|
194
|
+
# Check SHA-512-File
|
195
|
+
key_path = File.join(Dir.pwd, '/temp/key.txt')
|
196
|
+
self.gen_key(key_path, fakeValue, false)
|
197
|
+
shaValue = self.get_file_content(key_path).strip!
|
198
|
+
excepted = "cc6a7b0d89cc61c053f7018a305672bdb82bc07e5015f64bb063d9662be4ec81ec8afa819b009de266482b6bd56b7068def2524c32f5b5d4d9db49ee4578499d"
|
199
|
+
self.assert_equals("SHA-512-File", excepted, shaValue)
|
200
|
+
|
201
|
+
|
202
|
+
# Check LibreSSL
|
203
|
+
result = self.is_libre_ssl(false)
|
204
|
+
self.assert_equals("Is-LibreSSL", true, result)
|
205
|
+
result = self.is_libre_ssl(true)
|
206
|
+
self.assert_equals("Is-LibreSSL", false, result)
|
207
|
+
|
208
|
+
# Encrypt OpenSSL
|
209
|
+
clear_file = File.join(Dir.pwd, '/temp/clear.txt')
|
210
|
+
openssl_encrypt_file = File.join(Dir.pwd, '/temp/openssl_encrypted.txt')
|
211
|
+
self.content_to_file(clear_file, fakeValue)
|
212
|
+
self.encrypt_file(clear_file, openssl_encrypt_file, key_path, true)
|
213
|
+
result = File.file?(openssl_encrypt_file) && File.size(openssl_encrypt_file) > 10
|
214
|
+
self.assert_equals("Encrypt-OpenSSL", true, result)
|
215
|
+
|
216
|
+
# Encrypt LibreSSL
|
217
|
+
encrypt_file_libre = File.join(Dir.pwd, '/temp/libressl_encrypted.txt')
|
218
|
+
self.content_to_file(clear_file, fakeValue)
|
219
|
+
self.encrypt_file(clear_file, encrypt_file_libre, key_path, false)
|
220
|
+
result = File.file?(encrypt_file_libre) && File.size(encrypt_file_libre) > 10
|
221
|
+
self.assert_equals("Encrypt-LibreSSL", true, result)
|
222
|
+
|
223
|
+
# exit!
|
224
|
+
|
225
|
+
# Decrypt OpenSSL (from OpenSSL)
|
226
|
+
openssl_clear_file = File.join(Dir.pwd, '/temp/openssl_clear.txt')
|
227
|
+
self.decrypt_file(openssl_encrypt_file, openssl_clear_file, key_path, true)
|
228
|
+
decrypted = self.get_file_content(openssl_clear_file).strip!
|
229
|
+
self.assert_equals("Decrypt-OpenSSL", fakeValue, decrypted)
|
230
|
+
|
231
|
+
# Decrypt LibreSSL (from LibreSSL)
|
232
|
+
libressl_clear_file = File.join(Dir.pwd, '/temp/libressl_clear.txt')
|
233
|
+
self.decrypt_file(encrypt_file_libre, libressl_clear_file, key_path, false)
|
234
|
+
decrypted = self.get_file_content(libressl_clear_file).strip!
|
235
|
+
self.assert_equals("Decrypt-LibreSSL", fakeValue, decrypted)
|
236
|
+
|
237
|
+
# Decrypt LibreSSL (from OpenSSL)
|
238
|
+
libressl_clear_file = File.join(Dir.pwd, '/temp/libressl_from_openssl_clear.txt')
|
239
|
+
self.decrypt_file(openssl_encrypt_file, libressl_clear_file, key_path, false)
|
240
|
+
decrypted = self.get_file_content(libressl_clear_file).strip!
|
241
|
+
self.assert_equals("Decrypt-LibreSSL-from-OpenSSL", fakeValue, decrypted)
|
242
|
+
|
243
|
+
# Decrypt OpenSSL (from LibreSSL)
|
244
|
+
openssl_clear_file = File.join(Dir.pwd, '/temp/openssl_from_libressl_clear.txt')
|
245
|
+
self.decrypt_file(encrypt_file_libre, openssl_clear_file, key_path, true)
|
246
|
+
decrypted = self.get_file_content(openssl_clear_file).strip!
|
247
|
+
self.assert_equals("Decrypt-OpenSSL-from-LibreSSL", fakeValue, decrypted)
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.sign_apk(apk_path, keystore_path, key_password, alias_name, alias_password, zip_align, version_targeted)
|
252
|
+
|
253
|
+
build_tools_path = self.get_build_tools(version_targeted)
|
95
254
|
UI.message("Build-tools path: #{build_tools_path}")
|
96
255
|
|
97
|
-
# https://developer.android.com/studio/command-line/
|
98
|
-
if zip_align == true
|
99
|
-
apk_path_aligned = apk_path.gsub(".apk", "-aligned.apk")
|
100
|
-
`rm -f '#{apk_path_aligned}'`
|
101
|
-
UI.message("Aligning APK (zipalign): #{apk_path_aligned}")
|
102
|
-
`#{build_tools_path}zipalign -f -v 4 '#{apk_path}' '#{apk_path_aligned}'`
|
103
|
-
else
|
104
|
-
UI.message("No zip align!")
|
105
|
-
apk_path_aligned = apk_path
|
106
|
-
end
|
256
|
+
# https://developer.android.com/studio/command-line/apksigner
|
107
257
|
apk_path_signed = apk_path.gsub(".apk", "-signed.apk")
|
108
258
|
apk_path_signed = apk_path_signed.gsub("unsigned", "")
|
109
259
|
apk_path_signed = apk_path_signed.gsub("--", "-")
|
110
|
-
|
111
|
-
# https://developer.android.com/studio/command-line/apksigner
|
112
260
|
`rm -f '#{apk_path_signed}'`
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
261
|
+
|
262
|
+
UI.message("Signing APK (input): #{apk_path}")
|
263
|
+
apksigner_opts = ""
|
264
|
+
build_tools_version = self.get_build_tools_version(version_targeted)
|
265
|
+
UI.message("Build-tools version: #{build_tools_version}")
|
266
|
+
if Gem::Version.new(build_tools_version) >= Gem::Version.new('30')
|
267
|
+
apksigner_opts = "--v4-signing-enabled false "
|
268
|
+
end
|
269
|
+
output = `#{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 #{apksigner_opts}--out '#{apk_path_signed}' '#{apk_path}'`
|
270
|
+
puts ""
|
271
|
+
puts output
|
272
|
+
|
273
|
+
UI.message("Verifing APK signature (output): #{apk_path_signed}")
|
274
|
+
output = `#{build_tools_path}apksigner verify '#{apk_path_signed}'`
|
275
|
+
puts ""
|
276
|
+
puts output
|
277
|
+
|
278
|
+
|
279
|
+
# https://developer.android.com/studio/command-line/zipalign
|
280
|
+
if zip_align != false
|
281
|
+
apk_path_aligned = apk_path_signed.gsub(".apk", "-aligned.apk")
|
282
|
+
`rm -f '#{apk_path_aligned}'`
|
283
|
+
UI.message("Aligning APK (zipalign): #{apk_path_signed}")
|
284
|
+
output = `#{build_tools_path}zipalign -v 4 '#{apk_path_signed}' '#{apk_path_aligned}'`
|
285
|
+
puts ""
|
286
|
+
puts output
|
287
|
+
|
288
|
+
if !File.file?(apk_path_aligned)
|
289
|
+
raise "Aligned APK not exists!"
|
290
|
+
end
|
291
|
+
|
292
|
+
`rm -f '#{apk_path_signed}'`
|
293
|
+
apk_path_signed = apk_path_aligned
|
294
|
+
|
295
|
+
else
|
296
|
+
UI.message("No zip align - deactivated via parameter!")
|
297
|
+
end
|
117
298
|
|
118
299
|
apk_path_signed
|
300
|
+
end
|
301
|
+
|
302
|
+
def self.resolve_dir(path)
|
303
|
+
if !File.directory?(path)
|
304
|
+
path = File.join(Dir.pwd, path)
|
305
|
+
end
|
306
|
+
path
|
307
|
+
end
|
308
|
+
|
309
|
+
def self.resolve_file(path)
|
310
|
+
if !File.file?(path)
|
311
|
+
path = File.join(Dir.pwd, path)
|
312
|
+
end
|
313
|
+
path
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.content_to_file(file_path, content)
|
317
|
+
`echo #{content} > #{file_path}`
|
119
318
|
end
|
120
319
|
|
121
320
|
def self.get_file_content(file_path)
|
@@ -132,9 +331,7 @@ module Fastlane
|
|
132
331
|
|
133
332
|
if !apk_path.to_s.end_with?(".apk")
|
134
333
|
|
135
|
-
|
136
|
-
apk_path = File.join(Dir.pwd, apk_path)
|
137
|
-
end
|
334
|
+
apk_path = self.resolve_dir(apk_path)
|
138
335
|
|
139
336
|
pattern = File.join(apk_path, '*.apk')
|
140
337
|
files = Dir[pattern]
|
@@ -147,11 +344,7 @@ module Fastlane
|
|
147
344
|
end
|
148
345
|
|
149
346
|
else
|
150
|
-
|
151
|
-
if !File.file?(apk_path)
|
152
|
-
apk_path = File.join(Dir.pwd, apk_path)
|
153
|
-
end
|
154
|
-
|
347
|
+
apk_path = self.resolve_file(apk_path)
|
155
348
|
end
|
156
349
|
|
157
350
|
apk_path
|
@@ -177,6 +370,17 @@ module Fastlane
|
|
177
370
|
match_secret = params[:match_secret]
|
178
371
|
override_keystore = params[:override_keystore]
|
179
372
|
keystore_data = params[:keystore_data]
|
373
|
+
clear_keystore = params[:clear_keystore]
|
374
|
+
unit_test = params[:unit_test]
|
375
|
+
build_tools_version = params[:build_tools_version]
|
376
|
+
zip_align = params[:zip_align]
|
377
|
+
compat_key = params[:compat_key]
|
378
|
+
|
379
|
+
# Test OpenSSL/LibreSSL
|
380
|
+
if unit_test
|
381
|
+
result_test = self.test_security
|
382
|
+
exit!
|
383
|
+
end
|
180
384
|
|
181
385
|
# Init constants:
|
182
386
|
keystore_name = 'keystore.jks'
|
@@ -192,7 +396,12 @@ module Fastlane
|
|
192
396
|
end
|
193
397
|
|
194
398
|
# Check OpenSSL:
|
195
|
-
self.
|
399
|
+
self.check_ssl_version(false)
|
400
|
+
|
401
|
+
# Check is backward-compatibility is required:
|
402
|
+
if !compat_key.to_s.strip.empty?
|
403
|
+
UI.message("Compatiblity version: #{compat_key}")
|
404
|
+
end
|
196
405
|
|
197
406
|
# Init workign local directory:
|
198
407
|
dir_name = ENV['HOME'] + '/.match_keystore'
|
@@ -202,7 +411,11 @@ module Fastlane
|
|
202
411
|
end
|
203
412
|
|
204
413
|
# Init 'security password' for AES encryption:
|
205
|
-
|
414
|
+
if compat_key == "1"
|
415
|
+
key_name = "#{self.to_md5(git_url)}.hex"
|
416
|
+
else
|
417
|
+
key_name = "#{self.to_md5(git_url)}-#{self::KEY_VERSION}.hex"
|
418
|
+
end
|
206
419
|
key_path = File.join(dir_name, key_name)
|
207
420
|
# UI.message(key_path)
|
208
421
|
if !File.file?(key_path)
|
@@ -211,7 +424,7 @@ module Fastlane
|
|
211
424
|
raise "Security password is not defined! Please use 'match_secret' parameter for CI."
|
212
425
|
end
|
213
426
|
UI.message "Generating security key '#{key_name}'..."
|
214
|
-
self.gen_key(key_path, security_password)
|
427
|
+
self.gen_key(key_path, security_password, compat_key)
|
215
428
|
end
|
216
429
|
|
217
430
|
# Check is 'security password' is well initialized:
|
@@ -222,42 +435,40 @@ module Fastlane
|
|
222
435
|
raise "The security key '#{key_name}' is malformed, or not initialized!"
|
223
436
|
end
|
224
437
|
|
225
|
-
#
|
438
|
+
# Clear repo Keystore (local) - mostly for testing:
|
226
439
|
repo_dir = File.join(dir_name, self.to_md5(git_url))
|
227
|
-
|
440
|
+
if clear_keystore && File.directory?(repo_dir)
|
441
|
+
FileUtils.rm_rf(repo_dir)
|
442
|
+
UI.message("Local repo keystore (#{repo_dir}) directory deleted!")
|
443
|
+
end
|
444
|
+
|
445
|
+
# Create repo directory to sync remote Keystores repository:
|
228
446
|
unless File.directory?(repo_dir)
|
229
447
|
UI.message("Creating 'repo' directory...")
|
230
448
|
FileUtils.mkdir_p(repo_dir)
|
231
449
|
end
|
232
450
|
|
451
|
+
# Check if package name defined:
|
452
|
+
if package_name.to_s.strip.empty?
|
453
|
+
raise "Package name is not defined!"
|
454
|
+
end
|
455
|
+
|
456
|
+
# Define paths:
|
457
|
+
keystoreAppDir = File.join(repo_dir, package_name)
|
458
|
+
keystore_path = File.join(keystoreAppDir, keystore_name)
|
459
|
+
properties_path = File.join(keystoreAppDir, properties_name)
|
460
|
+
properties_encrypt_path = File.join(keystoreAppDir, properties_encrypt_name)
|
461
|
+
|
233
462
|
# Cloning/pulling GIT remote repository:
|
234
463
|
gitDir = File.join(repo_dir, '/.git')
|
235
464
|
if !File.directory?(gitDir)
|
236
465
|
UI.message("Cloning remote Keystores repository...")
|
237
|
-
puts ''
|
238
466
|
`git clone #{git_url} #{repo_dir}`
|
239
|
-
puts ''
|
240
467
|
else
|
241
468
|
UI.message("Pulling remote Keystores repository...")
|
242
|
-
puts ''
|
243
469
|
`cd #{repo_dir} && git pull`
|
244
|
-
puts ''
|
245
|
-
end
|
246
|
-
|
247
|
-
# Create sub-directory for Android app:
|
248
|
-
if package_name.to_s.strip.empty?
|
249
|
-
raise "Package name is not defined!"
|
250
|
-
end
|
251
|
-
keystoreAppDir = File.join(repo_dir, package_name)
|
252
|
-
unless File.directory?(keystoreAppDir)
|
253
|
-
UI.message("Creating '#{package_name}' keystore directory...")
|
254
|
-
FileUtils.mkdir_p(keystoreAppDir)
|
255
470
|
end
|
256
471
|
|
257
|
-
keystore_path = File.join(keystoreAppDir, keystore_name)
|
258
|
-
properties_path = File.join(keystoreAppDir, properties_name)
|
259
|
-
properties_encrypt_path = File.join(keystoreAppDir, properties_encrypt_name)
|
260
|
-
|
261
472
|
# Load parameters from JSON for CI or Unit Tests:
|
262
473
|
if keystore_data != nil && File.file?(keystore_data)
|
263
474
|
data_json = self.load_json(keystore_data)
|
@@ -274,6 +485,7 @@ module Fastlane
|
|
274
485
|
|
275
486
|
# Create keystore with command
|
276
487
|
override_keystore = !existing_keystore.to_s.strip.empty? && File.file?(existing_keystore)
|
488
|
+
UI.message("Existing Keystore: #{existing_keystore}")
|
277
489
|
if !File.file?(keystore_path) || override_keystore
|
278
490
|
|
279
491
|
if File.file?(keystore_path)
|
@@ -294,7 +506,7 @@ module Fastlane
|
|
294
506
|
end
|
295
507
|
|
296
508
|
# https://developer.android.com/studio/publish/app-signing
|
297
|
-
if !File.file?(existing_keystore)
|
509
|
+
if existing_keystore.to_s.strip.empty? || !File.file?(existing_keystore)
|
298
510
|
UI.message("Generating Android Keystore...")
|
299
511
|
|
300
512
|
full_name = self.prompt2(text: "Certificate First and Last Name: ", value: data_full_name)
|
@@ -335,7 +547,7 @@ module Fastlane
|
|
335
547
|
out_file.puts("aliasPassword=#{alias_password}")
|
336
548
|
out_file.close
|
337
549
|
|
338
|
-
self.encrypt_file(properties_path, properties_encrypt_path, key_path)
|
550
|
+
self.encrypt_file(properties_path, properties_encrypt_path, key_path, false)
|
339
551
|
File.delete(properties_path)
|
340
552
|
|
341
553
|
# Print Keystore data in repo:
|
@@ -352,9 +564,10 @@ module Fastlane
|
|
352
564
|
else
|
353
565
|
UI.message "Keystore file already exists, continue..."
|
354
566
|
|
355
|
-
self.decrypt_file(properties_encrypt_path, properties_path, key_path)
|
567
|
+
self.decrypt_file(properties_encrypt_path, properties_path, key_path, false)
|
356
568
|
|
357
569
|
properties = self.load_properties(properties_path)
|
570
|
+
# Pry::ColorPrinter.pp(properties)
|
358
571
|
key_password = properties['keyPassword']
|
359
572
|
alias_name = properties['aliasName']
|
360
573
|
alias_password = properties['aliasPassword']
|
@@ -380,12 +593,13 @@ module Fastlane
|
|
380
593
|
key_password,
|
381
594
|
alias_name,
|
382
595
|
alias_password,
|
383
|
-
|
596
|
+
zip_align, # Zip align
|
597
|
+
build_tools_version # Buil-tools version
|
384
598
|
)
|
385
599
|
puts ''
|
386
600
|
end
|
387
601
|
else
|
388
|
-
UI.message("No APK file found
|
602
|
+
UI.message("No APK file found at: #{apk_path}")
|
389
603
|
end
|
390
604
|
|
391
605
|
# Prepare contect shared values for next lanes:
|
@@ -457,7 +671,32 @@ module Fastlane
|
|
457
671
|
env_name: "MATCH_KEYSTORE_JSON_PATH",
|
458
672
|
description: "Required data to import an existing keystore, or create a new one",
|
459
673
|
optional: true,
|
460
|
-
type: String)
|
674
|
+
type: String),
|
675
|
+
FastlaneCore::ConfigItem.new(key: :build_tools_version,
|
676
|
+
env_name: "MATCH_KEYSTORE_BUILD_TOOLS_VERSION",
|
677
|
+
description: "Set built-tools version (by default latest available on machine)",
|
678
|
+
optional: true,
|
679
|
+
type: String),
|
680
|
+
FastlaneCore::ConfigItem.new(key: :zip_align,
|
681
|
+
env_name: "MATCH_KEYSTORE_ZIPALIGN",
|
682
|
+
description: "Define if plugin will run zipalign on APK before sign it (true by default)",
|
683
|
+
optional: true,
|
684
|
+
type: Boolean),
|
685
|
+
FastlaneCore::ConfigItem.new(key: :compat_key,
|
686
|
+
env_name: "MATCH_KEYSTORE_COMPAT_KEY",
|
687
|
+
description: "Define the compatibility key version used on local machine (nil by default)",
|
688
|
+
optional: true,
|
689
|
+
type: String),
|
690
|
+
FastlaneCore::ConfigItem.new(key: :clear_keystore,
|
691
|
+
env_name: "MATCH_KEYSTORE_CLEAR",
|
692
|
+
description: "Clear the local keystore (false by default)",
|
693
|
+
optional: true,
|
694
|
+
type: Boolean),
|
695
|
+
FastlaneCore::ConfigItem.new(key: :unit_test,
|
696
|
+
env_name: "MATCH_KEYSTORE_UNIT_TESTS",
|
697
|
+
description: "launch Unit Tests (false by default)",
|
698
|
+
optional: true,
|
699
|
+
type: Boolean)
|
461
700
|
]
|
462
701
|
end
|
463
702
|
|
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.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher NEY
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -167,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
167
|
- !ruby/object:Gem::Version
|
168
168
|
version: '0'
|
169
169
|
requirements: []
|
170
|
-
rubygems_version: 3.
|
170
|
+
rubygems_version: 3.2.6
|
171
171
|
signing_key:
|
172
172
|
specification_version: 4
|
173
173
|
summary: Easily sync your Android keystores across your team
|