dotenv-3.1.8/0000755000004100000410000000000015000126565013052 5ustar www-datawww-datadotenv-3.1.8/bin/0000755000004100000410000000000015000126565013622 5ustar www-datawww-datadotenv-3.1.8/bin/dotenv0000755000004100000410000000010415000126565015042 0ustar www-datawww-data#!/usr/bin/env ruby require "dotenv/cli" Dotenv::CLI.new(ARGV).run dotenv-3.1.8/lib/0000755000004100000410000000000015000126565013620 5ustar www-datawww-datadotenv-3.1.8/lib/dotenv/0000755000004100000410000000000015000126565015117 5ustar www-datawww-datadotenv-3.1.8/lib/dotenv/tasks.rb0000644000004100000410000000017315000126565016572 0ustar www-datawww-datadesc "Load environment settings from .env" task :dotenv do require "dotenv" Dotenv.load end task environment: :dotenv dotenv-3.1.8/lib/dotenv/autorestore.rb0000644000004100000410000000144115000126565020020 0ustar www-datawww-data# Automatically restore `ENV` to its original state after if defined?(RSpec.configure) RSpec.configure do |config| # Save ENV before the suite starts config.before(:suite) { Dotenv.save } # Restore ENV after each example config.after { Dotenv.restore } end end if defined?(ActiveSupport) ActiveSupport.on_load(:active_support_test_case) do ActiveSupport::TestCase.class_eval do # Save ENV before each test setup { Dotenv.save } # Restore ENV after each test teardown do Dotenv.restore rescue ThreadError => e # Restore will fail if running tests in parallel. warn e.message warn "Set `config.dotenv.autorestore = false` in `config/initializers/test.rb`" if defined?(Dotenv::Rails) end end end end dotenv-3.1.8/lib/dotenv/replay_logger.rb0000644000004100000410000000105115000126565020274 0ustar www-datawww-datamodule Dotenv # A logger that can be used before the apps real logger is initialized. class ReplayLogger < Logger def initialize super(nil) # Doesn't matter what this is, it won't be used. @logs = [] end # Override the add method to store logs so we can replay them to a real logger later. def add(*args, &block) @logs.push([args, block]) end # Replay the store logs to a real logger. def replay(logger) @logs.each { |args, block| logger.add(*args, &block) } @logs.clear end end end dotenv-3.1.8/lib/dotenv/missing_keys.rb0000644000004100000410000000040215000126565020144 0ustar www-datawww-datamodule Dotenv class Error < StandardError; end class MissingKeys < Error # :nodoc: def initialize(keys) key_word = "key#{(keys.size > 1) ? "s" : ""}" super("Missing required configuration #{key_word}: #{keys.inspect}") end end end dotenv-3.1.8/lib/dotenv/substitutions/0000755000004100000410000000000015000126565020056 5ustar www-datawww-datadotenv-3.1.8/lib/dotenv/substitutions/command.rb0000644000004100000410000000237715000126565022032 0ustar www-datawww-datarequire "English" module Dotenv module Substitutions # Substitute shell commands in a value. # # SHA=$(git rev-parse HEAD) # module Command class << self INTERPOLATED_SHELL_COMMAND = / (?\\)? # is it escaped with a backslash? \$ # literal $ (? # collect command content for eval \( # require opening paren (?:[^()]|\g)+ # allow any number of non-parens, or balanced # parens (by nesting the expression # recursively) \) # require closing paren ) /x def call(value, _env) # Process interpolated shell commands value.gsub(INTERPOLATED_SHELL_COMMAND) do |*| # Eliminate opening and closing parentheses command = $LAST_MATCH_INFO[:cmd][1..-2] if $LAST_MATCH_INFO[:backslash] # Command is escaped, don't replace it. $LAST_MATCH_INFO[0][1..] else # Execute the command and return the value `#{command}`.chomp end end end end end end end dotenv-3.1.8/lib/dotenv/substitutions/variable.rb0000644000004100000410000000156415000126565022176 0ustar www-datawww-datarequire "English" module Dotenv module Substitutions # Substitute variables in a value. # # HOST=example.com # URL="https://$HOST" # module Variable class << self VARIABLE = / (\\)? # is it escaped with a backslash? (\$) # literal $ (?!\() # shouldn't be followed by parenthesis \{? # allow brace wrapping ([A-Z0-9_]+)? # optional alpha nums \}? # closing brace /xi def call(value, env) value.gsub(VARIABLE) do |variable| match = $LAST_MATCH_INFO if match[1] == "\\" variable[1..] elsif match[3] env[match[3]] || ENV[match[3]] || "" else variable end end end end end end end dotenv-3.1.8/lib/dotenv/cli.rb0000644000004100000410000000254215000126565016216 0ustar www-datawww-datarequire "dotenv" require "dotenv/version" require "dotenv/template" require "optparse" module Dotenv # The `dotenv` command line interface. Run `$ dotenv --help` to see usage. class CLI < OptionParser attr_reader :argv, :filenames, :overwrite def initialize(argv = []) @argv = argv.dup @filenames = [] @ignore = false @overwrite = false super("Usage: dotenv [options]") separator "" on("-f FILES", Array, "List of env files to parse") do |list| @filenames = list end on("-i", "--ignore", "ignore missing env files") do @ignore = true end on("-o", "--overwrite", "overwrite existing ENV variables") do @overwrite = true end on("--overload") { @overwrite = true } on("-h", "--help", "Display help") do puts self exit end on("-v", "--version", "Show version") do puts "dotenv #{Dotenv::VERSION}" exit end on("-t", "--template=FILE", "Create a template env file") do |file| template = Dotenv::EnvTemplate.new(file) template.create_template end order!(@argv) end def run Dotenv.load(*@filenames, overwrite: @overwrite, ignore: @ignore) rescue Errno::ENOENT => e abort e.message else exec(*@argv) unless @argv.empty? end end end dotenv-3.1.8/lib/dotenv/rails.rb0000644000004100000410000000760315000126565016564 0ustar www-datawww-data# Since rubygems doesn't support optional dependencies, we have to manually check unless Gem::Requirement.new(">= 6.1").satisfied_by?(Gem::Version.new(Rails.version)) warn "dotenv 3.0 only supports Rails 6.1 or later. Use dotenv ~> 2.0." return end require "dotenv/replay_logger" require "dotenv/log_subscriber" Dotenv.instrumenter = ActiveSupport::Notifications # Watch all loaded env files with Spring ActiveSupport::Notifications.subscribe("load.dotenv") do |*args| if defined?(Spring) && Spring.respond_to?(:watch) event = ActiveSupport::Notifications::Event.new(*args) Spring.watch event.payload[:env].filename if Rails.application end end module Dotenv # Rails integration for using Dotenv to load ENV variables from a file class Rails < ::Rails::Railtie delegate :files, :files=, :overwrite, :overwrite=, :autorestore, :autorestore=, :logger, to: "config.dotenv" def initialize super config.dotenv = ActiveSupport::OrderedOptions.new.update( # Rails.logger is not available yet, so we'll save log messages and replay them when it is logger: Dotenv::ReplayLogger.new, overwrite: false, files: [ ".env.#{env}.local", (".env.local" unless env.test?), ".env.#{env}", ".env" ].compact, autorestore: env.test? && !defined?(ClimateControl) && !defined?(IceAge) ) end # Public: Load dotenv # # This will get called during the `before_configuration` callback, but you # can manually call `Dotenv::Rails.load` if you needed it sooner. def load Dotenv.load(*files.map { |file| root.join(file).to_s }, overwrite: overwrite) end def overload deprecator.warn("Dotenv::Rails.overload is deprecated. Set `Dotenv::Rails.overwrite = true` and call Dotenv::Rails.load instead.") Dotenv.load(*files.map { |file| root.join(file).to_s }, overwrite: true) end # Internal: `Rails.root` is nil in Rails 4.1 before the application is # initialized, so this falls back to the `RAILS_ROOT` environment variable, # or the current working directory. def root ::Rails.root || Pathname.new(ENV["RAILS_ROOT"] || Dir.pwd) end # Set a new logger and replay logs def logger=(new_logger) logger.replay new_logger if logger.is_a?(ReplayLogger) config.dotenv.logger = new_logger end # The current environment that the app is running in. # # When running `rake`, the Rails application is initialized in development, so we have to # check which rake tasks are being run to determine the environment. # # See https://github.com/bkeepers/dotenv/issues/219 def env @env ||= if defined?(Rake.application) && Rake.application.top_level_tasks.grep(TEST_RAKE_TASKS).any? env = Rake.application.options.show_tasks ? "development" : "test" ActiveSupport::EnvironmentInquirer.new(env) else ::Rails.env end end TEST_RAKE_TASKS = /^(default$|test(:|$)|parallel:spec|spec(:|$))/ def deprecator # :nodoc: @deprecator ||= ActiveSupport::Deprecation.new end # Rails uses `#method_missing` to delegate all class methods to the # instance, which means `Kernel#load` gets called here. We don't want that. def self.load instance.load end initializer "dotenv", after: :initialize_logger do |app| if logger.is_a?(ReplayLogger) self.logger = ActiveSupport::TaggedLogging.new(::Rails.logger).tagged("dotenv") end end initializer "dotenv.deprecator" do |app| app.deprecators[:dotenv] = deprecator if app.respond_to?(:deprecators) end initializer "dotenv.autorestore" do |app| require "dotenv/autorestore" if autorestore end config.before_configuration { load } end Railtie = ActiveSupport::Deprecation::DeprecatedConstantProxy.new("Dotenv::Railtie", "Dotenv::Rails", Dotenv::Rails.deprecator) end dotenv-3.1.8/lib/dotenv/rails-now.rb0000644000004100000410000000063615000126565017364 0ustar www-datawww-data# If you use gems that require environment variables to be set before they are # loaded, then list `dotenv` in the `Gemfile` before those other gems and # require `dotenv/load`. # # gem "dotenv", require: "dotenv/load" # gem "gem-that-requires-env-variables" # require "dotenv/load" warn '[DEPRECATION] `require "dotenv/rails-now"` is deprecated. Use `require "dotenv/load"` instead.', caller(1..1).first dotenv-3.1.8/lib/dotenv/environment.rb0000644000004100000410000000117015000126565020007 0ustar www-datawww-datamodule Dotenv # A `.env` file that will be read and parsed into a Hash class Environment < Hash attr_reader :filename, :overwrite # Create a new Environment # # @param filename [String] the path to the file to read # @param overwrite [Boolean] whether the parser should assume existing values will be overwritten def initialize(filename, overwrite: false) super() @filename = filename @overwrite = overwrite load end def load update Parser.call(read, overwrite: overwrite) end def read File.open(@filename, "rb:bom|utf-8", &:read) end end end dotenv-3.1.8/lib/dotenv/version.rb0000644000004100000410000000005515000126565017131 0ustar www-datawww-datamodule Dotenv VERSION = "3.1.8".freeze end dotenv-3.1.8/lib/dotenv/diff.rb0000644000004100000410000000341115000126565016353 0ustar www-datawww-datamodule Dotenv # A diff between multiple states of ENV. class Diff # The initial state attr_reader :a # The final or current state attr_reader :b # Create a new diff. If given a block, the state of ENV after the block will be preserved as # the final state for comparison. Otherwise, the current ENV will be the final state. # # @param a [Hash] the initial state, defaults to a snapshot of current ENV # @param b [Hash] the final state, defaults to the current ENV # @yield [diff] a block to execute before recording the final state def initialize(a: snapshot, b: ENV, &block) @a, @b = a, b block&.call self ensure @b = snapshot if block end # Return a Hash of keys added with their new values def added b.slice(*(b.keys - a.keys)) end # Returns a Hash of keys removed with their previous values def removed a.slice(*(a.keys - b.keys)) end # Returns of Hash of keys changed with an array of their previous and new values def changed (b.slice(*a.keys).to_a - a.to_a).map do |(k, v)| [k, [a[k], v]] end.to_h end # Returns a Hash of all added, changed, and removed keys and their new values def env b.slice(*(added.keys + changed.keys)).merge(removed.transform_values { |v| nil }) end # Returns true if any keys were added, removed, or changed def any? [added, removed, changed].any?(&:any?) end private def snapshot # `dup` should not be required here, but some people use `stub_const` to replace ENV with # a `Hash`. This ensures that we get a frozen copy of that instead of freezing the original. # https://github.com/bkeepers/dotenv/issues/482 ENV.to_h.dup.freeze end end end dotenv-3.1.8/lib/dotenv/log_subscriber.rb0000644000004100000410000000270415000126565020453 0ustar www-datawww-datarequire "active_support/log_subscriber" module Dotenv # Logs instrumented events # # Usage: # require "active_support/notifications" # require "dotenv/log_subscriber" # Dotenv.instrumenter = ActiveSupport::Notifications # class LogSubscriber < ActiveSupport::LogSubscriber attach_to :dotenv def logger Dotenv::Rails.logger end def load(event) env = event.payload[:env] info "Loaded #{color_filename(env.filename)}" end def update(event) diff = event.payload[:diff] changed = diff.env.keys.map { |key| color_var(key) } debug "Set #{changed.to_sentence}" if diff.any? end def save(event) info "Saved a snapshot of #{color_env_constant}" end def restore(event) diff = event.payload[:diff] removed = diff.removed.keys.map { |key| color(key, :RED) } restored = (diff.changed.keys + diff.added.keys).map { |key| color_var(key) } if removed.any? || restored.any? info "Restored snapshot of #{color_env_constant}" debug "Unset #{removed.to_sentence}" if removed.any? debug "Restored #{restored.to_sentence}" if restored.any? end end private def color_filename(filename) color(Pathname.new(filename).relative_path_from(Dotenv::Rails.root.to_s).to_s, :YELLOW) end def color_var(name) color(name, :CYAN) end def color_env_constant color("ENV", :GREEN) end end end dotenv-3.1.8/lib/dotenv/template.rb0000644000004100000410000000203515000126565017257 0ustar www-datawww-datamodule Dotenv EXPORT_COMMAND = "export ".freeze # Class for creating a template from a env file class EnvTemplate def initialize(env_file) @env_file = env_file end def create_template File.open(@env_file, "r") do |env_file| File.open("#{@env_file}.template", "w") do |env_template| env_file.each do |line| if is_comment?(line) env_template.puts line elsif (var = var_defined?(line)) if line.match(EXPORT_COMMAND) env_template.puts "export #{var}=#{var}" else env_template.puts "#{var}=#{var}" end elsif line_blank?(line) env_template.puts end end end end end private def is_comment?(line) line.strip.start_with?("#") end def var_defined?(line) match = Dotenv::Parser::LINE.match(line) match && match[:key] end def line_blank?(line) line.strip.length.zero? end end end dotenv-3.1.8/lib/dotenv/load.rb0000644000004100000410000000011515000126565016360 0ustar www-datawww-datarequire "dotenv" defined?(Dotenv::Rails) ? Dotenv::Rails.load : Dotenv.load dotenv-3.1.8/lib/dotenv/parser.rb0000644000004100000410000000640615000126565016746 0ustar www-datawww-datarequire "dotenv/substitutions/variable" require "dotenv/substitutions/command" if RUBY_VERSION > "1.8.7" module Dotenv # Error raised when encountering a syntax error while parsing a .env file. class FormatError < SyntaxError; end # Parses the `.env` file format into key/value pairs. # It allows for variable substitutions, command substitutions, and exporting of variables. class Parser @substitutions = [ Dotenv::Substitutions::Variable, Dotenv::Substitutions::Command ] LINE = / (?:^|\A) # beginning of line \s* # leading whitespace (?export\s+)? # optional export (?[\w.]+) # key (?: # optional separator and value (?:\s*=\s*?|:\s+?) # separator (? # optional value begin \s*'(?:\\'|[^'])*' # single quoted value | # or \s*"(?:\\"|[^"])*" # double quoted value | # or [^\#\n]+ # unquoted value )? # value end )? # separator and value end \s* # trailing whitespace (?:\#.*)? # optional comment (?:$|\z) # end of line /x QUOTED_STRING = /\A(['"])(.*)\1\z/m class << self attr_reader :substitutions def call(...) new(...).call end end def initialize(string, overwrite: false) # Convert line breaks to same format @string = string.gsub(/\r\n?/, "\n") @hash = {} @overwrite = overwrite end def call @string.scan(LINE) do match = $LAST_MATCH_INFO if existing?(match[:key]) # Use value from already defined variable @hash[match[:key]] = ENV[match[:key]] elsif match[:export] && !match[:value] # Check for exported variable with no value if !@hash.member?(match[:key]) raise FormatError, "Line #{match.to_s.inspect} has an unset variable" end else @hash[match[:key]] = parse_value(match[:value] || "") end end @hash end private # Determine if a variable is already defined and should not be overwritten. def existing?(key) !@overwrite && key != "DOTENV_LINEBREAK_MODE" && ENV.key?(key) end def parse_value(value) # Remove surrounding quotes value = value.strip.sub(QUOTED_STRING, '\2') maybe_quote = Regexp.last_match(1) # Expand new lines in double quoted values value = expand_newlines(value) if maybe_quote == '"' # Unescape characters and performs substitutions unless value is single quoted if maybe_quote != "'" value = unescape_characters(value) self.class.substitutions.each { |proc| value = proc.call(value, @hash) } end value end def unescape_characters(value) value.gsub(/\\([^$])/, '\1') end def expand_newlines(value) if (@hash["DOTENV_LINEBREAK_MODE"] || ENV["DOTENV_LINEBREAK_MODE"]) == "legacy" value.gsub('\n', "\n").gsub('\r', "\r") else value.gsub('\n', "\\\\\\n").gsub('\r', "\\\\\\r") end end end end dotenv-3.1.8/lib/dotenv.rb0000644000004100000410000001064215000126565015447 0ustar www-datawww-datarequire "dotenv/version" require "dotenv/parser" require "dotenv/environment" require "dotenv/missing_keys" require "dotenv/diff" # Shim to load environment variables from `.env files into `ENV`. module Dotenv extend self # An internal monitor to synchronize access to ENV in multi-threaded environments. SEMAPHORE = Monitor.new private_constant :SEMAPHORE attr_accessor :instrumenter # Loads environment variables from one or more `.env` files. See `#parse` for more details. def load(*filenames, overwrite: false, ignore: true) parse(*filenames, overwrite: overwrite, ignore: ignore) do |env| instrument(:load, env: env) do |payload| update(env, overwrite: overwrite) end end end # Same as `#load`, but raises Errno::ENOENT if any files don't exist def load!(*filenames) load(*filenames, ignore: false) end # same as `#load`, but will overwrite existing values in `ENV` def overwrite(*filenames) load(*filenames, overwrite: true) end alias_method :overload, :overwrite # same as `#overwrite`, but raises Errno::ENOENT if any files don't exist def overwrite!(*filenames) load(*filenames, overwrite: true, ignore: false) end alias_method :overload!, :overwrite! # Parses the given files, yielding for each file if a block is given. # # @param filenames [String, Array] Files to parse # @param overwrite [Boolean] Overwrite existing `ENV` values # @param ignore [Boolean] Ignore non-existent files # @param block [Proc] Block to yield for each parsed `Dotenv::Environment` # @return [Hash] parsed key/value pairs def parse(*filenames, overwrite: false, ignore: true, &block) filenames << ".env" if filenames.empty? filenames = filenames.reverse if overwrite filenames.reduce({}) do |hash, filename| begin env = Environment.new(File.expand_path(filename), overwrite: overwrite) env = block.call(env) if block rescue Errno::ENOENT, Errno::EISDIR raise unless ignore end hash.merge! env || {} end end # Save the current `ENV` to be restored later def save instrument(:save) do |payload| @diff = payload[:diff] = Dotenv::Diff.new end end # Restore `ENV` to a given state # # @param env [Hash] Hash of keys and values to restore, defaults to the last saved state # @param safe [Boolean] Is it safe to modify `ENV`? Defaults to `true` in the main thread, otherwise raises an error. def restore(env = @diff&.a, safe: Thread.current == Thread.main) # No previously saved or provided state to restore return unless env diff = Dotenv::Diff.new(b: env) return unless diff.any? unless safe raise ThreadError, <<~EOE.tr("\n", " ") Dotenv.restore is not thread safe. Use `Dotenv.modify { }` to update ENV for the duration of the block in a thread safe manner, or call `Dotenv.restore(safe: true)` to ignore this error. EOE end instrument(:restore, diff: diff) { ENV.replace(env) } end # Update `ENV` with the given hash of keys and values # # @param env [Hash] Hash of keys and values to set in `ENV` # @param overwrite [Boolean] Overwrite existing `ENV` values def update(env = {}, overwrite: false) instrument(:update) do |payload| diff = payload[:diff] = Dotenv::Diff.new do ENV.update(env.transform_keys(&:to_s)) do |key, old_value, new_value| # This block is called when a key exists. Return the new value if overwrite is true. overwrite ? new_value : old_value end end diff.env end end # Modify `ENV` for the block and restore it to its previous state afterwards. # # Note that the block is synchronized to prevent concurrent modifications to `ENV`, # so multiple threads will be executed serially. # # @param env [Hash] Hash of keys and values to set in `ENV` def modify(env = {}, &block) SEMAPHORE.synchronize do diff = Dotenv::Diff.new update(env, overwrite: true) block.call ensure restore(diff.a, safe: true) end end def require_keys(*keys) missing_keys = keys.flatten - ::ENV.keys return if missing_keys.empty? raise MissingKeys, missing_keys end private def instrument(name, payload = {}, &block) if instrumenter instrumenter.instrument("#{name}.dotenv", payload, &block) else block&.call payload end end end require "dotenv/rails" if defined?(Rails::Railtie) dotenv-3.1.8/dotenv.gemspec0000644000004100000410000000427415000126565015725 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: dotenv 3.1.8 ruby lib Gem::Specification.new do |s| s.name = "dotenv".freeze s.version = "3.1.8" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "changelog_uri" => "https://github.com/bkeepers/dotenv/releases", "funding_uri" => "https://github.com/sponsors/bkeepers" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Brandon Keepers".freeze] s.date = "2025-04-10" s.description = "Loads environment variables from `.env`.".freeze s.email = ["brandon@opensoul.org".freeze] s.executables = ["dotenv".freeze] s.files = ["LICENSE".freeze, "README.md".freeze, "bin/dotenv".freeze, "lib/dotenv.rb".freeze, "lib/dotenv/autorestore.rb".freeze, "lib/dotenv/cli.rb".freeze, "lib/dotenv/diff.rb".freeze, "lib/dotenv/environment.rb".freeze, "lib/dotenv/load.rb".freeze, "lib/dotenv/log_subscriber.rb".freeze, "lib/dotenv/missing_keys.rb".freeze, "lib/dotenv/parser.rb".freeze, "lib/dotenv/rails-now.rb".freeze, "lib/dotenv/rails.rb".freeze, "lib/dotenv/replay_logger.rb".freeze, "lib/dotenv/substitutions/command.rb".freeze, "lib/dotenv/substitutions/variable.rb".freeze, "lib/dotenv/tasks.rb".freeze, "lib/dotenv/template.rb".freeze, "lib/dotenv/version.rb".freeze] s.homepage = "https://github.com/bkeepers/dotenv".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 3.0".freeze) s.rubygems_version = "3.3.15".freeze s.summary = "Loads environment variables from `.env`.".freeze if s.respond_to? :specification_version then s.specification_version = 4 end if s.respond_to? :add_runtime_dependency then s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) end end dotenv-3.1.8/LICENSE0000644000004100000410000000205715000126565014063 0ustar www-datawww-dataCopyright (c) 2012 Brandon Keepers MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.dotenv-3.1.8/README.md0000644000004100000410000002752715000126565014346 0ustar www-datawww-data# dotenv [![Gem Version](https://badge.fury.io/rb/dotenv.svg)](https://badge.fury.io/rb/dotenv) Shim to load environment variables from `.env` into `ENV` in *development*.
Dotenv is proud to be sponsored by:
Stoked Seagull Software
Need help with a software project but don't know where to begin? Stoked Seagull can help.


Storing [configuration in the environment](http://12factor.net/config) is one of the tenets of a [twelve-factor app](http://12factor.net). Anything that is likely to change between deployment environments–such as resource handles for databases or credentials for external services–should be extracted from the code into environment variables. But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. dotenv loads variables from a `.env` file into `ENV` when the environment is bootstrapped. ## Installation Add this line to the top of your application's Gemfile and run `bundle install`: ```ruby gem 'dotenv', groups: [:development, :test] ``` ## Usage Add your application configuration to your `.env` file in the root of your project: ```shell S3_BUCKET=YOURS3BUCKET SECRET_KEY=YOURSECRETKEYGOESHERE ``` Whenever your application loads, these variables will be available in `ENV`: ```ruby config.fog_directory = ENV['S3_BUCKET'] ``` See the [API Docs](https://rubydoc.info/github/bkeepers/dotenv/main) for more. ### Rails Dotenv will automatically load when your Rails app boots. See [Customizing Rails](#customizing-rails) to change which files are loaded and when. ### Sinatra / Ruby Load Dotenv as early as possible in your application bootstrap process: ```ruby require 'dotenv/load' # or require 'dotenv' Dotenv.load ``` By default, `load` will look for a file called `.env` in the current working directory. Pass in multiple files and they will be loaded in order. The first value set for a variable will win. ```ruby require 'dotenv' Dotenv.load('file1.env', 'file2.env') ``` ### Autorestore in tests Since 3.0, dotenv in a Rails app will automatically restore `ENV` after each test. This means you can modify `ENV` in your tests without fear of leaking state to other tests. It works with both `ActiveSupport::TestCase` and `Rspec`. To disable this behavior, set `config.dotenv.autorestore = false` in `config/application.rb` or `config/environments/test.rb`. It is disabled by default if your app uses [climate_control](https://github.com/thoughtbot/climate_control) or [ice_age](https://github.com/dpep/ice_age_rb). To use this behavior outside of a Rails app, just `require "dotenv/autorestore"` in your test suite. See [`Dotenv.save`](https://rubydoc.info/github/bkeepers/dotenv/main/Dotenv:save), [Dotenv.restore](https://rubydoc.info/github/bkeepers/dotenv/main/Dotenv:restore), and [`Dotenv.modify(hash) { ... }`](https://rubydoc.info/github/bkeepers/dotenv/main/Dotenv:modify) for manual usage. ### Rake To ensure `.env` is loaded in rake, load the tasks: ```ruby require 'dotenv/tasks' task mytask: :dotenv do # things that require .env end ``` ### CLI You can use the `dotenv` executable load `.env` before launching your application: ```console $ dotenv ./script.rb ``` The `dotenv` executable also accepts the flag `-f`. Its value should be a comma-separated list of configuration files, in the order of the most important to the least important. All of the files must exist. There _must_ be a space between the flag and its value. ```console $ dotenv -f ".env.local,.env" ./script.rb ``` The `dotenv` executable can optionally ignore missing files with the `-i` or `--ignore` flag. For example, if the `.env.local` file does not exist, the following will ignore the missing file and only load the `.env` file. ```console $ dotenv -i -f ".env.local,.env" ./script.rb ``` ### Load Order If you use gems that require environment variables to be set before they are loaded, then list `dotenv` in the `Gemfile` before those other gems and require `dotenv/load`. ```ruby gem 'dotenv', require: 'dotenv/load' gem 'gem-that-requires-env-variables' ``` ### Customizing Rails Dotenv will load the following files depending on `RAILS_ENV`, with the first file having the highest precedence, and `.env` having the lowest precedence:
Priority Environment .gitignoreit? Notes
development test production
highest .env.development.local .env.test.local .env.production.local Yes Environment-specific local overrides
2nd .env.local N/A .env.local Yes Local overrides
3rd .env.development .env.test .env.production No Shared environment-specific variables
last .env .env .env Maybe Shared for all environments
These files are loaded during the `before_configuration` callback, which is fired when the `Application` constant is defined in `config/application.rb` with `class Application < Rails::Application`. If you need it to be initialized sooner, or need to customize the loading process, you can do so at the top of `application.rb` ```ruby # config/application.rb Bundler.require(*Rails.groups) # Load .env.local in test Dotenv::Rails.files.unshift(".env.local") if ENV["RAILS_ENV"] == "test" module YourApp class Application < Rails::Application # ... end end ``` Available options: * `Dotenv::Rails.files` - list of files to be loaded, in order of precedence. * `Dotenv::Rails.overwrite` - Overwrite existing `ENV` variables with contents of `.env*` files * `Dotenv::Rails.logger` - The logger to use for dotenv's logging. Defaults to `Rails.logger` * `Dotenv::Rails.autorestore` - Enable or disable [autorestore](#autorestore-in-tests) ### Multi-line values Multi-line values with line breaks must be surrounded with double quotes. ```shell PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY----- ... HkVN9... ... -----END DSA PRIVATE KEY-----" ``` Prior to 3.0, dotenv would replace `\n` in quoted strings with a newline, but that behavior is deprecated. To use the old behavior, set `DOTENV_LINEBREAK_MODE=legacy` before any variables that include `\n`: ```shell DOTENV_LINEBREAK_MODE=legacy PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nHkVN9...\n-----END DSA PRIVATE KEY-----\n" ``` ### Command Substitution You need to add the output of a command in one of your variables? Simply add it with `$(your_command)`: ```shell DATABASE_URL="postgres://$(whoami)@localhost/my_database" ``` ### Variable Substitution You need to add the value of another variable in one of your variables? You can reference the variable with `${VAR}` or often just `$VAR` in unquoted or double-quoted values. ```shell DATABASE_URL="postgres://${USER}@localhost/my_database" ``` If a value contains a `$` and it is not intended to be a variable, wrap it in single quotes. ```shell PASSWORD='pas$word' ``` ### Comments Comments may be added to your file as such: ```shell # This is a comment SECRET_KEY=YOURSECRETKEYGOESHERE # comment SECRET_HASH="something-with-a-#-hash" ``` ### Exports For compatability, you may also add `export` in front of each line so you can `source` the file in bash: ```shell export S3_BUCKET=YOURS3BUCKET export SECRET_KEY=YOURSECRETKEYGOESHERE ``` ### Required Keys If a particular configuration value is required but not set, it's appropriate to raise an error. To require configuration keys: ```ruby # config/initializers/dotenv.rb Dotenv.require_keys("SERVICE_APP_ID", "SERVICE_KEY", "SERVICE_SECRET") ``` If any of the configuration keys above are not set, your application will raise an error during initialization. This method is preferred because it prevents runtime errors in a production application due to improper configuration. ### Parsing To parse a list of env files for programmatic inspection without modifying the ENV: ```ruby Dotenv.parse(".env.local", ".env") # => {'S3_BUCKET' => 'YOURS3BUCKET', 'SECRET_KEY' => 'YOURSECRETKEYGOESHERE', ...} ``` This method returns a hash of the ENV var name/value pairs. ### Templates You can use the `-t` or `--template` flag on the dotenv cli to create a template of your `.env` file. ```console $ dotenv -t .env ``` A template will be created in your working directory named `{FILENAME}.template`. So in the above example, it would create a `.env.template` file. The template will contain all the environment variables in your `.env` file but with their values set to the variable names. ```shell # .env S3_BUCKET=YOURS3BUCKET SECRET_KEY=YOURSECRETKEYGOESHERE ``` Would become ```shell # .env.template S3_BUCKET=S3_BUCKET SECRET_KEY=SECRET_KEY ``` ## Frequently Answered Questions ### Can I use dotenv in production? dotenv was originally created to load configuration variables into `ENV` in *development*. There are typically better ways to manage configuration in production environments - such as `/etc/environment` managed by [Puppet](https://github.com/puppetlabs/puppet) or [Chef](https://github.com/chef/chef), `heroku config`, etc. However, some find dotenv to be a convenient way to configure Rails applications in staging and production environments, and you can do that by defining environment-specific files like `.env.production` or `.env.test`. If you use this gem to handle env vars for multiple Rails environments (development, test, production, etc.), please note that env vars that are general to all environments should be stored in `.env`. Then, environment specific env vars should be stored in `.env.`. ### Should I commit my .env file? Credentials should only be accessible on the machines that need access to them. Never commit sensitive information to a repository that is not needed by every development machine and server. Personally, I prefer to commit the `.env` file with development-only settings. This makes it easy for other developers to get started on the project without compromising credentials for other environments. If you follow this advice, make sure that all the credentials for your development environment are different from your other deployments and that the development credentials do not have access to any confidential data. ### Why is it not overwriting existing `ENV` variables? By default, it **won't** overwrite existing environment variables as dotenv assumes the deployment environment has more knowledge about configuration than the application does. To overwrite existing environment variables you can use `Dotenv.load files, overwrite: true`. You can also use the `-o` or `--overwrite` flag on the dotenv cli to overwrite existing `ENV` variables. ```console $ dotenv -o -f ".env.local,.env" ``` ## Contributing If you want a better idea of how dotenv works, check out the [Ruby Rogues Code Reading of dotenv](https://www.youtube.com/watch?v=lKmY_0uY86s). 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Added some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request