feat: example env files are listed hidden, not skipped #83
No files matched your search
+3
-2
@@ -29,8 +29,9 @@ anything by hand.
|
||||
## Which files
|
||||
|
||||
**Env files**: `.env`, `.env.local`, `prod.env` and the like, also when
|
||||
git-ignored (as they usually are), in the folders git tracks files in;
|
||||
not `.env.example`, `.sample`, `.template` and the like. They're read
|
||||
git-ignored (as they usually are), in the folders git tracks files in.
|
||||
`.env.example`, `.sample`, `.template` and the like are listed hidden
|
||||
(`H` shows them): someone may have put a real value in one. They're read
|
||||
directly; a file with SOPS metadata is always treated as SOPS, whatever
|
||||
its name.
|
||||
|
||||
|
||||
@@ -333,8 +333,9 @@ func (p *Provider) Containers(ctx context.Context, src provider.Source) ([]provi
|
||||
c.Detail = "sops · " + strings.Join(meta.methods, ", ")
|
||||
case isSops:
|
||||
return // a SOPS file where .sops.yaml says SOPS files don't go
|
||||
case IsEnv(f):
|
||||
case isEnvNamed(f):
|
||||
c.Detail = provider.DetailPlainEnv
|
||||
c.Hidden = isExample(f)
|
||||
case IsSpring(f):
|
||||
c.Detail = "spring · " + springFormat(f)
|
||||
c.Hidden = p.springHidden(r.root, path)
|
||||
@@ -717,7 +718,7 @@ func envFiles(root string, tracked []string) []string {
|
||||
dirs := map[string]bool{".": true}
|
||||
for _, f := range tracked {
|
||||
isTracked[f] = true
|
||||
if IsEnv(f) {
|
||||
if isEnvNamed(f) {
|
||||
out = append(out, f)
|
||||
}
|
||||
if d := path.Dir(f); !dirs[d] && !inPackages(f) {
|
||||
@@ -731,7 +732,7 @@ func envFiles(root string, tracked []string) []string {
|
||||
}
|
||||
for _, e := range entries {
|
||||
f := path.Join(d, e.Name())
|
||||
if e.Type().IsRegular() && !isTracked[f] && IsEnv(f) {
|
||||
if e.Type().IsRegular() && !isTracked[f] && isEnvNamed(f) {
|
||||
out = append(out, f)
|
||||
}
|
||||
}
|
||||
@@ -766,17 +767,30 @@ func inPackages(f string) bool {
|
||||
// .env, .env.local, prod.env; not .env.example and the like, nor one in
|
||||
// dependencies.
|
||||
func IsEnv(f string) bool {
|
||||
if inPackages(f) {
|
||||
return false
|
||||
return isEnvNamed(f) && !isExample(f)
|
||||
}
|
||||
|
||||
// isEnvNamed reports whether a file outside dependencies is named like an
|
||||
// env file, examples too.
|
||||
func isEnvNamed(f string) bool {
|
||||
base := strings.ToLower(path.Base(f))
|
||||
for _, ext := range []string{".example", ".sample", ".template", ".dist", ".tmpl"} {
|
||||
if strings.HasSuffix(base, ext) {
|
||||
return !inPackages(f) && (base == ".env" || strings.HasPrefix(base, ".env.") || strings.HasSuffix(base, ".env"))
|
||||
}
|
||||
|
||||
var exampleWords = []string{"example", "sample", "template", "dist", "tmpl"}
|
||||
|
||||
// isExample reports whether a file is an example or template of another:
|
||||
// .env.example, docker-compose.example.yml. They're listed hidden: someone
|
||||
// may have put a real value in one.
|
||||
func isExample(f string) bool {
|
||||
base := strings.ToLower(path.Base(f))
|
||||
for _, w := range exampleWords {
|
||||
if strings.HasSuffix(base, "."+w) || strings.Contains(base, "."+w+".") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return base == ".env" || strings.HasPrefix(base, ".env.") || strings.HasSuffix(base, ".env")
|
||||
}
|
||||
|
||||
// plainValue reads a key from a plain env file.
|
||||
func plainValue(file, name string) (provider.SecretValue, error) {
|
||||
|
||||
@@ -292,9 +292,12 @@ func TestPlainEnvFiles(t *testing.T) {
|
||||
got := map[string]string{}
|
||||
for _, c := range files {
|
||||
got[c.Name] = c.Detail
|
||||
if c.Hidden != (c.Name == ".env.example") {
|
||||
t.Errorf("%s: hidden %t", c.Name, c.Hidden)
|
||||
}
|
||||
if len(got) != 2 || got[".env"] != "env · plain text" || got["secrets/app.yaml"] != "sops · age" {
|
||||
t.Fatalf("files %v (want .env and the sops file; not the example or node_modules)", got)
|
||||
}
|
||||
if len(got) != 3 || got[".env"] != "env · plain text" || got[".env.example"] != "env · plain text" || got["secrets/app.yaml"] != "sops · age" {
|
||||
t.Fatalf("files %v (want .env, the example, hidden, and the sops file; not node_modules)", got)
|
||||
}
|
||||
|
||||
var env provider.Container
|
||||
@@ -319,7 +322,7 @@ func TestPlainEnvFiles(t *testing.T) {
|
||||
os.WriteFile(filepath.Join(root, ".sops.yaml"), []byte("creation_rules:\n - path_regex: nowhere/.*\n"), 0o644)
|
||||
exec.Command("git", "-C", root, "add", ".sops.yaml").Run()
|
||||
files, _ = New(Settings{Dir: root}).Containers(ctx, srcs[0])
|
||||
if names := names(files, func(c provider.Container) string { return c.Name }); !slices.Equal(names, []string{".env"}) {
|
||||
if names := names(files, func(c provider.Container) string { return c.Name }); !slices.Equal(names, []string{".env", ".env.example"}) {
|
||||
t.Errorf("with rules elsewhere: %v", names)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (m *Model) shareFiles(src provider.Source, cs []provider.Container) tea.Cmd
|
||||
return msg
|
||||
}
|
||||
for _, c := range all {
|
||||
if c.Detail == provider.DetailPlainEnv {
|
||||
if c.Detail == provider.DetailPlainEnv && !c.Hidden { // not .env.example
|
||||
cs = append(cs, c)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user