Czy można ustawić zmienne ENV dla środowiska programistycznego rails w moim kodzie?

83

Wiem, że mogę ustawić zmienne ENV w bash za pośrednictwem

export admin_password = "secret"

Ale czy jest sposób, aby to zrobić gdzieś w kodzie źródłowym moich railsów? Moja pierwsza próba była takaenvironment/development.rb

ENV['admin_password'] = "secret"

Ale to nie zadziałało. Czy jest na to sposób?

Lan
źródło
1
Zauważ, że polecenie bash powinno być export admin_password="secret", nie export admin_password = "secret".
Jacob Lockard,

Odpowiedzi:

81

[Aktualizacja]

Chociaż rozwiązanie w sekcji „stara odpowiedź” będzie działać w przypadku ogólnych problemów, w tej sekcji należy odpowiedzieć na konkretne pytanie po wyjaśnieniu zawartym w komentarzu.

Powinieneś być w stanie ustawić zmienne środowiskowe dokładnie tak, jak określono w swoim pytaniu. Jako przykład, mam aplikację Heroku, która używa podstawowego uwierzytelniania HTTP.

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :authenticate

  def authenticate
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV['HTTP_USER'] && password == ENV['HTTP_PASS']
    end
  end
end

# config/initializers/dev_environment.rb
unless Rails.env.production?
  ENV['HTTP_USER'] = 'testuser'
  ENV['HTTP_PASS'] = 'testpass'
end

Więc w twoim przypadku użyłbyś

unless Rails.env.production?
  ENV['admin_password'] = "secret"
end

Nie zapomnij zrestartować serwera, aby konfiguracja została ponownie załadowana!

[Stara odpowiedź]

W przypadku konfiguracji obejmującej całą aplikację można rozważyć następujące rozwiązanie:

Utwórz plik config/application.ymlz hashem opcji, do których chcesz mieć dostęp:

admin_password: something_secret
allow_registration: true
facebook:
  app_id: application_id_here
  app_secret: application_secret_here
  api_key: api_key_here

Teraz utwórz plik config/initializers/app_config.rbi dołącz następujące elementy:

require 'yaml'

yaml_data = YAML::load(ERB.new(IO.read(File.join(Rails.root, 'config', 'application.yml'))).result)
APP_CONFIG = HashWithIndifferentAccess.new(yaml_data)

Teraz, z dowolnego miejsca w aplikacji, możesz uzyskać dostęp APP_CONFIG[:admin_password]wraz ze wszystkimi innymi danymi. (Zauważ, że ponieważ inicjator zawiera ERB.new, twój plik YAML może zawierać znaczniki ERB).

Michelle Tilley
źródło
2
Cóż, używam Heroku i pozwalają ci ustawić pewne zmienne ENV dla rzeczy takich jak hasła, których nie chcesz sprawdzać w kontroli źródła. Próbuję znaleźć najlepszą rzecz do zrobienia na moim komputerze deweloperskim. I tak, próbuję użyć danych z mojej aplikacji.
Lan,
Miło to słyszeć! Jeśli uważasz, że odpowiedź jest prawidłowa, zaznacz ją, zaznaczając znacznik wyboru obok pytania. Pozwoli to zyskać reputację Ciebie i osoby odpowiadającej, a także pomoże osobom, które znajdą to pytanie w przyszłości, znaleźć poprawną odpowiedź.
Michelle Tilley,
O tak, jestem nowym użytkownikiem, więc muszę czekać 19 godzin, zanim będę mógł to sprawdzić. Jeszcze 3 godziny :) Ale dziękuję za pomoc! Właściwie, gdybyś zredagował swoją odpowiedź, usuwając dev_environment.rbkod i zastępując go moim, z przyjemnością zaznaczyłbym twoją odpowiedź, która jest znacznie dokładniejsza.
Lan
Podoba mi się to, ale z jakiegoś powodu (szyny 3 v 4?) nie mogłem załadować app_config.rb. Umieściłem kod w config / environment.rb i uzyskałem ten sam efekt. # Załaduj aplikację railsową require File.expand_path ('../ application', FILE ) require 'yaml' yaml_data = YAML :: load (ERB.new (IO.read (File.join (Rails.root, 'config', 'local_env.yml'))). result) APP_CONFIG = HashWithIndifferentAccess.new (yaml_data) # Zainicjuj aplikację railsową Rails3 :: Application.initialize!
Ben
Przepraszam, że otwieram taki stary wątek, ale czy ustawienie specyficzne dla środowiska deweloperskiego nie miałoby większego sensu w config/environments/development.rbpliku?
jeffdill2
130

Nigdy nie zakoduj na stałe poufnych informacji (danych logowania do konta, haseł itp . ) . Zamiast tego utwórz plik do przechowywania tych informacji jako zmienne środowiskowe (pary klucz / wartość) i wyklucz ten plik z systemu zarządzania kodem źródłowym. Na przykład, jeśli chodzi o Git (system zarządzania kodem źródłowym), wyklucz ten plik, dodając go do. gitignore :

-bash> echo '/config/app_environment_variables.rb' >> .gitignore 

/config/app_environment_variables.rb

ENV['HTTP_USER'] = 'devuser'
ENV['HTTP_PASS'] = 'devpass'

Oprócz tego dodaj następujące wiersze /config/environment.rbmiędzy requirewierszem a Application.initializewierszem:

# Load the app's custom environment variables here, so that they are loaded before environments/*.rb
app_environment_variables = File.join(Rails.root, 'config', 'app_environment_variables.rb')
load(app_environment_variables) if File.exists?(app_environment_variables)

Otóż ​​to!

Jak mówi powyższy komentarz, robiąc to, wcześniej załadujesz swoje zmienne środowiskowe environments/*.rb, co oznacza, że ​​będziesz mógł odwoływać się do swoich zmiennych w tych plikach (np environments/production.rb.). Jest to duża zaleta w stosunku do umieszczania pliku zmiennych środowiskowych w środku /config/initializers/.

Wewnątrz app_environment_variables.rbnie ma potrzeby rozróżniania środowisk, jeśli chodzi o rozwój lub produkcję, ponieważ nigdy nie zapiszesz tego pliku do systemu zarządzania kodem źródłowym, dlatego jest on domyślnie przeznaczony dla kontekstu programowania . Ale jeśli chcesz ustawić coś specjalnego dla środowiska testowego (lub na okazje, kiedy testujesz lokalnie testujesz tryb produkcyjny ), po prostu dodaj blok warunkowy poniżej wszystkich innych zmiennych:

if Rails.env.test?
  ENV['HTTP_USER'] = 'testuser'
  ENV['HTTP_PASS'] = 'testpass'
end

if Rails.env.production?
  ENV['HTTP_USER'] = 'produser'
  ENV['HTTP_PASS'] = 'prodpass'
end

Po każdej aktualizacji app_environment_variables.rburuchom ponownie serwer aplikacji. Zakładając, że korzystasz z Apache / Passenger lub rails server:

-bash> touch tmp/restart.txt

W swoim kodzie odwołaj się do zmiennych środowiskowych w następujący sposób:

def authenticate
  authenticate_or_request_with_http_basic do |username, password|
    username == ENV['HTTP_USER'] && password == ENV['HTTP_PASS']
  end
end

Zauważ, że wewnątrz app_environment_variables.rbmusisz określić wartości logiczne i liczby jako łańcuchy (np. ENV['SEND_MAIL'] = 'false'Nie tylko false, iENV['TIMEOUT'] = '30' nie tylko 30), w przeciwnym razie otrzymasz odpowiednio błędy can't convert false into Stringi can't convert Fixnum into String.

Przechowywanie i udostępnianie poufnych informacji

Ostatnim węzłem do zawiązania jest: jak udostępnić te poufne informacje swoim klientom i / lub partnerom? W celu zapewnienia ciągłości biznesowej (tj. Gdy zostaniesz trafiony spadającą gwiazdą, w jaki sposób Twoi klienci i / lub partnerzy wznowią pełne działanie witryny?) Twoi klienci i / lub partnerzy muszą znać wszystkie poświadczenia wymagane przez Twoją aplikację . Wysyłanie e-maili / Skypowanie się z takimi rzeczami jest niebezpieczne i prowadzi do nieładu. Przechowywanie go w udostępnionych Dokumentach Google nie jest złe (jeśli wszyscy używają https), ale aplikacja przeznaczona do przechowywania i udostępniania drobiazgów, takich jak hasła, byłaby idealna.

Jak ustawić zmienne środowiskowe w Heroku

Jeśli masz jedno środowisko na Heroku:

-bash> heroku config:add HTTP_USER='herouser'
-bash> heroku config:add HTTP_USER='heropass'

Jeśli masz wiele środowisk w Heroku:

-bash> heroku config:add HTTP_USER='staguser' --remote staging
-bash> heroku config:add HTTP_PASS='stagpass' --remote staging

-bash> heroku config:add HTTP_USER='produser' --remote production
-bash> heroku config:add HTTP_PASS='prodpass' --remote production

Foreman i .env

Wielu programistów używa Foreman (instalowanego z Heroku Toolbelt ) do uruchamiania swoich aplikacji lokalnie (w przeciwieństwie do Apache / Passenger lub rails server). Foreman i Heroku używają Procfiledo zadeklarowania, jakie polecenia są uruchamiane przez twoją aplikację , więc przejście z lokalnego dewelopera do Heroku jest płynne w tym względzie. Używam Foreman i Heroku w każdym projekcie Rails, więc ta wygoda jest świetna. Ale o to chodzi ... Foreman ładuje zmienne środowiskowe przechowywane /.envprzez dotenv, ale niestety dotenv zasadniczo analizuje plik tylko pod kątem key=valuepar; te pary nie stają się zmiennymi od razu tam i wtedy, więc nie możesz odwoływać się do już ustawionych zmiennych (aby utrzymać rzeczy SUCHE), ani nie możesz tam zrobić "Ruby" (jak wspomniano powyżej z warunkami), które możesz zrobić w/config/app_environment_variables.rb. Na przykład, jeśli chodzi o utrzymywanie rzeczy SUCHA, czasami robię takie rzeczy:

ENV['SUPPORT_EMAIL']='Company Support <[email protected]>'
ENV['MAILER_DEFAULT_FROM'] = ENV['SUPPORT_EMAIL']
ENV['MAILER_DEFAULT_TO']   = ENV['SUPPORT_EMAIL']

Dlatego używam Foremana do uruchamiania moich aplikacji lokalnie, ale nie używam jego .envpliku do ładowania zmiennych środowiskowych; raczej używam Foremana w połączeniu z /config/app_environment_variables.rbpodejściem opisanym powyżej.

user664833
źródło
3
Podoba mi się to rozwiązanie, ponieważ chciałem mieć pewność, że wrażliwe ustawienia (klucze API itp.) Są odpowiednio chronione w magazynie kluczy, zamiast w postaci zwykłego tekstu i nie są dołączane do repozytorium Git. Dzięki.
Rori Stumpf
10

Sposób, w jaki próbuję to zrobić w moim pytaniu, faktycznie działa!

# environment/development.rb

ENV['admin_password'] = "secret" 

Musiałem tylko zrestartować serwer. Myślałem, że reload!wystarczy uruchomić w konsoli rails, ale musiałem też zrestartować serwer WWW.

Wybieram własną odpowiedź, ponieważ uważam, że jest to lepsze miejsce do umieszczania i ustawiania zmiennych ENV

Lan
źródło
Cieszę się, że dowiedziałeś się, dlaczego to nie działa! Osobiście lubię zapisywać config/application.rbi config/environments/*.rbkonfigurować środowisko Railsów oraz używać innych metod do konfigurowania środowiska aplikacji , ale to z pewnością nie oznacza, że ​​jest to jedyny sposób (ani nawet „właściwy” sposób :)
Michelle Tilley
Trochę mnie odrzuciło, ponieważ próbowałem inaczej ustawić produkcję i rozwój. Ale teraz całkowicie się z tobą zgadzam! Dziękuję nie tylko za odpowiedź na moje pierwotne pytanie, ale także za pomoc w lepszym zrozumieniu struktury aplikacji rails!
Lan
8

Poza rozwiązaniami tutaj, istnieją czystsze alternatywy, jeśli używasz niektórych serwerów programistycznych.

Z Heroku's Foreman , możesz tworzyć zmienne środowiskowe dla projektu w .envpliku:

ADMIN_PASSOWRD="secret"

Z Pow możesz użyć .powenvpliku:

export ADMIN_PASSOWRD="secret"
Aupajo
źródło
2

Myślę, że najlepszym sposobem jest przechowywanie ich w jakimś pliku yml, a następnie załadowanie tego pliku za pomocą tego polecenia w pliku intializera

APP_CONFIG = YAML.load_file("#{Rails.root}/config/CONFIG.yml")[Rails.env].to_hash

możesz łatwo uzyskać dostęp do zmiennych konfiguracyjnych związanych ze środowiskiem.

Struktura wartości klucza w pliku Yml:

development:
  app_key: 'abc'
  app_secret: 'abc'

production:
  app_key: 'xyz'
  app_secret: 'ghq'
Taimoor Changaiz
źródło
1

Środowisko systemowe i środowisko szyn to różne rzeczy. ENVpozwól nam pracować ze środowiskiem railsów, ale jeśli to, co chcesz zrobić, to zmienić środowisko systemu w czasie wykonywania, możesz po prostu otoczyć polecenie lewymi apostrofami.

# ruby code
`export admin_password="secret"`
# more ruby code
goncalossilva
źródło
1
Tylko uwaga, która exportbędzie narzekać na spacje; spróbujexport admin_password="secret"
Michelle Tilley,
Zapomniałem o tym (bardzo ważnym) szczególe. Poprawiłem moją odpowiedź, dzięki!
goncalossilva,
To nie działa, ponieważ lewe apostrofy tworzą podpowłokę, a zmienne ustawione w podpowłoce nie mogą wpływać na środowisko rodzica.
AndrewF,
Nie, to działa. Odpowiedź, jak jasno wyjaśniono, nie jest skierowana do środowiska rodzica. Istnieją doskonałe odpowiedzi, które instruują, jak to zrobić.
goncalossilva
1

Skrypt do ładowania .envpliku niestandardowego : Dodaj następujące wiersze do/config/environment.rb , między requirewierszem a Application.initializewierszem:

# Load the app's custom environment variables here, so that they are loaded before environments/*.rb

app_environment_variables = File.join(Rails.root, 'config', 'local_environment.env')
if File.exists?(app_environment_variables)
  lines = File.readlines(app_environment_variables)
  lines.each do |line|
    line.chomp!
    next if line.empty? or line[0] == '#'
    parts = line.partition '='
    raise "Wrong line: #{line} in #{app_environment_variables}" if parts.last.empty?
    ENV[parts.first] = parts.last
  end
end

I config/local_environment.env (będziesz chciał .gitignore) będzie wyglądać tak:

# This is ignored comment
DATABASE_URL=mysql2://user:[email protected]:3307/database
RACK_ENV=development

(Na podstawie rozwiązania @ user664833)

Daniel Garmoshka
źródło