From 5a4acf7ac42c44a17a645e8d3682fd0cc145428d Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Fri, 26 Jul 2019 15:56:49 +0200 Subject: Prettify diff generated by git for encripted file: - @sinsoku had the idea and started implementing it few months ago but sadly didn't finish it. This PR is taking over his work. The credentials feature has changed a lot since @sinsoku opened hi PR, it was easier to just restart from scratch instead of checking out his branch. Sinsoku will get all the credit he deserves for this idea :) TL;DR on that that feature is to make the `git diff` or `git log` of encrypted files to be readable. The previous implementation was only setting up the git required configuration for the first time Rails was bootstraped, so I decided to instead provide the user a choice to opt-in for readable diff credential whenever a user types the `bin/rails credentials:edit` command. The question won't be asked in the future the user has already answered or if the user already opted in. Co-authored-by: Takumi Shotoku --- .../rails/command/helpers/pretty_credentials.rb | 55 +++++++++++ .../commands/credentials/credentials_command.rb | 34 +++++-- railties/test/commands/credentials_test.rb | 101 ++++++++++++++++++++- railties/test/isolation/abstract_unit.rb | 4 +- 4 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 railties/lib/rails/command/helpers/pretty_credentials.rb (limited to 'railties') diff --git a/railties/lib/rails/command/helpers/pretty_credentials.rb b/railties/lib/rails/command/helpers/pretty_credentials.rb new file mode 100644 index 0000000000..873ed0e825 --- /dev/null +++ b/railties/lib/rails/command/helpers/pretty_credentials.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "fileutils" + +module Rails + module Command + module Helpers + module PrettyCredentials + Error = Class.new(StandardError) + + def opt_in_pretty_credentials + unless already_answered? || already_opted_in? + answer = yes?("Would you like to make the credentials diff from git more readable in the future? [Y/n]") + end + + opt_in! if answer + FileUtils.touch(tracker) unless answer.nil? + rescue Error + say("Couldn't setup git to prettify the credentials diff") + end + + private + def already_answered? + tracker.exist? + end + + def already_opted_in? + system_call("git config --get 'diff.rails_credentials.textconv'", accepted_codes: [0, 1]) + end + + def opt_in! + system_call("git config diff.rails_credentials.textconv 'bin/rails credentials:show'", accepted_codes: [0]) + + git_attributes = Rails.root.join(".gitattributes") + File.open(git_attributes, "a+") do |file| + file.write(<<~EOM) + config/credentials/*.yml.enc diff=rails_credentials + config/credentials.yml.enc diff=rails_credentials + EOM + end + end + + def tracker + Rails.root.join("tmp", "rails_pretty_credentials") + end + + def system_call(command_line, accepted_codes:) + result = system(command_line) + raise(Error) if accepted_codes.exclude?($?.exitstatus) + result + end + end + end + end +end diff --git a/railties/lib/rails/commands/credentials/credentials_command.rb b/railties/lib/rails/commands/credentials/credentials_command.rb index e23a1b3008..772e105007 100644 --- a/railties/lib/rails/commands/credentials/credentials_command.rb +++ b/railties/lib/rails/commands/credentials/credentials_command.rb @@ -2,12 +2,15 @@ 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 self.environment_desc = "Uses credentials from config/credentials/:environment.yml.enc encrypted by config/credentials/:environment.key key" @@ -34,20 +37,29 @@ module Rails 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 - extract_environment_option_from_argument(default_environment: nil) + 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) require_application! - say credentials.read.presence || missing_credentials_message + say credentials(git_textconv_path).read.presence || fallback_message || missing_credentials_message + rescue => e + raise(e) unless git_textconv_path + fallback_message end private - def credentials - Rails.application.encrypted(content_path, key_path: key_path) + def credentials(content = nil) + Rails.application.encrypted(content || content_path, key_path: key_path) end def ensure_encryption_key_has_been_added @@ -77,7 +89,6 @@ module Rails end end - def content_path options[:environment] ? "config/credentials/#{options[:environment]}.yml.enc" : "config/credentials.yml.enc" end @@ -86,6 +97,17 @@ module Rails options[:environment] ? "config/credentials/#{options[:environment]}.key" : "config/master.key" end + def extract_environment_from_path(path) + regex = %r{ + ([A-Za-z0-9]+) # match the environment + (?