datasecops-cli 0.4.4__tar.gz → 0.4.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/CHANGELOG.md +8 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/PKG-INFO +1 -1
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/pyproject.toml +1 -1
- datasecops_cli-0.4.5/src/datasecops_cli/__init__.py +1 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/main.py +7 -1
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/downloads.py +4 -2
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/bootstrap_service.py +5 -2
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/dbt_project_generator.py +19 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/download_service.py +42 -1
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_main.py +118 -1
- datasecops_cli-0.4.4/src/datasecops_cli/__init__.py +0 -1
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/.github/workflows/auto-tag.yml +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/.github/workflows/publish-cli.yml +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/.gitignore +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/DEVELOPMENT.md +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/LICENSE +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/README.md +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/docs/getting-started.md +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/docs/legacy.md +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/docs/legacy_plan_of_action.md +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/docs/mcp-server.md +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/mcp-servers.json +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/setup.ps1 +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/setup.sh +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/config.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/__init__.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/configuration.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/development.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/menus/git_operations.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/models/__init__.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/models/git_helpers.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/models/project_config.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/__init__.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/dbt_runner.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/directory_scaffolder.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/git_service.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/linting_service.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/skill_service.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/snowflake_service.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/upstream_service.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/utilities/__init__.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/utilities/display.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/utilities/file_utils.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/utilities/yaml_utils.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_mcp/__init__.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_mcp/__main__.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_mcp/connection.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_mcp/server.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/__init__.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_config.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_file_utils.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_models.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_version.py +0 -0
- {datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/tests/test_yaml_utils.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the DataSecOps CLI are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.4.5] - 2026-05-18
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Package downloads now respect profile selection** — `datasecops download packages` and the interactive downloads menu now only include dbt packages enabled on the active profile, instead of downloading all packages from the global catalog. This matches the native app's Streamlit UI behavior.
|
|
10
|
+
- **Upstream package exclusion** — packages already provided by upstream projects are automatically excluded from `packages.yml`, preventing duplicate dependencies when using multi-project setups.
|
|
11
|
+
- **`generate_packages_yml()` filtering** — the dbt project generator now also filters packages by profile, fixing the same issue in the bootstrap/init path.
|
|
12
|
+
|
|
5
13
|
## [0.4.4] - 2026-05-18
|
|
6
14
|
|
|
7
15
|
### Added
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.5"
|
|
@@ -565,7 +565,10 @@ def _run_download(config: Config, items: list[str],
|
|
|
565
565
|
if not download_service.download_pipelines(platform=platform, profile_name=config.profile_name):
|
|
566
566
|
failed = True
|
|
567
567
|
elif item == "packages":
|
|
568
|
-
if not download_service.download_dbt_packages(
|
|
568
|
+
if not download_service.download_dbt_packages(
|
|
569
|
+
config.dbt_project_dir, profile=config.profile,
|
|
570
|
+
all_profiles=config.all_profiles
|
|
571
|
+
):
|
|
569
572
|
failed = True
|
|
570
573
|
elif item == "macros":
|
|
571
574
|
if not download_service.download_macros(config.profile_name, config.dbt_project_dir):
|
|
@@ -704,6 +707,7 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
|
|
|
704
707
|
project_settings=config.project_settings,
|
|
705
708
|
profile=config.profile,
|
|
706
709
|
source_control=config.source_control,
|
|
710
|
+
all_profiles=config.all_profiles,
|
|
707
711
|
)
|
|
708
712
|
dl_menu.show()
|
|
709
713
|
|
|
@@ -722,6 +726,7 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
|
|
|
722
726
|
project_dir=config.project_dir,
|
|
723
727
|
project_settings=config.project_settings,
|
|
724
728
|
profile=config.profile,
|
|
729
|
+
all_profiles=config.all_profiles,
|
|
725
730
|
)
|
|
726
731
|
bootstrap.run(platform=platform, install_skills=install_skills,
|
|
727
732
|
skill_targets=skill_targets, run_deps=run_deps)
|
|
@@ -784,6 +789,7 @@ def _run_bootstrap(config: Config):
|
|
|
784
789
|
project_dir=config.project_dir,
|
|
785
790
|
project_settings=config.project_settings,
|
|
786
791
|
profile=config.profile,
|
|
792
|
+
all_profiles=config.all_profiles,
|
|
787
793
|
)
|
|
788
794
|
success = bootstrap.run(platform=platform, install_skills=install_skills,
|
|
789
795
|
skill_targets=skill_targets, run_deps=run_deps)
|
|
@@ -19,7 +19,7 @@ class DownloadsMenu:
|
|
|
19
19
|
def __init__(self, download_service: DownloadService, skill_service: SkillService,
|
|
20
20
|
dbt_runner: DbtRunner, profile_name: str, dbt_project_dir: Path,
|
|
21
21
|
project_settings: ProjectSettings = None, profile: ProjectProfile = None,
|
|
22
|
-
source_control: SourceControl = None):
|
|
22
|
+
source_control: SourceControl = None, all_profiles: list[ProjectProfile] = None):
|
|
23
23
|
self.downloads = download_service
|
|
24
24
|
self.skills = skill_service
|
|
25
25
|
self.dbt = dbt_runner
|
|
@@ -28,6 +28,7 @@ class DownloadsMenu:
|
|
|
28
28
|
self.project_settings = project_settings
|
|
29
29
|
self.profile = profile
|
|
30
30
|
self.source_control = source_control
|
|
31
|
+
self.all_profiles = all_profiles
|
|
31
32
|
|
|
32
33
|
def show(self) -> None:
|
|
33
34
|
self._menu()
|
|
@@ -46,7 +47,8 @@ class DownloadsMenu:
|
|
|
46
47
|
complete_action()
|
|
47
48
|
elif option == 3:
|
|
48
49
|
display_action_header("Download dbt Packages")
|
|
49
|
-
self.downloads.download_dbt_packages(self.dbt_project_dir
|
|
50
|
+
self.downloads.download_dbt_packages(self.dbt_project_dir, profile=self.profile,
|
|
51
|
+
all_profiles=self.all_profiles)
|
|
50
52
|
if get_input_true_false("Run dbt deps now?"):
|
|
51
53
|
self.dbt.deps()
|
|
52
54
|
complete_action()
|
{datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/bootstrap_service.py
RENAMED
|
@@ -14,11 +14,13 @@ class BootstrapService:
|
|
|
14
14
|
"""Bootstraps a new dbt project with all framework configuration."""
|
|
15
15
|
|
|
16
16
|
def __init__(self, snowflake_service: SnowflakeService, project_dir: Path,
|
|
17
|
-
project_settings: ProjectSettings, profile: ProjectProfile
|
|
17
|
+
project_settings: ProjectSettings, profile: ProjectProfile,
|
|
18
|
+
all_profiles: list[ProjectProfile] = None):
|
|
18
19
|
self.sf = snowflake_service
|
|
19
20
|
self.project_dir = project_dir
|
|
20
21
|
self.project_settings = project_settings
|
|
21
22
|
self.profile = profile
|
|
23
|
+
self.all_profiles = all_profiles or []
|
|
22
24
|
self.download_service = DownloadService(snowflake_service, project_dir)
|
|
23
25
|
self.skill_service = SkillService(snowflake_service)
|
|
24
26
|
|
|
@@ -92,7 +94,8 @@ class BootstrapService:
|
|
|
92
94
|
# Step 5: Generate packages.yml
|
|
93
95
|
info_line("")
|
|
94
96
|
info_line("[5] Generating packages.yml...")
|
|
95
|
-
if self.download_service.download_dbt_packages(dbt_project_dir, profile=self.profile
|
|
97
|
+
if self.download_service.download_dbt_packages(dbt_project_dir, profile=self.profile,
|
|
98
|
+
all_profiles=self.all_profiles):
|
|
96
99
|
steps_passed += 1
|
|
97
100
|
else:
|
|
98
101
|
info_line(" (skipped - no dbt packages config in native app)")
|
{datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/dbt_project_generator.py
RENAMED
|
@@ -165,17 +165,36 @@ def generate_profiles_yml(
|
|
|
165
165
|
def generate_packages_yml(
|
|
166
166
|
profile: ProjectProfile,
|
|
167
167
|
packages_config: dict,
|
|
168
|
+
all_profiles: list[ProjectProfile] = None,
|
|
168
169
|
) -> str:
|
|
169
170
|
"""Generate packages.yml from profile's packages and upstream projects.
|
|
170
171
|
|
|
171
172
|
Args:
|
|
172
173
|
profile: The project profile.
|
|
173
174
|
packages_config: The DBT_PACKAGES framework config.
|
|
175
|
+
all_profiles: All project profiles (used for upstream package exclusion).
|
|
174
176
|
|
|
175
177
|
Returns:
|
|
176
178
|
The YAML content as a string.
|
|
177
179
|
"""
|
|
178
180
|
packages = packages_config.get("packages", [])
|
|
181
|
+
|
|
182
|
+
# Filter to only packages enabled on the profile
|
|
183
|
+
if profile.dbt_packages:
|
|
184
|
+
from datasecops_cli.services.download_service import _collect_upstream_packages
|
|
185
|
+
|
|
186
|
+
enabled = set(profile.dbt_packages)
|
|
187
|
+
|
|
188
|
+
# Exclude packages already provided by upstream projects
|
|
189
|
+
if profile.upstream_projects and all_profiles:
|
|
190
|
+
profile_map = {p.profile_name: p for p in all_profiles}
|
|
191
|
+
upstream_pkgs = _collect_upstream_packages(
|
|
192
|
+
profile.upstream_projects, profile_map
|
|
193
|
+
)
|
|
194
|
+
enabled -= upstream_pkgs
|
|
195
|
+
|
|
196
|
+
packages = [p for p in packages if p.get("name") in enabled]
|
|
197
|
+
|
|
179
198
|
pkg_list: list[dict[str, Any]] = []
|
|
180
199
|
|
|
181
200
|
for pkg in packages:
|
{datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/download_service.py
RENAMED
|
@@ -10,6 +10,30 @@ from datasecops_cli.utilities.display import info_line, success_line, error_line
|
|
|
10
10
|
from datasecops_cli.utilities.file_utils import write_file, ensure_dir
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _collect_upstream_packages(
|
|
14
|
+
upstream_names: list[str],
|
|
15
|
+
profile_map: dict[str, ProjectProfile],
|
|
16
|
+
visited: set[str] | None = None,
|
|
17
|
+
) -> set[str]:
|
|
18
|
+
"""Recursively collect dbt package names from all upstream projects."""
|
|
19
|
+
if visited is None:
|
|
20
|
+
visited = set()
|
|
21
|
+
result: set[str] = set()
|
|
22
|
+
for name in upstream_names:
|
|
23
|
+
if name in visited:
|
|
24
|
+
continue
|
|
25
|
+
visited.add(name)
|
|
26
|
+
upstream = profile_map.get(name)
|
|
27
|
+
if upstream:
|
|
28
|
+
result.update(upstream.dbt_packages)
|
|
29
|
+
result.update(
|
|
30
|
+
_collect_upstream_packages(
|
|
31
|
+
upstream.upstream_projects or [], profile_map, visited
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
return result
|
|
35
|
+
|
|
36
|
+
|
|
13
37
|
class DownloadService:
|
|
14
38
|
"""Downloads configurations from the native app."""
|
|
15
39
|
|
|
@@ -279,7 +303,8 @@ class DownloadService:
|
|
|
279
303
|
success_line(f"Downloaded {count} script(s) to {scripts_dir}")
|
|
280
304
|
return True
|
|
281
305
|
|
|
282
|
-
def download_dbt_packages(self, dbt_project_dir: Path, profile: ProjectProfile = None
|
|
306
|
+
def download_dbt_packages(self, dbt_project_dir: Path, profile: ProjectProfile = None,
|
|
307
|
+
all_profiles: list[ProjectProfile] = None) -> bool:
|
|
283
308
|
info_line("Downloading dbt package versions...")
|
|
284
309
|
raw = self.sf.get_framework_config("DBT_PACKAGES")
|
|
285
310
|
if not raw:
|
|
@@ -287,6 +312,22 @@ class DownloadService:
|
|
|
287
312
|
return False
|
|
288
313
|
|
|
289
314
|
packages = raw.get("packages", [])
|
|
315
|
+
|
|
316
|
+
# Filter to only packages enabled on the active profile
|
|
317
|
+
if profile and profile.dbt_packages:
|
|
318
|
+
enabled = set(profile.dbt_packages)
|
|
319
|
+
|
|
320
|
+
# Exclude packages already provided by upstream projects
|
|
321
|
+
if profile.upstream_projects and all_profiles:
|
|
322
|
+
profile_map = {p.profile_name: p for p in all_profiles}
|
|
323
|
+
upstream_pkgs = _collect_upstream_packages(
|
|
324
|
+
profile.upstream_projects, profile_map
|
|
325
|
+
)
|
|
326
|
+
enabled -= upstream_pkgs
|
|
327
|
+
|
|
328
|
+
packages = [p for p in packages if p.get("name") in enabled]
|
|
329
|
+
info_line(f" Filtered to {len(packages)} package(s) for profile '{profile.profile_name}'")
|
|
330
|
+
|
|
290
331
|
pkg_list = []
|
|
291
332
|
for pkg in packages:
|
|
292
333
|
source = pkg.get("source", "git")
|
|
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
|
|
6
6
|
from datasecops_cli.main import _build_parser, _run_download, DOWNLOAD_ITEMS
|
|
7
7
|
from datasecops_cli.config import Config
|
|
8
|
+
from datasecops_cli.models.project_config import ProjectProfile
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class TestBuildParser:
|
|
@@ -64,6 +65,11 @@ class TestRunDownload:
|
|
|
64
65
|
config.project_dir = tmp_path
|
|
65
66
|
config.dbt_project_dir = tmp_path
|
|
66
67
|
config.profile_name = "test_profile"
|
|
68
|
+
config.profile = ProjectProfile(
|
|
69
|
+
profile_name="test_profile",
|
|
70
|
+
dbt_packages=["dbt_utils", "dbt_expectations"],
|
|
71
|
+
)
|
|
72
|
+
config.all_profiles = [config.profile]
|
|
67
73
|
# source_control defaults to GitHub
|
|
68
74
|
return config
|
|
69
75
|
|
|
@@ -119,7 +125,9 @@ class TestRunDownload:
|
|
|
119
125
|
_run_download(config, ["packages"])
|
|
120
126
|
|
|
121
127
|
assert exc.value.code == 0
|
|
122
|
-
mock_ds.download_dbt_packages.assert_called_once_with(
|
|
128
|
+
mock_ds.download_dbt_packages.assert_called_once_with(
|
|
129
|
+
tmp_path, profile=config.profile, all_profiles=config.all_profiles
|
|
130
|
+
)
|
|
123
131
|
|
|
124
132
|
@patch("datasecops_cli.main._connect_and_load")
|
|
125
133
|
def test_download_macros(self, mock_connect, tmp_path):
|
|
@@ -300,3 +308,112 @@ class TestRunDownload:
|
|
|
300
308
|
_run_download(config, ["install-dbt"])
|
|
301
309
|
|
|
302
310
|
assert exc.value.code == 1
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class TestDownloadPackageFiltering:
|
|
314
|
+
"""Tests for dbt package filtering by profile."""
|
|
315
|
+
|
|
316
|
+
def test_download_dbt_packages_filters_by_profile(self, tmp_path):
|
|
317
|
+
"""Only packages in profile.dbt_packages should appear in packages.yml."""
|
|
318
|
+
from datasecops_cli.services.download_service import DownloadService
|
|
319
|
+
|
|
320
|
+
mock_sf = MagicMock()
|
|
321
|
+
mock_sf.get_framework_config.return_value = {
|
|
322
|
+
"packages": [
|
|
323
|
+
{"name": "dbt_utils", "source": "git", "url": "https://github.com/dbt-labs/dbt-utils.git", "latest_version": "1.0.0"},
|
|
324
|
+
{"name": "dbt_expectations", "source": "git", "url": "https://github.com/calogica/dbt-expectations.git", "latest_version": "0.10.0"},
|
|
325
|
+
{"name": "dbt_constraints", "source": "git", "url": "https://github.com/Snowflake-Labs/dbt_constraints.git", "latest_version": "0.6.0"},
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
profile = ProjectProfile(
|
|
330
|
+
profile_name="test",
|
|
331
|
+
dbt_packages=["dbt_utils", "dbt_expectations"],
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
service = DownloadService(mock_sf, tmp_path)
|
|
335
|
+
service.download_dbt_packages(tmp_path, profile=profile)
|
|
336
|
+
|
|
337
|
+
import yaml
|
|
338
|
+
result = yaml.safe_load((tmp_path / "packages.yml").read_text())
|
|
339
|
+
assert len(result["packages"]) == 2
|
|
340
|
+
urls = [p.get("git") for p in result["packages"]]
|
|
341
|
+
assert "https://github.com/dbt-labs/dbt-utils.git" in urls
|
|
342
|
+
assert "https://github.com/calogica/dbt-expectations.git" in urls
|
|
343
|
+
assert "https://github.com/Snowflake-Labs/dbt_constraints.git" not in urls
|
|
344
|
+
|
|
345
|
+
def test_download_dbt_packages_excludes_upstream(self, tmp_path):
|
|
346
|
+
"""Packages provided by upstream projects should be excluded."""
|
|
347
|
+
from datasecops_cli.services.download_service import DownloadService
|
|
348
|
+
|
|
349
|
+
mock_sf = MagicMock()
|
|
350
|
+
mock_sf.get_framework_config.return_value = {
|
|
351
|
+
"packages": [
|
|
352
|
+
{"name": "dbt_utils", "source": "git", "url": "https://github.com/dbt-labs/dbt-utils.git", "latest_version": "1.0.0"},
|
|
353
|
+
{"name": "dbt_expectations", "source": "git", "url": "https://github.com/calogica/dbt-expectations.git", "latest_version": "0.10.0"},
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
upstream_profile = ProjectProfile(
|
|
358
|
+
profile_name="upstream_project",
|
|
359
|
+
dbt_packages=["dbt_utils"],
|
|
360
|
+
)
|
|
361
|
+
profile = ProjectProfile(
|
|
362
|
+
profile_name="test",
|
|
363
|
+
dbt_packages=["dbt_utils", "dbt_expectations"],
|
|
364
|
+
upstream_projects=["upstream_project"],
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
service = DownloadService(mock_sf, tmp_path)
|
|
368
|
+
service.download_dbt_packages(tmp_path, profile=profile, all_profiles=[profile, upstream_profile])
|
|
369
|
+
|
|
370
|
+
import yaml
|
|
371
|
+
result = yaml.safe_load((tmp_path / "packages.yml").read_text())
|
|
372
|
+
# dbt_utils excluded (provided by upstream), only dbt_expectations + local upstream dep
|
|
373
|
+
git_packages = [p for p in result["packages"] if "git" in p]
|
|
374
|
+
local_packages = [p for p in result["packages"] if "local" in p]
|
|
375
|
+
assert len(git_packages) == 1
|
|
376
|
+
assert git_packages[0]["git"] == "https://github.com/calogica/dbt-expectations.git"
|
|
377
|
+
assert len(local_packages) == 1
|
|
378
|
+
assert local_packages[0]["local"] == "local_packages/upstream_project"
|
|
379
|
+
|
|
380
|
+
def test_download_dbt_packages_no_profile_downloads_all(self, tmp_path):
|
|
381
|
+
"""When no profile is passed, all packages should be included."""
|
|
382
|
+
from datasecops_cli.services.download_service import DownloadService
|
|
383
|
+
|
|
384
|
+
mock_sf = MagicMock()
|
|
385
|
+
mock_sf.get_framework_config.return_value = {
|
|
386
|
+
"packages": [
|
|
387
|
+
{"name": "dbt_utils", "source": "git", "url": "https://github.com/dbt-labs/dbt-utils.git", "latest_version": "1.0.0"},
|
|
388
|
+
{"name": "dbt_expectations", "source": "git", "url": "https://github.com/calogica/dbt-expectations.git", "latest_version": "0.10.0"},
|
|
389
|
+
{"name": "dbt_constraints", "source": "git", "url": "https://github.com/Snowflake-Labs/dbt_constraints.git", "latest_version": "0.6.0"},
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
service = DownloadService(mock_sf, tmp_path)
|
|
394
|
+
service.download_dbt_packages(tmp_path, profile=None)
|
|
395
|
+
|
|
396
|
+
import yaml
|
|
397
|
+
result = yaml.safe_load((tmp_path / "packages.yml").read_text())
|
|
398
|
+
assert len(result["packages"]) == 3
|
|
399
|
+
|
|
400
|
+
def test_download_dbt_packages_empty_dbt_packages_downloads_all(self, tmp_path):
|
|
401
|
+
"""When profile.dbt_packages is empty, all packages should be included."""
|
|
402
|
+
from datasecops_cli.services.download_service import DownloadService
|
|
403
|
+
|
|
404
|
+
mock_sf = MagicMock()
|
|
405
|
+
mock_sf.get_framework_config.return_value = {
|
|
406
|
+
"packages": [
|
|
407
|
+
{"name": "dbt_utils", "source": "git", "url": "https://github.com/dbt-labs/dbt-utils.git", "latest_version": "1.0.0"},
|
|
408
|
+
{"name": "dbt_expectations", "source": "git", "url": "https://github.com/calogica/dbt-expectations.git", "latest_version": "0.10.0"},
|
|
409
|
+
]
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
profile = ProjectProfile(profile_name="test", dbt_packages=[])
|
|
413
|
+
|
|
414
|
+
service = DownloadService(mock_sf, tmp_path)
|
|
415
|
+
service.download_dbt_packages(tmp_path, profile=profile)
|
|
416
|
+
|
|
417
|
+
import yaml
|
|
418
|
+
result = yaml.safe_load((tmp_path / "packages.yml").read_text())
|
|
419
|
+
assert len(result["packages"]) == 2
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/directory_scaffolder.py
RENAMED
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/linting_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/snowflake_service.py
RENAMED
|
File without changes
|
{datasecops_cli-0.4.4 → datasecops_cli-0.4.5}/src/datasecops_cli/services/upstream_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|