Repository: caddyserver/caddy (language: go)

Issue:
[feature request] ignore dotfiles by default when parsing drop-in configuration
here is the error log:
```
root@xxxxxx:~/git/private_proxy_config# docker logs -f caddy
{"level":"info","ts":1673350699.8861008,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
Error: adapting config using caddyfile: /etc/caddy/Caddyfile:39 - Error during parsing: Could not read tokens while importing /etc/caddy/sites-enabled/.gitkeep: EOF

root@xxxxxx:~/git/private_proxy_config# file caddy/sites-enabled/.gitkeep
caddy/sites-enabled/.gitkeep: empty
```

---

I was trying to git-ops my caddy configuration. Some machines do not need the drop-in configuration while others do.
I decide to put an empty file in the `sites-enabled` sub-dir so it would be tracked by git.


Discussion / hints:
Ignoring dot files by default seems like a good idea, when the `*` is a sole path segment.

The empty files part is a duplicate of https://github.com/caddyserver/caddy/issues/5180

I'm thinking we can probably change it from an error to a warning. Silently ignoring empty files seems not ideal.
I'm so on the fence about empty config files. They are nearly always an error, except for a few times they aren't. It feels wrong to start a server that is missing configuration that is expected.
Which is why I suggest a warning as a middle-ground/compromise. It'll make it work for situations where users expect it to work, and still put up yellow flags in cases where it's probably a mistake.
I'm probably OK with a warning.
> I'm so on the fence about empty config files. They are nearly always an error, except for a few times they aren't. It feels wrong to start a server that is missing configuration that is expected.

How about ignoring dotfiles by default? I found you are making a point here, I agree that ignoring the empty config files is not a good idea.

Fix patch:
diff --git a/caddyconfig/caddyfile/parse.go b/caddyconfig/caddyfile/parse.go
index 82c515b970b..edc86f28a8d 100644
--- a/caddyconfig/caddyfile/parse.go
+++ b/caddyconfig/caddyfile/parse.go
@@ -389,6 +389,20 @@ func (p *parser) doImport() error {
 			} else {
 				return p.Errf("File to import not found: %s", importPattern)
 			}
+		} else {
+			// See issue #5295 - should skip any files that start with a . when iterating over them.
+			sep := string(filepath.Separator)
+			segGlobPattern := strings.Split(globPattern, sep)
+			if strings.HasPrefix(segGlobPattern[len(segGlobPattern)-1], "*") {
+				var tmpMatches []string
+				for _, m := range matches {
+					seg := strings.Split(m, sep)
+					if !strings.HasPrefix(seg[len(seg)-1], ".") {
+						tmpMatches = append(tmpMatches, m)
+					}
+				}
+				matches = tmpMatches
+			}
 		}
 
 		// collect all the imported tokens
@@ -451,6 +465,12 @@ func (p *parser) doSingleImport(importFile string) ([]Token, error) {
 		return nil, p.Errf("Could not read imported file %s: %v", importFile, err)
 	}
 
+	// only warning in case of empty files
+	if len(input) == 0 || len(strings.TrimSpace(string(input))) == 0 {
+		caddy.Log().Warn("Import file is empty", zap.String("file", importFile))
+		return []Token{}, nil
+	}
+
 	importedTokens, err := allTokens(importFile, input)
 	if err != nil {
 		return nil, p.Errf("Could not read tokens while importing %s: %v", importFile, err)


Test patch:
diff --git a/caddyconfig/caddyfile/parse_test.go b/caddyconfig/caddyfile/parse_test.go
index e3eff564001..4d18cc44fca 100644
--- a/caddyconfig/caddyfile/parse_test.go
+++ b/caddyconfig/caddyfile/parse_test.go
@@ -187,6 +187,23 @@ func TestParseOneAndImport(t *testing.T) {
 
 		{`import testdata/not_found.txt`, true, []string{}, []int{}},
 
+		// empty file should just log a warning, and result in no tokens
+		{`import testdata/empty.txt`, false, []string{}, []int{}},
+
+		{`import testdata/only_white_space.txt`, false, []string{}, []int{}},
+
+		// import path/to/dir/* should skip any files that start with a . when iterating over them.
+		{`localhost
+		  dir1 arg1
+		  import testdata/glob/*`, false, []string{
+			"localhost",
+		}, []int{2, 3, 1}},
+
+		// import path/to/dir/.* should continue to read all dotfiles in a dir.
+		{`import testdata/glob/.*`, false, []string{
+			"host1",
+		}, []int{1, 2}},
+
 		{`""`, false, []string{}, []int{}},
 
 		{``, false, []string{}, []int{}},
diff --git a/caddyconfig/caddyfile/testdata/empty.txt b/caddyconfig/caddyfile/testdata/empty.txt
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/caddyconfig/caddyfile/testdata/glob/.dotfile.txt b/caddyconfig/caddyfile/testdata/glob/.dotfile.txt
new file mode 100644
index 00000000000..faab100c604
--- /dev/null
+++ b/caddyconfig/caddyfile/testdata/glob/.dotfile.txt
@@ -0,0 +1,4 @@
+host1 {
+	dir1
+	dir2 arg1
+}
diff --git a/caddyconfig/caddyfile/testdata/glob/import_test1.txt b/caddyconfig/caddyfile/testdata/glob/import_test1.txt
new file mode 100644
index 00000000000..dac7b29be09
--- /dev/null
+++ b/caddyconfig/caddyfile/testdata/glob/import_test1.txt
@@ -0,0 +1,2 @@
+dir2 arg1 arg2
+dir3
\ No newline at end of file
diff --git a/caddyconfig/caddyfile/testdata/only_white_space.txt b/caddyconfig/caddyfile/testdata/only_white_space.txt
new file mode 100644
index 00000000000..705327cd431
--- /dev/null
+++ b/caddyconfig/caddyfile/testdata/only_white_space.txt
@@ -0,0 +1,7 @@
+
+
+ 
+  
+　
+
+