Skip to content

Commit 0b1d401

Browse files
authored
Merge pull request #181 from dbt-labs/fix/issue-180-directory-support
2 parents dc790b4 + 0c8ecca commit 0b1d401

3 files changed

Lines changed: 95 additions & 4 deletions

File tree

docs/advanced_config/glob_config_files.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ dbt-jobs-as-code plan "jobs/**/*.yml" # (1)!
1414

1515
1. Depending on your shell you might have to quote the pattern or not. For example, for `zsh` quoting is required as otherwise the shell will try to expand the pattern before passing it to the command.
1616

17-
If the provided config is a directory, we automatically search for all the `*.yml` files in this directory. This is particularly relevant for users with a shell not supporting the `*` character.
17+
If the provided config is a directory, we automatically search for all the `*.yml` and `*.yaml` files in this directory. This is particularly relevant for users with a shell not supporting the `*` character.
18+
19+
```bash
20+
dbt-jobs-as-code plan ./jobs # Equivalent to ./jobs/*.yml + ./jobs/*.yaml
21+
```

src/dbt_jobs_as_code/loader/load.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import glob
2+
import os
23

34
from beartype.typing import List, Optional, Set
45
from jinja2 import Environment, StrictUndefined, meta
@@ -177,14 +178,37 @@ def _get_jinja_variables(input: str) -> Set[str]:
177178
return meta.find_undeclared_variables(parsed_input)
178179

179180

181+
def _resolve_pattern(pattern: str) -> List[str]:
182+
"""
183+
Resolve a file pattern to a list of file paths.
184+
185+
If the pattern is a directory, it will be expanded to match all .yml and .yaml files
186+
within that directory.
187+
188+
Args:
189+
pattern: File path, directory path, or glob pattern
190+
191+
Returns:
192+
List of matching file paths
193+
"""
194+
if os.path.isdir(pattern):
195+
# If a directory is provided, match all .yml and .yaml files in it
196+
yml_files = glob.glob(os.path.join(pattern, "*.yml"))
197+
yaml_files = glob.glob(os.path.join(pattern, "*.yaml"))
198+
return yml_files + yaml_files
199+
else:
200+
return glob.glob(pattern)
201+
202+
180203
def resolve_file_paths(
181204
config_pattern: Optional[str], vars_pattern: Optional[str] = None
182205
) -> tuple[List[str], List[str]]:
183206
"""
184207
Resolve glob patterns to lists of file paths.
185208
186209
Args:
187-
config_pattern: Glob pattern for config files
210+
config_pattern: Glob pattern, file path, or directory path for config files.
211+
If a directory is provided, all .yml and .yaml files in it will be matched.
188212
vars_pattern: Optional glob pattern for vars files
189213
190214
Returns:
@@ -196,13 +220,13 @@ def resolve_file_paths(
196220
if not config_pattern:
197221
return [], []
198222

199-
config_files = glob.glob(config_pattern)
223+
config_files = _resolve_pattern(config_pattern)
200224
if not config_files:
201225
raise LoadingJobsYAMLError(f"No files found matching pattern: {config_pattern}")
202226

203227
vars_files = []
204228
if vars_pattern:
205-
vars_files = glob.glob(vars_pattern)
229+
vars_files = _resolve_pattern(vars_pattern)
206230
if not vars_files:
207231
raise LoadingJobsYAMLError(f"No files found matching pattern: {vars_pattern}")
208232

tests/loader/test_loader.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,69 @@ def test_resolve_file_paths_with_glob(self, tmp_path):
393393
assert all(f.endswith(".yml") for f in config_files)
394394
assert vars_files == []
395395

396+
def test_resolve_file_paths_directory_with_yml_files(self, tmp_path):
397+
"""Test resolving a directory containing .yml files (issue #180)"""
398+
jobs_dir = tmp_path / "jobs"
399+
jobs_dir.mkdir()
400+
(jobs_dir / "job1.yml").write_text("content1")
401+
(jobs_dir / "job2.yml").write_text("content2")
402+
(jobs_dir / "other.txt").write_text("should be ignored")
403+
404+
config_files, vars_files = resolve_file_paths(str(jobs_dir))
405+
assert len(config_files) == 2
406+
assert all(f.endswith(".yml") for f in config_files)
407+
assert vars_files == []
408+
409+
def test_resolve_file_paths_directory_with_yaml_files(self, tmp_path):
410+
"""Test resolving a directory containing .yaml files (issue #180)"""
411+
jobs_dir = tmp_path / "jobs"
412+
jobs_dir.mkdir()
413+
(jobs_dir / "job1.yaml").write_text("content1")
414+
(jobs_dir / "job2.yaml").write_text("content2")
415+
416+
config_files, vars_files = resolve_file_paths(str(jobs_dir))
417+
assert len(config_files) == 2
418+
assert all(f.endswith(".yaml") for f in config_files)
419+
assert vars_files == []
420+
421+
def test_resolve_file_paths_directory_with_mixed_yml_yaml(self, tmp_path):
422+
"""Test resolving a directory containing both .yml and .yaml files (issue #180)"""
423+
jobs_dir = tmp_path / "jobs"
424+
jobs_dir.mkdir()
425+
(jobs_dir / "job1.yml").write_text("content1")
426+
(jobs_dir / "job2.yaml").write_text("content2")
427+
(jobs_dir / "job3.yml").write_text("content3")
428+
429+
config_files, vars_files = resolve_file_paths(str(jobs_dir))
430+
assert len(config_files) == 3
431+
yml_count = sum(1 for f in config_files if f.endswith(".yml"))
432+
yaml_count = sum(1 for f in config_files if f.endswith(".yaml"))
433+
assert yml_count == 2
434+
assert yaml_count == 1
435+
436+
def test_resolve_file_paths_empty_directory(self, tmp_path):
437+
"""Test error when directory contains no yml/yaml files (issue #180)"""
438+
empty_dir = tmp_path / "empty"
439+
empty_dir.mkdir()
440+
(empty_dir / "other.txt").write_text("not a yaml file")
441+
442+
with pytest.raises(LoadingJobsYAMLError, match="No files found matching pattern"):
443+
resolve_file_paths(str(empty_dir))
444+
445+
def test_resolve_file_paths_directory_for_vars(self, tmp_path):
446+
"""Test resolving a directory for vars files (issue #180)"""
447+
config_file = tmp_path / "config.yml"
448+
config_file.write_text("content")
449+
450+
vars_dir = tmp_path / "vars"
451+
vars_dir.mkdir()
452+
(vars_dir / "vars1.yml").write_text("var1: value1")
453+
(vars_dir / "vars2.yaml").write_text("var2: value2")
454+
455+
config_files, vars_files = resolve_file_paths(str(config_file), str(vars_dir))
456+
assert config_files == [str(config_file)]
457+
assert len(vars_files) == 2
458+
396459
def test_resolve_file_paths_with_vars(self, tmp_path):
397460
"""Test resolving both config and vars files"""
398461
config_file = tmp_path / "config.yml"

0 commit comments

Comments
 (0)