From 03e44f93001db97953917e0a100c627e189e2be6 Mon Sep 17 00:00:00 2001
From: Kasper Timm Hansen <kaspth@gmail.com>
Date: Sun, 4 Aug 2019 01:32:41 +0200
Subject: Revise credentials diffing flow to use a separate diff command

Didn't like the complicated stuff that happened on credentials:edit. It
would append to .gitattributes multiple times. Though I see why it was
written that way.

I'm cutting off for now, but since this new flow would require each developer
to run --enable perhaps this should really be:

1. Developer enrolls Rails app by running `credentials:diff --enable`
2. credentials:edit checks .gitattributes for `diff=rails_credentials` and
   if the current file is covered by that.
3. If so, set up the "rails_credentials" driver automatically.
---
 railties/lib/rails/commands/credentials/USAGE      | 14 +++++++
 .../commands/credentials/credentials_command.rb    | 19 ++++++----
 .../credentials/credentials_command/diffing.rb     | 43 +++++++---------------
 3 files changed, 39 insertions(+), 37 deletions(-)

(limited to 'railties/lib')

diff --git a/railties/lib/rails/commands/credentials/USAGE b/railties/lib/rails/commands/credentials/USAGE
index c8d3fb9eda..0396fcb403 100644
--- a/railties/lib/rails/commands/credentials/USAGE
+++ b/railties/lib/rails/commands/credentials/USAGE
@@ -30,6 +30,20 @@ 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.
+
+Any credentials files are set to use the "rails_credentials" diff driver in .gitattributes.
+Since Git requires the diff driver to be set up in a config file, the command uses
+the project local .git/config. Since that config isn't stored in Git each team member
+must enable separately.
+
+Or set up the "rails_credentials" diff driver globally with:
+
+   git config --global diff.rails_credentials.textconv "bin/rails credentials:diff"
+
 === 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 d1054f8b63..9b3a7dbb86 100644
--- a/railties/lib/rails/commands/credentials/credentials_command.rb
+++ b/railties/lib/rails/commands/credentials/credentials_command.rb
@@ -38,7 +38,6 @@ module Rails
         end
 
         say "File encrypted and saved."
-        enable_credentials_diffing
       rescue ActiveSupport::MessageEncryptor::InvalidMessage
         say "Couldn't decrypt #{content_path}. Perhaps you passed the wrong key?"
       end
@@ -50,14 +49,20 @@ module Rails
         say credentials.read.presence || missing_credentials_message
       end
 
-      def diff(content_path)
-        @content_path = content_path
+      option :enable, type: :boolean, default: false,
+        desc: "Pass `--enable` to make credential files diffable with `git diff`"
 
-        extract_environment_option_from_argument(default_environment: extract_environment_from_path(content_path))
-        require_application!
+      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
-      rescue
+          say credentials.read.presence || credentials.content_path.read
+        else
+          require_application!
+          enable_diffing if options[:enable]
+        end
+      rescue ActiveSupport::MessageEncryptor::InvalidMessage
         say credentials.content_path.read
       end
 
diff --git a/railties/lib/rails/commands/credentials/credentials_command/diffing.rb b/railties/lib/rails/commands/credentials/credentials_command/diffing.rb
index 1598ecaa8d..b7330178a3 100644
--- a/railties/lib/rails/commands/credentials/credentials_command/diffing.rb
+++ b/railties/lib/rails/commands/credentials/credentials_command/diffing.rb
@@ -3,45 +3,28 @@
 module Rails::Command::CredentialsCommand::Diffing # :nodoc:
   class Error < StandardError; end
 
-  def enable_credentials_diffing
-    unless already_answered? || enabled?
-      answer = yes?("Would you like to make the credentials diff from git more readable in the future? [Y/n]")
+  def enable_diffing
+    if enabled?
+      say "Already enabled!"
+    else
+      enable
+      say "Diffing enabled! Editing a credentials file will display a diff of what actually changed."
     end
-
-    enable if answer
-    FileUtils.touch(tracker) unless answer.nil?
   rescue Error
-    say "Couldn't setup git to enable credentials diffing"
+    say "Couldn't setup Git to enable credentials diffing."
   end
 
   private
-    def already_answered?
-      tracker.exist?
-    end
-
     def enabled?
-      system_call("git config --get 'diff.rails_credentials.textconv'", accepted_codes: [0, 1])
+      system "git config --get diff.rails_credentials.textconv", out: File::NULL
     end
 
     def enable
-      system_call("git config diff.rails_credentials.textconv 'bin/rails credentials:diff'", 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
+      raise Error unless system("git config diff.rails_credentials.textconv 'bin/rails credentials:diff'")
 
-    def system_call(command_line, accepted_codes:)
-      result = system(command_line)
-      raise(Error) if accepted_codes.exclude?($?.exitstatus)
-      result
+      Rails.root.join(".gitattributes").write(<<~end_of_template, mode: "a")
+        config/credentials/*.yml.enc diff=rails_credentials
+        config/credentials.yml.enc diff=rails_credentials
+      end_of_template
     end
 end
-- 
cgit v1.2.3