Skip to content

config: relative paths in config files should resolve against the config file's directory, not cwd #900

@reuvenharrison

Description

@reuvenharrison

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:

  1. Capture the resolved config file path: viper.GetViper().ConfigFileUsed() returns the absolute path of the file that was loaded.
  2. 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).
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions