datasecops-cli 0.5.4__tar.gz → 0.5.6__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 (56) hide show
  1. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/CHANGELOG.md +14 -0
  2. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/PKG-INFO +1 -1
  3. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/pyproject.toml +1 -1
  4. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/main.py +9 -6
  5. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/menus/configuration.py +1 -1
  6. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/menus/downloads.py +1 -1
  7. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/download_service.py +9 -2
  8. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/utilities/mcp.py +17 -1
  9. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/.github/workflows/auto-tag.yml +0 -0
  10. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/.github/workflows/publish-cli.yml +0 -0
  11. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/.github/workflows/test.yml +0 -0
  12. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/.gitignore +0 -0
  13. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/DEVELOPMENT.md +0 -0
  14. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/LICENSE +0 -0
  15. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/README.md +0 -0
  16. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/docs/getting-started.md +0 -0
  17. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/docs/git-operations.md +0 -0
  18. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/docs/legacy.md +0 -0
  19. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/docs/legacy_plan_of_action.md +0 -0
  20. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/docs/mcp-server.md +0 -0
  21. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/mcp-servers.json +0 -0
  22. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/setup.ps1 +0 -0
  23. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/setup.sh +0 -0
  24. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/__init__.py +0 -0
  25. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/config.py +0 -0
  26. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/menus/__init__.py +0 -0
  27. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/menus/development.py +0 -0
  28. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/menus/git_operations.py +0 -0
  29. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/models/__init__.py +0 -0
  30. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/models/git_helpers.py +0 -0
  31. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/models/project_config.py +0 -0
  32. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/__init__.py +0 -0
  33. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/bootstrap_service.py +0 -0
  34. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/dbt_project_generator.py +0 -0
  35. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/dbt_runner.py +0 -0
  36. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/directory_scaffolder.py +0 -0
  37. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/git_service.py +0 -0
  38. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/linting_service.py +0 -0
  39. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/skill_service.py +0 -0
  40. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/snowflake_service.py +0 -0
  41. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/services/upstream_service.py +0 -0
  42. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/utilities/__init__.py +0 -0
  43. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/utilities/display.py +0 -0
  44. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/utilities/file_utils.py +0 -0
  45. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_cli/utilities/yaml_utils.py +0 -0
  46. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_mcp/__init__.py +0 -0
  47. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_mcp/__main__.py +0 -0
  48. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_mcp/connection.py +0 -0
  49. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/src/datasecops_mcp/server.py +0 -0
  50. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/tests/__init__.py +0 -0
  51. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/tests/test_config.py +0 -0
  52. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/tests/test_file_utils.py +0 -0
  53. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/tests/test_main.py +0 -0
  54. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/tests/test_models.py +0 -0
  55. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/tests/test_version.py +0 -0
  56. {datasecops_cli-0.5.4 → datasecops_cli-0.5.6}/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.5.6] - 2026-06-19
6
+
7
+ ### Fixed
8
+
9
+ - **AzureDevOps pipeline platform filter mismatch** — the platform comparison in `download_pipelines` now normalizes by stripping spaces (e.g. `"Azure DevOps"` → `"azuredevops"`) so pipelines returned by the stored procedure are no longer silently skipped when the caller passes `"azuredevops"`.
10
+
11
+ ## [0.5.5] - 2026-06-19
12
+
13
+ ### Fixed
14
+
15
+ - **AzureDevOps pipelines download to correct folder** — `download_pipelines` now writes AzureDevOps pipeline files to `.azuredevops/workflows/` instead of the project root, matching the platform convention (similar to how GitHub pipelines are written to `.github/workflows/`).
16
+ - **MCP server setup recognises all AzureDevOps platform variants** — the initial setup flow in `main.py` now uses case-insensitive matching for the platform name (`"AzureDevOps"` and `"Azure DevOps"` both work), consistent with the menu paths.
17
+ - **Cortex Code MCP server uses absolute venv path** — the `datasecops-framework` MCP server entry in `.snowflake/cortex/mcp.json` now uses the absolute path to the project's `.venv/Scripts/datasecops-mcp` (Windows) or `.venv/bin/datasecops-mcp` (Unix) instead of the bare `datasecops-mcp` command, fixing "Process exited with code 1" errors in Cortex Code Desktop when the venv isn't on the system PATH.
18
+
5
19
  ## [0.5.4] - 2026-06-05
6
20
 
7
21
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datasecops-cli
3
- Version: 0.5.4
3
+ Version: 0.5.6
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.5.4"
7
+ version = "0.5.6"
8
8
  description = "DataSecOps Framework CLI for Snowflake Native App"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -307,7 +307,7 @@ def _run_setup(project_dir: Path):
307
307
 
308
308
  info_line("")
309
309
  info_line("Which AI tool are you configuring?")
310
- tool_options = ["VS Code (Copilot)", "Cursor", "Claude Code", "Cortex Code"]
310
+ tool_options = ["Cortex Code", "Cursor", "Claude Code", "VS Code (Copilot)"]
311
311
  tool_options.append("Skip")
312
312
  tool_choice = select_from_list(tool_options, "tool", add_back=False)
313
313
 
@@ -316,9 +316,12 @@ def _run_setup(project_dir: Path):
316
316
  info_line("Adding MCP servers to Cortex Code...")
317
317
  mcp_path = project_dir / ".snowflake" / "cortex" / "mcp.json"
318
318
 
319
+ from datasecops_cli.utilities.mcp import _resolve_mcp_command
320
+ mcp_command = _resolve_mcp_command()
321
+
319
322
  servers = {"datasecops-framework": {
320
323
  "type": "stdio",
321
- "command": "datasecops-mcp",
324
+ "command": mcp_command,
322
325
  "args": ["--connection-name", connection_name, "--app-database", app_database],
323
326
  }}
324
327
 
@@ -336,7 +339,7 @@ def _run_setup(project_dir: Path):
336
339
  server_entry["headers"] = {"Authorization": f"Bearer {snowflake_pat}"}
337
340
  servers["datasecops-docs"] = server_entry
338
341
 
339
- if platform_choice == "GitHub":
342
+ if platform_choice.lower() == "github":
340
343
  github_token = get_input_string("Enter your GitHub PAT (or press Enter to skip): ", allow_empty=True)
341
344
  if github_token:
342
345
  servers["github"] = {
@@ -345,7 +348,7 @@ def _run_setup(project_dir: Path):
345
348
  "args": ["-y", "@modelcontextprotocol/server-github"],
346
349
  "env": {"GITHUB_PERSONAL_ACCESS_TOKEN": github_token},
347
350
  }
348
- elif platform_choice == "Azure DevOps":
351
+ elif platform_choice.lower() in ("azuredevops", "azure devops"):
349
352
  azure_org = get_input_string("Enter your Azure DevOps org URL (e.g. https://dev.azure.com/myorg): ")
350
353
  azure_pat = get_input_string("Enter your Azure DevOps PAT (or press Enter to skip): ", allow_empty=True)
351
354
  if azure_pat:
@@ -387,13 +390,13 @@ def _run_setup(project_dir: Path):
387
390
  }
388
391
  info_line("Note: Replace <YOUR_PAT> in datasecops-docs headers with your Snowflake PAT")
389
392
 
390
- if platform_choice == "GitHub":
393
+ if platform_choice.lower() == "github":
391
394
  servers["github"] = {
392
395
  "command": "npx",
393
396
  "args": ["-y", "@modelcontextprotocol/server-github"],
394
397
  "env": {"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"},
395
398
  }
396
- elif platform_choice == "Azure DevOps":
399
+ elif platform_choice.lower() in ("azuredevops", "azure devops"):
397
400
  servers["azure-devops"] = {
398
401
  "command": "npx",
399
402
  "args": ["-y", "@tiberriver256/mcp-server-azure-devops"],
@@ -150,7 +150,7 @@ class ConfigurationMenu:
150
150
  # Select target tool
151
151
  info_line("")
152
152
  info_line("Which AI tool are you configuring?")
153
- tool_options = ["VS Code (Copilot)", "Cursor", "Claude Code", "Cortex Code"]
153
+ tool_options = ["Cortex Code", "Cursor", "Claude Code", "VS Code (Copilot)"]
154
154
  tool_choice = select_from_list(tool_options, "tool", add_back=False)
155
155
 
156
156
  if tool_choice == "Cortex Code":
@@ -134,7 +134,7 @@ class DownloadsMenu:
134
134
  # Select target tool
135
135
  info_line("")
136
136
  info_line("Which AI tool are you configuring?")
137
- tool_options = ["VS Code (Copilot)", "Cursor", "Claude Code", "Cortex Code"]
137
+ tool_options = ["Cortex Code", "Cursor", "Claude Code", "VS Code (Copilot)"]
138
138
  tool_choice = select_from_list(tool_options, "tool", add_back=False)
139
139
 
140
140
  if tool_choice == "Cortex Code":
@@ -129,16 +129,23 @@ class DownloadService:
129
129
  error_line("No pipeline configuration found in native app")
130
130
  return False
131
131
 
132
+ def _normalize_platform(p: str) -> str:
133
+ return p.lower().replace(" ", "")
134
+
135
+ normalized = _normalize_platform(platform)
132
136
  pipelines = raw.get("pipelines", [])
133
137
  count = 0
134
138
  for pipe in pipelines:
135
- if pipe.get("platform", "github").lower() != platform.lower() or not pipe.get("enabled", True):
139
+ pipe_platform = _normalize_platform(pipe.get("platform", "github"))
140
+ if pipe_platform != normalized or not pipe.get("enabled", True):
136
141
  continue
137
142
  filename = pipe.get("filename", "")
138
143
  yaml_content = pipe.get("yaml_content", "")
139
144
  if filename and yaml_content:
140
- if platform.lower() == "github":
145
+ if normalized == "github":
141
146
  dest = self.project_dir / ".github" / "workflows" / filename
147
+ elif normalized == "azuredevops":
148
+ dest = self.project_dir / ".azuredevops" / "workflows" / filename
142
149
  else:
143
150
  dest = self.project_dir / filename
144
151
  write_file(dest, yaml_content)
@@ -1,12 +1,27 @@
1
1
  """MCP server configuration utilities."""
2
2
 
3
3
  import json
4
+ import shutil
5
+ import sys
4
6
  from pathlib import Path
5
7
 
6
8
  from datasecops_cli.utilities.display import get_input_string, success_line
7
9
  from datasecops_cli.utilities.file_utils import ensure_dir, write_file
8
10
 
9
11
 
12
+ def _resolve_mcp_command() -> str:
13
+ """Resolve the path to the datasecops-mcp executable.
14
+
15
+ Checks shutil.which first (covers global/pipx installs), then falls back
16
+ to the project .venv path.
17
+ """
18
+ found = shutil.which("datasecops-mcp")
19
+ if found:
20
+ return found
21
+ venv_bin = "Scripts" if sys.platform == "win32" else "bin"
22
+ return f".venv/{venv_bin}/datasecops-mcp"
23
+
24
+
10
25
  def build_cortex_mcp_servers(
11
26
  servers: list[str],
12
27
  connection_name: str,
@@ -16,13 +31,14 @@ def build_cortex_mcp_servers(
16
31
 
17
32
  Returns a dict suitable for {"mcpServers": ...} JSON output.
18
33
  """
34
+ mcp_command = _resolve_mcp_command()
19
35
  server_config = {}
20
36
 
21
37
  for server in servers:
22
38
  if server == "datasecops-framework":
23
39
  server_config["datasecops-framework"] = {
24
40
  "type": "stdio",
25
- "command": "datasecops-mcp",
41
+ "command": mcp_command,
26
42
  "args": ["--connection-name", connection_name, "--app-database", app_database],
27
43
  }
28
44
 
File without changes
File without changes
File without changes
File without changes