Skip to content

Commit

Permalink
Allow ignoring specified environment variables
Browse files Browse the repository at this point in the history
Add `[env] / ignore_keys` configuration to ignore the listed environment
variables, in addition to the default ones.

Fixes direnv#401
  • Loading branch information
tmatilai committed Jan 22, 2022
1 parent 3c84231 commit 532beaf
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 16 deletions.
14 changes: 14 additions & 0 deletions internal/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Config struct {
WarnTimeout time.Duration
WhitelistPrefix []string
WhitelistExact map[string]bool
EnvIgnoreKeys []string
}

type tomlDuration struct {
Expand All @@ -45,6 +46,7 @@ type tomlConfig struct {
*tomlGlobal // For backward-compatibility
Global *tomlGlobal `toml:"global"`
Whitelist tomlWhitelist `toml:"whitelist"`
Env tomlEnv `toml:"env"`
}

type tomlGlobal struct {
Expand All @@ -60,6 +62,10 @@ type tomlWhitelist struct {
Exact []string
}

type tomlEnv struct {
IgnoreKeys []string `toml:"ignore_keys"`
}

// LoadConfig opens up the direnv configuration from the Env.
func LoadConfig(env Env) (config *Config, err error) {
config = &Config{
Expand Down Expand Up @@ -94,6 +100,8 @@ func LoadConfig(env Env) (config *Config, err error) {
config.WhitelistPrefix = make([]string, 0)
config.WhitelistExact = make(map[string]bool)

config.EnvIgnoreKeys = make([]string, 0)

// Load the TOML config
config.TomlPath = filepath.Join(config.ConfDir, "direnv.toml")
if _, statErr := os.Stat(config.TomlPath); statErr != nil {
Expand All @@ -118,6 +126,12 @@ func LoadConfig(env Env) (config *Config, err error) {
return
}

if env[DIRENV_IGNORE_KEYS] != "" {
config.EnvIgnoreKeys = strings.Split(env[DIRENV_IGNORE_KEYS], ",")
} else {
config.EnvIgnoreKeys = append(config.EnvIgnoreKeys, tomlConf.Env.IgnoreKeys...)
}

config.WhitelistPrefix = append(config.WhitelistPrefix, tomlConf.Whitelist.Prefix...)

for _, path := range tomlConf.Whitelist.Exact {
Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ const (
DIRENV_DIFF = "DIRENV_DIFF"

DIRENV_DUMP_FILE_PATH = "DIRENV_DUMP_FILE_PATH"

DIRENV_IGNORE_KEYS = "DIRENV_IGNORE_KEYS"
)
1 change: 1 addition & 0 deletions internal/cmd/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func (env Env) CleanContext() {
delete(env, DIRENV_FILE)
delete(env, DIRENV_DUMP_FILE_PATH)
delete(env, DIRENV_WATCHES)
delete(env, DIRENV_IGNORE_KEYS)
}

// LoadEnv unmarshals the env back from a gzenv string
Expand Down
41 changes: 31 additions & 10 deletions internal/cmd/env_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import (
"github.com/direnv/direnv/v2/gzenv"
)

// IgnoredKeys is list of keys we don't want to deal with
var IgnoredKeys = map[string]bool{
// DefaultIgnoredKeys is list of keys we don't want to deal with
var DefaultIgnoredKeys = map[string]bool{
// direnv env config
"DIRENV_CONFIG": true,
"DIRENV_BASH": true,
"DIRENV_CONFIG": true,
"DIRENV_BASH": true,
"DIRENV_IGNORE_KEYS": true,

// should only be available inside of the .envrc or .env
"DIRENV_IN_ENVRC": true,
Expand All @@ -27,6 +28,11 @@ var IgnoredKeys = map[string]bool{
"_": true,
}

// IgnoredKeys represents all the keys we don't want to deal with
type IgnoredKeys struct {
keys map[string]bool
}

// EnvDiff represents the diff between two environments
type EnvDiff struct {
Prev map[string]string `json:"p"`
Expand All @@ -43,13 +49,15 @@ func NewEnvDiff() *EnvDiff {
func BuildEnvDiff(e1, e2 Env) *EnvDiff {
diff := NewEnvDiff()

ignoredKeys := NewIgnoredKeys(e2)

in := func(key string, e Env) bool {
_, ok := e[key]
return ok
}

for key := range e1 {
if IgnoredEnv(key) {
if ignoredKeys.IsIgnored(key) {
continue
}
if e2[key] != e1[key] || !in(key, e2) {
Expand All @@ -58,7 +66,7 @@ func BuildEnvDiff(e1, e2 Env) *EnvDiff {
}

for key := range e2 {
if IgnoredEnv(key) {
if ignoredKeys.IsIgnored(key) {
continue
}
if e2[key] != e1[key] || !in(key, e1) {
Expand Down Expand Up @@ -131,16 +139,29 @@ func (diff *EnvDiff) Serialize() string {
return gzenv.Marshal(diff)
}

//// Utils
//// IgnoredKeys

// NewIgnoredKeys creates a NewIgnoredKeys including all keys to ignore
func NewIgnoredKeys(env Env) *IgnoredKeys {
ignoredKeys := make(map[string]bool)

for key, value := range DefaultIgnoredKeys {
ignoredKeys[key] = value
}
for _, key := range strings.Split(env[DIRENV_IGNORE_KEYS], ",") {
ignoredKeys[key] = true
}
return &IgnoredKeys{ignoredKeys}
}

// IgnoredEnv returns true if the key should be ignored in environment diffs.
func IgnoredEnv(key string) bool {
// IsIgnored returns true if the key should be ignored in environment diffs.
func (ignoredKeys *IgnoredKeys) IsIgnored(key string) bool {
if strings.HasPrefix(key, "__fish") {
return true
}
if strings.HasPrefix(key, "BASH_FUNC_") {
return true
}
_, found := IgnoredKeys[key]
_, found := ignoredKeys.keys[key]
return found
}
25 changes: 19 additions & 6 deletions internal/cmd/env_diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,33 @@ func TestEnvDiffEmptyValue(t *testing.T) {
}
}

func TestIgnoredEnv(t *testing.T) {
if !IgnoredEnv(DIRENV_BASH) {
func TestIgnoredKeysv(t *testing.T) {
env := Env{"DIRENV_IGNORE_KEYS": "FOO,BAR"}
ignoredKeys := NewIgnoredKeys(env)

if !ignoredKeys.IsIgnored(DIRENV_BASH) {
t.Fail()
}
if ignoredKeys.IsIgnored(DIRENV_DIFF) {
t.Fail()
}
if !ignoredKeys.IsIgnored("_") {
t.Fail()
}
if !ignoredKeys.IsIgnored("__fish_foo") {
t.Fail()
}
if IgnoredEnv(DIRENV_DIFF) {
if !ignoredKeys.IsIgnored("__fishx") {
t.Fail()
}
if !IgnoredEnv("_") {
if !ignoredKeys.IsIgnored("FOO") {
t.Fail()
}
if !IgnoredEnv("__fish_foo") {
if !ignoredKeys.IsIgnored("BAR") {
t.Fail()
}
if !IgnoredEnv("__fishx") {

if ignoredKeys.IsIgnored("BAZ") {
t.Fail()
}
}
1 change: 1 addition & 0 deletions internal/cmd/rc.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ func (rc *RC) Load(previousEnv Env) (newEnv Env, err error) {
direnv := config.SelfPath
newEnv = previousEnv.Copy()
newEnv[DIRENV_WATCHES] = rc.times.Marshal()
newEnv[DIRENV_IGNORE_KEYS] = strings.Join(config.EnvIgnoreKeys, ",")
defer func() {
// Record directory changes even if load is disallowed or fails
newEnv[DIRENV_DIR] = "-" + filepath.Dir(rc.path)
Expand Down
18 changes: 18 additions & 0 deletions man/direnv.toml.1
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,24 @@ In this example, the following .envrc files will not be implicitly allowed (alth

.RE

.SH [env]
.SS \fB\fCignore_keys\fR
.PP
List of environment variable names which should be ignored, in addition to some default ones.

.PP
Example:

.PP
.RS

.nf
[env]
ignore_keys = ["TMUX_PANE", "SSH_AUTH_SOCK"]

.fi
.RE

.SH COPYRIGHT
.PP
MIT licence - Copyright (C) 2019 @zimbatm and contributors
Expand Down
13 changes: 13 additions & 0 deletions man/direnv.toml.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ In this example, the following .envrc files will not be implicitly allowed (alth
* `/home/user/code/project-b/subproject-c/.envrc`
* `/home/user/code/.envrc`

## [env]

### `ignore_keys`

List of environment variable names which should be ignored, in addition to some default ones.

Example:

```toml
[env]
ignore_keys = ["TMUX_PANE", "SSH_AUTH_SOCK"]
```

COPYRIGHT
---------

Expand Down

0 comments on commit 532beaf

Please sign in to comment.