fastlane-plugin-match_keystore 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
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
|