rails_credentials_manager 0.0.1.pre.alpha
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/.rspec +3 -0
- data/.vscode/settings.json +6 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +86 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/rails_credentials_manager +5 -0
- data/bin/rcm +1 -0
- data/bin/setup +8 -0
- data/lib/rails_credentials_manager/base.rb +51 -0
- data/lib/rails_credentials_manager/commands.rb +82 -0
- data/lib/rails_credentials_manager/conflicts.rb +120 -0
- data/lib/rails_credentials_manager/diff.rb +79 -0
- data/lib/rails_credentials_manager/get.rb +18 -0
- data/lib/rails_credentials_manager/list.rb +26 -0
- data/lib/rails_credentials_manager/set.rb +60 -0
- data/lib/rails_credentials_manager/version.rb +3 -0
- data/lib/rails_credentials_manager.rb +29 -0
- data/rails_credentials_manager.gemspec +37 -0
- metadata +168 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0171fe9558f8b916c9c46a6d3bec41043e9215836ec05f3d72ff243372df4ffb
|
4
|
+
data.tar.gz: 41422006a52a35ef9f93d561b3bbd16ed0c7e35e68c65d9dd04ca7ab884e121a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9aac4f9fa8ad4addfcf073f4c416e71f99ba0290d75cf813294420c15d1c11c86d1ee362b67385bcb2b1b3b2dc3a760039ac82ab5ccacc5567f0a7af47ef1a7b
|
7
|
+
data.tar.gz: f405bf4561c3c68b28822e909b04455302bea73bab00a8cce230884215d7d88696b4c0f0373e4c918ec8cf25ba4448291c05e95d9b2cfdb7f760bbbd0349b10c
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rails_credentials_manager (0.0.1.pre.alpha)
|
5
|
+
activesupport (>= 6.0)
|
6
|
+
dry-cli (~> 0.7)
|
7
|
+
hash_diff (~> 1.0)
|
8
|
+
pastel (~> 0.8)
|
9
|
+
tty-command (~> 0.10)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
activesupport (7.0.3.1)
|
15
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
|
+
i18n (>= 1.6, < 2)
|
17
|
+
minitest (>= 5.1)
|
18
|
+
tzinfo (~> 2.0)
|
19
|
+
ast (2.4.2)
|
20
|
+
concurrent-ruby (1.1.10)
|
21
|
+
diff-lcs (1.5.0)
|
22
|
+
dry-cli (0.7.0)
|
23
|
+
hash_diff (1.0.0)
|
24
|
+
i18n (1.12.0)
|
25
|
+
concurrent-ruby (~> 1.0)
|
26
|
+
json (2.6.2)
|
27
|
+
minitest (5.16.2)
|
28
|
+
parallel (1.22.1)
|
29
|
+
parser (3.1.2.0)
|
30
|
+
ast (~> 2.4.1)
|
31
|
+
pastel (0.8.0)
|
32
|
+
tty-color (~> 0.5)
|
33
|
+
rainbow (3.1.1)
|
34
|
+
rake (13.0.6)
|
35
|
+
regexp_parser (2.5.0)
|
36
|
+
rexml (3.2.5)
|
37
|
+
rspec (3.11.0)
|
38
|
+
rspec-core (~> 3.11.0)
|
39
|
+
rspec-expectations (~> 3.11.0)
|
40
|
+
rspec-mocks (~> 3.11.0)
|
41
|
+
rspec-core (3.11.0)
|
42
|
+
rspec-support (~> 3.11.0)
|
43
|
+
rspec-expectations (3.11.0)
|
44
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
45
|
+
rspec-support (~> 3.11.0)
|
46
|
+
rspec-mocks (3.11.1)
|
47
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
48
|
+
rspec-support (~> 3.11.0)
|
49
|
+
rspec-support (3.11.0)
|
50
|
+
rubocop (1.31.2)
|
51
|
+
json (~> 2.3)
|
52
|
+
parallel (~> 1.10)
|
53
|
+
parser (>= 3.1.0.0)
|
54
|
+
rainbow (>= 2.2.2, < 4.0)
|
55
|
+
regexp_parser (>= 1.8, < 3.0)
|
56
|
+
rexml (>= 3.2.5, < 4.0)
|
57
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
58
|
+
ruby-progressbar (~> 1.7)
|
59
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
60
|
+
rubocop-ast (1.19.1)
|
61
|
+
parser (>= 3.1.1.0)
|
62
|
+
rubocop-performance (1.14.3)
|
63
|
+
rubocop (>= 1.7.0, < 2.0)
|
64
|
+
rubocop-ast (>= 0.4.0)
|
65
|
+
ruby-progressbar (1.11.0)
|
66
|
+
standard (1.13.0)
|
67
|
+
rubocop (= 1.31.2)
|
68
|
+
rubocop-performance (= 1.14.3)
|
69
|
+
tty-color (0.6.0)
|
70
|
+
tty-command (0.10.1)
|
71
|
+
pastel (~> 0.8)
|
72
|
+
tzinfo (2.0.5)
|
73
|
+
concurrent-ruby (~> 1.0)
|
74
|
+
unicode-display_width (2.2.0)
|
75
|
+
|
76
|
+
PLATFORMS
|
77
|
+
x86_64-darwin-21
|
78
|
+
|
79
|
+
DEPENDENCIES
|
80
|
+
rails_credentials_manager!
|
81
|
+
rake (~> 13.0)
|
82
|
+
rspec (~> 3.0)
|
83
|
+
standard (~> 1.13)
|
84
|
+
|
85
|
+
BUNDLED WITH
|
86
|
+
2.2.29
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Uscreen
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Rails::Credentials::Manager
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rails/credentials/manager`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'rails_credentials_manager'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle install
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install rails_credentials_manager
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rails_credentials_manager.
|
36
|
+
|
37
|
+
## License
|
38
|
+
|
39
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "rails_credentials_manager"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/rcm
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
bin/rails_credentials_manager
|
data/bin/setup
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module RailsCredentialsManager
|
2
|
+
class Base
|
3
|
+
AVAILABLE_ENVIRONMENTS = %i[development test staging production].freeze
|
4
|
+
|
5
|
+
def initialize(environment_list)
|
6
|
+
@environment_list = environment_list.map(&:to_sym).keep_if { |env| env.to_sym.in?(AVAILABLE_ENVIRONMENTS) }
|
7
|
+
abort pastel.red("No valid environments specified. Valid example: `-e development,test`") if @environment_list.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
def configs
|
11
|
+
@configs ||= @environment_list.inject({}) { |conf, env| conf.merge({env => config_for(env)}) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def pastel
|
15
|
+
@pastel ||= Pastel.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def print_key_and_value(key, value)
|
19
|
+
print pastel.blue("\t#{key}:\t")
|
20
|
+
|
21
|
+
case value
|
22
|
+
when "not set"
|
23
|
+
print pastel.red("not set")
|
24
|
+
when ->(v) { v.is_a?(String) }
|
25
|
+
print pastel.yellow(value)
|
26
|
+
else
|
27
|
+
print pastel.yellow(value.inspect)
|
28
|
+
end
|
29
|
+
|
30
|
+
print "\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def config_has_keys?(config, keys_path)
|
34
|
+
dig_keys = keys_path[0...-1]
|
35
|
+
return config.key?(keys_path.first) if dig_keys.empty?
|
36
|
+
|
37
|
+
config.dig(*dig_keys)&.key?(keys_path.last)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def config_for(environment)
|
43
|
+
ActiveSupport::EncryptedConfiguration.new(
|
44
|
+
config_path: "config/credentials/#{environment}.yml.enc",
|
45
|
+
key_path: "config/credentials/#{environment}.key",
|
46
|
+
env_key: "RAILS_MASTER_KEY",
|
47
|
+
raise_if_missing_key: true
|
48
|
+
).config
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module RailsCredentialsManager
|
2
|
+
module CLI
|
3
|
+
module Commands
|
4
|
+
class Get < Dry::CLI::Command
|
5
|
+
desc "Find keys in credentials files for each environment"
|
6
|
+
|
7
|
+
argument :keys, type: :array, required: true, desc: "keys to show"
|
8
|
+
|
9
|
+
option :environments,
|
10
|
+
aliases: ["e"],
|
11
|
+
type: :array,
|
12
|
+
default: RailsCredentialsManager::Base::AVAILABLE_ENVIRONMENTS,
|
13
|
+
desc: "filter for environments"
|
14
|
+
|
15
|
+
def call(keys:, environments:, **)
|
16
|
+
RailsCredentialsManager::Get.new(environments).perform(keys)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class List < Dry::CLI::Command
|
21
|
+
desc "List of all keys for each environment"
|
22
|
+
|
23
|
+
option :environments,
|
24
|
+
aliases: ["e"],
|
25
|
+
type: :array,
|
26
|
+
default: RailsCredentialsManager::Base::AVAILABLE_ENVIRONMENTS,
|
27
|
+
desc: "filter for environments"
|
28
|
+
|
29
|
+
def call(environments:, **)
|
30
|
+
RailsCredentialsManager::List.new(environments).perform
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Set < Dry::CLI::Command
|
35
|
+
desc "Set a value to the key provided for given environments"
|
36
|
+
|
37
|
+
argument :key, type: :string, required: true
|
38
|
+
argument :value, type: :string, required: true
|
39
|
+
|
40
|
+
option :environments,
|
41
|
+
aliases: ["e"],
|
42
|
+
type: :array,
|
43
|
+
default: [],
|
44
|
+
desc: "filter for environments"
|
45
|
+
|
46
|
+
def call(key:, value:, environments:, **)
|
47
|
+
RailsCredentialsManager::Set.new(environments).perform(key, value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Diff < Dry::CLI::Command
|
52
|
+
desc "Show credentials diff between given branch (heroku by default) and current changes"
|
53
|
+
|
54
|
+
argument :branch, type: :string, default: "heroku", required: false
|
55
|
+
|
56
|
+
option :environments,
|
57
|
+
aliases: ["e"],
|
58
|
+
type: :array,
|
59
|
+
default: RailsCredentialsManager::Base::AVAILABLE_ENVIRONMENTS,
|
60
|
+
desc: "filter for environments"
|
61
|
+
|
62
|
+
def call(branch:, environments:, **)
|
63
|
+
RailsCredentialsManager::Diff.new(environments).perform(branch)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Conflicts < Dry::CLI::Command
|
68
|
+
desc "Help to resolve merge conflicts for credentials"
|
69
|
+
|
70
|
+
option :environments,
|
71
|
+
aliases: ["e"],
|
72
|
+
type: :array,
|
73
|
+
default: RailsCredentialsManager::Base::AVAILABLE_ENVIRONMENTS,
|
74
|
+
desc: "filter for environments"
|
75
|
+
|
76
|
+
def call(environments:, **)
|
77
|
+
RailsCredentialsManager::Conflicts.new(environments).perform
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require "active_support/encrypted_file"
|
2
|
+
|
3
|
+
module RailsCredentialsManager
|
4
|
+
class Conflicts < RailsCredentialsManager::Base
|
5
|
+
REGEXP = /^<{7} HEAD(\r?\n(?!={7}\r?\n).*)*\r?\n={7}(\r?\n(?!>{7} ).*)*\r?\n>{7} \S+/m
|
6
|
+
|
7
|
+
def perform
|
8
|
+
@environment_list.each do |env|
|
9
|
+
puts pastel.green("#{env}:")
|
10
|
+
cred_content = File.read("config/credentials/#{env}.yml.enc")
|
11
|
+
parsed_conflict = REGEXP.match(cred_content)
|
12
|
+
|
13
|
+
unless parsed_conflict
|
14
|
+
puts "✅ No conflicts found"
|
15
|
+
next
|
16
|
+
end
|
17
|
+
|
18
|
+
our_config = config_to_compare_for(env, parsed_conflict[1].strip)
|
19
|
+
their_config = config_to_compare_for(env, parsed_conflict[2].strip)
|
20
|
+
@merged_config = our_config.deep_merge(their_config)
|
21
|
+
deep_print_diff(HashDiff.diff(their_config, our_config))
|
22
|
+
|
23
|
+
# removes "---\n" in the very beginning
|
24
|
+
merged_config_as_string = @merged_config.deep_stringify_keys.to_yaml[4..]
|
25
|
+
rewrite_config_for(env, merged_config_as_string)
|
26
|
+
puts "✅ Merged config for #{env} has been saved"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def config_to_compare_for(environment, encripted_file_content)
|
33
|
+
deserialize(decript(key_for(environment), encripted_file_content)).deep_symbolize_keys
|
34
|
+
end
|
35
|
+
|
36
|
+
def key_for(environment)
|
37
|
+
Pathname.new("config/credentials/#{environment}.key").binread.strip
|
38
|
+
end
|
39
|
+
|
40
|
+
def decript(key, content)
|
41
|
+
ActiveSupport::MessageEncryptor.new([key].pack("H*"), cipher: "aes-128-gcm")
|
42
|
+
.decrypt_and_verify(content)
|
43
|
+
end
|
44
|
+
|
45
|
+
def deserialize(raw_config)
|
46
|
+
# rubocop:disable Security/YAMLLoad
|
47
|
+
YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(raw_config) : YAML.load(raw_config)
|
48
|
+
# rubocop:enable Security/YAMLLoad
|
49
|
+
end
|
50
|
+
|
51
|
+
def deep_print_diff(diff, key_path = [])
|
52
|
+
if diff.empty?
|
53
|
+
puts pastel.green "\t✅ No differences"
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
diff.each do |current_key, values|
|
58
|
+
if values.is_a?(Hash)
|
59
|
+
values.each { |k, val| deep_print_diff({k => val}, key_path + [current_key]) }
|
60
|
+
elsif values.is_a?(Array)
|
61
|
+
prev_value, current_value = values
|
62
|
+
print_diff_row(key_path + [current_key], prev_value, current_value)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def print_diff_row(key_path, prev_value, current_value)
|
68
|
+
if prev_value == HashDiff::NO_VALUE
|
69
|
+
if current_value.is_a?(Hash)
|
70
|
+
current_value.each { |value_key, value| print_diff_row(key_path + [value_key], prev_value, value) }
|
71
|
+
else
|
72
|
+
puts "\tADDED #{pastel.cyan("THEIR")} new key #{pastel.blue(key_path.join("."))}: #{pastel.yellow(current_value.inspect)}"
|
73
|
+
end
|
74
|
+
elsif current_value == HashDiff::NO_VALUE
|
75
|
+
if prev_value.is_a?(Hash)
|
76
|
+
prev_value.each { |value_key, value| print_diff_row(key_path + [value_key], value, current_value) }
|
77
|
+
else
|
78
|
+
puts "\tADDED #{pastel.cyan("OUR")} new key #{pastel.blue(key_path.join("."))}: #{pastel.yellow(prev_value.inspect)}"
|
79
|
+
end
|
80
|
+
else # Changed
|
81
|
+
puts "\t❗️ The key #{pastel.blue(key_path.join("."))} changed in both branches, their: #{pastel.yellow(prev_value.inspect)}, our: #{pastel.yellow(current_value.inspect)}"
|
82
|
+
puts "Which one should we use? Please type `their` or `our` to apply particular change or enter to abort."
|
83
|
+
print "> "
|
84
|
+
value_to_use = $stdin.gets.chomp
|
85
|
+
merge_input_handler(value_to_use, key_path, prev_value, current_value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def merge_input_handler(input_value, key_path, their, our)
|
90
|
+
if input_value == "their"
|
91
|
+
deep_set!(@merged_config, key_path, their)
|
92
|
+
puts "✅ #{pastel.blue(key_path.join("."))} set as #{pastel.yellow(their.inspect)}"
|
93
|
+
elsif input_value == "our"
|
94
|
+
deep_set!(@merged_config, key_path, our)
|
95
|
+
puts "✅ #{pastel.blue(key_path.join("."))} set as #{pastel.yellow(our.inspect)}"
|
96
|
+
else
|
97
|
+
abort("❌ Merge aborted. Config did not save.")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def deep_set!(obj, keys, value)
|
102
|
+
key = keys.first
|
103
|
+
if keys.length == 1
|
104
|
+
obj[key] = value
|
105
|
+
else
|
106
|
+
obj[key] = {} unless obj[key]
|
107
|
+
deep_set!(obj[key], keys.slice(1..-1), value)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def rewrite_config_for(environment, new_config)
|
112
|
+
ActiveSupport::EncryptedFile.new(
|
113
|
+
content_path: "config/credentials/#{environment}.yml.enc",
|
114
|
+
key_path: "config/credentials/#{environment}.key",
|
115
|
+
env_key: "RAILS_MASTER_KEY",
|
116
|
+
raise_if_missing_key: true
|
117
|
+
).write(new_config)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "tty-command"
|
2
|
+
|
3
|
+
module RailsCredentialsManager
|
4
|
+
class Diff < RailsCredentialsManager::Base
|
5
|
+
def perform(branch_to_compare)
|
6
|
+
cmd = TTY::Command.new(pty: true, printer: :null)
|
7
|
+
|
8
|
+
configs.each do |env, config|
|
9
|
+
puts pastel.green("#{env}:")
|
10
|
+
|
11
|
+
result = cmd.run!("echo `git show origin/#{branch_to_compare}:config/credentials/#{env}.yml.enc`")
|
12
|
+
encripted_file_content = result.out.strip
|
13
|
+
|
14
|
+
if encripted_file_content.blank?
|
15
|
+
puts "❗️ Can not find #{env} credentials file in origin/#{branch_to_compare} branch"
|
16
|
+
next
|
17
|
+
end
|
18
|
+
branch_config = config_to_compare_for(env, encripted_file_content)
|
19
|
+
|
20
|
+
deep_print_diff(HashDiff.diff(branch_config, config))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def config_to_compare_for(environment, encripted_file_content)
|
27
|
+
deserialize(decript(key_for(environment), encripted_file_content)).deep_symbolize_keys
|
28
|
+
end
|
29
|
+
|
30
|
+
def key_for(environment)
|
31
|
+
Pathname.new("config/credentials/#{environment}.key").binread.strip
|
32
|
+
end
|
33
|
+
|
34
|
+
def decript(key, content)
|
35
|
+
ActiveSupport::MessageEncryptor.new([key].pack("H*"), cipher: "aes-128-gcm")
|
36
|
+
.decrypt_and_verify(content)
|
37
|
+
end
|
38
|
+
|
39
|
+
def deserialize(raw_config)
|
40
|
+
# rubocop:disable Security/YAMLLoad
|
41
|
+
YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(raw_config) : YAML.load(raw_config)
|
42
|
+
# rubocop:enable Security/YAMLLoad
|
43
|
+
end
|
44
|
+
|
45
|
+
def deep_print_diff(diff, key_path = [])
|
46
|
+
if diff.empty?
|
47
|
+
puts pastel.green "\t✅ No differences"
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
diff.each do |current_key, values|
|
52
|
+
if values.is_a?(Hash)
|
53
|
+
values.each { |k, val| deep_print_diff({k => val}, key_path + [current_key]) }
|
54
|
+
elsif values.is_a?(Array)
|
55
|
+
prev_value, current_value = values
|
56
|
+
print_diff_row(key_path + [current_key], prev_value, current_value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def print_diff_row(key_path, prev_value, current_value)
|
62
|
+
if prev_value == HashDiff::NO_VALUE
|
63
|
+
if current_value.is_a?(Hash)
|
64
|
+
current_value.each { |value_key, value| print_diff_row(key_path + [value_key], prev_value, value) }
|
65
|
+
else
|
66
|
+
puts pastel.bright_green("\t#{key_path.join(".")}:\t ADDED: #{current_value.inspect}")
|
67
|
+
end
|
68
|
+
elsif current_value == HashDiff::NO_VALUE
|
69
|
+
if prev_value.is_a?(Hash)
|
70
|
+
prev_value.each { |value_key, value| print_diff_row(key_path + [value_key], value, current_value) }
|
71
|
+
else
|
72
|
+
puts pastel.bright_red("\t#{key_path.join(".")}:\tREMOVED: was #{prev_value.inspect}")
|
73
|
+
end
|
74
|
+
else # Changed
|
75
|
+
puts pastel.bright_cyan("\t#{key_path.join(".")}:\tCHANGED: #{prev_value.inspect} => #{current_value.inspect}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RailsCredentialsManager
|
2
|
+
class Get < RailsCredentialsManager::Base
|
3
|
+
def perform(keys)
|
4
|
+
abort pastel.red("At least one key required") if keys.empty?
|
5
|
+
|
6
|
+
keys_with_path = keys.index_with { |key| key.split(".").map(&:to_sym) }
|
7
|
+
|
8
|
+
configs.each do |env, config|
|
9
|
+
puts pastel.green("#{env}:")
|
10
|
+
|
11
|
+
keys_with_path.each do |full_key, key_path|
|
12
|
+
value = config_has_keys?(config, key_path) ? configs[env].dig(*key_path) : "not set"
|
13
|
+
print_key_and_value(full_key, value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RailsCredentialsManager
|
2
|
+
class List < RailsCredentialsManager::Base
|
3
|
+
def perform
|
4
|
+
configs.each do |env, config|
|
5
|
+
puts pastel.green("#{env}:")
|
6
|
+
|
7
|
+
config.each do |key, value|
|
8
|
+
deep_print_key_and_value(value, [key])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def deep_print_key_and_value(object, key_path)
|
16
|
+
object.each do |current_key, value|
|
17
|
+
if value.is_a?(Hash)
|
18
|
+
value.each { |k, val| deep_print_key_and_value({k => val}, key_path + [current_key]) }
|
19
|
+
else
|
20
|
+
full_key = (key_path + [current_key]).join(".")
|
21
|
+
print_key_and_value(full_key, value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module EaRailsCredentialsManagerrl
|
2
|
+
class Set < RailsCredentialsManager::Base
|
3
|
+
def perform(key, new_value)
|
4
|
+
key_with_path = key.split(".").map(&:to_sym)
|
5
|
+
new_value = normalize_new_value(new_value)
|
6
|
+
|
7
|
+
configs.each do |env, config|
|
8
|
+
puts pastel.green("#{env}:")
|
9
|
+
update_config = true
|
10
|
+
|
11
|
+
if config_has_keys?(config, key_with_path)
|
12
|
+
previous_value = configs[env].dig(*key_with_path)
|
13
|
+
if previous_value == new_value
|
14
|
+
value = "NOT CHANGED. The value already the same: #{new_value.inspect}"
|
15
|
+
update_config = false
|
16
|
+
else
|
17
|
+
value = "CHANGED: #{new_value.inspect}; previous value: #{previous_value.inspect}"
|
18
|
+
end
|
19
|
+
else
|
20
|
+
value = "ADDED: #{new_value}"
|
21
|
+
end
|
22
|
+
|
23
|
+
print_key_and_value(key, value)
|
24
|
+
|
25
|
+
if update_config
|
26
|
+
updated_config = config.dup
|
27
|
+
deep_set!(updated_config, key_with_path, new_value)
|
28
|
+
# removes "---\n" in the very beginning
|
29
|
+
config_as_string = updated_config.deep_stringify_keys.to_yaml[4..]
|
30
|
+
rewrite_config_for(env, config_as_string)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def rewrite_config_for(environment, new_config)
|
36
|
+
ActiveSupport::EncryptedConfiguration.new(
|
37
|
+
config_path: "config/credentials/#{environment}.yml.enc",
|
38
|
+
key_path: "config/credentials/#{environment}.key",
|
39
|
+
env_key: "RAILS_MASTER_KEY",
|
40
|
+
raise_if_missing_key: true
|
41
|
+
).write(new_config)
|
42
|
+
end
|
43
|
+
|
44
|
+
def deep_set!(obj, keys, value)
|
45
|
+
key = keys.first
|
46
|
+
if keys.length == 1
|
47
|
+
obj[key] = value
|
48
|
+
else
|
49
|
+
obj[key] = {} unless obj[key]
|
50
|
+
deep_set!(obj[key], keys.slice(1..-1), value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def normalize_new_value(new_value)
|
55
|
+
return if new_value.in?(%w[nil null])
|
56
|
+
|
57
|
+
new_value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext"
|
3
|
+
require "active_support/encrypted_configuration"
|
4
|
+
require "dry/cli"
|
5
|
+
require "pastel"
|
6
|
+
require "hash_diff"
|
7
|
+
|
8
|
+
require_relative "rails_credentials_manager/base"
|
9
|
+
require_relative "rails_credentials_manager/commands"
|
10
|
+
require_relative "rails_credentials_manager/get"
|
11
|
+
require_relative "rails_credentials_manager/list"
|
12
|
+
require_relative "rails_credentials_manager/set"
|
13
|
+
require_relative "rails_credentials_manager/diff"
|
14
|
+
require_relative "rails_credentials_manager/conflicts"
|
15
|
+
require_relative "rails_credentials_manager/version"
|
16
|
+
|
17
|
+
module RailsCredentialsManager
|
18
|
+
module CLI
|
19
|
+
module Commands
|
20
|
+
extend Dry::CLI::Registry
|
21
|
+
|
22
|
+
register "get", Get
|
23
|
+
register "list", List
|
24
|
+
register "set", Set
|
25
|
+
register "diff", Diff
|
26
|
+
register "conflicts", Conflicts
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative "lib/rails_credentials_manager/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "rails_credentials_manager"
|
5
|
+
spec.version = RailsCredentialsManager::VERSION
|
6
|
+
spec.authors = ["Sergey Andronov"]
|
7
|
+
spec.email = ["dev@uscreen.tv"]
|
8
|
+
|
9
|
+
spec.summary = "The tool what you miss for managing Rails credentials"
|
10
|
+
spec.description = "The tool what you miss for managing Rails credentials"
|
11
|
+
spec.homepage = "https://github.com/Uscreen-video/rails_credentials_manager"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = ">= 2.7.0"
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/Uscreen-video/rails_credentials_manager/blob/main/CHANGELOG.md"
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
23
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
spec.executables = %w[rails_credentials_manager rcm]
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_dependency "activesupport", ">= 6.0"
|
30
|
+
spec.add_dependency "dry-cli", "~> 0.7"
|
31
|
+
spec.add_dependency "tty-command", "~> 0.10"
|
32
|
+
spec.add_dependency "pastel", "~> 0.8"
|
33
|
+
spec.add_dependency "hash_diff", "~> 1.0"
|
34
|
+
|
35
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
36
|
+
spec.add_development_dependency "standard", "~> 1.13"
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_credentials_manager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.pre.alpha
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergey Andronov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-cli
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: tty-command
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.10'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.10'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pastel
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hash_diff
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: standard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.13'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.13'
|
111
|
+
description: The tool what you miss for managing Rails credentials
|
112
|
+
email:
|
113
|
+
- dev@uscreen.tv
|
114
|
+
executables:
|
115
|
+
- rails_credentials_manager
|
116
|
+
- rcm
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- ".rspec"
|
121
|
+
- ".vscode/settings.json"
|
122
|
+
- CHANGELOG.md
|
123
|
+
- Gemfile
|
124
|
+
- Gemfile.lock
|
125
|
+
- LICENSE.txt
|
126
|
+
- README.md
|
127
|
+
- Rakefile
|
128
|
+
- bin/console
|
129
|
+
- bin/rails_credentials_manager
|
130
|
+
- bin/rcm
|
131
|
+
- bin/setup
|
132
|
+
- lib/rails_credentials_manager.rb
|
133
|
+
- lib/rails_credentials_manager/base.rb
|
134
|
+
- lib/rails_credentials_manager/commands.rb
|
135
|
+
- lib/rails_credentials_manager/conflicts.rb
|
136
|
+
- lib/rails_credentials_manager/diff.rb
|
137
|
+
- lib/rails_credentials_manager/get.rb
|
138
|
+
- lib/rails_credentials_manager/list.rb
|
139
|
+
- lib/rails_credentials_manager/set.rb
|
140
|
+
- lib/rails_credentials_manager/version.rb
|
141
|
+
- rails_credentials_manager.gemspec
|
142
|
+
homepage: https://github.com/Uscreen-video/rails_credentials_manager
|
143
|
+
licenses:
|
144
|
+
- MIT
|
145
|
+
metadata:
|
146
|
+
homepage_uri: https://github.com/Uscreen-video/rails_credentials_manager
|
147
|
+
source_code_uri: https://github.com/Uscreen-video/rails_credentials_manager
|
148
|
+
changelog_uri: https://github.com/Uscreen-video/rails_credentials_manager/blob/main/CHANGELOG.md
|
149
|
+
post_install_message:
|
150
|
+
rdoc_options: []
|
151
|
+
require_paths:
|
152
|
+
- lib
|
153
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 2.7.0
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">"
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: 1.3.1
|
163
|
+
requirements: []
|
164
|
+
rubygems_version: 3.1.6
|
165
|
+
signing_key:
|
166
|
+
specification_version: 4
|
167
|
+
summary: The tool what you miss for managing Rails credentials
|
168
|
+
test_files: []
|