fastlane-plugin-flint 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +55 -0
- data/lib/assets/FlintfileTemplate +15 -0
- data/lib/assets/KeystorePropertiesTemplate +4 -0
- data/lib/assets/READMETemplate.md +46 -0
- data/lib/fastlane/plugin/flint.rb +32 -0
- data/lib/fastlane/plugin/flint/actions/flint_action.rb +261 -0
- data/lib/fastlane/plugin/flint/actions/flint_change_password.rb +20 -0
- data/lib/fastlane/plugin/flint/actions/flint_nuke.rb +15 -0
- data/lib/fastlane/plugin/flint/actions/flint_setup.rb +59 -0
- data/lib/fastlane/plugin/flint/helper/change_password.rb +67 -0
- data/lib/fastlane/plugin/flint/helper/commands_generator.rb +100 -0
- data/lib/fastlane/plugin/flint/helper/encrypt.rb +138 -0
- data/lib/fastlane/plugin/flint/helper/flint_helper.rb +16 -0
- data/lib/fastlane/plugin/flint/helper/generator.rb +70 -0
- data/lib/fastlane/plugin/flint/helper/git_helper.rb +211 -0
- data/lib/fastlane/plugin/flint/helper/nuke.rb +119 -0
- data/lib/fastlane/plugin/flint/helper/table_printer.rb +31 -0
- data/lib/fastlane/plugin/flint/helper/utils.rb +49 -0
- data/lib/fastlane/plugin/flint/version.rb +5 -0
- metadata +189 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require_relative '../helper/flint_helper'
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
module Actions
|
6
|
+
class FlintChangePasswordAction < FlintAction
|
7
|
+
def self.run(params)
|
8
|
+
params.load_configuration_file('Flintfile')
|
9
|
+
|
10
|
+
FastlaneCore::PrintTable.print_values(config: params,
|
11
|
+
hide_keys: [:workspace],
|
12
|
+
title: "Summary for flint #{Fastlane::VERSION}")
|
13
|
+
|
14
|
+
Flint::ChangePassword.update(params: params)
|
15
|
+
UI.success("Successfully changed the password. Make sure to update the password on all your clients and servers")
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require_relative '../helper/flint_helper'
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
module Actions
|
6
|
+
class FlintNukeAction < FlintAction
|
7
|
+
def self.run(params)
|
8
|
+
params.load_configuration_file('Flintfile')
|
9
|
+
|
10
|
+
Flint::Nuke.new.run(params, type: 'development')
|
11
|
+
Flint::Nuke.new.run(params, type: 'release')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require_relative '../helper/git_helper'
|
3
|
+
require_relative '../helper/encrypt'
|
4
|
+
|
5
|
+
module Fastlane
|
6
|
+
module Actions
|
7
|
+
class FlintSetupAction < FlintAction
|
8
|
+
def self.run(params)
|
9
|
+
containing = FastlaneCore::Helper.fastlane_enabled_folder_path
|
10
|
+
path = File.join(containing, "Flintfile")
|
11
|
+
|
12
|
+
if File.exist?(path)
|
13
|
+
FastlaneCore::UI.user_error!("You already have a Flintfile in this directory (#{path})")
|
14
|
+
return 0
|
15
|
+
end
|
16
|
+
|
17
|
+
template = File.read("#{Flint::ROOT}/assets/FlintfileTemplate")
|
18
|
+
|
19
|
+
UI.important("Please create a new, private git repository")
|
20
|
+
UI.important("to store the keystores there")
|
21
|
+
|
22
|
+
url = UI.input("URL of the Git Repo: ")
|
23
|
+
|
24
|
+
template.gsub!("[[GIT_URL]]", url)
|
25
|
+
File.write(path, template)
|
26
|
+
UI.success("Successfully created '#{path}'. You can open the file using a code editor.")
|
27
|
+
|
28
|
+
UI.important("Please mopdify build.gradle file (usually under app/build.gradle):")
|
29
|
+
UI.message("Add before `android {` line:")
|
30
|
+
UI.message(" // Load keystore")
|
31
|
+
UI.message(" def keystorePropertiesFile = rootProject.file(\"keystore.properties\");")
|
32
|
+
UI.message(" def keystoreProperties = new Properties()")
|
33
|
+
UI.message(" keystoreProperties.load(new FileInputStream(keystorePropertiesFile))")
|
34
|
+
UI.message("Add after the closing bracket for `defaultConfig {`:")
|
35
|
+
UI.message(" signingConfigs {")
|
36
|
+
UI.message(" development {")
|
37
|
+
UI.message(" storeFile file(keystoreProperties['storeFile'])")
|
38
|
+
UI.message(" storePassword keystoreProperties['storePassword']")
|
39
|
+
UI.message(" keyAlias keystoreProperties['keyAlias']")
|
40
|
+
UI.message(" keyPassword keystoreProperties['keyPassword']")
|
41
|
+
UI.message(" }")
|
42
|
+
UI.message(" release {")
|
43
|
+
UI.message(" storeFile file(keystoreProperties['storeFile'])")
|
44
|
+
UI.message(" storePassword keystoreProperties['storePassword']")
|
45
|
+
UI.message(" keyAlias keystoreProperties['keyAlias']")
|
46
|
+
UI.message(" keyPassword keystoreProperties['keyPassword']")
|
47
|
+
UI.message(" }")
|
48
|
+
UI.message(" }")
|
49
|
+
UI.important("This will load the appropriate keystore during release builds")
|
50
|
+
|
51
|
+
UI.important("You can now run `fastlane flint type:development` and `fastlane flint type:release`")
|
52
|
+
UI.message("On the first run for each environment it will create the keystore for you.")
|
53
|
+
UI.message("From then on, it will automatically import the existing keystores.")
|
54
|
+
UI.message("For more information visit https://docs.fastlane.tools/actions/flint/")
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative 'encrypt'
|
2
|
+
require_relative 'git_helper'
|
3
|
+
require_relative 'generator'
|
4
|
+
|
5
|
+
module Fastlane
|
6
|
+
module Flint
|
7
|
+
# These functions should only be used while in (UI.) interactive mode
|
8
|
+
class ChangePassword
|
9
|
+
def self.update(params: nil, from: nil, to: nil)
|
10
|
+
ensure_ui_interactive
|
11
|
+
from ||= ChangePassword.ask_password(message: "Old passphrase for Git Repo: ", confirm: false)
|
12
|
+
to ||= ChangePassword.ask_password(message: "New passphrase for Git Repo: ", confirm: true)
|
13
|
+
GitHelper.clear_changes
|
14
|
+
encrypt = Encrypt.new
|
15
|
+
workspace = GitHelper.clone(params[:git_url],
|
16
|
+
params[:shallow_clone],
|
17
|
+
manual_password: from,
|
18
|
+
skip_docs: params[:skip_docs],
|
19
|
+
branch: params[:git_branch],
|
20
|
+
git_full_name: params[:git_full_name],
|
21
|
+
git_user_email: params[:git_user_email],
|
22
|
+
clone_branch_directly: params[:clone_branch_directly],
|
23
|
+
encrypt: encrypt)
|
24
|
+
encrypt.clear_password(params[:git_url])
|
25
|
+
encrypt.store_password(params[:git_url], to)
|
26
|
+
|
27
|
+
if params[:app_identifier].kind_of?(Array)
|
28
|
+
app_identifiers = params[:app_identifier]
|
29
|
+
else
|
30
|
+
app_identifiers = params[:app_identifier].to_s.split(/\s*,\s*/).uniq
|
31
|
+
end
|
32
|
+
app_identifier = app_identifiers[0].gsub! '.', '_'
|
33
|
+
|
34
|
+
for cert_type in Flint.environments do
|
35
|
+
alias_name = "%s-%s" % [app_identifier, cert_type]
|
36
|
+
keystore_name = "%s.keystore" % alias_name
|
37
|
+
Flint::Generator.update_keystore_password(workspace, keystore_name, alias_name, from, to)
|
38
|
+
end
|
39
|
+
|
40
|
+
message = "[fastlane] Changed passphrase"
|
41
|
+
GitHelper.commit_changes(workspace, message, params[:git_url], params[:git_branch], nil, encrypt)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.ask_password(message: "Passphrase for Git Repo: ", confirm: true)
|
45
|
+
ensure_ui_interactive
|
46
|
+
loop do
|
47
|
+
password = UI.password(message)
|
48
|
+
if confirm
|
49
|
+
password2 = UI.password("Type passphrase again: ")
|
50
|
+
if password == password2
|
51
|
+
return password
|
52
|
+
end
|
53
|
+
else
|
54
|
+
return password
|
55
|
+
end
|
56
|
+
UI.error("Passphrases differ. Try again")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.ensure_ui_interactive
|
61
|
+
raise "This code should only run in interactive mode" unless UI.interactive?
|
62
|
+
end
|
63
|
+
|
64
|
+
private_class_method :ensure_ui_interactive
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'commander'
|
2
|
+
|
3
|
+
require 'fastlane_core/configuration/configuration'
|
4
|
+
require_relative 'nuke'
|
5
|
+
require_relative 'git_helper'
|
6
|
+
require_relative 'change_password'
|
7
|
+
require_relative 'encrypt'
|
8
|
+
|
9
|
+
HighLine.track_eof = false
|
10
|
+
|
11
|
+
module Flint
|
12
|
+
class CommandsGenerator
|
13
|
+
include Commander::Methods
|
14
|
+
|
15
|
+
def run
|
16
|
+
|
17
|
+
global_option('--verbose') { FastlaneCore::Globals.verbose = true }
|
18
|
+
|
19
|
+
command :run do |c|
|
20
|
+
c.syntax = 'fastlane flint'
|
21
|
+
c.description = Flint::DESCRIPTION
|
22
|
+
|
23
|
+
FastlaneCore::CommanderGenerator.new.generate(Flint::Options.available_options, command: c)
|
24
|
+
|
25
|
+
c.action do |args, options|
|
26
|
+
if args.count > 0
|
27
|
+
FastlaneCore::UI.user_error!("Please run `fastlane flint [type]`, allowed values: development or release")
|
28
|
+
end
|
29
|
+
|
30
|
+
params = FastlaneCore::Configuration.create(Flint::Options.available_options, options.__hash__)
|
31
|
+
params.load_configuration_file("Flintfile")
|
32
|
+
Flint::Runner.new.run(params)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Flint.environments.each do |type|
|
37
|
+
command type do |c|
|
38
|
+
c.syntax = "fastlane flint #{type}"
|
39
|
+
c.description = "Run flint for a #{type} profile"
|
40
|
+
|
41
|
+
FastlaneCore::CommanderGenerator.new.generate(Flint::Options.available_options, command: c)
|
42
|
+
|
43
|
+
c.action do |args, options|
|
44
|
+
params = FastlaneCore::Configuration.create(Flint::Options.available_options, options.__hash__)
|
45
|
+
params.load_configuration_file("Flintfile") # this has to be done *before* overwriting the value
|
46
|
+
params[:type] = type.to_s
|
47
|
+
Flint::Runner.new.run(params)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
command :decrypt do |c|
|
53
|
+
c.syntax = "fastlane flint decrypt"
|
54
|
+
c.description = "Decrypts the repository and keeps it on the filesystem"
|
55
|
+
|
56
|
+
FastlaneCore::CommanderGenerator.new.generate(Flint::Options.available_options, command: c)
|
57
|
+
|
58
|
+
c.action do |args, options|
|
59
|
+
params = FastlaneCore::Configuration.create(Flint::Options.available_options, options.__hash__)
|
60
|
+
params.load_configuration_file("Flintfile")
|
61
|
+
encrypt = Encrypt.new
|
62
|
+
decrypted_repo = Flint::GitHelper.clone(params[:git_url],
|
63
|
+
params[:shallow_clone],
|
64
|
+
branch: params[:git_branch],
|
65
|
+
clone_branch_directly: params[:clone_branch_directly],
|
66
|
+
encrypt: encrypt)
|
67
|
+
UI.success("Repo is at: '#{decrypted_repo}'")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
command "nuke" do |c|
|
72
|
+
# We have this empty command here, since otherwise the normal `flint` command will be executed
|
73
|
+
c.syntax = "fastlane flint nuke"
|
74
|
+
c.description = "Delete all keystores"
|
75
|
+
c.action do |args, options|
|
76
|
+
FastlaneCore::UI.user_error!("Please run `fastlane flint nuke [type], allowed values: development and release.")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
["development", "release"].each do |type|
|
81
|
+
command "nuke #{type}" do |c|
|
82
|
+
c.syntax = "fastlane flint nuke #{type}"
|
83
|
+
c.description = "Delete all keystores of the type #{type}"
|
84
|
+
|
85
|
+
FastlaneCore::CommanderGenerator.new.generate(Flint::Options.available_options, command: c)
|
86
|
+
|
87
|
+
c.action do |args, options|
|
88
|
+
params = FastlaneCore::Configuration.create(Flint::Options.available_options, options.__hash__)
|
89
|
+
params.load_configuration_file("Flintfile")
|
90
|
+
Flint::Nuke.new.run(params, type: type.to_s)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
default_command(:run)
|
96
|
+
|
97
|
+
run!
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require_relative 'change_password'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Flint
|
5
|
+
class Encrypt
|
6
|
+
require 'base64'
|
7
|
+
require 'openssl'
|
8
|
+
require 'securerandom'
|
9
|
+
require 'shellwords'
|
10
|
+
|
11
|
+
def initialize()
|
12
|
+
# Keep the password in the memory so we can reuse it later on
|
13
|
+
@tmp_password = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def server_name(git_url)
|
17
|
+
["flint", git_url].join("_")
|
18
|
+
end
|
19
|
+
|
20
|
+
def password(git_url)
|
21
|
+
password = ENV["FLINT_PASSWORD"]
|
22
|
+
unless password
|
23
|
+
if @tmp_password
|
24
|
+
password = @tmp_password
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
unless password && password != ''
|
29
|
+
if !UI.interactive?
|
30
|
+
UI.error("The FLINT_PASSWORD environment variable did not contain a password.")
|
31
|
+
UI.error("Bailing out instead of asking for a password, since this is non-interactive mode.")
|
32
|
+
UI.user_error!("Try setting the FLINT_PASSWORD environment variable, or temporarily enable interactive mode to store a password.")
|
33
|
+
else
|
34
|
+
UI.important("Enter the passphrase that should be used to encrypt/decrypt your keystores")
|
35
|
+
UI.important("Make sure to remember the password, as you'll need it when you run flint again")
|
36
|
+
password = ChangePassword.ask_password(confirm: true)
|
37
|
+
store_password(git_url, password)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
return password
|
42
|
+
end
|
43
|
+
|
44
|
+
def store_password(git_url, password)
|
45
|
+
@tmp_password = password
|
46
|
+
end
|
47
|
+
|
48
|
+
def clear_password(git_url)
|
49
|
+
@tmp_password = ""
|
50
|
+
end
|
51
|
+
|
52
|
+
def encrypt_repo(path: nil, git_url: nil)
|
53
|
+
iterate(path) do |current|
|
54
|
+
encrypt(path: current,
|
55
|
+
password: password(git_url))
|
56
|
+
UI.success("🔒 Encrypted '#{File.basename(current)}'") if FastlaneCore::Globals.verbose?
|
57
|
+
end
|
58
|
+
UI.success("🔒 Successfully encrypted keystores repo")
|
59
|
+
end
|
60
|
+
|
61
|
+
def decrypt_repo(path: nil, git_url: nil, manual_password: nil)
|
62
|
+
iterate(path) do |current|
|
63
|
+
begin
|
64
|
+
decrypt(path: current,
|
65
|
+
password: manual_password || password(git_url))
|
66
|
+
rescue
|
67
|
+
UI.error("Couldn't decrypt the repo, please make sure you enter the right password! %s" % manual_password || password(git_url))
|
68
|
+
UI.user_error!("Invalid password passed via 'FLINT_PASSWORD'") if ENV["FLINT_PASSWORD"]
|
69
|
+
clear_password(git_url)
|
70
|
+
password(git_url)
|
71
|
+
decrypt_repo(path: path, git_url: git_url)
|
72
|
+
return
|
73
|
+
end
|
74
|
+
UI.success("🔓 Decrypted '#{File.basename(current)}'") if FastlaneCore::Globals.verbose?
|
75
|
+
end
|
76
|
+
UI.success("🔓 Successfully decrypted keystores repo")
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def iterate(source_path)
|
82
|
+
Dir[File.join(source_path, "**", "*.{keystore}")].each do |path|
|
83
|
+
next if File.directory?(path)
|
84
|
+
yield(path)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# We encrypt with MD5 because that was the most common default value in older fastlane versions which used the local OpenSSL installation
|
89
|
+
# A more secure key and IV generation is needed in the future
|
90
|
+
# IV should be randomly generated and provided unencrypted
|
91
|
+
# salt should be randomly generated and provided unencrypted (like in the current implementation)
|
92
|
+
# key should be generated with OpenSSL::KDF::pbkdf2_hmac with properly chosen parameters
|
93
|
+
# Short explanation about salt and IV: https://stackoverflow.com/a/1950674/6324550
|
94
|
+
def encrypt(path: nil, password: nil)
|
95
|
+
UI.user_error!("No password supplied") if password.to_s.strip.length == 0
|
96
|
+
|
97
|
+
data_to_encrypt = File.read(path)
|
98
|
+
salt = SecureRandom.random_bytes(8)
|
99
|
+
|
100
|
+
cipher = OpenSSL::Cipher.new('AES-256-CBC')
|
101
|
+
cipher.encrypt
|
102
|
+
cipher.pkcs5_keyivgen(password, salt, 1, "MD5")
|
103
|
+
encrypted_data = "Salted__" + salt + cipher.update(data_to_encrypt) + cipher.final
|
104
|
+
|
105
|
+
File.write(path, Base64.encode64(encrypted_data))
|
106
|
+
rescue FastlaneCore::Interface::FastlaneError
|
107
|
+
raise
|
108
|
+
rescue => error
|
109
|
+
UI.error(error.to_s)
|
110
|
+
UI.crash!("Error encrypting '#{path}'")
|
111
|
+
end
|
112
|
+
|
113
|
+
# The encryption parameters in this implementations reflect the old behaviour which depended on the users' local OpenSSL version
|
114
|
+
# 1.0.x OpenSSL and earlier versions use MD5, 1.1.0c and newer uses SHA256, we try both before giving an error
|
115
|
+
def decrypt(path: nil, password: nil, hash_algorithm: "MD5")
|
116
|
+
stored_data = Base64.decode64(File.read(path))
|
117
|
+
salt = stored_data[8..15]
|
118
|
+
data_to_decrypt = stored_data[16..-1]
|
119
|
+
|
120
|
+
decipher = OpenSSL::Cipher.new('AES-256-CBC')
|
121
|
+
decipher.decrypt
|
122
|
+
decipher.pkcs5_keyivgen(password, salt, 1, hash_algorithm)
|
123
|
+
|
124
|
+
decrypted_data = decipher.update(data_to_decrypt) + decipher.final
|
125
|
+
|
126
|
+
File.binwrite(path, decrypted_data)
|
127
|
+
rescue => error
|
128
|
+
fallback_hash_algorithm = "SHA256"
|
129
|
+
if hash_algorithm != fallback_hash_algorithm
|
130
|
+
decrypt(path, password, fallback_hash_algorithm)
|
131
|
+
else
|
132
|
+
UI.error(error.to_s)
|
133
|
+
UI.crash!("Error decrypting '#{path}'")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
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 FlintHelper
|
8
|
+
# class methods that you define here become available in your action
|
9
|
+
# as `Helper::FlintHelper.your_method`
|
10
|
+
#
|
11
|
+
def self.show_message
|
12
|
+
UI.message("Hello from the flint plugin helper!")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
module Flint
|
6
|
+
# Generate missing resources
|
7
|
+
class Generator
|
8
|
+
def self.generate_keystore(params, keystore_name, alias_name, password)
|
9
|
+
# Ensure output dir exists
|
10
|
+
output_dir = File.join(params[:workspace], "certs")
|
11
|
+
FileUtils.mkdir_p output_dir
|
12
|
+
output_path = File.join(output_dir, keystore_name)
|
13
|
+
|
14
|
+
full_name = params[:full_name]
|
15
|
+
org = params[:orgization]
|
16
|
+
org_unit = params[:orgization_unit]
|
17
|
+
city_locality = params[:city]
|
18
|
+
state_province = params[:state]
|
19
|
+
country = params[:country]
|
20
|
+
valid_days = 10000 # > 27 years
|
21
|
+
|
22
|
+
cmd = "keytool -genkey -v -keystore %s -alias %s " % [output_path, alias_name]
|
23
|
+
cmd << "-keyalg RSA -keysize 2048 -validity %s -keypass %s -storepass %s " % [valid_days, password, password]
|
24
|
+
cmd << "-dname \"CN=#{full_name}, OU=#{org_unit}, O=#{org}, L=#{city_locality}, S=#{state_province}, C=#{country}\""
|
25
|
+
|
26
|
+
begin
|
27
|
+
output = IO.popen(cmd)
|
28
|
+
error = output.read
|
29
|
+
output.close
|
30
|
+
raise error unless $?.exitstatus == 0
|
31
|
+
rescue => ex
|
32
|
+
raise ex
|
33
|
+
end
|
34
|
+
|
35
|
+
return output_path
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.update_keystore_password(workspace, keystore_name, alias_name, password, new_password)
|
39
|
+
output_dir = File.join(workspace, "certs")
|
40
|
+
output_path = File.join(output_dir, keystore_name)
|
41
|
+
|
42
|
+
if File.file?(output_path)
|
43
|
+
cmd = "keytool -storepasswd -v -keystore %s -storepass %s -new %s" % [output_path, password, new_password]
|
44
|
+
begin
|
45
|
+
output = IO.popen(cmd)
|
46
|
+
error = output.read
|
47
|
+
output.close
|
48
|
+
raise error unless $?.exitstatus == 0
|
49
|
+
rescue => ex
|
50
|
+
raise ex
|
51
|
+
end
|
52
|
+
|
53
|
+
cmd = "keytool -keypasswd -v -keystore %s -alias %s -keypass %s -storepass %s -new %s" % [
|
54
|
+
output_path, alias_name, password, new_password, new_password]
|
55
|
+
|
56
|
+
begin
|
57
|
+
output = IO.popen(cmd)
|
58
|
+
error = output.read
|
59
|
+
output.close
|
60
|
+
raise error unless $?.exitstatus == 0
|
61
|
+
rescue => ex
|
62
|
+
raise ex
|
63
|
+
end
|
64
|
+
else
|
65
|
+
UI.message("output_path does not exist %s" % output_path)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|