Diffing Rails credentials in Rails 6.0
Rails credentials are a pretty nice feature to deal with secrets in your Rails application. Instead of setting a whole list of secrets in your server’s environment variables, you only have to set the decryption key RAILS_MASTER_KEY
(or RAILS_PRODUCTION_KEY
if you are using environment-specific credentials).
Something that can be annoying with Rail’s credentials is that it is hard to resolve merge-conflicts within them or to compare your current credentials to some other branch’s credentials since git’s diff will only show you blobs.
Since Rails version 6.1 there is a credential diff task which will help with this, but I am working on a Rails 6.0 project right now and there are some dependency issues that don’t allow us to upgrade yet.
Some people have suggested a .gitattributes
based diffing option, but that didn’t play nice for me with multi-environment credentials.
This led to me writing the following script:
#!/usr/bin/env ruby
require 'open3'
def branch_exists?(branch_name)
o, s = Open3.capture2("git rev-parse --verify #{branch_name}")
s.success?
end
def write_credentials_to_file(credentials_path, key_path, target_path)
o, s = Open3.capture2("bin/rails encrypted:show --key #{key_path} #{credentials_path}")
File.write(target_path, o)
end
def write_credential_to_file(environment, target_path, credentials_base: "config/credentials")
write_credentials_to_file("#{credentials_base}/#{environment}.yml.enc", "config/credentials/#{environment}.key", target_path)
end
def compare_conflicted_credentials(environment)
`git checkout --ours config/credentials/#{environment}.yml.enc`
write_credential_to_file(environment, 'tmp/ours')
`git checkout --theirs config/credentials/#{environment}.yml.enc`
write_credential_to_file(environment, 'tmp/theirs')
puts "\n\n\n"
puts "'Ours' is what is in master, 'theirs' is what is in your branch'"
puts "\n\n\n"
puts `icdiff tmp/ours tmp/theirs`
ensure
`rm tmp/ours`
`rm tmp/theirs`
end
def compare_to_branch(environment, branch_name)
write_credential_to_file(environment, 'tmp/your_branch')
`mkdir -p tmp/credentials`
`git show #{branch_name}:config/credentials/#{environment}.yml.enc > tmp/credentials/#{environment}.yml.enc`
write_credential_to_file(environment, 'tmp/other_branch', credentials_base: 'tmp/credentials')
puts `icdiff tmp/your_branch tmp/other_branch`
ensure
`rm tmp/your_branch`
`rm tmp/other_branch`
`rm -r tmp/credentials`
end
cred_path = ARGV[0]
raise 'USAGE: diff_my_creds.sh PATH_TO_CREDS' if cred_path.nil?
environment = cred_path.match(/config\/credentials\/([a-z]+).yml.enc/)[1]
raise "CANNOT DETERMINE ENVIRONMENT FROM #{cred_path}" if environment.nil?
compare_to_branch = ARGV[1] if ARGV[1]
if compare_to_branch
raise "GIVEN BRANCH DOES NOT EXIST" unless branch_exists?(compare_to_branch)
compare_to_branch(environment, compare_to_branch)
else
compare_conflicted_credentials(environment)
end
`rails credentials:edit -e #{environment}`
Put this in bin/diff_my_creds.sh
and do chmod +x bin/diff_my_creds.sh
to make it runnable.
Usage when you have a conflict on lets say config/credentials/test.yml.enc
:
bin/diff_my_creds.sh config/credentials/test.yml.enc
Usage when you want to diff with a branch:
bin/diff_my_creds.sh config/credentials/test.yml.enc master # or any other branch name than 'master'
Output:
tmp/your_branch tmp/other_branch
access_key_id: 123 access_key_id: 123
secret_access_key: 345 secret_access_key: 345
some_service: some_service:
api_key: abc api_key: abc
nice_day_to: diff
A dependency for showing the diff in this way is icdiff
which you can install with brew install icdiff
, but you could replace the diff command with anything you want, the basic idea stays the same.