env fix
This commit is contained in:
@@ -89,3 +89,45 @@ func TestTheTierFallbackDoesNotHideAMissingModel(t *testing.T) {
|
||||
t.Fatal("balanced resolved to something despite being unset")
|
||||
}
|
||||
}
|
||||
|
||||
// Where a secret is allowed to live.
|
||||
//
|
||||
// `.env`, `.env.local` and `.env.production` are all tracked by git, so a key
|
||||
// written to any of them is a key published. There was nowhere else, and the
|
||||
// standing instruction was to export it in the shell on every run — which is
|
||||
// the kind of instruction people route around by editing a tracked file.
|
||||
func TestASecretsFileIsReadBeforeAnyTrackedEnvFile(t *testing.T) {
|
||||
order := envFileOrder("local")
|
||||
|
||||
if len(order) == 0 || order[0] != ".env.secrets" {
|
||||
t.Fatalf(".env.secrets is not read first, so a tracked file wins: %v", order)
|
||||
}
|
||||
// godotenv does not overwrite, so being first IS what makes it authoritative.
|
||||
// Being merely present would let .env.local decide the key instead.
|
||||
for _, tracked := range []string{".env.local", ".env"} {
|
||||
for i, name := range order {
|
||||
if name == tracked && i == 0 {
|
||||
t.Fatalf("%s is read first; a secret there would be committed", tracked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheEnvironmentsOwnFileBeatsTheSharedOne(t *testing.T) {
|
||||
// `.env.production` must be consulted before the shared `.env`, or a
|
||||
// production deployment silently takes the local defaults.
|
||||
order := envFileOrder("production")
|
||||
|
||||
var production, shared int = -1, -1
|
||||
for i, name := range order {
|
||||
switch name {
|
||||
case ".env.production":
|
||||
production = i
|
||||
case ".env":
|
||||
shared = i
|
||||
}
|
||||
}
|
||||
if production < 0 || shared < 0 || production > shared {
|
||||
t.Fatalf("the environment's own file does not take precedence: %v", order)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,10 +465,21 @@ func (c *Config) validate() error {
|
||||
//
|
||||
// APP_ENV is read from the real environment before any file, so a file cannot
|
||||
// change which environment it is loaded for.
|
||||
func loadEnvFiles() {
|
||||
appEnv := env("APP_ENV", EnvLocal)
|
||||
// `.env.secrets` is read FIRST and is the only one of these git does not track.
|
||||
// godotenv never overwrites a value already set, so first read wins — which is
|
||||
// what makes this file the place a key belongs. Every other file here is in the
|
||||
// repository, so a secret written to one is a secret published; there was
|
||||
// previously nowhere to put a key at all, and the answer was "export it in your
|
||||
// shell every time", which is the kind of instruction people route around.
|
||||
// envFileOrder is the read order, and the order is the rule: godotenv never
|
||||
// overwrites a value already set, so whichever file names a variable first is
|
||||
// the one that decides it.
|
||||
func envFileOrder(appEnv string) []string {
|
||||
return []string{".env.secrets", ".env." + appEnv, ".env"}
|
||||
}
|
||||
|
||||
for _, name := range []string{".env." + appEnv, ".env"} {
|
||||
func loadEnvFiles() {
|
||||
for _, name := range envFileOrder(env("APP_ENV", EnvLocal)) {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user