datasecops-cli 0.2.0__tar.gz → 0.2.1__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.
Files changed (47) hide show
  1. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/CHANGELOG.md +14 -0
  2. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/PKG-INFO +1 -1
  3. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/pyproject.toml +1 -1
  4. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/main.py +3 -2
  5. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/menus/development.py +19 -1
  6. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/menus/downloads.py +2 -1
  7. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/services/bootstrap_service.py +2 -1
  8. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/services/download_service.py +7 -1
  9. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/.github/workflows/publish-cli.yml +0 -0
  10. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/.gitignore +0 -0
  11. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/DEVELOPMENT.md +0 -0
  12. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/LICENSE +0 -0
  13. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/README.md +0 -0
  14. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/docs/getting-started.md +0 -0
  15. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/docs/legacy.md +0 -0
  16. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/docs/legacy_plan_of_action.md +0 -0
  17. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/docs/mcp-server.md +0 -0
  18. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/mcp-servers.json +0 -0
  19. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/setup.ps1 +0 -0
  20. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/setup.sh +0 -0
  21. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/__init__.py +0 -0
  22. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/config.py +0 -0
  23. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/menus/__init__.py +0 -0
  24. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/menus/git_operations.py +0 -0
  25. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/models/__init__.py +0 -0
  26. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/models/git_helpers.py +0 -0
  27. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/models/project_config.py +0 -0
  28. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/services/__init__.py +0 -0
  29. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/services/dbt_runner.py +0 -0
  30. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/services/git_service.py +0 -0
  31. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/services/linting_service.py +0 -0
  32. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/services/skill_service.py +0 -0
  33. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/services/snowflake_service.py +0 -0
  34. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/utilities/__init__.py +0 -0
  35. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/utilities/display.py +0 -0
  36. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/utilities/file_utils.py +0 -0
  37. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_cli/utilities/yaml_utils.py +0 -0
  38. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_mcp/__init__.py +0 -0
  39. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_mcp/__main__.py +0 -0
  40. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_mcp/connection.py +0 -0
  41. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/src/datasecops_mcp/server.py +0 -0
  42. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/tests/__init__.py +0 -0
  43. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/tests/test_config.py +0 -0
  44. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/tests/test_file_utils.py +0 -0
  45. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/tests/test_models.py +0 -0
  46. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/tests/test_version.py +0 -0
  47. {datasecops_cli-0.2.0 → datasecops_cli-0.2.1}/tests/test_yaml_utils.py +0 -0
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to the DataSecOps CLI are documented in this file.
4
4
 
5
+ ## [0.2.1] - 2026-05-10
6
+
7
+ ### Fixed
8
+
9
+ - **SQLFluff config now includes `[sqlfluff:templater:dbt]` section** with `project_dir` and `profiles_dir` when downloaded from the framework, eliminating the need for `${DBT_PROFILES_DIR}` environment variable substitution
10
+ - **Auto-download `.sqlfluff` before linting** — the lint menu now automatically downloads the config from the framework if it doesn't exist locally, instead of failing silently or erroring
11
+ - **CI lint action handles missing `.sqlfluffconfig`** — the `tasks/lint-sql` action now only runs `envsubst` if `.sqlfluffconfig` exists, and accepts a pre-generated `.sqlfluff` file directly
12
+
13
+ ### Changed
14
+
15
+ - `download_sqlfluff_config()` accepts an optional `profiles_dir` parameter to embed in the generated config
16
+ - `DevelopmentMenu` and `DownloadsMenu` pass the resolved `profiles_dir` through to the download service
17
+ - `.sqlfluff` no longer needs to be committed to client repos — it is generated on-the-fly from the framework
18
+
5
19
  ## [0.2.0] - 2026-05-09
6
20
 
7
21
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasecops-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: DataSecOps Framework CLI for Snowflake Native App
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "datasecops-cli"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "DataSecOps Framework CLI for Snowflake Native App"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -88,12 +88,13 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
88
88
 
89
89
  while option != 0:
90
90
  if option == 1:
91
+ profiles_dir = str(config.get_dbt_profiles_dir())
91
92
  if git_service:
92
93
  dev_menu = DevelopmentMenu(dbt_runner, linting_service, git_service, profile_name,
93
- download_service=download_service)
94
+ download_service=download_service, profiles_dir=profiles_dir)
94
95
  else:
95
96
  dev_menu = DevelopmentMenu(dbt_runner, linting_service, None, profile_name,
96
- download_service=download_service)
97
+ download_service=download_service, profiles_dir=profiles_dir)
97
98
  dev_menu.show()
98
99
 
99
100
  elif option == 2 and git_service:
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  from datasecops_cli.services.dbt_runner import DbtRunner
2
4
  from datasecops_cli.services.linting_service import LintingService
3
5
  from datasecops_cli.services.download_service import DownloadService
@@ -12,12 +14,14 @@ from datasecops_cli.utilities.display import (
12
14
  class DevelopmentMenu:
13
15
  def __init__(self, dbt_runner: DbtRunner, linting_service: LintingService,
14
16
  git_service: GitService, profile_name: str,
15
- download_service: DownloadService = None):
17
+ download_service: DownloadService = None,
18
+ profiles_dir: str = None):
16
19
  self.dbt = dbt_runner
17
20
  self.linting = linting_service
18
21
  self.git = git_service
19
22
  self.profile_name = profile_name
20
23
  self.downloads = download_service
24
+ self.profiles_dir = profiles_dir
21
25
 
22
26
  def show(self) -> None:
23
27
  self._menu()
@@ -136,6 +140,17 @@ class DevelopmentMenu:
136
140
  self.dbt.test(select=select)
137
141
  complete_action()
138
142
 
143
+ def _ensure_sqlfluff_config(self) -> bool:
144
+ """Download .sqlfluff from the framework if it doesn't exist locally."""
145
+ config_path = self.linting.project_dir / ".sqlfluff"
146
+ if config_path.exists():
147
+ return True
148
+ if not self.downloads:
149
+ error_line("No .sqlfluff config found and download service not available")
150
+ return False
151
+ info_line("No .sqlfluff config found — downloading from framework...")
152
+ return self.downloads.download_sqlfluff_config(profiles_dir=self.profiles_dir)
153
+
139
154
  def _lint_menu(self) -> None:
140
155
  clear()
141
156
  display_action_header("SQLFluff Linting Options")
@@ -147,6 +162,9 @@ class DevelopmentMenu:
147
162
  menu_option(6, "install - Install SQLFluff requirements from framework")
148
163
  menu_option(0, "back - Return to development menu")
149
164
  option = get_input_number("Choose an option: ")
165
+ if option in (1, 2, 3, 4, 5) and not self._ensure_sqlfluff_config():
166
+ complete_action()
167
+ return
150
168
  if option == 1:
151
169
  changed = [f.file for f in self.git.get_changed_files()] if self.git else []
152
170
  self.linting.lint_modified(fix=False, changed_files=changed)
@@ -29,7 +29,8 @@ class DownloadsMenu:
29
29
  while option != 0:
30
30
  if option == 1:
31
31
  display_action_header("Download SQLFluff Config")
32
- self.downloads.download_sqlfluff_config()
32
+ profiles_dir = str(Path(self.project_settings.profile_dir).expanduser()) if self.project_settings else None
33
+ self.downloads.download_sqlfluff_config(profiles_dir=profiles_dir)
33
34
  complete_action()
34
35
  elif option == 2:
35
36
  display_action_header("Download Pipeline Files")
@@ -68,7 +68,8 @@ class BootstrapService:
68
68
  # Step 3: Download SQLFluff config
69
69
  info_line("")
70
70
  info_line("[3] Downloading SQLFluff configuration...")
71
- if self.download_service.download_sqlfluff_config():
71
+ profiles_dir = str(Path(self.project_settings.profile_dir).expanduser())
72
+ if self.download_service.download_sqlfluff_config(profiles_dir=profiles_dir):
72
73
  steps_passed += 1
73
74
  else:
74
75
  info_line(" (skipped - no SQLFluff config in native app)")
@@ -17,7 +17,7 @@ class DownloadService:
17
17
  self.sf = snowflake_service
18
18
  self.project_dir = project_dir
19
19
 
20
- def download_sqlfluff_config(self) -> bool:
20
+ def download_sqlfluff_config(self, profiles_dir: str = None) -> bool:
21
21
  info_line("Downloading SQLFluff configuration...")
22
22
  raw = self.sf.get_framework_config("SQLFLUFF_RULES")
23
23
  if not raw:
@@ -33,6 +33,12 @@ class DownloadService:
33
33
  for opt_key, opt_val in opts.items():
34
34
  lines.append(f"{opt_key} = {opt_val}")
35
35
 
36
+ lines.append("")
37
+ lines.append("[sqlfluff:templater:dbt]")
38
+ lines.append("project_dir = .")
39
+ if profiles_dir:
40
+ lines.append(f"profiles_dir = {profiles_dir}")
41
+
36
42
  lines.append("")
37
43
  lines.append("[sqlfluff:indentation]")
38
44
  indentation = raw.get("indentation", {})
File without changes
File without changes
File without changes
File without changes