├── .github └── workflows │ └── main.yml ├── .gitignore ├── .rspec ├── CODE_OF_CONDUCT.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── config └── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── gemfiles ├── rails_52.gemfile └── rails_edge.gemfile ├── lib ├── rails-env-credentials.rb ├── rails │ └── commands │ │ └── env_credentials_command.rb ├── rails_env_credentials.rb └── rails_env_credentials │ ├── config.rb │ ├── railtie.rb │ └── version.rb ├── rails-env-credentials.gemspec └── spec ├── fake_app.rb ├── rails └── commands │ └── env_credentials_command_spec.rb ├── rails_env_credentials └── railties_spec.rb ├── rails_env_credentials_spec.rb └── spec_helper.rb /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Ruby 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | ruby: 14 | - 2.4 15 | - 2.5 16 | - 2.6 17 | - 2.7 18 | gemfile: 19 | - rails_52 20 | env: 21 | BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile 22 | 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Set up Ruby 26 | uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: ${{ matrix.ruby }} 29 | bundler-cache: true 30 | - name: Run the default task 31 | run: bundle exec rake 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | 13 | Gemfile.lock 14 | gemfiles/*.lock 15 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at sinsoku.listy@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in rails-env-credentials.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 sinsoku 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/rails-env-credentials.svg)](https://badge.fury.io/rb/rails-env-credentials) 2 | [![Ruby](https://github.com/sinsoku/rails-env-credentials/actions/workflows/main.yml/badge.svg)](https://github.com/sinsoku/rails-env-credentials/actions/workflows/main.yml) 3 | 4 | # RailsEnvCredentials 5 | 6 | It enhances the Credentials feature introduced by Rails v5.2.0. 7 | 8 | ## Installation 9 | 10 | Add this line to your Rails application's Gemfile: 11 | 12 | ```ruby 13 | group :development, :test do 14 | gem 'rails-env-credentials' 15 | end 16 | ``` 17 | 18 | And then execute: 19 | 20 | ``` 21 | $ bundle 22 | ``` 23 | 24 | ## Usage 25 | 26 | RailsEnvCredentials manages credentials and key pairs with the following: 27 | 28 | ``` 29 | config/credentials-development.yml.enc 30 | config/credentials-test.yml.enc 31 | config/credentials.yml.enc 32 | master-development.key 33 | master-test.key 34 | master.key 35 | ``` 36 | 37 | It also manages environment variables for each env. 38 | 39 | ``` 40 | RAILS_MASTER_KEY_DEVELOPMENT 41 | RAILS_MASTER_KEY_TEST 42 | RAILS_MASTER_KEY 43 | ``` 44 | 45 | You can use appropriate credentials depending on `Rails.env`. 46 | 47 | ```console 48 | $ rails env_credentials:show -e development 49 | # config/credentials-development.yml.enc 50 | aws: 51 | bucket: foo-dev 52 | 53 | $ rails env_credentials:show -e production 54 | # config/credentials.yml.enc 55 | aws: 56 | bucket: foo-prod 57 | 58 | $ rails runner -e development 'pp Rails.application.credentials.aws.bucket' 59 | "foo-dev" 60 | $ rails runner -e production 'pp Rails.application.credentials.aws.bucket' 61 | "foo-prod" 62 | ``` 63 | 64 | ## Generating secrets and a master key 65 | 66 | It automatically generate encrypted file and the master key when you starts editing credentials at first: 67 | 68 | ``` 69 | $ rails env_credentials:edit -e development 70 | ``` 71 | 72 | ## Show secrets 73 | 74 | You want to see decrypted contents, use `env_credentials:show`: 75 | 76 | ``` 77 | $ rails env_credentials:show -e development 78 | ``` 79 | 80 | ## Additional information 81 | 82 | ### Other environments support 83 | 84 | For example, if the `config/environments/staging.rb` exists, you will generate `config/credentials-staging.yml.enc`. 85 | 86 | ``` 87 | $ rails env_credentials:edit -e staging 88 | ``` 89 | 90 | ### Display a diff 91 | 92 | You can’t directly compare encrypted files between two versions, but it turns out you can see a diff using Git attributes. 93 | 94 | Put the following line in your `.gitattributes` file: 95 | 96 | ``` 97 | config/credentials*.yml.enc diff=env_credentials 98 | ``` 99 | 100 | Then configure Git to use `env_credentials:show`: 101 | 102 | ``` 103 | $ git config diff.env_credentials.textconv 'rails env_credentials:show --file' 104 | ``` 105 | 106 | This tells Git that encrypted files should decrypt by the `env_credentials:show` task when you try to display a diff. 107 | 108 | ## Why make this gem? 109 | 110 | Credentials is a good feature, but we cannot use it on development and test environment. 111 | 112 | DHH wrote as follow in the pull request for initial implementation: 113 | 114 | > It's only in production (and derivative environments, like exposed betas) where the secret actually needs to be secret. 115 | > 116 | > refs: https://github.com/rails/rails/pull/30067 117 | 118 | However, I have to manage secrets and a master key different from production for testing in the staging environment. 119 | 120 | I do not have the confidence to explain explicit use cases to Rails team, so I implemented as a gem. 121 | 122 | ## Development 123 | 124 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 125 | 126 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 127 | 128 | ## Contributing 129 | 130 | Bug reports and pull requests are welcome on GitHub at https://github.com/sinsoku/rails-env-credentials. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 131 | 132 | ## License 133 | 134 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 135 | 136 | ## Code of Conduct 137 | 138 | Everyone interacting in the Rails::Env::Credentials project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/sinsoku/rails-env-credentials/blob/main/CODE_OF_CONDUCT.md). 139 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "rails-env-credentials" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinsoku/rails-env-credentials/10973e8890e95e250a0a3687b3f16d7acd8ec9d0/config/environments/development.rb -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinsoku/rails-env-credentials/10973e8890e95e250a0a3687b3f16d7acd8ec9d0/config/environments/production.rb -------------------------------------------------------------------------------- /config/environments/staging.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinsoku/rails-env-credentials/10973e8890e95e250a0a3687b3f16d7acd8ec9d0/config/environments/staging.rb -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinsoku/rails-env-credentials/10973e8890e95e250a0a3687b3f16d7acd8ec9d0/config/environments/test.rb -------------------------------------------------------------------------------- /gemfiles/rails_52.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "rails", "~> 5.2.0" 4 | 5 | gemspec path: ".." 6 | -------------------------------------------------------------------------------- /gemfiles/rails_edge.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | gem "rails", github: "rails/rails" 6 | 7 | gemspec path: ".." 8 | -------------------------------------------------------------------------------- /lib/rails-env-credentials.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails_env_credentials" 4 | -------------------------------------------------------------------------------- /lib/rails/commands/env_credentials_command.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails/command/environment_argument" 4 | require "rails/commands/credentials/credentials_command" 5 | require "rails_env_credentials" 6 | 7 | using(Module.new do 8 | refine Module do 9 | def const_set!(name, value) 10 | remove_const(name) 11 | const_set(name, value) 12 | end 13 | end 14 | end) 15 | 16 | module Rails 17 | module Command 18 | class EnvCredentialsCommand < Rails::Command::CredentialsCommand 19 | include EnvironmentArgument 20 | 21 | argument :file, optional: true, banner: "file" 22 | class_option :file, aliases: "-f", type: :string 23 | 24 | def edit 25 | set_credentials_env_from_argument! 26 | super 27 | end 28 | 29 | def show 30 | set_credentials_env_from_argument! 31 | super 32 | end 33 | 34 | private 35 | 36 | def set_credentials_env_from_argument! 37 | extract_environment_option_from_argument 38 | 39 | if options.file 40 | RailsEnvCredentials.config_path = options.file 41 | elsif available_environments.include?(options.environment) 42 | RailsEnvCredentials.env = options[:environment] 43 | else 44 | raise "'#{options.environment}' environment is not found. Available: #{available_environments}" 45 | end 46 | end 47 | 48 | def master_key_generator 49 | require "rails/generators" 50 | require "rails/generators/rails/master_key/master_key_generator" 51 | 52 | key_path = RailsEnvCredentials.options[:key_path] 53 | Rails::Generators::MasterKeyGenerator.const_set!(:MASTER_KEY_PATH, Pathname.new(key_path)) 54 | Rails::Generators::MasterKeyGenerator.new 55 | end 56 | 57 | def credentials_generator 58 | require "rails/generators" 59 | require "rails/generators/rails/credentials/credentials_generator" 60 | 61 | Rails::Generators::CredentialsGenerator.prepend(RailsEnvCredentials::CredentialsOverwrite) 62 | Rails::Generators::CredentialsGenerator.new 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/rails_env_credentials.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "rails" 4 | require "rails_env_credentials/config" 5 | require "rails_env_credentials/railtie" 6 | require "rails_env_credentials/version" 7 | 8 | module RailsEnvCredentials 9 | class << self 10 | def config_path=(path) 11 | if path.end_with?('credentials.yml.enc') 12 | env = 'production' 13 | else 14 | env = /-(\w+)\.yml\.enc\Z/.match(path).to_a[1] 15 | end 16 | @config = Config.new(env: env, config_path: path) 17 | end 18 | 19 | def env=(env) 20 | @config = Config.new(env: env) 21 | end 22 | 23 | def options 24 | (@config || Config.new).to_options 25 | end 26 | 27 | def credentials 28 | ActiveSupport::EncryptedConfiguration.new(**options) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/rails_env_credentials/config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RailsEnvCredentials 4 | class Config 5 | attr_reader :env 6 | 7 | def initialize(env: nil, config_path: nil) 8 | @env = env.nil? ? Rails.env : ActiveSupport::StringInquirer.new(env) 9 | @config_path = config_path 10 | end 11 | 12 | def to_options 13 | env_key = env_suffix("RAILS_MASTER_KEY", "_").upcase 14 | key_path = "config/#{env_suffix("master")}.key" 15 | 16 | { 17 | config_path: config_path, 18 | env_key: env_key, 19 | key_path: key_path, 20 | raise_if_missing_key: false 21 | } 22 | end 23 | 24 | private 25 | 26 | def config_path 27 | @config_path || "config/#{env_suffix("credentials")}.yml.enc" 28 | end 29 | 30 | def env_suffix(str, suffix = "-") 31 | env.production? ? str : [str, suffix, env].join 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/rails_env_credentials/railtie.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RailsEnvCredentials 4 | module CredentialsOverwrite 5 | def credentials 6 | @credentials ||= RailsEnvCredentials.credentials 7 | end 8 | end 9 | 10 | class Railtie < ::Rails::Railtie 11 | config.before_configuration do 12 | is_credentials_command = Rails.const_defined?(:Command) && 13 | Rails::Command.const_defined?(:CredentialsCommand) && 14 | !Rails::Command.const_defined?(:EnvCredentialsCommand) 15 | 16 | Rails::Application.prepend(CredentialsOverwrite) unless is_credentials_command 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/rails_env_credentials/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RailsEnvCredentials 4 | VERSION = "0.1.5" 5 | end 6 | -------------------------------------------------------------------------------- /rails-env-credentials.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path("../lib", __FILE__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require "rails_env_credentials/version" 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = "rails-env-credentials" 9 | spec.version = RailsEnvCredentials::VERSION 10 | spec.authors = ["sinsoku"] 11 | spec.email = ["sinsoku.listy@gmail.com"] 12 | 13 | spec.summary = "It enhances the credentials configuration introduced by Rails v5.2.0" 14 | spec.description = "It enhances the credentials configuration introduced by Rails v5.2.0" 15 | spec.homepage = "https://github.com/sinsoku/rails-env-credentials" 16 | spec.license = "MIT" 17 | 18 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 19 | f.match(%r{^(test|spec|features)/}) 20 | end 21 | spec.bindir = "exe" 22 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 23 | spec.require_paths = ["lib"] 24 | 25 | spec.add_dependency "rails", ">= 5.2.0.rc1" 26 | 27 | spec.add_development_dependency "bundler", "~> 2.0" 28 | spec.add_development_dependency "rake", "~> 10.0" 29 | spec.add_development_dependency "rspec", "~> 3.0" 30 | spec.add_development_dependency "simplecov" 31 | end 32 | -------------------------------------------------------------------------------- /spec/fake_app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module FakeApp 4 | class Application < Rails::Application 5 | config.eager_load = false 6 | config.logger = Logger.new(nil).tap do |log| 7 | def log.write(msg); end 8 | end 9 | end 10 | end 11 | FakeApp::Application.initialize! 12 | -------------------------------------------------------------------------------- /spec/rails/commands/env_credentials_command_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_support/testing/stream" 4 | 5 | RSpec.describe "Rails::Command::EnvCredentialsCommand" do 6 | include ActiveSupport::Testing::Stream 7 | 8 | after do 9 | Dir["config/*.yml.enc"].each { |path| FileUtils.rm_rf(path) } 10 | Dir["config/*.key"].each { |path| FileUtils.rm_rf(path) } 11 | RailsEnvCredentials.instance_variable_set(:@config, nil) 12 | Rails.application.instance_variable_set(:@credentials, nil) 13 | end 14 | 15 | describe "#edit" do 16 | context "no arguments" do 17 | it "generates files for the test environment" do 18 | quietly { run_edit_command } 19 | 20 | expect(File).to be_exist 'config/credentials-test.yml.enc' 21 | expect(File).to be_exist 'config/master-test.key' 22 | end 23 | end 24 | 25 | context "with the environment option" do 26 | it "generates files for the production environment" do 27 | quietly { run_edit_command(args: %w[-e production]) } 28 | 29 | expect(File).to be_exist 'config/credentials.yml.enc' 30 | expect(File).to be_exist 'config/master.key' 31 | end 32 | end 33 | 34 | context "with the wrong environment" do 35 | it do 36 | expect { 37 | run_edit_command(args: %w[-e foo]) 38 | }.to raise_error(a_string_starting_with("'foo' environment is not found")) 39 | end 40 | end 41 | end 42 | 43 | describe "#show" do 44 | context "no arguments" do 45 | it "shows credentials" do 46 | quietly { run_edit_command(editor: "eval echo api_key: abc >") } 47 | 48 | expect { run_show_command }.to output(/api_key: abc/).to_stdout 49 | end 50 | end 51 | 52 | context "with the file option to show credentials for other environment" do 53 | it "shows credentials for other environment" do 54 | quietly { run_edit_command(editor: "eval echo api_key: abc >", args: %w[-e production]) } 55 | 56 | expect { run_show_command(args: ["-f", "config/credentials.yml.enc"]) }.to output(/api_key: abc/).to_stdout 57 | end 58 | end 59 | 60 | context "with the file option to show credentials in temporary directory" do 61 | it "shows credentials in temporary directory" do 62 | quietly { run_edit_command(editor: "eval echo api_key: abc >") } 63 | 64 | Tempfile.open(['', '-test.yml.enc']) do |f| 65 | encrypted = File.read("config/credentials-test.yml.enc") 66 | f.write(encrypted) 67 | f.rewind 68 | 69 | quietly { run_edit_command(editor: "eval echo api_key: def >") } 70 | Rails.application.instance_variable_set(:@credentials, nil) 71 | 72 | expect { run_show_command(args: ["-f", f.path]) }.to output(/api_key: abc/).to_stdout 73 | end 74 | end 75 | end 76 | end 77 | 78 | private 79 | 80 | def run_edit_command(editor: ":", args: []) 81 | switch_env("EDITOR", editor) do 82 | Rails::Command.invoke("env_credentials:edit", args) 83 | end 84 | end 85 | 86 | def run_show_command(args: []) 87 | Rails::Command.invoke("env_credentials:show", args) 88 | end 89 | 90 | def switch_env(key, value) 91 | old, ENV[key] = ENV[key], value 92 | yield 93 | ensure 94 | ENV[key] = old 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /spec/rails_env_credentials/railties_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe RailsEnvCredentials::Railtie do 4 | describe "Rails.application.credentials" do 5 | subject(:credentials) { Rails.application.credentials } 6 | 7 | it "returns credentials for development" do 8 | expect(credentials.content_path.to_s).to eq "config/credentials-test.yml.enc" 9 | expect(credentials.key_path.to_s).to eq "config/master-test.key" 10 | expect(credentials.env_key).to eq "RAILS_MASTER_KEY_TEST" 11 | expect(credentials.raise_if_missing_key).to be false 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/rails_env_credentials_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe RailsEnvCredentials do 4 | def valid_test_options 5 | { 6 | config_path: "config/credentials-test.yml.enc", 7 | env_key: "RAILS_MASTER_KEY_TEST", 8 | key_path: "config/master-test.key", 9 | raise_if_missing_key: false, 10 | } 11 | end 12 | 13 | def valid_production_options 14 | { 15 | config_path: "config/credentials.yml.enc", 16 | env_key: "RAILS_MASTER_KEY", 17 | key_path: "config/master.key", 18 | raise_if_missing_key: false, 19 | } 20 | end 21 | 22 | describe ".config_path=" do 23 | subject { RailsEnvCredentials.options } 24 | 25 | context "when the argument is 'credentials-development.yml.enc'" do 26 | let(:config_path) { 'credentials-test.yml.enc' } 27 | before { RailsEnvCredentials.config_path = config_path } 28 | it { is_expected.to eq valid_test_options.merge(config_path: config_path) } 29 | end 30 | 31 | context "when the argument is 'credentials.yml.enc'" do 32 | let(:config_path) { 'credentials.yml.enc' } 33 | before { RailsEnvCredentials.config_path = config_path } 34 | it { is_expected.to eq valid_production_options.merge(config_path: config_path) } 35 | end 36 | end 37 | 38 | describe ".env=" do 39 | subject { RailsEnvCredentials.options } 40 | 41 | context "when the argument is 'test'" do 42 | before { RailsEnvCredentials.env = "test" } 43 | it { is_expected.to eq valid_test_options } 44 | end 45 | 46 | context "when the argument is 'production'" do 47 | before { RailsEnvCredentials.env = "production" } 48 | it { is_expected.to eq valid_production_options } 49 | end 50 | end 51 | 52 | describe '.config' do 53 | before { RailsEnvCredentials.instance_variable_set(:@config, nil) } 54 | subject { RailsEnvCredentials.options } 55 | 56 | context "when Rails.env is 'test'" do 57 | it { is_expected.to eq valid_test_options } 58 | end 59 | 60 | context "when Rails.env is 'production'" do 61 | before { allow(Rails).to receive(:env) { ActiveSupport::StringInquirer.new('production') } } 62 | it { is_expected.to eq valid_production_options } 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require 'simplecov' 3 | SimpleCov.start 4 | 5 | require "bundler/setup" 6 | require "rails-env-credentials" 7 | 8 | RSpec.configure do |config| 9 | # Enable flags like --only-failures and --next-failure 10 | config.example_status_persistence_file_path = ".rspec_status" 11 | 12 | # Disable RSpec exposing methods globally on `Module` and `main` 13 | config.disable_monkey_patching! 14 | 15 | config.expect_with :rspec do |c| 16 | c.syntax = :expect 17 | end 18 | 19 | ignore_content = File.read(".gitignore") 20 | config.after(:suite) { File.write(".gitignore", ignore_content) } 21 | end 22 | 23 | require "rails/command" 24 | require "fake_app" 25 | --------------------------------------------------------------------------------