fastlane-plugin-match_android_keystore 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: be8b1887dca784747d3306b005303e378823ba098ac638a890ddb25fb35beb46
4
+ data.tar.gz: 2f48a015941531af513b0d37345708cb4a88bdbc8612b7850545f16ffc3dabb0
5
+ SHA512:
6
+ metadata.gz: ba3750317acb413e15a4c2f074e5af89d123b2c305738fac50a518c3ed12faeb00fca7d532fe6bb5f6809224e7c71afc82f256ffbb5da1ada7fc2d48b2792481
7
+ data.tar.gz: 8876914e4a91a2dc8db0a4a8f2e85f4eb1bee16990a17f9eca8f2a2195d5a11c19d083aec5e3d5f7efc5622883d319802718b38894f34b621fdf76137f580d06
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 izzis92 <=>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # match_android_keystore plugin
2
+
3
+ [![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-match_android_keystore)
4
+
5
+ ## Getting Started
6
+
7
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-match_android_keystore`, add it to your project by running:
8
+
9
+ ```bash
10
+ fastlane add_plugin match_android_keystore
11
+ ```
12
+
13
+ ## About match_android_keystore
14
+
15
+ Get android keystores from github
16
+
17
+ **Note to author:** Add a more detailed description about this plugin here. If your plugin contains multiple actions, make sure to mention them here.
18
+
19
+ ## Example
20
+
21
+ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
22
+
23
+ **Note to author:** Please set up a sample project to make it easy for users to explore what your plugin does. Provide everything that is necessary to try out the plugin in this project (including a sample Xcode/Android project if necessary)
24
+
25
+ ## Run tests for this plugin
26
+
27
+ To run both the tests, and code style validation, run
28
+
29
+ ```
30
+ rake
31
+ ```
32
+
33
+ To automatically fix many of the styling issues, use
34
+ ```
35
+ rubocop -a
36
+ ```
37
+
38
+ ## Issues and Feedback
39
+
40
+ For any other issues and feedback about this plugin, please submit it to this repository.
41
+
42
+ ## Troubleshooting
43
+
44
+ If you have trouble using plugins, check out the [Plugins Troubleshooting](https://docs.fastlane.tools/plugins/plugins-troubleshooting/) guide.
45
+
46
+ ## Using _fastlane_ Plugins
47
+
48
+ For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
49
+
50
+ ## About _fastlane_
51
+
52
+ _fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/match_android_keystore/version'
2
+
3
+ module Fastlane
4
+ module MatchAndroidKeystore
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::MatchAndroidKeystore.all_classes.each do |current|
15
+ require current
16
+ end
@@ -0,0 +1,378 @@
1
+ require 'fastlane/action'
2
+ require 'fileutils'
3
+ require 'os'
4
+ require 'json'
5
+ require 'digest'
6
+ require_relative '../helper/match_keystore_android_helper'
7
+
8
+ module Fastlane
9
+ module Actions
10
+ module SharedValues
11
+ MATCH_KEYSTORE_PATH = :MATCH_KEYSTORE_PATH
12
+ MATCH_KEYSTORE_ALIAS_NAME = :MATCH_KEYSTORE_ALIAS_NAME
13
+ MATCH_KEY_PASSWORD = :MATCH_KEY_PASSWORD
14
+ MATCH_ALIAS_PASSWORD = :MATCH_ALIAS_PASSWORD
15
+ end
16
+
17
+ class MatchKeystoreAction < Action
18
+
19
+ def self.to_md5(value)
20
+ hash_value = Digest::MD5.hexdigest value
21
+ hash_value
22
+ end
23
+
24
+ def self.load_json(json_path)
25
+ file = File.read(json_path)
26
+ data_hash = JSON.parse(file)
27
+ data_hash
28
+ end
29
+
30
+ def self.load_properties(properties_filename)
31
+ properties = {}
32
+ File.open(properties_filename, 'r') do |properties_file|
33
+ properties_file.read.each_line do |line|
34
+ line.strip!
35
+ if (line[0] != ?# and line[0] != ?=)
36
+ i = line.index('=')
37
+ if (i)
38
+ properties[line[0..i - 1].strip] = line[i + 1..-1].strip
39
+ else
40
+ properties[line] = ''
41
+ end
42
+ end
43
+ end
44
+ end
45
+ properties
46
+ end
47
+
48
+ def self.get_android_home
49
+ `rm -f android_home.txt`
50
+ `echo $ANDROID_HOME > android_home.txt`
51
+ data = File.read("android_home.txt")
52
+ android_home = data.strip
53
+ `rm -f android_home.txt`
54
+ android_home
55
+ end
56
+
57
+ def self.get_build_tools
58
+ android_home = self.get_android_home()
59
+ build_tools_root = File.join(android_home, '/build-tools')
60
+
61
+ sub_dirs = Dir.glob(File.join(build_tools_root, '*', ''))
62
+ build_tools_last_version = ''
63
+ for sub_dir in sub_dirs
64
+ build_tools_last_version = sub_dir
65
+ end
66
+
67
+ build_tools_last_version
68
+ end
69
+
70
+ def self.check_openssl_version
71
+ output = `openssl version`
72
+ if !output.start_with?("OpenSSL")
73
+ raise "Please install OpenSSL 1.1.1 at least https://www.openssl.org/"
74
+ end
75
+ UI.message("OpenSSL version: " + output.strip)
76
+ end
77
+
78
+ def self.gen_key(key_path, password)
79
+ `rm -f '#{key_path}'`
80
+ `echo "#{password}" | openssl dgst -sha512 | awk '{print $2}' | cut -c1-128 > '#{key_path}'`
81
+ end
82
+
83
+ def self.encrypt_file(clear_file, encrypt_file, key_path)
84
+ `rm -f '#{encrypt_file}'`
85
+ `openssl enc -aes-256-cbc -salt -pbkdf2 -in '#{clear_file}' -out '#{encrypt_file}' -pass file:'#{key_path}'`
86
+ end
87
+
88
+ def self.decrypt_file(encrypt_file, clear_file, key_path)
89
+ `rm -f '#{clear_file}'`
90
+ `openssl enc -d -aes-256-cbc -pbkdf2 -in '#{encrypt_file}' -out '#{clear_file}' -pass file:'#{key_path}'`
91
+ end
92
+
93
+ def self.get_file_content(file_path)
94
+ data = File.read(file_path)
95
+ data
96
+ end
97
+
98
+ def self.prompt2(params)
99
+ # UI.message("prompt2: #{params[:value]}")
100
+ if params[:value].to_s.empty?
101
+ return_value = other_action.prompt(text: params[:text], secure_text: params[:secure_text], ci_input: params[:ci_input])
102
+ else
103
+ return_value = params[:value]
104
+ end
105
+ return_value
106
+ end
107
+
108
+ def self.run(params)
109
+
110
+ # Get input parameters:
111
+ git_url = params[:git_url]
112
+ package_name = params[:package_name]
113
+ existing_keystore = params[:existing_keystore]
114
+ match_secret = params[:match_secret]
115
+ override_keystore = params[:override_keystore]
116
+ keystore_data = params[:keystore_data]
117
+
118
+ # Init constants:
119
+ keystore_name = 'keystore.jks'
120
+ properties_name = 'keystore.properties'
121
+ keystore_info_name = 'keystore.txt'
122
+ properties_encrypt_name = 'keystore.properties.enc'
123
+
124
+ # Check Android Home env:
125
+ android_home = self.get_android_home()
126
+ UI.message("Android SDK: #{android_home}")
127
+ if android_home.to_s.strip.empty?
128
+ raise "The environment variable ANDROID_HOME is not defined, or Android SDK is not installed!"
129
+ end
130
+
131
+ # Check OpenSSL:
132
+ self.check_openssl_version
133
+
134
+ # Init workign local directory:
135
+ dir_name = ENV['HOME'] + '/.match_keystore'
136
+ unless File.directory?(dir_name)
137
+ UI.message("Creating '.match_keystore' working directory...")
138
+ FileUtils.mkdir_p(dir_name)
139
+ end
140
+
141
+ # Init 'security password' for AES encryption:
142
+ key_name = "#{self.to_md5(git_url)}.hex"
143
+ key_path = File.join(dir_name, key_name)
144
+ # UI.message(key_path)
145
+ if !File.file?(key_path)
146
+ security_password = self.prompt2(text: "Security password: ", secure_text: true, value: match_secret)
147
+ if security_password.to_s.strip.empty?
148
+ raise "Security password is not defined! Please use 'match_secret' parameter for CI."
149
+ end
150
+ UI.message "Generating security key '#{key_name}'..."
151
+ self.gen_key(key_path, security_password)
152
+ end
153
+
154
+ # Check is 'security password' is well initialized:
155
+ tmpkey = self.get_file_content(key_path).strip
156
+ if tmpkey.length == 128
157
+ UI.message "Security key '#{key_name}' initialized"
158
+ else
159
+ raise "The security key '#{key_name}' is malformed, or not initialized!"
160
+ end
161
+
162
+ # Create repo directory to sync remote Keystores repository:
163
+ repo_dir = File.join(dir_name, self.to_md5(git_url))
164
+ # UI.message(repo_dir)
165
+ unless File.directory?(repo_dir)
166
+ UI.message("Creating 'repo' directory...")
167
+ FileUtils.mkdir_p(repo_dir)
168
+ end
169
+
170
+ # Cloning GIT remote repository:
171
+ gitDir = File.join(repo_dir, '/.git')
172
+ unless File.directory?(gitDir)
173
+ UI.message("Cloning remote Keystores repository...")
174
+ puts ''
175
+ `git clone #{git_url} #{repo_dir}`
176
+ puts ''
177
+ end
178
+
179
+ # Create sub-directory for Android app:
180
+ if package_name.to_s.strip.empty?
181
+ raise "Package name is not defined!"
182
+ end
183
+ keystoreAppDir = File.join(repo_dir, package_name)
184
+ unless File.directory?(keystoreAppDir)
185
+ UI.message("Creating '#{package_name}' keystore directory...")
186
+ FileUtils.mkdir_p(keystoreAppDir)
187
+ end
188
+
189
+ keystore_path = File.join(keystoreAppDir, keystore_name)
190
+ properties_path = File.join(keystoreAppDir, properties_name)
191
+ properties_encrypt_path = File.join(keystoreAppDir, properties_encrypt_name)
192
+
193
+ # Load parameters from JSON for CI or Unit Tests:
194
+ if keystore_data != nil && File.file?(keystore_data)
195
+ data_json = self.load_json(keystore_data)
196
+ data_key_password = data_json['key_password']
197
+ data_alias_name = data_json['alias_name']
198
+ data_alias_password = data_json['alias_password']
199
+ data_full_name = data_json['full_name']
200
+ data_org_unit = data_json['org_unit']
201
+ data_org = data_json['org']
202
+ data_city_locality = data_json['city_locality']
203
+ data_state_province = data_json['state_province']
204
+ data_country = data_json['country']
205
+ end
206
+
207
+ # Create keystore with command
208
+ override_keystore = !existing_keystore.to_s.strip.empty? && File.file?(existing_keystore)
209
+ if !File.file?(keystore_path) || override_keystore
210
+
211
+ if File.file?(keystore_path)
212
+ FileUtils.remove_dir(keystore_path)
213
+ end
214
+
215
+ key_password = self.prompt2(text: "Keystore Password: ", value: data_key_password)
216
+ if key_password.to_s.strip.empty?
217
+ raise "Keystore Password is not definined!"
218
+ end
219
+ alias_name = self.prompt2(text: "Keystore Alias name: ", value: data_alias_name)
220
+ if alias_name.to_s.strip.empty?
221
+ raise "Keystore Alias name is not definined!"
222
+ end
223
+ alias_password = self.prompt2(text: "Keystore Alias password: ", value: data_alias_password)
224
+ if alias_password.to_s.strip.empty?
225
+ raise "Keystore Alias password is not definined!"
226
+ end
227
+
228
+ # https://developer.android.com/studio/publish/app-signing
229
+ if !File.file?(existing_keystore)
230
+ UI.message("Generating Android Keystore...")
231
+
232
+ full_name = self.prompt2(text: "Certificate First and Last Name: ", value: data_full_name)
233
+ org_unit = self.prompt2(text: "Certificate Organisation Unit: ", value: data_org_unit)
234
+ org = self.prompt2(text: "Certificate Organisation: ", value: data_org)
235
+ city_locality = self.prompt2(text: "Certificate City or Locality: ", value: data_city_locality)
236
+ state_province = self.prompt2(text: "Certificate State or Province: ", value: data_state_province)
237
+ country = self.prompt2(text: "Certificate Country Code (XX): ", value: data_country)
238
+
239
+ keytool_parts = [
240
+ "keytool -genkey -v",
241
+ "-keystore '#{keystore_path}'",
242
+ "-alias '#{alias_name}'",
243
+ "-keyalg RSA -keysize 2048 -validity 10000",
244
+ "-storepass '#{alias_password}'",
245
+ "-keypass '#{key_password}'",
246
+ "-dname \"CN=#{full_name}, OU=#{org_unit}, O=#{org}, L=#{city_locality}, S=#{state_province}, C=#{country}\"",
247
+ ]
248
+ sh keytool_parts.join(" ")
249
+ else
250
+ UI.message("Copy existing keystore to match_keystore repository...")
251
+ `cp #{existing_keystore} #{keystore_path}`
252
+ end
253
+
254
+ UI.message("Generating Keystore properties...")
255
+
256
+ if File.file?(properties_path)
257
+ FileUtils.remove_dir(properties_path)
258
+ end
259
+
260
+ # Build URL:
261
+ store_file = git_url + '/' + package_name + '/' + keystore_name
262
+
263
+ out_file = File.new(properties_path, "w")
264
+ out_file.puts("keyFile=#{store_file}")
265
+ out_file.puts("keyPassword=#{key_password}")
266
+ out_file.puts("aliasName=#{alias_name}")
267
+ out_file.puts("aliasPassword=#{alias_password}")
268
+ out_file.close
269
+
270
+ self.encrypt_file(properties_path, properties_encrypt_path, key_path)
271
+ File.delete(properties_path)
272
+
273
+ # Print Keystore data in repo:
274
+ keystore_info_path = File.join(keystoreAppDir, keystore_info_name)
275
+ `yes "" | keytool -list -v -keystore '#{keystore_path}' -storepass '#{key_password}' > '#{keystore_info_path}'`
276
+
277
+ UI.message("Upload new Keystore to remote repository...")
278
+ puts ''
279
+ `cd '#{repo_dir}' && git add .`
280
+ `cd '#{repo_dir}' && git commit -m "[ADD] Keystore for app '#{package_name}'."`
281
+ `cd '#{repo_dir}' && git push`
282
+ puts ''
283
+
284
+ else
285
+ UI.message "Keystore file already exists, continue..."
286
+
287
+ self.decrypt_file(properties_encrypt_path, properties_path, key_path)
288
+
289
+ properties = self.load_properties(properties_path)
290
+ key_password = properties['keyPassword']
291
+ alias_name = properties['aliasName']
292
+ alias_password = properties['aliasPassword']
293
+
294
+ File.delete(properties_path)
295
+ end
296
+
297
+ # Prepare contect shared values for next lanes:
298
+ Actions.lane_context[SharedValues::MATCH_KEYSTORE_PATH] = keystore_path
299
+ Actions.lane_context[SharedValues::MATCH_KEYSTORE_ALIAS_NAME] = alias_name
300
+ Actions.lane_context[SharedValues::MATCH_KEY_PASSWORD] = key_password
301
+ Actions.lane_context[SharedValues::MATCH_ALIAS_PASSWORD] = alias_password
302
+
303
+ end
304
+
305
+ def self.description
306
+ "Easily sync your Android keystores across your team"
307
+ end
308
+
309
+ def self.authors
310
+ ["Christopher NEY"]
311
+ end
312
+
313
+ def self.return_value
314
+ "Prepare Keystore local path, alias name, and passwords for the specified App."
315
+ end
316
+
317
+ def self.output
318
+ [
319
+ ['MATCH_KEYSTORE_ALIAS_NAME', 'Keystore Alias Name.'],
320
+ ['MATCH_KEYSTORE_APK_SIGNED', 'Path of the signed APK.']
321
+ ['MATCH_KEY_PASSWORD', 'keystore password']
322
+ ['MATCH_ALIAS_PASSWORD', 'alias password']
323
+ ]
324
+ end
325
+
326
+ def self.details
327
+ # Optional:
328
+ "This way, your entire team can use the same account and have one code signing identity without any manual work or confusion."
329
+ end
330
+
331
+ def self.available_options
332
+ [
333
+ FastlaneCore::ConfigItem.new(key: :git_url,
334
+ env_name: "MATCH_KEYSTORE_GIT_URL",
335
+ description: "The URL of the Git repository (Github, BitBucket...)",
336
+ optional: false,
337
+ type: String),
338
+ FastlaneCore::ConfigItem.new(key: :package_name,
339
+ env_name: "MATCH_KEYSTORE_PACKAGE_NAME",
340
+ description: "The package name of the App",
341
+ optional: false,
342
+ type: String),
343
+ FastlaneCore::ConfigItem.new(key: :apk_path,
344
+ env_name: "MATCH_KEYSTORE_APK_PATH",
345
+ description: "Path of the APK file to sign",
346
+ optional: true,
347
+ type: String),
348
+ FastlaneCore::ConfigItem.new(key: :match_secret,
349
+ env_name: "MATCH_KEYSTORE_SECRET",
350
+ description: "Secret to decrypt keystore.properties file (CI)",
351
+ optional: true,
352
+ type: String),
353
+ FastlaneCore::ConfigItem.new(key: :existing_keystore,
354
+ env_name: "MATCH_KEYSTORE_EXISTING",
355
+ description: "Path of an existing Keystore",
356
+ optional: true,
357
+ type: String),
358
+ FastlaneCore::ConfigItem.new(key: :override_keystore,
359
+ env_name: "MATCH_KEYSTORE_OVERRIDE",
360
+ description: "Override an existing Keystore (false by default)",
361
+ optional: true,
362
+ type: Boolean),
363
+ FastlaneCore::ConfigItem.new(key: :keystore_data,
364
+ env_name: "MATCH_KEYSTORE_JSON_PATH",
365
+ description: "Required data to import an existing keystore, or create a new one",
366
+ optional: true,
367
+ type: String)
368
+ ]
369
+ end
370
+
371
+ def self.is_supported?(platform)
372
+ # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
373
+ # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
374
+ [:android].include?(platform)
375
+ end
376
+ end
377
+ end
378
+ end
@@ -0,0 +1,16 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class MatchAndroidKeystoreHelper
8
+ # class methods that you define here become available in your action
9
+ # as `Helper::MatchAndroidKeystoreHelper.your_method`
10
+ #
11
+ def self.show_message
12
+ UI.message("Hello from the match_android_keystore plugin helper!")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module MatchAndroidKeystore
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-match_android_keystore
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - izzis92
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec_junit_formatter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.49.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.49.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-require_tools
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: fastlane
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 2.149.1
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 2.149.1
139
+ description:
140
+ email: '='
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - README.md
147
+ - lib/fastlane/plugin/match_android_keystore.rb
148
+ - lib/fastlane/plugin/match_android_keystore/actions/match_android_keystore_action.rb
149
+ - lib/fastlane/plugin/match_android_keystore/helper/match_android_keystore_helper.rb
150
+ - lib/fastlane/plugin/match_android_keystore/version.rb
151
+ homepage: https://github.com/izzis92/fastlane-plugin-match_android_keystore
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubygems_version: 3.0.3
171
+ signing_key:
172
+ specification_version: 4
173
+ summary: Get android keystores from github
174
+ test_files: []