diff options
Diffstat (limited to 'railties/lib/rails/commands')
3 files changed, 86 insertions, 27 deletions
diff --git a/railties/lib/rails/commands/credentials/USAGE b/railties/lib/rails/commands/credentials/USAGE index c8d3fb9eda..6b896ab02a 100644 --- a/railties/lib/rails/commands/credentials/USAGE +++ b/railties/lib/rails/commands/credentials/USAGE @@ -30,6 +30,21 @@ You could prepend that to your server's start command like this: RAILS_MASTER_KEY="very-secret-and-secure" server.start +=== Set up Git to Diff Credentials + +Rails provides `rails credentials:diff --enable` to instruct Git to call `rails credentials:diff` +when `git diff` is run on a credentials file. + +Running the command enrolls the project such that all credentials files use the +"rails_credentials" diff driver in .gitattributes. + +Additionally since Git requires the driver itself to be set up in a config file +that isn't tracked Rails automatically ensures it's configured when running +`credentials:edit`. + +Otherwise each co-worker would have to run enable manually, including on each new +repo clone. + === Editing Credentials This will open a temporary file in `$EDITOR` with the decrypted contents to edit diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb index 772e105007..9cde44558b 100644 --- a/railties/lib/rails/commands/credentials/credentials_command.rb +++ b/railties/lib/rails/commands/credentials/credentials_command.rb @@ -1,18 +1,19 @@ # frozen_string_literal: true +require "pathname" require "active_support" require "rails/command/helpers/editor" -require "rails/command/helpers/pretty_credentials" require "rails/command/environment_argument" -require "pathname" module Rails module Command class CredentialsCommand < Rails::Command::Base # :nodoc: include Helpers::Editor - include Helpers::PrettyCredentials include EnvironmentArgument + require_relative "credentials_command/diffing" + include Diffing + self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key" no_commands do @@ -31,35 +32,44 @@ module Rails ensure_encryption_key_has_been_added if credentials.key.nil? ensure_credentials_have_been_added + ensure_rails_credentials_driver_is_set catch_editing_exceptions do change_credentials_in_system_editor end say "File encrypted and saved." - opt_in_pretty_credentials rescue ActiveSupport::MessageEncryptor::InvalidMessage say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?" end - def show(git_textconv_path = nil) - if git_textconv_path - default_environment = extract_environment_from_path(git_textconv_path) - fallback_message = File.read(git_textconv_path) - end - - extract_environment_option_from_argument(default_environment: default_environment) + def show + extract_environment_option_from_argument(default_environment: nil) require_application! - say credentials(git_textconv_path).read.presence || fallback_message || missing_credentials_message - rescue => e - raise(e) unless git_textconv_path - fallback_message + say credentials.read.presence || missing_credentials_message + end + + option :enroll, type: :boolean, default: false, + desc: "Enrolls project in credential file diffing with `git diff`" + + def diff(content_path = nil) + if @content_path = content_path + extract_environment_option_from_argument(default_environment: extract_environment_from_path(content_path)) + require_application! + + say credentials.read.presence || credentials.content_path.read + else + require_application! + enroll_project_in_credentials_diffing if options[:enroll] + end + rescue ActiveSupport::MessageEncryptor::InvalidMessage + say credentials.content_path.read end private - def credentials(content = nil) - Rails.application.encrypted(content || content_path, key_path: key_path) + def credentials + Rails.application.encrypted(content_path, key_path: key_path) end def ensure_encryption_key_has_been_added @@ -89,8 +99,9 @@ module Rails end end + def content_path - options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc" + @content_path ||= options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc" end def key_path @@ -98,15 +109,7 @@ module Rails end def extract_environment_from_path(path) - regex = %r{ - ([A-Za-z0-9]+) # match the environment - (?<!credentials) # don't match if file contains the word "credentials" - # in such case, the environment should be the default one - \.yml\.enc # look for `.yml.enc` file extension - }x - path.match(regex) - - Regexp.last_match(1) + available_environments.find { |env| path.include? env } if path.match?(/\.yml\.enc$/) end def encryption_key_file_generator diff --git a/railties/lib/rails/commands/credentials/credentials_command/diffing.rb b/railties/lib/rails/commands/credentials/credentials_command/diffing.rb new file mode 100644 index 0000000000..1d34c68074 --- /dev/null +++ b/railties/lib/rails/commands/credentials/credentials_command/diffing.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Rails::Command::CredentialsCommand::Diffing # :nodoc: + def enroll_project_in_credentials_diffing + if enrolled? + true + else + gitattributes.write(<<~end_of_template, mode: "a") + config/credentials/*.yml.enc diff=rails_credentials + config/credentials.yml.enc diff=rails_credentials + end_of_template + + say "Project successfully enrolled!" + say "Rails ensures the rails_credentials diff driver is set when running `credentials:edit`. See `credentials:help` for more." + end + end + + def ensure_rails_credentials_driver_is_set + set_driver if enrolled? && !driver_configured? + end + + private + def enrolled? + gitattributes.read.match?(/config\/credentials(\/\*)?\.yml\.enc diff=rails_credentials/) + rescue Errno::ENOENT + false + end + + def driver_configured? + system "git config --get diff.rails_credentials.textconv", out: File::NULL + end + + def set_driver + puts "running" + system "git config diff.rails_credentials.textconv 'bin/rails credentials:diff'" + end + + def gitattributes + Rails.root.join(".gitattributes") + end +end |