Problem
Path-valued flags inside an oasdiff config file are resolved against the current working directory, not the directory containing the config file. This breaks the --config <path> workflow when the config file isn't in cwd.
Reproduction
$ oasdiff breaking \
data/openapi31-ref-sibling-base.yaml \
data/openapi31-ref-sibling-revision.yaml \
--config examples/.oasdiff.yaml
Error: can't process err ignore file: open ignore-err-example.txt: no such file or directory
examples/.oasdiff.yaml contains err-ignore: ignore-err-example.txt. The file exists at examples/ignore-err-example.txt. The CLI looks for it at <cwd>/ignore-err-example.txt — which doesn't exist.
Expected behaviour
Most tools in this category resolve relative paths in config files against the config file's own directory, not against cwd:
- ESLint: paths in
.eslintrc are relative to the config file
- Prettier: same
- golangci-lint: same (per docs)
This makes a config file self-contained — wherever it lives, the auxiliary files it references are colocated and resolution Just Works.
Affected flags
Anything in the Config struct (internal/viper.go) that takes a path:
err-ignore
warn-ignore
severity-levels
template
- (any future path-valued flag added under viper binding)
Why this matters now
#899 (feat(config): rename default config to .oasdiff.*; add --config flag and OASDIFF_CONFIG env var) makes --config <path> and OASDIFF_CONFIG first-class. With those, customers will routinely point at config files that aren't in cwd — exactly the case where cwd-relative resolution silently fails. The lookup-mechanism PR doesn't fix this; it surfaces it.
Proposed fix
When a config-file path is resolved (whether from --config, OASDIFF_CONFIG, or the default cwd lookup), determine its directory. Path-valued flags read from that file are resolved against that directory unless they're already absolute.
Roughly, after ReadInConfig in internal/viper.go::readConfFile:
- Capture the resolved config file path:
viper.GetViper().ConfigFileUsed() returns the absolute path of the file that was loaded.
- After
validate() / before flag binding, walk the path-valued fields in Config (or just specific keys: err-ignore, warn-ignore, severity-levels, template); if the value is a relative path, rewrite it to filepath.Join(filepath.Dir(configPath), value).
- Don't rewrite when the config file is in cwd (the rewrite is a no-op in that case anyway, but worth confirming behaviour doesn't subtly change for current users).
Backward compatibility
This is a semantic change for any existing user who relied on cwd-relative resolution while using a config file in a non-cwd location. Realistically:
- Default-cwd-lookup users (
.oasdiff.yaml next to where they run): no change. Config dir == cwd, paths resolve identically.
--config <path> users: behaviour shifts. Today they need absolute paths or to invoke from the same directory; after this change, paths in their config work as colocated.
The shift is from "broken-by-default for --config" to "works-by-default for --config" — an improvement, but technically a semantic change. Worth calling out in release notes.
Implementation scope
internal/viper.go — ~20-line change
- New test cases in
internal/viper_test.go covering: relative path in config file resolves correctly when config is in cwd vs --config vs OASDIFF_CONFIG; absolute paths in config are not rewritten.
- Documentation update in
docs/CONFIG-FILES.md and examples/.oasdiff.yaml.
Out of scope
- Resolving
$variables inside config-file paths (env-var expansion). Separate ergonomic concern; defer.
- Upward search for config files in parent directories. Separate concern; defer.
Related
- #899 — the lookup-mechanism PR that surfaces this issue.
Problem
Path-valued flags inside an oasdiff config file are resolved against the current working directory, not the directory containing the config file. This breaks the
--config <path>workflow when the config file isn't in cwd.Reproduction
$ oasdiff breaking \ data/openapi31-ref-sibling-base.yaml \ data/openapi31-ref-sibling-revision.yaml \ --config examples/.oasdiff.yaml Error: can't process err ignore file: open ignore-err-example.txt: no such file or directoryexamples/.oasdiff.yamlcontainserr-ignore: ignore-err-example.txt. The file exists atexamples/ignore-err-example.txt. The CLI looks for it at<cwd>/ignore-err-example.txt— which doesn't exist.Expected behaviour
Most tools in this category resolve relative paths in config files against the config file's own directory, not against cwd:
.eslintrcare relative to the config fileThis makes a config file self-contained — wherever it lives, the auxiliary files it references are colocated and resolution Just Works.
Affected flags
Anything in the
Configstruct (internal/viper.go) that takes a path:err-ignorewarn-ignoreseverity-levelstemplateWhy this matters now
#899 (
feat(config): rename default config to .oasdiff.*; add --config flag and OASDIFF_CONFIG env var) makes--config <path>andOASDIFF_CONFIGfirst-class. With those, customers will routinely point at config files that aren't in cwd — exactly the case where cwd-relative resolution silently fails. The lookup-mechanism PR doesn't fix this; it surfaces it.Proposed fix
When a config-file path is resolved (whether from
--config,OASDIFF_CONFIG, or the default cwd lookup), determine its directory. Path-valued flags read from that file are resolved against that directory unless they're already absolute.Roughly, after
ReadInConfigininternal/viper.go::readConfFile:viper.GetViper().ConfigFileUsed()returns the absolute path of the file that was loaded.validate()/ before flag binding, walk the path-valued fields inConfig(or just specific keys:err-ignore,warn-ignore,severity-levels,template); if the value is a relative path, rewrite it tofilepath.Join(filepath.Dir(configPath), value).Backward compatibility
This is a semantic change for any existing user who relied on cwd-relative resolution while using a config file in a non-cwd location. Realistically:
.oasdiff.yamlnext to where they run): no change. Config dir == cwd, paths resolve identically.--config <path>users: behaviour shifts. Today they need absolute paths or to invoke from the same directory; after this change, paths in their config work as colocated.The shift is from "broken-by-default for
--config" to "works-by-default for--config" — an improvement, but technically a semantic change. Worth calling out in release notes.Implementation scope
internal/viper.go— ~20-line changeinternal/viper_test.gocovering: relative path in config file resolves correctly when config is in cwd vs--configvsOASDIFF_CONFIG; absolute paths in config are not rewritten.docs/CONFIG-FILES.mdandexamples/.oasdiff.yaml.Out of scope
$variablesinside config-file paths (env-var expansion). Separate ergonomic concern; defer.Related