require "tty-command" module RailsCredentialsManager class Diff < RailsCredentialsManager::Base def perform(branch_to_compare) cmd = TTY::Command.new(pty: true, printer: :null) configs.each do |env, config| puts pastel.green("#{env}:") result = cmd.run!("echo `git show origin/#{branch_to_compare}:config/credentials/#{env}.yml.enc`") encripted_file_content = result.out.strip if encripted_file_content.blank? puts "❗️ Can not find #{env} credentials file in origin/#{branch_to_compare} branch" next end branch_config = config_to_compare_for(env, encripted_file_content) deep_print_diff(HashDiff.diff(branch_config, config)) end end private def config_to_compare_for(environment, encripted_file_content) deserialize(decript(key_for(environment), encripted_file_content)).deep_symbolize_keys end def key_for(environment) Pathname.new("config/credentials/#{environment}.key").binread.strip end def decript(key, content) ActiveSupport::MessageEncryptor.new([key].pack("H*"), cipher: "aes-128-gcm") .decrypt_and_verify(content) end def deserialize(raw_config) # rubocop:disable Security/YAMLLoad YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(raw_config) : YAML.load(raw_config) # rubocop:enable Security/YAMLLoad end def deep_print_diff(diff, key_path = []) if diff.empty? puts pastel.green "\t✅ No differences" return end diff.each do |current_key, values| if values.is_a?(Hash) values.each { |k, val| deep_print_diff({k => val}, key_path + [current_key]) } elsif values.is_a?(Array) prev_value, current_value = values print_diff_row(key_path + [current_key], prev_value, current_value) end end end def print_diff_row(key_path, prev_value, current_value) if prev_value == HashDiff::NO_VALUE if current_value.is_a?(Hash) current_value.each { |value_key, value| print_diff_row(key_path + [value_key], prev_value, value) } else puts pastel.bright_green("\t#{key_path.join(".")}:\t ADDED: #{current_value.inspect}") end elsif current_value == HashDiff::NO_VALUE if prev_value.is_a?(Hash) prev_value.each { |value_key, value| print_diff_row(key_path + [value_key], value, current_value) } else puts pastel.bright_red("\t#{key_path.join(".")}:\tREMOVED: was #{prev_value.inspect}") end else # Changed puts pastel.bright_cyan("\t#{key_path.join(".")}:\tCHANGED: #{prev_value.inspect} => #{current_value.inspect}") end end end end