Abort startup on a config file pixa cannot read (closes #176)
check / check (push) Failing after 2s
check / check (push) Failing after 2s
Of the places pixa looks for its config file on its own, it passed over any place where os.Stat failed, so a file in a directory pixa may not enter was skipped without a word and pixa started on a later file or on the environment and defaults. Now only a file that does not exist is passed over; any other error aborts startup naming the file, as a file that does not parse already did. README.md says so where it gives the search order. Model: opus-5-5
This commit is contained in:
@@ -354,10 +354,10 @@ pixa finds: `/etc/pixa/config.yml`, `/etc/pixa/config.yaml`,
|
|||||||
`~/.config/pixa/config.yml`, `~/.config/pixa/config.yaml`, then `config.yml`
|
`~/.config/pixa/config.yml`, `~/.config/pixa/config.yaml`, then `config.yml`
|
||||||
and `config.yaml` in the working directory. A named file that does not exist,
|
and `config.yaml` in the working directory. A named file that does not exist,
|
||||||
cannot be read or does not parse aborts startup. Of the files pixa looks for on
|
cannot be read or does not parse aborts startup. Of the files pixa looks for on
|
||||||
its own, one it finds but cannot read or parse aborts startup; one it cannot
|
its own, only one that does not exist is passed over, without a message. One
|
||||||
find, for any reason, is passed over without a message, even when the file is
|
that pixa cannot read or parse aborts startup, naming the file. So does one in a
|
||||||
there in a directory pixa may not enter. With no file, pixa uses the environment
|
directory pixa may not enter, whether or not it is there, since pixa cannot
|
||||||
and the defaults.
|
tell. With no file, pixa uses the environment and the defaults.
|
||||||
|
|
||||||
| Variable | Config key | Meaning |
|
| Variable | Config key | Meaning |
|
||||||
| ------------------------------------ | ------------------------------- | ---------------------------------------------------------------------------- |
|
| ------------------------------------ | ------------------------------- | ---------------------------------------------------------------------------- |
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ P2: security: referer blacklist
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-10-04 a config file pixa cannot read aborts startup (closes #176): of the
|
||||||
|
places pixa looks for its config file on its own, only one where the file does
|
||||||
|
not exist is passed over; any other error, such as a directory on the path
|
||||||
|
that pixa may not enter, aborts startup naming the file, as a file that does
|
||||||
|
not parse already did.
|
||||||
- 2026-10-04 request IDs returned and passed on, and `/v1/e/` revalidates
|
- 2026-10-04 request IDs returned and passed on, and `/v1/e/` revalidates
|
||||||
(closes #84): pixa's own `RequestID` middleware, in place of chi's, gives each
|
(closes #84): pixa's own `RequestID` middleware, in place of chi's, gives each
|
||||||
request an ID, its own `X-Request-ID` when that is at most 64 letters, digits,
|
request an ID, its own `X-Request-ID` when that is at most 64 letters, digits,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package config
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -778,10 +779,17 @@ func loadConfigFile(log *slog.Logger, appName string) (*smartconfig.Config, erro
|
|||||||
for _, path := range configPaths {
|
for _, path := range configPaths {
|
||||||
cleanPath := filepath.Clean(path)
|
cleanPath := filepath.Clean(path)
|
||||||
|
|
||||||
|
// Only a config file that does not exist is skipped. One that
|
||||||
|
// cannot be read or does not parse is a fatal startup error.
|
||||||
_, statErr := os.Stat(cleanPath)
|
_, statErr := os.Stat(cleanPath)
|
||||||
if statErr == nil {
|
if errors.Is(statErr, fs.ErrNotExist) {
|
||||||
// A config file that exists but does not parse is a fatal
|
continue
|
||||||
// startup error, never something to skip over.
|
}
|
||||||
|
|
||||||
|
if statErr != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read config file %s: %w", path, statErr)
|
||||||
|
}
|
||||||
|
|
||||||
sc, err := smartconfig.NewFromConfigPath(path)
|
sc, err := smartconfig.NewFromConfigPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse config file %s: %w", path, err)
|
return nil, fmt.Errorf("failed to parse config file %s: %w", path, err)
|
||||||
@@ -791,7 +799,6 @@ func loadConfigFile(log *slog.Logger, appName string) (*smartconfig.Config, erro
|
|||||||
|
|
||||||
return sc, nil
|
return sc, nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil //nolint:nilnil // nil config is valid (use defaults)
|
return nil, nil //nolint:nilnil // nil config is valid (use defaults)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user