1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
package cmd
import (
"fmt"
"log"
"sort"
"strings"
)
func supportedShellFormattedString() string {
res := "["
for k := range supportedShellList {
res += k + ", "
}
res = strings.TrimSuffix(res, ", ")
res += "]"
return res
}
// CmdExport is `direnv export $0`
var CmdExport = &Cmd{
Name: "export",
Desc: `Loads an .envrc or .env and prints the diff in terms of exports.
Supported SHELL values are: ` + supportedShellFormattedString(),
Args: []string{"SHELL"},
Private: false,
Action: cmdWithWarnTimeout(actionWithConfig(exportCommand)),
}
func exportCommand(currentEnv Env, args []string, config *Config) (err error) {
defer log.SetPrefix(log.Prefix())
log.SetPrefix(log.Prefix() + "export:")
logDebug("start")
var target string
if len(args) > 1 {
target = args[1]
}
shell := DetectShell(target)
if shell == nil {
return fmt.Errorf("unknown target shell '%s'", target)
}
logDebug("loading RCs")
loadedRC := config.LoadedRC()
toLoad := findEnvUp(config.WorkDir, config.LoadDotenv)
if loadedRC == nil && toLoad == "" {
return
}
logDebug("updating RC")
log.SetPrefix(log.Prefix() + "update:")
logDebug("Determining action:")
logDebug("toLoad: %#v", toLoad)
logDebug("loadedRC: %#v", loadedRC)
switch {
case toLoad == "":
logDebug("no RC found, unloading")
case loadedRC == nil:
logDebug("no RC (implies no DIRENV_DIFF),loading")
case loadedRC.path != toLoad:
logDebug("new RC, loading")
case loadedRC.times.Check() != nil:
logDebug("file changed, reloading")
default:
logDebug("no update needed")
return
}
var previousEnv, newEnv Env
if previousEnv, err = config.Revert(currentEnv); err != nil {
err = fmt.Errorf("Revert() failed: %w", err)
logDebug("err: %v", err)
return
}
if toLoad == "" {
logStatus(config, "unloading")
newEnv = previousEnv.Copy()
newEnv.CleanContext()
} else {
newEnv, err = config.EnvFromRC(toLoad, previousEnv)
if err != nil {
logDebug("err: %v", err)
// If loading fails, fall through and deliver a diff anyway,
// but still exit with an error. This prevents retrying on
// every prompt.
}
if newEnv == nil {
// unless of course, the error was in hashing and timestamp loading,
// in which case we have to abort because we don't know what timestamp
// to put in the diff!
return
}
}
if out := diffStatus(previousEnv.Diff(newEnv)); out != "" && !config.HideEnvDiff {
logStatus(config, "export %s", out)
}
diffString, diffErr := currentEnv.Diff(newEnv).ToShell(shell)
if diffErr != nil {
return fmt.Errorf("ToShell() failed: %w", diffErr)
}
logDebug("env diff %s", diffString)
fmt.Print(diffString)
return
}
// Return a string of +/-/~ indicators of an environment diff
func diffStatus(oldDiff *EnvDiff) string {
if oldDiff.Any() {
var out []string
for key := range oldDiff.Prev {
_, ok := oldDiff.Next[key]
if !ok && !direnvKey(key) {
out = append(out, "-"+key)
}
}
for key := range oldDiff.Next {
_, ok := oldDiff.Prev[key]
if direnvKey(key) {
continue
}
if ok {
out = append(out, "~"+key)
} else {
out = append(out, "+"+key)
}
}
sort.Strings(out)
return strings.Join(out, " ")
}
return ""
}
func direnvKey(key string) bool {
return strings.HasPrefix(key, "DIRENV_")
}
|