Skip to content

Commit

Permalink
Look for per-project configuration file in parent directories
Browse files Browse the repository at this point in the history
Inspired be ESLint's search, it looks for configuration files in all
parent directories up until it reaches the user's home or root.

closes #571
  • Loading branch information
chutzimir committed May 11, 2023
1 parent 019c87d commit 584ae2c
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 7 deletions.
3 changes: 2 additions & 1 deletion docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ If ``-c`` is not provided, yamllint will look for a configuration file in the
following locations (by order of preference):

- a file named ``.yamllint``, ``.yamllint.yaml``, or ``.yamllint.yml`` in the
current working directory
current working directory, or a parent directory. The search for this file is
terminated at the user's home, or the root.
- a filename referenced by ``$YAMLLINT_CONFIG_FILE``, if set
- a file named ``$XDG_CONFIG_HOME/yamllint/config`` or
``~/.config/yamllint/config``, if present
Expand Down
24 changes: 24 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,3 +734,27 @@ def test_config_file(self):

self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, '', ''))

def test_parent_config_file(self):
workspace = { 'a/b/c/d/e/f/g/a.yml': 'hello: world\n' }
conf = ('---\n'
'extends: relaxed\n')

for conf_file in ('.yamllint', '.yamllint.yml', '.yamllint.yaml'):
with self.subTest(conf_file):
with temp_workspace(workspace):
with RunContext(self) as ctx:
os.chdir('a/b/c/d/e/f')
cli.run(('-f', 'parsable', '.'))

self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, './g/a.yml:1:1: [warning] missing document '
'start "---" (document-start)\n', ''))

with temp_workspace({**workspace, **{conf_file: conf}}):
with RunContext(self) as ctx:
os.chdir('a/b/c/d/e/f')
cli.run(('-f', 'parsable', '.'))

self.assertEqual((ctx.returncode, ctx.stdout, ctx.stderr),
(0, '', ''))
20 changes: 14 additions & 6 deletions yamllint/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ def show_problems(problems, file, args_format, no_warn):

return max_level

def find_project_config(path='.'):

for filename in [ '.yamllint', '.yamllint.yaml', '.yamllint.yml' ]:
file = os.path.join(path, filename)
if os.path.isfile(file):
return file

if os.path.abspath(path) == os.path.abspath(os.path.expanduser('~')):
return False
if os.path.abspath(path) == os.path.abspath('/'):
return False
return find_project_config(path=os.path.join(path, '..'))

def run(argv=None):
parser = argparse.ArgumentParser(prog=APP_NAME,
Expand Down Expand Up @@ -192,12 +204,8 @@ def run(argv=None):
conf = YamlLintConfig(content=args.config_data)
elif args.config_file is not None:
conf = YamlLintConfig(file=args.config_file)
elif os.path.isfile('.yamllint'):
conf = YamlLintConfig(file='.yamllint')
elif os.path.isfile('.yamllint.yaml'):
conf = YamlLintConfig(file='.yamllint.yaml')
elif os.path.isfile('.yamllint.yml'):
conf = YamlLintConfig(file='.yamllint.yml')
elif find_project_config():
conf = YamlLintConfig(file=find_project_config())
elif os.path.isfile(user_global_config):
conf = YamlLintConfig(file=user_global_config)
else:
Expand Down

0 comments on commit 584ae2c

Please sign in to comment.