datasecops-cli 0.5.1__tar.gz → 0.5.2__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.5.1 → datasecops_cli-0.5.2}/CHANGELOG.md +21 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/PKG-INFO +54 -4
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/README.md +53 -3
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/docs/getting-started.md +16 -13
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/docs/mcp-server.md +12 -2
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/mcp-servers.json +9 -1
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/pyproject.toml +1 -1
- datasecops_cli-0.5.2/src/datasecops_cli/__init__.py +1 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/main.py +40 -43
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/menus/configuration.py +11 -57
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/menus/development.py +24 -3
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/menus/downloads.py +11 -57
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/linting_service.py +29 -1
- datasecops_cli-0.5.2/src/datasecops_cli/utilities/mcp.py +74 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_mcp/connection.py +8 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_mcp/server.py +71 -0
- datasecops_cli-0.5.1/src/datasecops_cli/__init__.py +0 -1
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/.github/workflows/auto-tag.yml +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/.github/workflows/publish-cli.yml +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/.github/workflows/test.yml +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/.gitignore +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/DEVELOPMENT.md +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/LICENSE +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/docs/git-operations.md +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/docs/legacy.md +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/docs/legacy_plan_of_action.md +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/setup.ps1 +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/setup.sh +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/config.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/menus/__init__.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/menus/git_operations.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/models/__init__.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/models/git_helpers.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/models/project_config.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/__init__.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/bootstrap_service.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/dbt_project_generator.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/dbt_runner.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/directory_scaffolder.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/download_service.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/git_service.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/skill_service.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/snowflake_service.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/upstream_service.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/utilities/__init__.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/utilities/display.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/utilities/file_utils.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/utilities/yaml_utils.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_mcp/__init__.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_mcp/__main__.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/tests/__init__.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/tests/test_config.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/tests/test_file_utils.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/tests/test_main.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/tests/test_models.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/tests/test_version.py +0 -0
- {datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/tests/test_yaml_utils.py +0 -0
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the DataSecOps CLI are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.5.2] - 2026-06-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`get_sqlfluff_config` MCP tool** — exposes the rendered `.sqlfluff` INI config file from the native app, allowing AI tools to retrieve the exact linting configuration without needing a local `.sqlfluff` file.
|
|
10
|
+
- **`get_documentation` MCP tool** — exposes framework documentation topics (model development, data governance, deployment, etc.) so AI tools can reference framework conventions directly.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Cortex Code MCP config is now project-level** — MCP server registration for Cortex Code now writes `.snowflake/cortex/mcp.json` in the project root instead of calling `cortex mcp add`. This enables project-scoped MCP servers in Cortex Code Desktop and removes the dependency on the `cortex` binary being installed.
|
|
15
|
+
- **All MCP servers included in Cortex Code config** — the datasecops-framework, GitHub, Azure DevOps, and dbt MCP servers are all written to the project-level config file with `"type": "stdio"` field.
|
|
16
|
+
- **Cortex Code always available as an AI tool option** — removed the `shutil.which("cortex")` check since we no longer need the cortex binary for MCP configuration.
|
|
17
|
+
- **Expanded `_ALLOWED_PROCEDURES`** — added `get_sqlfluff_config_file` and `get_documentation` to the MCP connection allowlist.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Linting now installs dbt-snowflake at framework-pinned version** — the CLI lint menu fetches both SQLFluff and dbt requirements from the native app and ensures all packages (including `dbt-core` and `dbt-snowflake`) are installed at the correct versions before linting.
|
|
22
|
+
- **Linting runs `dbt deps` if packages are missing** — if `packages.yml` exists but `dbt_packages/` is empty, `dbt deps` is run automatically before linting to satisfy the sqlfluff dbt templater.
|
|
23
|
+
- **Default `.sqlfluffignore` created on first lint** — excludes `target/`, `dbt_packages/`, `dbt_modules/`, `logs/`, `macros/`, `models/cortex/agents`, `models/cortex/semantic_views`, `objects/stages/config/`, and `objects/file_formats/config/`.
|
|
24
|
+
- **MCP linting tools check for dbt-snowflake** — the MCP server lint/fix tools now verify `dbt-snowflake` is installed (and install it if missing) before running SQLFluff.
|
|
25
|
+
|
|
5
26
|
## [0.5.1] - 2026-06-04
|
|
6
27
|
|
|
7
28
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datasecops-cli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: DataSecOps Framework CLI for Snowflake Native App
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -158,10 +158,60 @@ The package includes an MCP (Model Context Protocol) server that exposes your fr
|
|
|
158
158
|
|
|
159
159
|
### Setup for AI Tools
|
|
160
160
|
|
|
161
|
-
**Cortex Code
|
|
161
|
+
**Cortex Code** — add to `.snowflake/cortex/mcp.json` in your project root:
|
|
162
162
|
|
|
163
|
-
```
|
|
164
|
-
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"mcpServers": {
|
|
166
|
+
"datasecops-framework": {
|
|
167
|
+
"type": "stdio",
|
|
168
|
+
"command": "datasecops-mcp",
|
|
169
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
170
|
+
},
|
|
171
|
+
"github": {
|
|
172
|
+
"type": "stdio",
|
|
173
|
+
"command": "npx",
|
|
174
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
175
|
+
"env": {
|
|
176
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN": "<your-github-pat>"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"dbt": {
|
|
180
|
+
"type": "stdio",
|
|
181
|
+
"command": "uvx",
|
|
182
|
+
"args": ["dbt-mcp"]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
For Azure DevOps instead of GitHub:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"mcpServers": {
|
|
193
|
+
"datasecops-framework": {
|
|
194
|
+
"type": "stdio",
|
|
195
|
+
"command": "datasecops-mcp",
|
|
196
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
197
|
+
},
|
|
198
|
+
"azure-devops": {
|
|
199
|
+
"type": "stdio",
|
|
200
|
+
"command": "npx",
|
|
201
|
+
"args": ["-y", "@tiberriver256/mcp-server-azure-devops"],
|
|
202
|
+
"env": {
|
|
203
|
+
"AZURE_DEVOPS_ORG_URL": "https://dev.azure.com/<your-org>",
|
|
204
|
+
"AZURE_DEVOPS_AUTH_METHOD": "pat",
|
|
205
|
+
"AZURE_DEVOPS_PAT": "<your-azure-pat>"
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"dbt": {
|
|
209
|
+
"type": "stdio",
|
|
210
|
+
"command": "uvx",
|
|
211
|
+
"args": ["dbt-mcp"]
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
165
215
|
```
|
|
166
216
|
|
|
167
217
|
**VS Code / Cursor** — add to `.vscode/mcp.json` or `.cursor/mcp.json`:
|
|
@@ -139,10 +139,60 @@ The package includes an MCP (Model Context Protocol) server that exposes your fr
|
|
|
139
139
|
|
|
140
140
|
### Setup for AI Tools
|
|
141
141
|
|
|
142
|
-
**Cortex Code
|
|
142
|
+
**Cortex Code** — add to `.snowflake/cortex/mcp.json` in your project root:
|
|
143
143
|
|
|
144
|
-
```
|
|
145
|
-
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"mcpServers": {
|
|
147
|
+
"datasecops-framework": {
|
|
148
|
+
"type": "stdio",
|
|
149
|
+
"command": "datasecops-mcp",
|
|
150
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
151
|
+
},
|
|
152
|
+
"github": {
|
|
153
|
+
"type": "stdio",
|
|
154
|
+
"command": "npx",
|
|
155
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
156
|
+
"env": {
|
|
157
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN": "<your-github-pat>"
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"dbt": {
|
|
161
|
+
"type": "stdio",
|
|
162
|
+
"command": "uvx",
|
|
163
|
+
"args": ["dbt-mcp"]
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
For Azure DevOps instead of GitHub:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"mcpServers": {
|
|
174
|
+
"datasecops-framework": {
|
|
175
|
+
"type": "stdio",
|
|
176
|
+
"command": "datasecops-mcp",
|
|
177
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
178
|
+
},
|
|
179
|
+
"azure-devops": {
|
|
180
|
+
"type": "stdio",
|
|
181
|
+
"command": "npx",
|
|
182
|
+
"args": ["-y", "@tiberriver256/mcp-server-azure-devops"],
|
|
183
|
+
"env": {
|
|
184
|
+
"AZURE_DEVOPS_ORG_URL": "https://dev.azure.com/<your-org>",
|
|
185
|
+
"AZURE_DEVOPS_AUTH_METHOD": "pat",
|
|
186
|
+
"AZURE_DEVOPS_PAT": "<your-azure-pat>"
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
"dbt": {
|
|
190
|
+
"type": "stdio",
|
|
191
|
+
"command": "uvx",
|
|
192
|
+
"args": ["dbt-mcp"]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
146
196
|
```
|
|
147
197
|
|
|
148
198
|
**VS Code / Cursor** — add to `.vscode/mcp.json` or `.cursor/mcp.json`:
|
|
@@ -215,20 +215,23 @@ Create `.cursor/mcp.json` in your project root with the same format as above.
|
|
|
215
215
|
|
|
216
216
|
### Cortex Code (CoCo)
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
Create `.snowflake/cortex/mcp.json` in your project root:
|
|
219
219
|
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"mcpServers": {
|
|
223
|
+
"datasecops-framework": {
|
|
224
|
+
"type": "stdio",
|
|
225
|
+
"command": "datasecops-mcp",
|
|
226
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
227
|
+
},
|
|
228
|
+
"dbt": {
|
|
229
|
+
"type": "stdio",
|
|
230
|
+
"command": "uvx",
|
|
231
|
+
"args": ["dbt-mcp"]
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
232
235
|
```
|
|
233
236
|
|
|
234
237
|
Verify all servers are connected:
|
|
@@ -24,8 +24,18 @@ If `--connection-name` and `--app-database` are not provided, the server falls b
|
|
|
24
24
|
|
|
25
25
|
### Cortex Code
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
Create `.snowflake/cortex/mcp.json` in your project root:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"datasecops-framework": {
|
|
33
|
+
"type": "stdio",
|
|
34
|
+
"command": "datasecops-mcp",
|
|
35
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
29
39
|
```
|
|
30
40
|
|
|
31
41
|
### Claude Code
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"datasecops-framework": {
|
|
4
|
+
"type": "stdio",
|
|
4
5
|
"command": "datasecops-mcp",
|
|
5
6
|
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"],
|
|
6
|
-
"env": {},
|
|
7
7
|
"description": "DataSecOps Framework governance rules, branching conventions, linting config, and project profiles from your Snowflake Native App"
|
|
8
8
|
},
|
|
9
9
|
"github": {
|
|
10
|
+
"type": "stdio",
|
|
10
11
|
"command": "npx",
|
|
11
12
|
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
12
13
|
"env": {
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"description": "GitHub API access for PRs, issues, checks, and repository management"
|
|
16
17
|
},
|
|
17
18
|
"azure-devops": {
|
|
19
|
+
"type": "stdio",
|
|
18
20
|
"command": "npx",
|
|
19
21
|
"args": ["-y", "@tiberriver256/mcp-server-azure-devops"],
|
|
20
22
|
"env": {
|
|
@@ -23,6 +25,12 @@
|
|
|
23
25
|
"AZURE_DEVOPS_PAT": "<your-azure-pat>"
|
|
24
26
|
},
|
|
25
27
|
"description": "Azure DevOps API access for PRs, pipelines, work items, and boards"
|
|
28
|
+
},
|
|
29
|
+
"dbt": {
|
|
30
|
+
"type": "stdio",
|
|
31
|
+
"command": "uvx",
|
|
32
|
+
"args": ["dbt-mcp"],
|
|
33
|
+
"description": "dbt lineage, model discovery, codegen, and semantic layer"
|
|
26
34
|
}
|
|
27
35
|
}
|
|
28
36
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.2"
|
|
@@ -307,66 +307,63 @@ 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"]
|
|
311
|
-
if shutil.which("cortex"):
|
|
312
|
-
tool_options.append("Cortex Code")
|
|
310
|
+
tool_options = ["VS Code (Copilot)", "Cursor", "Claude Code", "Cortex Code"]
|
|
313
311
|
tool_options.append("Skip")
|
|
314
312
|
tool_choice = select_from_list(tool_options, "tool", add_back=False)
|
|
315
313
|
|
|
316
314
|
if tool_choice == "Cortex Code":
|
|
317
315
|
info_line("")
|
|
318
316
|
info_line("Adding MCP servers to Cortex Code...")
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
warning_line(
|
|
327
|
-
"cortex not found — run manually: cortex mcp add datasecops-framework "
|
|
328
|
-
f"-- datasecops-mcp --connection-name {connection_name} --app-database {app_database}"
|
|
329
|
-
)
|
|
317
|
+
mcp_path = project_dir / ".snowflake" / "cortex" / "mcp.json"
|
|
318
|
+
|
|
319
|
+
servers = {"datasecops-framework": {
|
|
320
|
+
"type": "stdio",
|
|
321
|
+
"command": "datasecops-mcp",
|
|
322
|
+
"args": ["--connection-name", connection_name, "--app-database", app_database],
|
|
323
|
+
}}
|
|
330
324
|
|
|
331
325
|
# Add AI Agent MCP server if enabled
|
|
332
326
|
if ai_agent_mcp_url:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
327
|
+
snowflake_pat = get_input_string(
|
|
328
|
+
"Enter your Snowflake PAT for datasecops-docs MCP server (or press Enter to skip): ",
|
|
329
|
+
allow_empty=True,
|
|
330
|
+
)
|
|
331
|
+
server_entry = {
|
|
332
|
+
"type": "http",
|
|
333
|
+
"url": ai_agent_mcp_url,
|
|
334
|
+
}
|
|
335
|
+
if snowflake_pat:
|
|
336
|
+
server_entry["headers"] = {"Authorization": f"Bearer {snowflake_pat}"}
|
|
337
|
+
servers["datasecops-docs"] = server_entry
|
|
342
338
|
|
|
343
339
|
if platform_choice == "GitHub":
|
|
344
340
|
github_token = get_input_string("Enter your GitHub PAT (or press Enter to skip): ", allow_empty=True)
|
|
345
341
|
if github_token:
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
except FileNotFoundError:
|
|
353
|
-
warning_line("cortex not found — configure GitHub MCP server manually")
|
|
342
|
+
servers["github"] = {
|
|
343
|
+
"type": "stdio",
|
|
344
|
+
"command": "npx",
|
|
345
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
346
|
+
"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": github_token},
|
|
347
|
+
}
|
|
354
348
|
elif platform_choice == "Azure DevOps":
|
|
355
349
|
azure_org = get_input_string("Enter your Azure DevOps org URL (e.g. https://dev.azure.com/myorg): ")
|
|
356
350
|
azure_pat = get_input_string("Enter your Azure DevOps PAT (or press Enter to skip): ", allow_empty=True)
|
|
357
351
|
if azure_pat:
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
352
|
+
servers["azure-devops"] = {
|
|
353
|
+
"type": "stdio",
|
|
354
|
+
"command": "npx",
|
|
355
|
+
"args": ["-y", "@tiberriver256/mcp-server-azure-devops"],
|
|
356
|
+
"env": {
|
|
357
|
+
"AZURE_DEVOPS_ORG_URL": azure_org,
|
|
358
|
+
"AZURE_DEVOPS_AUTH_METHOD": "pat",
|
|
359
|
+
"AZURE_DEVOPS_PAT": azure_pat,
|
|
360
|
+
},
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
from datasecops_cli.utilities.file_utils import ensure_dir, write_file
|
|
364
|
+
ensure_dir(mcp_path.parent)
|
|
365
|
+
write_file(mcp_path, json.dumps({"mcpServers": servers}, indent=2))
|
|
366
|
+
success_line(f"MCP config written to {mcp_path}")
|
|
370
367
|
|
|
371
368
|
elif tool_choice in ("VS Code (Copilot)", "Cursor", "Claude Code"):
|
|
372
369
|
dir_map = {
|
|
@@ -150,9 +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"]
|
|
154
|
-
if shutil.which("cortex"):
|
|
155
|
-
tool_options.append("Cortex Code")
|
|
153
|
+
tool_options = ["VS Code (Copilot)", "Cursor", "Claude Code", "Cortex Code"]
|
|
156
154
|
tool_choice = select_from_list(tool_options, "tool", add_back=False)
|
|
157
155
|
|
|
158
156
|
if tool_choice == "Cortex Code":
|
|
@@ -163,60 +161,16 @@ class ConfigurationMenu:
|
|
|
163
161
|
complete_action()
|
|
164
162
|
|
|
165
163
|
def _install_mcp_cortex(self, servers: list[str], platform: str) -> None:
|
|
166
|
-
"""
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
capture_output=True, text=True)
|
|
177
|
-
success_line("Added datasecops-framework MCP server")
|
|
178
|
-
except FileNotFoundError:
|
|
179
|
-
warning_line(
|
|
180
|
-
"cortex not found — run manually: cortex mcp add datasecops-framework "
|
|
181
|
-
f"-- datasecops-mcp --connection-name {conn} --app-database {app_db}"
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
elif server == "GitHub":
|
|
185
|
-
token = get_input_string("Enter your GitHub PAT (or press Enter to skip): ", allow_empty=True)
|
|
186
|
-
if token:
|
|
187
|
-
try:
|
|
188
|
-
subprocess.run(["cortex", "mcp", "add", "github",
|
|
189
|
-
"-e", f"GITHUB_PERSONAL_ACCESS_TOKEN={token}",
|
|
190
|
-
"--", "npx", "-y", "@modelcontextprotocol/server-github"],
|
|
191
|
-
capture_output=True, text=True)
|
|
192
|
-
success_line("Added GitHub MCP server")
|
|
193
|
-
except FileNotFoundError:
|
|
194
|
-
warning_line("cortex not found — configure GitHub MCP server manually")
|
|
195
|
-
|
|
196
|
-
elif server == "Azure DevOps":
|
|
197
|
-
org = get_input_string("Enter your Azure DevOps org URL (e.g. https://dev.azure.com/myorg): ")
|
|
198
|
-
pat = get_input_string("Enter your Azure DevOps PAT (or press Enter to skip): ", allow_empty=True)
|
|
199
|
-
if pat:
|
|
200
|
-
try:
|
|
201
|
-
subprocess.run(["cortex", "mcp", "add", "azure-devops",
|
|
202
|
-
"-e", f"AZURE_DEVOPS_ORG_URL={org}",
|
|
203
|
-
"-e", "AZURE_DEVOPS_AUTH_METHOD=pat",
|
|
204
|
-
"-e", f"AZURE_DEVOPS_PAT={pat}",
|
|
205
|
-
"--", "npx", "-y", "@tiberriver256/mcp-server-azure-devops"],
|
|
206
|
-
capture_output=True, text=True)
|
|
207
|
-
success_line("Added Azure DevOps MCP server")
|
|
208
|
-
except FileNotFoundError:
|
|
209
|
-
warning_line("cortex not found — configure Azure DevOps MCP server manually")
|
|
210
|
-
|
|
211
|
-
elif server == "dbt":
|
|
212
|
-
try:
|
|
213
|
-
subprocess.run(["cortex", "mcp", "add", "dbt", "--", "uvx", "dbt-mcp"],
|
|
214
|
-
capture_output=True, text=True)
|
|
215
|
-
success_line("Added dbt MCP server")
|
|
216
|
-
except FileNotFoundError:
|
|
217
|
-
warning_line("cortex not found — run manually: cortex mcp add dbt -- uvx dbt-mcp")
|
|
218
|
-
|
|
219
|
-
success_line("Cortex Code MCP servers configured")
|
|
164
|
+
"""Write MCP server config to .snowflake/cortex/mcp.json for Cortex Code."""
|
|
165
|
+
from datasecops_cli.utilities.mcp import write_cortex_mcp_config
|
|
166
|
+
|
|
167
|
+
mcp_path = self.project_dir / ".snowflake" / "cortex" / "mcp.json"
|
|
168
|
+
write_cortex_mcp_config(
|
|
169
|
+
mcp_path=mcp_path,
|
|
170
|
+
servers=servers,
|
|
171
|
+
connection_name=self.datasecops_config.connection_name,
|
|
172
|
+
app_database=self.datasecops_config.app_database,
|
|
173
|
+
)
|
|
220
174
|
|
|
221
175
|
def _install_mcp_json(self, servers: list[str], platform: str, tool: str) -> None:
|
|
222
176
|
"""Write MCP server config as mcp.json for VS Code, Cursor, or Claude Code."""
|
|
@@ -164,9 +164,30 @@ class DevelopmentMenu:
|
|
|
164
164
|
info_line("No .sqlfluff config found — downloading from framework...")
|
|
165
165
|
if not self.downloads.download_sqlfluff_config(profiles_dir=self.profiles_dir, dbt_project_dir=self.linting.project_dir):
|
|
166
166
|
return False
|
|
167
|
-
# Fetch pinned versions from the native app and ensure they're installed
|
|
168
|
-
required =
|
|
169
|
-
|
|
167
|
+
# Fetch pinned versions from the native app (sqlfluff + dbt) and ensure they're installed
|
|
168
|
+
required = []
|
|
169
|
+
if self.downloads:
|
|
170
|
+
sqlfluff_pkgs = self.downloads.get_sqlfluff_requirements() or []
|
|
171
|
+
dbt_pkgs = self.downloads.get_dbt_requirements() or []
|
|
172
|
+
required = sqlfluff_pkgs + dbt_pkgs
|
|
173
|
+
if not self.linting.ensure_templater_installed(required_packages=required or None):
|
|
174
|
+
return False
|
|
175
|
+
# Ensure dbt project packages are installed (required by sqlfluff dbt templater)
|
|
176
|
+
self._ensure_dbt_packages()
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
def _ensure_dbt_packages(self) -> None:
|
|
180
|
+
"""Ensure dbt packages are installed for the sqlfluff dbt templater."""
|
|
181
|
+
packages_dir = self.linting.project_dir / "dbt_packages"
|
|
182
|
+
if packages_dir.exists() and any(packages_dir.iterdir()):
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
packages_yml = self.linting.project_dir / "packages.yml"
|
|
186
|
+
if not packages_yml.exists():
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
info_line("Installing dbt packages (required for linting)...")
|
|
190
|
+
self.dbt.deps()
|
|
170
191
|
|
|
171
192
|
def _lint_menu(self) -> None:
|
|
172
193
|
clear()
|
|
@@ -134,9 +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"]
|
|
138
|
-
if shutil.which("cortex"):
|
|
139
|
-
tool_options.append("Cortex Code")
|
|
137
|
+
tool_options = ["VS Code (Copilot)", "Cursor", "Claude Code", "Cortex Code"]
|
|
140
138
|
tool_choice = select_from_list(tool_options, "tool", add_back=False)
|
|
141
139
|
|
|
142
140
|
if tool_choice == "Cortex Code":
|
|
@@ -147,60 +145,16 @@ class DownloadsMenu:
|
|
|
147
145
|
complete_action()
|
|
148
146
|
|
|
149
147
|
def _install_mcp_cortex(self, servers: list[str], platform: str) -> None:
|
|
150
|
-
"""
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
capture_output=True, text=True)
|
|
161
|
-
success_line("Added datasecops-framework MCP server")
|
|
162
|
-
except FileNotFoundError:
|
|
163
|
-
warning_line(
|
|
164
|
-
"cortex not found — run manually: cortex mcp add datasecops-framework "
|
|
165
|
-
f"-- datasecops-mcp --connection-name {conn} --app-database {app_db}"
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
elif server == "GitHub":
|
|
169
|
-
token = get_input_string("Enter your GitHub PAT (or press Enter to skip): ", allow_empty=True)
|
|
170
|
-
if token:
|
|
171
|
-
try:
|
|
172
|
-
subprocess.run(["cortex", "mcp", "add", "github",
|
|
173
|
-
"-e", f"GITHUB_PERSONAL_ACCESS_TOKEN={token}",
|
|
174
|
-
"--", "npx", "-y", "@modelcontextprotocol/server-github"],
|
|
175
|
-
capture_output=True, text=True)
|
|
176
|
-
success_line("Added GitHub MCP server")
|
|
177
|
-
except FileNotFoundError:
|
|
178
|
-
warning_line("cortex not found — configure GitHub MCP server manually")
|
|
179
|
-
|
|
180
|
-
elif server == "Azure DevOps":
|
|
181
|
-
org = get_input_string("Enter your Azure DevOps org URL (e.g. https://dev.azure.com/myorg): ")
|
|
182
|
-
pat = get_input_string("Enter your Azure DevOps PAT (or press Enter to skip): ", allow_empty=True)
|
|
183
|
-
if pat:
|
|
184
|
-
try:
|
|
185
|
-
subprocess.run(["cortex", "mcp", "add", "azure-devops",
|
|
186
|
-
"-e", f"AZURE_DEVOPS_ORG_URL={org}",
|
|
187
|
-
"-e", "AZURE_DEVOPS_AUTH_METHOD=pat",
|
|
188
|
-
"-e", f"AZURE_DEVOPS_PAT={pat}",
|
|
189
|
-
"--", "npx", "-y", "@tiberriver256/mcp-server-azure-devops"],
|
|
190
|
-
capture_output=True, text=True)
|
|
191
|
-
success_line("Added Azure DevOps MCP server")
|
|
192
|
-
except FileNotFoundError:
|
|
193
|
-
warning_line("cortex not found — configure Azure DevOps MCP server manually")
|
|
194
|
-
|
|
195
|
-
elif server == "dbt":
|
|
196
|
-
try:
|
|
197
|
-
subprocess.run(["cortex", "mcp", "add", "dbt", "--", "uvx", "dbt-mcp"],
|
|
198
|
-
capture_output=True, text=True)
|
|
199
|
-
success_line("Added dbt MCP server")
|
|
200
|
-
except FileNotFoundError:
|
|
201
|
-
warning_line("cortex not found — run manually: cortex mcp add dbt -- uvx dbt-mcp")
|
|
202
|
-
|
|
203
|
-
success_line("Cortex Code MCP servers configured")
|
|
148
|
+
"""Write MCP server config to .snowflake/cortex/mcp.json for Cortex Code."""
|
|
149
|
+
from datasecops_cli.utilities.mcp import write_cortex_mcp_config
|
|
150
|
+
|
|
151
|
+
mcp_path = Path.cwd() / ".snowflake" / "cortex" / "mcp.json"
|
|
152
|
+
write_cortex_mcp_config(
|
|
153
|
+
mcp_path=mcp_path,
|
|
154
|
+
servers=servers,
|
|
155
|
+
connection_name=self.datasecops_config.connection_name,
|
|
156
|
+
app_database=self.datasecops_config.app_database,
|
|
157
|
+
)
|
|
204
158
|
|
|
205
159
|
def _install_mcp_json(self, servers: list[str], platform: str, tool: str) -> None:
|
|
206
160
|
"""Write MCP server config as mcp.json for VS Code, Cursor, or Claude Code."""
|
{datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/linting_service.py
RENAMED
|
@@ -82,7 +82,7 @@ class LintingService:
|
|
|
82
82
|
return self.install_requirements(to_install)
|
|
83
83
|
|
|
84
84
|
# No pinned versions — just ensure sqlfluff packages are present
|
|
85
|
-
missing = [pkg for pkg in ["sqlfluff", "sqlfluff-templater-dbt"] if not installed.get(pkg)]
|
|
85
|
+
missing = [pkg for pkg in ["sqlfluff", "sqlfluff-templater-dbt", "dbt-snowflake"] if not installed.get(pkg)]
|
|
86
86
|
if not missing:
|
|
87
87
|
return True
|
|
88
88
|
warning_line(f"Missing packages: {', '.join(missing)}")
|
|
@@ -99,6 +99,9 @@ class LintingService:
|
|
|
99
99
|
config_path = self.project_dir / ".sqlfluff"
|
|
100
100
|
if config_path.exists():
|
|
101
101
|
cmd += ["--config", str(config_path)]
|
|
102
|
+
|
|
103
|
+
# Ensure .sqlfluffignore is created with defaults if it doesn't exist
|
|
104
|
+
self._ensure_sqlfluffignore()
|
|
102
105
|
|
|
103
106
|
info_line(f"Running: sqlfluff {action}")
|
|
104
107
|
result = subprocess.run(cmd, capture_output=False, cwd=str(self.project_dir))
|
|
@@ -109,6 +112,31 @@ class LintingService:
|
|
|
109
112
|
else:
|
|
110
113
|
error_line(f"SQLFluff {action} failed with exit code {result.returncode}")
|
|
111
114
|
return result
|
|
115
|
+
|
|
116
|
+
def _ensure_sqlfluffignore(self) -> None:
|
|
117
|
+
"""Create a default .sqlfluffignore if one doesn't exist."""
|
|
118
|
+
ignore_path = self.project_dir / ".sqlfluffignore"
|
|
119
|
+
if ignore_path.exists():
|
|
120
|
+
return
|
|
121
|
+
default_content = "\n".join([
|
|
122
|
+
"# Directories to exclude from linting",
|
|
123
|
+
"target/",
|
|
124
|
+
"dbt_packages/",
|
|
125
|
+
"dbt_modules/",
|
|
126
|
+
"logs/",
|
|
127
|
+
"macros/",
|
|
128
|
+
"",
|
|
129
|
+
"# Ignore cortex agents and semantic views - spec is part of the body",
|
|
130
|
+
"models/cortex/agents",
|
|
131
|
+
"models/cortex/semantic_views",
|
|
132
|
+
"",
|
|
133
|
+
"# Ignore config-only object directories",
|
|
134
|
+
"objects/stages/config/",
|
|
135
|
+
"objects/file_formats/config/",
|
|
136
|
+
"",
|
|
137
|
+
])
|
|
138
|
+
ignore_path.write_text(default_content, encoding="utf-8")
|
|
139
|
+
info_line(f"Created default .sqlfluffignore at {ignore_path}")
|
|
112
140
|
|
|
113
141
|
def lint_modified(self, fix: bool = False, changed_files: list[str] = None) -> None:
|
|
114
142
|
if not changed_files:
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""MCP server configuration utilities."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from datasecops_cli.utilities.display import get_input_string, success_line
|
|
7
|
+
from datasecops_cli.utilities.file_utils import ensure_dir, write_file
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build_cortex_mcp_servers(
|
|
11
|
+
servers: list[str],
|
|
12
|
+
connection_name: str,
|
|
13
|
+
app_database: str,
|
|
14
|
+
) -> dict:
|
|
15
|
+
"""Build an MCP server config dict for selected servers, prompting for credentials.
|
|
16
|
+
|
|
17
|
+
Returns a dict suitable for {"mcpServers": ...} JSON output.
|
|
18
|
+
"""
|
|
19
|
+
server_config = {}
|
|
20
|
+
|
|
21
|
+
for server in servers:
|
|
22
|
+
if server == "datasecops-framework":
|
|
23
|
+
server_config["datasecops-framework"] = {
|
|
24
|
+
"type": "stdio",
|
|
25
|
+
"command": "datasecops-mcp",
|
|
26
|
+
"args": ["--connection-name", connection_name, "--app-database", app_database],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
elif server == "GitHub":
|
|
30
|
+
token = get_input_string("Enter your GitHub PAT (or press Enter to skip): ", allow_empty=True)
|
|
31
|
+
if token:
|
|
32
|
+
server_config["github"] = {
|
|
33
|
+
"type": "stdio",
|
|
34
|
+
"command": "npx",
|
|
35
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
36
|
+
"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": token},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
elif server == "Azure DevOps":
|
|
40
|
+
org = get_input_string("Enter your Azure DevOps org URL (e.g. https://dev.azure.com/myorg): ")
|
|
41
|
+
pat = get_input_string("Enter your Azure DevOps PAT (or press Enter to skip): ", allow_empty=True)
|
|
42
|
+
if pat:
|
|
43
|
+
server_config["azure-devops"] = {
|
|
44
|
+
"type": "stdio",
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "@tiberriver256/mcp-server-azure-devops"],
|
|
47
|
+
"env": {
|
|
48
|
+
"AZURE_DEVOPS_ORG_URL": org,
|
|
49
|
+
"AZURE_DEVOPS_AUTH_METHOD": "pat",
|
|
50
|
+
"AZURE_DEVOPS_PAT": pat,
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
elif server == "dbt":
|
|
55
|
+
server_config["dbt"] = {
|
|
56
|
+
"type": "stdio",
|
|
57
|
+
"command": "uvx",
|
|
58
|
+
"args": ["dbt-mcp"],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return server_config
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def write_cortex_mcp_config(
|
|
65
|
+
mcp_path: Path,
|
|
66
|
+
servers: list[str],
|
|
67
|
+
connection_name: str,
|
|
68
|
+
app_database: str,
|
|
69
|
+
) -> None:
|
|
70
|
+
"""Build and write .snowflake/cortex/mcp.json for Cortex Code."""
|
|
71
|
+
server_config = build_cortex_mcp_servers(servers, connection_name, app_database)
|
|
72
|
+
ensure_dir(mcp_path.parent)
|
|
73
|
+
write_file(mcp_path, json.dumps({"mcpServers": server_config}, indent=2))
|
|
74
|
+
success_line(f"MCP config written to {mcp_path}")
|
|
@@ -79,6 +79,8 @@ class MCPSnowflakeService:
|
|
|
79
79
|
"get_source_definitions",
|
|
80
80
|
"get_access_groups",
|
|
81
81
|
"get_support_contacts",
|
|
82
|
+
"get_sqlfluff_config_file",
|
|
83
|
+
"get_documentation",
|
|
82
84
|
})
|
|
83
85
|
|
|
84
86
|
def _call_procedure(self, proc_name: str, *args) -> Any:
|
|
@@ -118,6 +120,12 @@ class MCPSnowflakeService:
|
|
|
118
120
|
def get_support_contacts(self) -> list:
|
|
119
121
|
return self._call_procedure("get_support_contacts") or []
|
|
120
122
|
|
|
123
|
+
def get_sqlfluff_config_file(self) -> str:
|
|
124
|
+
return self._call_procedure("get_sqlfluff_config_file") or ""
|
|
125
|
+
|
|
126
|
+
def get_documentation(self) -> list:
|
|
127
|
+
return self._call_procedure("get_documentation") or []
|
|
128
|
+
|
|
121
129
|
def close(self):
|
|
122
130
|
if self._conn:
|
|
123
131
|
self._conn.close()
|
|
@@ -311,6 +311,36 @@ def get_support_contacts() -> str:
|
|
|
311
311
|
return json.dumps(raw, indent=2)
|
|
312
312
|
|
|
313
313
|
|
|
314
|
+
@mcp.tool()
|
|
315
|
+
def get_sqlfluff_config() -> str:
|
|
316
|
+
"""Get the rendered SQLFluff configuration file (.sqlfluff INI format).
|
|
317
|
+
|
|
318
|
+
Returns the fully rendered .sqlfluff config file content as an INI string.
|
|
319
|
+
This is the actual config that should be written to the project's .sqlfluff file.
|
|
320
|
+
Use this when you need the exact linting configuration for the project.
|
|
321
|
+
"""
|
|
322
|
+
sf = get_snowflake_service()
|
|
323
|
+
raw = sf.get_sqlfluff_config_file()
|
|
324
|
+
if not raw:
|
|
325
|
+
return "No rendered SQLFluff config found in native app"
|
|
326
|
+
return raw
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
@mcp.tool()
|
|
330
|
+
def get_documentation() -> str:
|
|
331
|
+
"""Get all documentation topics from the framework.
|
|
332
|
+
|
|
333
|
+
Returns documentation topics with their content, covering areas like
|
|
334
|
+
model development, object development, data governance, and deployment.
|
|
335
|
+
Use this to understand framework conventions and best practices.
|
|
336
|
+
"""
|
|
337
|
+
sf = get_snowflake_service()
|
|
338
|
+
raw = sf.get_documentation()
|
|
339
|
+
if not raw:
|
|
340
|
+
return "No documentation found in native app"
|
|
341
|
+
return json.dumps(raw, indent=2)
|
|
342
|
+
|
|
343
|
+
|
|
314
344
|
# --- Git / Source Control Tools ---
|
|
315
345
|
|
|
316
346
|
|
|
@@ -412,6 +442,7 @@ def lint_sql(file_path: str) -> str:
|
|
|
412
442
|
from pathlib import Path
|
|
413
443
|
|
|
414
444
|
project_dir = _find_project_dir()
|
|
445
|
+
_ensure_dbt_packages(project_dir)
|
|
415
446
|
target = Path(file_path)
|
|
416
447
|
if not target.is_absolute():
|
|
417
448
|
target = project_dir / target
|
|
@@ -453,6 +484,7 @@ def fix_sql(file_path: str) -> str:
|
|
|
453
484
|
from pathlib import Path
|
|
454
485
|
|
|
455
486
|
project_dir = _find_project_dir()
|
|
487
|
+
_ensure_dbt_packages(project_dir)
|
|
456
488
|
target = Path(file_path)
|
|
457
489
|
if not target.is_absolute():
|
|
458
490
|
target = project_dir / target
|
|
@@ -493,6 +525,7 @@ def lint_project(fix: bool = False) -> str:
|
|
|
493
525
|
from pathlib import Path
|
|
494
526
|
|
|
495
527
|
project_dir = _find_project_dir()
|
|
528
|
+
_ensure_dbt_packages(project_dir)
|
|
496
529
|
models_dir = project_dir / "models"
|
|
497
530
|
if not models_dir.exists():
|
|
498
531
|
return json.dumps({"error": f"No models directory found at {models_dir}"})
|
|
@@ -546,6 +579,44 @@ def _find_project_dir() -> "Path":
|
|
|
546
579
|
return cwd
|
|
547
580
|
|
|
548
581
|
|
|
582
|
+
def _ensure_dbt_packages(project_dir: "Path") -> None:
|
|
583
|
+
"""Ensure dbt-snowflake and dbt packages are available for sqlfluff dbt templater."""
|
|
584
|
+
import subprocess
|
|
585
|
+
from pathlib import Path
|
|
586
|
+
|
|
587
|
+
# Check that dbt-snowflake Python package is installed (required by sqlfluff-templater-dbt)
|
|
588
|
+
try:
|
|
589
|
+
import dbt.adapters.snowflake # noqa: F401
|
|
590
|
+
except ImportError:
|
|
591
|
+
# Attempt to install dbt-snowflake
|
|
592
|
+
subprocess.run(
|
|
593
|
+
["uv", "pip", "install", "dbt-snowflake"],
|
|
594
|
+
capture_output=True, text=True,
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
# Ensure dbt project packages are installed (refs to macros in packages)
|
|
598
|
+
packages_dir = project_dir / "dbt_packages"
|
|
599
|
+
if packages_dir.exists() and any(packages_dir.iterdir()):
|
|
600
|
+
return # Already installed
|
|
601
|
+
|
|
602
|
+
# Also check legacy dbt_modules directory
|
|
603
|
+
modules_dir = project_dir / "dbt_modules"
|
|
604
|
+
if modules_dir.exists() and any(modules_dir.iterdir()):
|
|
605
|
+
return
|
|
606
|
+
|
|
607
|
+
# Check if packages.yml exists before running deps
|
|
608
|
+
packages_yml = project_dir / "packages.yml"
|
|
609
|
+
if not packages_yml.exists():
|
|
610
|
+
return # No packages to install
|
|
611
|
+
|
|
612
|
+
# Run dbt deps to install packages
|
|
613
|
+
import shutil
|
|
614
|
+
for cmd in [["dbtf", "deps"], ["dbt", "deps"]]:
|
|
615
|
+
if shutil.which(cmd[0]):
|
|
616
|
+
subprocess.run(cmd, capture_output=True, text=True, cwd=str(project_dir))
|
|
617
|
+
return
|
|
618
|
+
|
|
619
|
+
|
|
549
620
|
def main():
|
|
550
621
|
"""Run the MCP server via stdio transport."""
|
|
551
622
|
import argparse
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.5.1"
|
|
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.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/bootstrap_service.py
RENAMED
|
File without changes
|
{datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/dbt_project_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/directory_scaffolder.py
RENAMED
|
File without changes
|
{datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/download_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/src/datasecops_cli/services/snowflake_service.py
RENAMED
|
File without changes
|
{datasecops_cli-0.5.1 → datasecops_cli-0.5.2}/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
|