Support VISUAL env var, and prefer it over EDITOR

This commit is contained in:
Summer ☀️ 2023-05-30 20:24:30 -06:00
parent 95af5fce71
commit a062d182c3
12 changed files with 85 additions and 37 deletions

@ -1,3 +1,8 @@
* Support `VISUAL` environment variable for commands which open an editor,
and prefer it over `EDITOR`.
*Summer ☀️*
* Add engine's `test/fixtures` path to `fixture_paths` in `on_load` hook if
path exists and is under the Rails application root.

@ -8,11 +8,15 @@ module Command
module Helpers
module Editor
private
def editor
ENV["VISUAL"].to_s.empty? ? ENV["EDITOR"] : ENV["VISUAL"]
end
def display_hint_if_system_editor_not_specified
if ENV["EDITOR"].to_s.empty?
say "No $EDITOR to open file in. Assign one like this:"
if editor.to_s.empty?
say "No $VISUAL or $EDITOR to open file in. Assign one like this:"
say ""
say %(EDITOR="mate --wait" #{executable(current_subcommand)})
say %(VISUAL="mate --wait" #{executable(current_subcommand)})
say ""
say "For editors that fork and exit immediately, it's important to pass a wait flag;"
say "otherwise, the file will be saved immediately with no chance to edit."
@ -22,7 +26,7 @@ def display_hint_if_system_editor_not_specified
end
def system_editor(file_path)
system(*Shellwords.split(ENV["EDITOR"]), file_path.to_s)
system(*Shellwords.split(editor), file_path.to_s)
end
def using_system_editor

@ -48,8 +48,8 @@ Set up Git to Diff Credentials:
To disenroll from this feature, run `<%= executable(:diff) %> --disenroll`.
Editing Credentials:
This will open a temporary file in `$EDITOR` with the decrypted contents to edit
the encrypted credentials.
This will open a temporary file in `$VISUAL` or `$EDITOR` with the decrypted
contents to edit the encrypted credentials.
When the temporary file is next saved the contents are encrypted and written to
`config/credentials.yml.enc` while the file itself is destroyed to prevent credentials

@ -14,7 +14,7 @@ class CredentialsCommand < Rails::Command::Base # :nodoc:
require_relative "credentials_command/diffing"
include Diffing
desc "edit", "Open the decrypted credentials in `$EDITOR` for editing"
desc "edit", "Open the decrypted credentials in `$VISUAL` or `$EDITOR` for editing"
def edit
load_environment_config!
load_generators

@ -16,7 +16,7 @@ Examples:
<%= executable(:edit) %> config/encrypted_file.yml.enc
This opens a temporary file in `$EDITOR` with the decrypted contents for editing.
This opens a temporary file in `$VISUAL` or `$EDITOR` with the decrypted contents for editing.
To print the decrypted contents of an encrypted file use:

@ -12,7 +12,7 @@ class EncryptedCommand < Rails::Command::Base # :nodoc:
class_option :key, aliases: "-k", type: :string,
default: "config/master.key", desc: "The Rails.root relative path to the encryption key"
desc "edit", "Open the decrypted file in `$EDITOR` for editing"
desc "edit", "Open the decrypted file in `$VISUAL` or `$EDITOR` for editing"
def edit(*)
load_environment_config!

@ -53,8 +53,8 @@ Setup:
Editing Secrets:
After `<%= executable(:setup) %>`, run `<%= executable(:edit) %>`.
That command opens a temporary file in `$EDITOR` with the decrypted contents of
`config/secrets.yml.enc` to edit the encrypted secrets.
That command opens a temporary file in `$VISUAL` or `$EDITOR` with the decrypted
contents of `config/secrets.yml.enc` to edit the encrypted secrets.
When the temporary file is next saved the contents are encrypted and written to
`config/secrets.yml.enc` while the file itself is destroyed to prevent secrets

@ -9,7 +9,7 @@ module Command
class SecretsCommand < Rails::Command::Base # :nodoc:
include Helpers::Editor
desc "edit", "**deprecated** Open the secrets in `$EDITOR` for editing"
desc "edit", "**deprecated** Open the secrets in `$VISUAL` or `$EDITOR` for editing"
def edit
Rails.deprecator.warn(<<~MSG.squish)
`bin/rails secrets:edit` is deprecated in favor of credentials and will be removed in Rails 7.2.

@ -11,13 +11,25 @@ class Rails::Command::CredentialsTest < ActiveSupport::TestCase
setup :build_app
teardown :teardown_app
test "edit without editor gives hint" do
run_edit_command(editor: "").tap do |output|
assert_match "No $EDITOR to open file in", output
test "edit without visual or editor gives hint" do
run_edit_command(visual: "", editor: "").tap do |output|
assert_match "No $VISUAL or $EDITOR to open file in", output
assert_match "rails credentials:edit", output
end
end
test "edit with visual but not editor does not give hint" do
run_edit_command(visual: "cat", editor: "").tap do |output|
assert_no_match "No $VISUAL or $EDITOR to open file in", output
end
end
test "edit with editor but not visual does not give hint" do
run_edit_command(visual: "", editor: "cat").tap do |output|
assert_no_match "No $VISUAL or $EDITOR to open file in", output
end
end
test "edit credentials" do
# Run twice to ensure credentials can be reread after first edit pass.
2.times do
@ -145,7 +157,7 @@ class Rails::Command::CredentialsTest < ActiveSupport::TestCase
assert_match %r/file encrypted and saved/i, run_edit_command
interrupt_command_process = %(ruby -e "Process.kill 'INT', Process.ppid")
output = run_edit_command(editor: interrupt_command_process)
output = run_edit_command(visual: interrupt_command_process)
assert_no_match %r/file encrypted and saved/i, output
assert_match %r/nothing saved/i, output
@ -154,7 +166,7 @@ class Rails::Command::CredentialsTest < ActiveSupport::TestCase
test "edit command preserves user's content even if it contains invalid YAML" do
write_invalid_yaml = %(ruby -e "File.write ARGV[0], 'foo: bar: bad'")
assert_match %r/WARNING: Invalid YAML/, run_edit_command(editor: write_invalid_yaml)
assert_match %r/WARNING: Invalid YAML/, run_edit_command(visual: write_invalid_yaml)
assert_match %r/foo: bar: bad/, run_edit_command
end
@ -327,10 +339,12 @@ class Rails::Command::CredentialsTest < ActiveSupport::TestCase
private
DEFAULT_CREDENTIALS_PATTERN = /access_key_id: 123\n.*secret_key_base: \h{128}\n/m
def run_edit_command(editor: "cat", environment: nil, **options)
switch_env("EDITOR", editor) do
args = environment ? ["--environment", environment] : []
rails "credentials:edit", args, **options
def run_edit_command(visual: "cat", editor: "cat", environment: nil, **options)
switch_env("VISUAL", visual) do
switch_env("EDITOR", editor) do
args = environment ? ["--environment", environment] : []
rails "credentials:edit", args, **options
end
end
end
@ -346,7 +360,7 @@ def run_diff_command(path = nil, enroll: nil, disenroll: nil, **options)
def write_credentials(content, **options)
switch_env("CONTENT", content) do
run_edit_command(editor: %(ruby -e "File.write ARGV[0], ENV['CONTENT']"), **options)
run_edit_command(visual: %(ruby -e "File.write ARGV[0], ENV['CONTENT']"), **options)
end
end

@ -14,13 +14,25 @@ class Rails::Command::EncryptedTest < ActiveSupport::TestCase
@encrypted_file = "config/tokens.yml.enc"
end
test "edit without editor gives hint" do
run_edit_command(editor: "").tap do |output|
assert_match "No $EDITOR to open file in", output
test "edit without visual or editor gives hint" do
run_edit_command(visual: "", editor: "").tap do |output|
assert_match "No $VISUAL or $EDITOR to open file in", output
assert_match "rails encrypted:edit", output
end
end
test "edit with visual but not editor does not give hint" do
run_edit_command(visual: "cat", editor: "").tap do |output|
assert_no_match "No $VISUAL or $EDITOR to open file in", output
end
end
test "edit with editor but not visual does not give hint" do
run_edit_command(visual: "", editor: "cat").tap do |output|
assert_no_match "No $VISUAL or $EDITOR to open file in", output
end
end
test "edit encrypted file" do
# Run twice to ensure file can be reread after first edit pass.
2.times do
@ -85,7 +97,7 @@ class Rails::Command::EncryptedTest < ActiveSupport::TestCase
assert_match %r/file encrypted and saved/i, run_edit_command
interrupt_command_process = %(ruby -e "Process.kill 'INT', Process.ppid")
output = run_edit_command(editor: interrupt_command_process)
output = run_edit_command(visual: interrupt_command_process)
assert_no_match %r/file encrypted and saved/i, output
assert_match %r/nothing saved/i, output
@ -94,7 +106,7 @@ class Rails::Command::EncryptedTest < ActiveSupport::TestCase
test "edit command preserves user's content even if it contains invalid YAML" do
write_invalid_yaml = %(ruby -e "File.write ARGV[0], 'foo: bar: bad'")
assert_match %r/WARNING: Invalid YAML/, run_edit_command(editor: write_invalid_yaml)
assert_match %r/WARNING: Invalid YAML/, run_edit_command(visual: write_invalid_yaml)
assert_match %r/foo: bar: bad/, run_edit_command
end
@ -139,9 +151,11 @@ class Rails::Command::EncryptedTest < ActiveSupport::TestCase
end
private
def run_edit_command(file = @encrypted_file, key: nil, editor: "cat", **options)
switch_env("EDITOR", editor) do
rails "encrypted:edit", prepare_args(file, key), **options
def run_edit_command(file = @encrypted_file, key: nil, visual: "cat", editor: "cat", **options)
switch_env("VISUAL", visual) do
switch_env("EDITOR", editor) do
rails "encrypted:edit", prepare_args(file, key), **options
end
end
end

@ -10,8 +10,16 @@ class Rails::Command::SecretsTest < ActiveSupport::TestCase
setup :build_app
teardown :teardown_app
test "edit without editor gives hint" do
assert_match "No $EDITOR to open file in", run_edit_command(editor: "")
test "edit without visual or editor gives hint" do
assert_match "No $VISUAL or $EDITOR to open file in", run_edit_command(visual: "", editor: "")
end
test "edit with visual but not editor does not give hint" do
assert_no_match "No $VISUAL or $EDITOR to open file in", run_edit_command(visual: "cat", editor: "")
end
test "edit with editor but not visual does not give hint" do
assert_no_match "No $VISUAL or $EDITOR to open file in", run_edit_command(visual: "", editor: "cat")
end
test "edit secrets" do
@ -44,9 +52,11 @@ def prevent_deprecation
end
end
def run_edit_command(editor: "cat")
switch_env("EDITOR", editor) do
rails "secrets:edit", allow_failure: true
def run_edit_command(visual: "cat", editor: "cat")
switch_env("VISUAL", visual) do
switch_env("EDITOR", editor) do
rails "secrets:edit", allow_failure: true
end
end
end

@ -251,8 +251,9 @@
# Permit the avatar param.
substitute.call("app/controllers/users_controller.rb", /:admin/, ":admin, :avatar")
if ENV["EDITOR"]
`#{ENV["EDITOR"]} #{File.expand_path(app_name)}`
editor = ENV["VISUAL"] || ENV["EDITOR"]
if editor
`#{editor} #{File.expand_path(app_name)}`
end
puts "Booting a Rails server. Verify the release by:"