datasecops-cli 0.4.9__tar.gz → 0.5.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.
- datasecops_cli-0.5.1/.github/workflows/test.yml +28 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/CHANGELOG.md +28 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/PKG-INFO +3 -3
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/README.md +2 -2
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/docs/getting-started.md +3 -3
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/docs/git-operations.md +7 -4
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/docs/mcp-server.md +48 -18
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/mcp-servers.json +1 -1
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/pyproject.toml +1 -1
- datasecops_cli-0.5.1/src/datasecops_cli/__init__.py +1 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/config.py +7 -1
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/main.py +21 -5
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/configuration.py +15 -4
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/downloads.py +19 -6
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/git_operations.py +14 -7
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/models/project_config.py +15 -2
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_mcp/connection.py +11 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_mcp/server.py +38 -5
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/tests/test_models.py +1 -2
- datasecops_cli-0.4.9/src/datasecops_cli/__init__.py +0 -1
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/.github/workflows/auto-tag.yml +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/.github/workflows/publish-cli.yml +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/.gitignore +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/DEVELOPMENT.md +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/LICENSE +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/docs/legacy.md +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/docs/legacy_plan_of_action.md +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/setup.ps1 +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/setup.sh +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/__init__.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/menus/development.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/models/__init__.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/models/git_helpers.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/__init__.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/bootstrap_service.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/dbt_project_generator.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/dbt_runner.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/directory_scaffolder.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/download_service.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/git_service.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/linting_service.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/skill_service.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/snowflake_service.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/upstream_service.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/utilities/__init__.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/utilities/display.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/utilities/file_utils.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/utilities/yaml_utils.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_mcp/__init__.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_mcp/__main__.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/tests/__init__.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/tests/test_config.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/tests/test_file_utils.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/tests/test_main.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/tests/test_version.py +0 -0
- {datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/tests/test_yaml_utils.py +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ['3.11', '3.12', '3.13']
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout repository
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install package with test dependencies
|
|
25
|
+
run: pip install -e ".[test]"
|
|
26
|
+
|
|
27
|
+
- name: Run tests
|
|
28
|
+
run: pytest --tb=short
|
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the DataSecOps CLI are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.5.1] - 2026-06-04
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **MCP server CLI arguments** — `datasecops-mcp` now accepts `--connection-name` and `--app-database` arguments, eliminating the dependency on `.datasecops.yml` being in the working directory. This fixes connection failures when AI tools (Cortex Code, Cursor, etc.) launch the MCP server from a different working directory.
|
|
10
|
+
- **`init_snowflake_service()` function** — new public API in `datasecops_mcp.connection` to pre-initialize the Snowflake service singleton with explicit parameters.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **MCP registration passes connection details** — all CLI paths that register the MCP server (setup, configuration menu, downloads menu) now pass `--connection-name` and `--app-database` to the registration command and `mcp.json` args.
|
|
15
|
+
- **`DownloadsMenu` accepts `datasecops_config`** — the downloads menu now receives the `DatasecopsConfig` so it can pass connection details during MCP server registration.
|
|
16
|
+
|
|
17
|
+
## [0.5.0] - 2026-05-21
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Work Items MCP tool** — new `get_work_items_config` tool in the MCP server returns the configured ticket system (Azure DevOps, Jira, or GitHub Issues), whether ticket numbers are required, and system-specific connection details. Used by the `new-branch` skill.
|
|
22
|
+
- **WorkItems model** — new `WorkItems` model in `project_config.py` stores ticket system configuration separately from source control.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **`ticket_number_required` moved from Source Control to Work Items** — the ticket requirement is now part of the WORK_ITEMS configuration (separate from SOURCE_CONTROL). The `get_branching_rules` MCP tool no longer includes this field; use `get_work_items_config` instead.
|
|
27
|
+
- **Branch format default updated** — default branch format changed from `{branch_type}/{branch_name}` to `{branch_type}/{ticket_name}_{name}` with explicit placeholders for ticket and description.
|
|
28
|
+
- **Branch creation uses `str.replace()` instead of `str.format()`** — supports the new `{ticket_name}` and `{name}` placeholders in branch format without breaking on missing keys.
|
|
29
|
+
- **`validate_branch_name` reads ticket requirement from WORK_ITEMS** — the MCP validation tool now queries the WORK_ITEMS config for `ticket_number_required` instead of SOURCE_CONTROL.
|
|
30
|
+
- **Config class loads WORK_ITEMS** — `Config.load_from_native_app()` now also fetches the WORK_ITEMS configuration.
|
|
31
|
+
- **Git operations menu accepts work_items** — `GitOperationsMenu` now receives `WorkItems` to determine ticket enforcement during branch creation.
|
|
32
|
+
|
|
5
33
|
## [0.4.9] - 2026-05-21
|
|
6
34
|
|
|
7
35
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datasecops-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: DataSecOps Framework CLI for Snowflake Native App
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -161,7 +161,7 @@ The package includes an MCP (Model Context Protocol) server that exposes your fr
|
|
|
161
161
|
**Cortex Code:**
|
|
162
162
|
|
|
163
163
|
```bash
|
|
164
|
-
cortex mcp add datasecops-framework -- datasecops-mcp
|
|
164
|
+
cortex mcp add datasecops-framework -- datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
**VS Code / Cursor** — add to `.vscode/mcp.json` or `.cursor/mcp.json`:
|
|
@@ -171,7 +171,7 @@ cortex mcp add datasecops-framework -- datasecops-mcp
|
|
|
171
171
|
"mcpServers": {
|
|
172
172
|
"datasecops-framework": {
|
|
173
173
|
"command": "datasecops-mcp",
|
|
174
|
-
"args": []
|
|
174
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
|
@@ -142,7 +142,7 @@ The package includes an MCP (Model Context Protocol) server that exposes your fr
|
|
|
142
142
|
**Cortex Code:**
|
|
143
143
|
|
|
144
144
|
```bash
|
|
145
|
-
cortex mcp add datasecops-framework -- datasecops-mcp
|
|
145
|
+
cortex mcp add datasecops-framework -- datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
**VS Code / Cursor** — add to `.vscode/mcp.json` or `.cursor/mcp.json`:
|
|
@@ -152,7 +152,7 @@ cortex mcp add datasecops-framework -- datasecops-mcp
|
|
|
152
152
|
"mcpServers": {
|
|
153
153
|
"datasecops-framework": {
|
|
154
154
|
"command": "datasecops-mcp",
|
|
155
|
-
"args": []
|
|
155
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
}
|
|
@@ -156,7 +156,7 @@ Create `.vscode/mcp.json` in your project root:
|
|
|
156
156
|
"mcpServers": {
|
|
157
157
|
"datasecops-framework": {
|
|
158
158
|
"command": "datasecops-mcp",
|
|
159
|
-
"args": []
|
|
159
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
160
160
|
},
|
|
161
161
|
"dbt": {
|
|
162
162
|
"command": "uvx",
|
|
@@ -185,7 +185,7 @@ For Azure DevOps instead of GitHub:
|
|
|
185
185
|
"mcpServers": {
|
|
186
186
|
"datasecops-framework": {
|
|
187
187
|
"command": "datasecops-mcp",
|
|
188
|
-
"args": []
|
|
188
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
189
189
|
},
|
|
190
190
|
"dbt": {
|
|
191
191
|
"command": "uvx",
|
|
@@ -219,7 +219,7 @@ Add servers from the command line:
|
|
|
219
219
|
|
|
220
220
|
```bash
|
|
221
221
|
# DataSecOps Framework
|
|
222
|
-
cortex mcp add datasecops-framework -- datasecops-mcp
|
|
222
|
+
cortex mcp add datasecops-framework -- datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
|
|
223
223
|
|
|
224
224
|
# dbt
|
|
225
225
|
cortex mcp add dbt -- uvx dbt-mcp
|
|
@@ -50,12 +50,15 @@ Submenu for branch management operations.
|
|
|
50
50
|
|
|
51
51
|
#### New Branch
|
|
52
52
|
|
|
53
|
-
Creates a branch using the
|
|
54
|
-
- Prompts for branch type (feature, bugfix, hotfix — or configured types)
|
|
55
|
-
- Prompts for ticket number (required or optional based on config)
|
|
56
|
-
- Prompts for branch name
|
|
53
|
+
Creates a branch using the configured `branch_format` (default: `{branch_type}/{ticket_name}_{name}`):
|
|
54
|
+
- Prompts for branch type (feature, bugfix, hotfix — or configured types from Source Control settings)
|
|
55
|
+
- Prompts for ticket number (required or optional based on Work Items config `ticket_number_required`)
|
|
56
|
+
- Prompts for branch name (description)
|
|
57
|
+
- Builds the full name using `format_map` with placeholders: `{branch_type}`, `{ticket_name}`, `{name}`, `{branch_name}` (legacy)
|
|
57
58
|
- Creates from `origin/main`, checks out, and pushes with upstream tracking
|
|
58
59
|
|
|
60
|
+
> **Tip:** Use the `new-branch` Cortex Code skill instead of the CLI menu for an AI-assisted workflow that also fetches ticket details and creates a plan document.
|
|
61
|
+
|
|
59
62
|
#### Delete Branch
|
|
60
63
|
|
|
61
64
|
- Cannot delete the current branch
|
|
@@ -13,32 +13,25 @@ pip install datasecops-cli[mcp]
|
|
|
13
13
|
Run the server:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
datasecops-mcp
|
|
16
|
+
datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
The server communicates via stdio transport and is designed to be launched by an MCP client (not run interactively).
|
|
20
20
|
|
|
21
|
+
If `--connection-name` and `--app-database` are not provided, the server falls back to reading `.datasecops.yml` from the current working directory.
|
|
22
|
+
|
|
21
23
|
## Configuring Your AI Tool
|
|
22
24
|
|
|
23
25
|
### Cortex Code
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```json
|
|
28
|
-
{
|
|
29
|
-
"mcpServers": {
|
|
30
|
-
"datasecops-framework": {
|
|
31
|
-
"command": "datasecops-mcp",
|
|
32
|
-
"args": []
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
27
|
+
```bash
|
|
28
|
+
cortex mcp add datasecops-framework -- datasecops-mcp --connection-name <CONNECTION> --app-database <APP_DB>
|
|
36
29
|
```
|
|
37
30
|
|
|
38
31
|
### Claude Code
|
|
39
32
|
|
|
40
33
|
```bash
|
|
41
|
-
claude mcp add datasecops-framework datasecops-mcp
|
|
34
|
+
claude mcp add datasecops-framework datasecops-mcp -- --connection-name <CONNECTION> --app-database <APP_DB>
|
|
42
35
|
```
|
|
43
36
|
|
|
44
37
|
### Cursor / VS Code
|
|
@@ -50,7 +43,7 @@ Add to `.cursor/mcp.json` or your IDE's MCP settings:
|
|
|
50
43
|
"mcpServers": {
|
|
51
44
|
"datasecops-framework": {
|
|
52
45
|
"command": "datasecops-mcp",
|
|
53
|
-
"args": []
|
|
46
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
54
47
|
}
|
|
55
48
|
}
|
|
56
49
|
}
|
|
@@ -58,7 +51,7 @@ Add to `.cursor/mcp.json` or your IDE's MCP settings:
|
|
|
58
51
|
|
|
59
52
|
## Prerequisites
|
|
60
53
|
|
|
61
|
-
The MCP server
|
|
54
|
+
The MCP server connects to Snowflake using the connection name specified via CLI arguments (or from `.datasecops.yml`):
|
|
62
55
|
|
|
63
56
|
```yaml
|
|
64
57
|
connection_name: my_snowflake_connection
|
|
@@ -74,7 +67,8 @@ It uses your Snowflake connection from `~/.snowflake/connections.toml` to authen
|
|
|
74
67
|
|
|
75
68
|
| Tool | Description |
|
|
76
69
|
|------|-------------|
|
|
77
|
-
| `get_branching_rules` | Branch types, naming conventions,
|
|
70
|
+
| `get_branching_rules` | Branch types, naming conventions, environment branches, merge strategies |
|
|
71
|
+
| `get_work_items_config` | Work item system (Azure DevOps / Jira / GitHub Issues), ticket requirement, connection details |
|
|
78
72
|
| `get_linting_rules` | SQLFluff rules (dialect, indentation, enabled rule codes) |
|
|
79
73
|
| `get_dbt_packages` | Approved packages with pinned versions |
|
|
80
74
|
| `get_pipeline_config` | CI/CD pipeline YAML templates (GitHub Actions or Azure DevOps) |
|
|
@@ -110,6 +104,11 @@ The MCP server works transparently in the background. When you ask your AI assis
|
|
|
110
104
|
|
|
111
105
|
### Example Interactions
|
|
112
106
|
|
|
107
|
+
**Starting work on a ticket (using the `new-branch` skill):**
|
|
108
|
+
> "Start work on ticket JIRA-456"
|
|
109
|
+
|
|
110
|
+
The AI calls `get_branching_rules` for branch format and types, `get_work_items_config` for the ticket system, fetches the ticket details via the platform MCP server (Azure DevOps / Atlassian / GitHub), creates a branch like `feature/JIRA-456_add-customer-dim`, and saves the ticket details to a plan document.
|
|
111
|
+
|
|
113
112
|
**Creating a branch:**
|
|
114
113
|
> "Create a new branch for ticket JIRA-456 to add a customer dimension model"
|
|
115
114
|
|
|
@@ -166,7 +165,7 @@ For full platform integration (PR creation, CI status, issue tracking), add the
|
|
|
166
165
|
"mcpServers": {
|
|
167
166
|
"datasecops-framework": {
|
|
168
167
|
"command": "datasecops-mcp",
|
|
169
|
-
"args": []
|
|
168
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
170
169
|
},
|
|
171
170
|
"github": {
|
|
172
171
|
"command": "npx",
|
|
@@ -193,7 +192,7 @@ This enables:
|
|
|
193
192
|
"mcpServers": {
|
|
194
193
|
"datasecops-framework": {
|
|
195
194
|
"command": "datasecops-mcp",
|
|
196
|
-
"args": []
|
|
195
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
197
196
|
},
|
|
198
197
|
"azure-devops": {
|
|
199
198
|
"command": "npx",
|
|
@@ -212,8 +211,39 @@ This enables:
|
|
|
212
211
|
- Creating pull requests with templates
|
|
213
212
|
- Checking build pipeline status
|
|
214
213
|
- Linking to work items
|
|
214
|
+
- Managing and updating work items
|
|
215
215
|
- Managing boards and sprints
|
|
216
216
|
|
|
217
|
+
### Atlassian (Jira)
|
|
218
|
+
|
|
219
|
+
The Atlassian Rovo MCP Server provides access to Jira, Confluence, and Compass. Authentication is handled via OAuth 2.1 (browser flow) or API token — no local credentials needed.
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"mcpServers": {
|
|
224
|
+
"datasecops-framework": {
|
|
225
|
+
"command": "datasecops-mcp",
|
|
226
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"]
|
|
227
|
+
},
|
|
228
|
+
"atlassian": {
|
|
229
|
+
"url": "https://mcp.atlassian.com/v1/mcp/authv2"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Or via CLI:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
cortex mcp add atlassian https://mcp.atlassian.com/v1/mcp/authv2 --transport http
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
This enables:
|
|
242
|
+
- Searching, creating, and updating Jira issues
|
|
243
|
+
- Reading Confluence pages for context
|
|
244
|
+
- Querying Compass components and dependencies
|
|
245
|
+
- Linking tickets to branches and PRs
|
|
246
|
+
|
|
217
247
|
## Combined Workflow Example
|
|
218
248
|
|
|
219
249
|
With both the framework MCP server and GitHub MCP server configured, a developer can say:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"datasecops-framework": {
|
|
4
4
|
"command": "datasecops-mcp",
|
|
5
|
-
"args": [],
|
|
5
|
+
"args": ["--connection-name", "<CONNECTION>", "--app-database", "<APP_DB>"],
|
|
6
6
|
"env": {},
|
|
7
7
|
"description": "DataSecOps Framework governance rules, branching conventions, linting config, and project profiles from your Snowflake Native App"
|
|
8
8
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.1"
|
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from datasecops_cli.models.project_config import (
|
|
5
|
-
DatasecopsConfig, ProjectSettings, SourceControl, ProjectProfile, DbtTarget
|
|
5
|
+
DatasecopsConfig, ProjectSettings, SourceControl, WorkItems, ProjectProfile, DbtTarget
|
|
6
6
|
)
|
|
7
7
|
from datasecops_cli.utilities.yaml_utils import read_datasecops_config, read_dbt_project
|
|
8
8
|
from datasecops_cli.utilities.display import error_line, info_line
|
|
@@ -15,6 +15,7 @@ class Config:
|
|
|
15
15
|
self.datasecops: DatasecopsConfig = DatasecopsConfig()
|
|
16
16
|
self.project_settings: ProjectSettings = ProjectSettings()
|
|
17
17
|
self.source_control: SourceControl = SourceControl()
|
|
18
|
+
self.work_items: WorkItems = WorkItems()
|
|
18
19
|
self.profile: Optional[ProjectProfile] = None
|
|
19
20
|
self.all_profiles: list[ProjectProfile] = []
|
|
20
21
|
self.project_dir: Path = Path.cwd()
|
|
@@ -77,6 +78,11 @@ class Config:
|
|
|
77
78
|
if raw:
|
|
78
79
|
self.source_control = SourceControl(**{k: v for k, v in raw.items() if k in SourceControl.model_fields})
|
|
79
80
|
|
|
81
|
+
# Load work items
|
|
82
|
+
raw = snowflake_service.get_framework_config("WORK_ITEMS")
|
|
83
|
+
if raw:
|
|
84
|
+
self.work_items = WorkItems(**{k: v for k, v in raw.items() if k in WorkItems.model_fields})
|
|
85
|
+
|
|
80
86
|
# Load project profiles
|
|
81
87
|
profiles_data = snowflake_service.get_project_profiles()
|
|
82
88
|
if profiles_data:
|
|
@@ -175,11 +175,12 @@ def _run_setup(project_dir: Path):
|
|
|
175
175
|
# --- Connect to Snowflake ---
|
|
176
176
|
info_line("")
|
|
177
177
|
info_line("Connecting to Snowflake...")
|
|
178
|
-
from datasecops_cli.models.project_config import DatasecopsConfig, ProjectSettings, ProjectProfile, SourceControl
|
|
178
|
+
from datasecops_cli.models.project_config import DatasecopsConfig, ProjectSettings, ProjectProfile, SourceControl, WorkItems
|
|
179
179
|
temp_config = DatasecopsConfig(connection_name=connection_name, app_database=app_database)
|
|
180
180
|
temp_sf = SnowflakeService(temp_config)
|
|
181
181
|
project_settings = ProjectSettings()
|
|
182
182
|
source_control = SourceControl()
|
|
183
|
+
work_items = WorkItems()
|
|
183
184
|
matched_profile = None
|
|
184
185
|
|
|
185
186
|
try:
|
|
@@ -196,6 +197,11 @@ def _run_setup(project_dir: Path):
|
|
|
196
197
|
if raw_sc:
|
|
197
198
|
source_control = SourceControl(**{k: v for k, v in raw_sc.items() if k in SourceControl.model_fields})
|
|
198
199
|
|
|
200
|
+
# Load WORK_ITEMS settings (for ticket system and ticket_number_required)
|
|
201
|
+
raw_wi = temp_sf.get_framework_config("WORK_ITEMS")
|
|
202
|
+
if raw_wi:
|
|
203
|
+
work_items = WorkItems(**{k: v for k, v in raw_wi.items() if k in WorkItems.model_fields})
|
|
204
|
+
|
|
199
205
|
# Get account identifier for MCP URL construction
|
|
200
206
|
account_identifier = ""
|
|
201
207
|
try:
|
|
@@ -311,11 +317,16 @@ def _run_setup(project_dir: Path):
|
|
|
311
317
|
info_line("")
|
|
312
318
|
info_line("Adding MCP servers to Cortex Code...")
|
|
313
319
|
try:
|
|
314
|
-
subprocess.run(["cortex", "mcp", "add", "datasecops-framework", "--",
|
|
320
|
+
subprocess.run(["cortex", "mcp", "add", "datasecops-framework", "--",
|
|
321
|
+
"datasecops-mcp", "--connection-name", connection_name,
|
|
322
|
+
"--app-database", app_database],
|
|
315
323
|
capture_output=True, text=True)
|
|
316
324
|
success_line("Added datasecops-framework MCP server")
|
|
317
325
|
except FileNotFoundError:
|
|
318
|
-
warning_line(
|
|
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
|
+
)
|
|
319
330
|
|
|
320
331
|
# Add AI Agent MCP server if enabled
|
|
321
332
|
if ai_agent_mcp_url:
|
|
@@ -366,7 +377,10 @@ def _run_setup(project_dir: Path):
|
|
|
366
377
|
mcp_dir = dir_map.get(tool_choice, ".vscode")
|
|
367
378
|
mcp_path = project_dir / mcp_dir / "mcp.json"
|
|
368
379
|
|
|
369
|
-
servers = {"datasecops-framework": {
|
|
380
|
+
servers = {"datasecops-framework": {
|
|
381
|
+
"command": "datasecops-mcp",
|
|
382
|
+
"args": ["--connection-name", connection_name, "--app-database", app_database],
|
|
383
|
+
}}
|
|
370
384
|
|
|
371
385
|
# Add AI Agent MCP server if enabled
|
|
372
386
|
if ai_agent_mcp_url:
|
|
@@ -683,7 +697,8 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
|
|
|
683
697
|
elif option == 2 and git_service:
|
|
684
698
|
git_menu = GitOperationsMenu(
|
|
685
699
|
git_service, config.source_control,
|
|
686
|
-
config.get_deployment_branches(), profile_name
|
|
700
|
+
config.get_deployment_branches(), profile_name,
|
|
701
|
+
work_items=config.work_items
|
|
687
702
|
)
|
|
688
703
|
git_menu.show()
|
|
689
704
|
|
|
@@ -741,6 +756,7 @@ def _main_menu(config: Config, dbt_runner: DbtRunner, git_service: GitService,
|
|
|
741
756
|
profile=config.profile,
|
|
742
757
|
source_control=config.source_control,
|
|
743
758
|
all_profiles=config.all_profiles,
|
|
759
|
+
datasecops_config=config.datasecops,
|
|
744
760
|
)
|
|
745
761
|
dl_menu.show()
|
|
746
762
|
|
|
@@ -167,13 +167,19 @@ class ConfigurationMenu:
|
|
|
167
167
|
info_line("")
|
|
168
168
|
for server in servers:
|
|
169
169
|
if server == "datasecops-framework":
|
|
170
|
+
conn = self.datasecops_config.connection_name
|
|
171
|
+
app_db = self.datasecops_config.app_database
|
|
170
172
|
try:
|
|
171
|
-
subprocess.run(["cortex", "mcp", "add", "datasecops-framework",
|
|
172
|
-
"
|
|
173
|
+
subprocess.run(["cortex", "mcp", "add", "datasecops-framework", "--",
|
|
174
|
+
"datasecops-mcp", "--connection-name", conn,
|
|
175
|
+
"--app-database", app_db],
|
|
173
176
|
capture_output=True, text=True)
|
|
174
177
|
success_line("Added datasecops-framework MCP server")
|
|
175
178
|
except FileNotFoundError:
|
|
176
|
-
warning_line(
|
|
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
|
+
)
|
|
177
183
|
|
|
178
184
|
elif server == "GitHub":
|
|
179
185
|
token = get_input_string("Enter your GitHub PAT (or press Enter to skip): ", allow_empty=True)
|
|
@@ -228,7 +234,12 @@ class ConfigurationMenu:
|
|
|
228
234
|
|
|
229
235
|
for server in servers:
|
|
230
236
|
if server == "datasecops-framework":
|
|
231
|
-
|
|
237
|
+
conn = self.datasecops_config.connection_name
|
|
238
|
+
app_db = self.datasecops_config.app_database
|
|
239
|
+
server_config["datasecops-framework"] = {
|
|
240
|
+
"command": "datasecops-mcp",
|
|
241
|
+
"args": ["--connection-name", conn, "--app-database", app_db],
|
|
242
|
+
}
|
|
232
243
|
elif server == "GitHub":
|
|
233
244
|
server_config["github"] = {
|
|
234
245
|
"command": "npx",
|
|
@@ -3,7 +3,7 @@ import json
|
|
|
3
3
|
import shutil
|
|
4
4
|
import subprocess
|
|
5
5
|
|
|
6
|
-
from datasecops_cli.models.project_config import ProjectProfile, ProjectSettings, SourceControl
|
|
6
|
+
from datasecops_cli.models.project_config import DatasecopsConfig, ProjectProfile, ProjectSettings, SourceControl
|
|
7
7
|
from datasecops_cli.services.download_service import DownloadService
|
|
8
8
|
from datasecops_cli.services.skill_service import SkillService
|
|
9
9
|
from datasecops_cli.services.dbt_runner import DbtRunner
|
|
@@ -19,7 +19,8 @@ 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, all_profiles: list[ProjectProfile] = None
|
|
22
|
+
source_control: SourceControl = None, all_profiles: list[ProjectProfile] = None,
|
|
23
|
+
datasecops_config: DatasecopsConfig = None):
|
|
23
24
|
self.downloads = download_service
|
|
24
25
|
self.skills = skill_service
|
|
25
26
|
self.dbt = dbt_runner
|
|
@@ -29,6 +30,7 @@ class DownloadsMenu:
|
|
|
29
30
|
self.profile = profile
|
|
30
31
|
self.source_control = source_control
|
|
31
32
|
self.all_profiles = all_profiles
|
|
33
|
+
self.datasecops_config = datasecops_config or DatasecopsConfig()
|
|
32
34
|
|
|
33
35
|
def show(self) -> None:
|
|
34
36
|
self._menu()
|
|
@@ -149,13 +151,19 @@ class DownloadsMenu:
|
|
|
149
151
|
info_line("")
|
|
150
152
|
for server in servers:
|
|
151
153
|
if server == "datasecops-framework":
|
|
154
|
+
conn = self.datasecops_config.connection_name
|
|
155
|
+
app_db = self.datasecops_config.app_database
|
|
152
156
|
try:
|
|
153
|
-
subprocess.run(["cortex", "mcp", "add", "datasecops-framework",
|
|
154
|
-
"
|
|
157
|
+
subprocess.run(["cortex", "mcp", "add", "datasecops-framework", "--",
|
|
158
|
+
"datasecops-mcp", "--connection-name", conn,
|
|
159
|
+
"--app-database", app_db],
|
|
155
160
|
capture_output=True, text=True)
|
|
156
161
|
success_line("Added datasecops-framework MCP server")
|
|
157
162
|
except FileNotFoundError:
|
|
158
|
-
warning_line(
|
|
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
|
+
)
|
|
159
167
|
|
|
160
168
|
elif server == "GitHub":
|
|
161
169
|
token = get_input_string("Enter your GitHub PAT (or press Enter to skip): ", allow_empty=True)
|
|
@@ -210,7 +218,12 @@ class DownloadsMenu:
|
|
|
210
218
|
|
|
211
219
|
for server in servers:
|
|
212
220
|
if server == "datasecops-framework":
|
|
213
|
-
|
|
221
|
+
conn = self.datasecops_config.connection_name
|
|
222
|
+
app_db = self.datasecops_config.app_database
|
|
223
|
+
server_config["datasecops-framework"] = {
|
|
224
|
+
"command": "datasecops-mcp",
|
|
225
|
+
"args": ["--connection-name", conn, "--app-database", app_db],
|
|
226
|
+
}
|
|
214
227
|
elif server == "GitHub":
|
|
215
228
|
server_config["github"] = {
|
|
216
229
|
"command": "npx",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
|
|
1
3
|
from datasecops_cli.services.git_service import GitService
|
|
2
|
-
from datasecops_cli.models.project_config import SourceControl
|
|
4
|
+
from datasecops_cli.models.project_config import SourceControl, WorkItems
|
|
3
5
|
from datasecops_cli.utilities.display import (
|
|
4
6
|
clear, section_header, display_action_header, menu_option,
|
|
5
7
|
get_input_number, get_input_string, get_input_true_false,
|
|
@@ -10,9 +12,11 @@ from datasecops_cli.utilities.display import (
|
|
|
10
12
|
|
|
11
13
|
class GitOperationsMenu:
|
|
12
14
|
def __init__(self, git_service: GitService, source_control: SourceControl,
|
|
13
|
-
deployment_branches: dict[str, str], profile_name: str
|
|
15
|
+
deployment_branches: dict[str, str], profile_name: str,
|
|
16
|
+
work_items: WorkItems = None):
|
|
14
17
|
self.git = git_service
|
|
15
18
|
self.source_control = source_control
|
|
19
|
+
self.work_items = work_items or WorkItems()
|
|
16
20
|
self.deployment_branches = deployment_branches
|
|
17
21
|
self.profile_name = profile_name
|
|
18
22
|
|
|
@@ -106,7 +110,7 @@ class GitOperationsMenu:
|
|
|
106
110
|
return
|
|
107
111
|
|
|
108
112
|
ticket = ""
|
|
109
|
-
if self.
|
|
113
|
+
if self.work_items.ticket_number_required:
|
|
110
114
|
ticket = get_input_string("Enter ticket number: ")
|
|
111
115
|
if ticket == "0":
|
|
112
116
|
return
|
|
@@ -119,10 +123,13 @@ class GitOperationsMenu:
|
|
|
119
123
|
|
|
120
124
|
branch_name = name.replace(" ", "-").lower()
|
|
121
125
|
fmt = self.source_control.branch_format
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
ctx = {
|
|
127
|
+
"branch_type": selected_type,
|
|
128
|
+
"ticket_name": ticket,
|
|
129
|
+
"name": branch_name,
|
|
130
|
+
"branch_name": f"{ticket}_{branch_name}" if ticket else branch_name,
|
|
131
|
+
}
|
|
132
|
+
full_name = fmt.format_map(defaultdict(str, ctx))
|
|
126
133
|
|
|
127
134
|
self.git.create_branch(full_name)
|
|
128
135
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from pydantic import BaseModel, Field
|
|
2
2
|
from typing import Optional
|
|
3
3
|
|
|
4
|
+
# Centralized default branch format used across the CLI and MCP server
|
|
5
|
+
DEFAULT_BRANCH_FORMAT = "{branch_type}/{ticket_name}_{name}"
|
|
6
|
+
|
|
4
7
|
class DatasecopsConfig(BaseModel):
|
|
5
8
|
"""Local .datasecops.yml config."""
|
|
6
9
|
connection_name: str = ""
|
|
@@ -60,11 +63,21 @@ class EnvironmentBranch(BaseModel):
|
|
|
60
63
|
|
|
61
64
|
class SourceControl(BaseModel):
|
|
62
65
|
branch_types: list[BranchType] = Field(default_factory=list)
|
|
63
|
-
|
|
64
|
-
branch_format: str = "{branch_type}/{branch_name}"
|
|
66
|
+
branch_format: str = DEFAULT_BRANCH_FORMAT
|
|
65
67
|
source_control_platform: str = "GitHub"
|
|
66
68
|
environments: list[EnvironmentBranch] = Field(default_factory=list)
|
|
67
69
|
|
|
70
|
+
class WorkItems(BaseModel):
|
|
71
|
+
system: str = "" # "azure_devops", "jira", "github_issues", or ""
|
|
72
|
+
ticket_number_required: bool = False
|
|
73
|
+
azure_devops_org_url: str = ""
|
|
74
|
+
azure_devops_project: str = ""
|
|
75
|
+
azure_devops_auth_method: str = "pat"
|
|
76
|
+
jira_project_key: str = ""
|
|
77
|
+
github_owner: str = ""
|
|
78
|
+
github_repo: str = ""
|
|
79
|
+
github_auth_method: str = "pat"
|
|
80
|
+
|
|
68
81
|
class ProjectProfile(BaseModel):
|
|
69
82
|
project_id: int = 0
|
|
70
83
|
profile_name: str = ""
|
|
@@ -141,6 +141,17 @@ def load_datasecops_config(project_dir: Path = None) -> dict:
|
|
|
141
141
|
_service: Optional[MCPSnowflakeService] = None
|
|
142
142
|
|
|
143
143
|
|
|
144
|
+
def init_snowflake_service(connection_name: str, app_database: str) -> MCPSnowflakeService:
|
|
145
|
+
"""Pre-initialize the Snowflake service with explicit parameters.
|
|
146
|
+
|
|
147
|
+
Use this when connection_name and app_database are provided via CLI arguments,
|
|
148
|
+
bypassing the need for .datasecops.yml in the working directory.
|
|
149
|
+
"""
|
|
150
|
+
global _service
|
|
151
|
+
_service = MCPSnowflakeService(connection_name, app_database)
|
|
152
|
+
return _service
|
|
153
|
+
|
|
154
|
+
|
|
144
155
|
def get_snowflake_service(project_dir: Path = None) -> MCPSnowflakeService:
|
|
145
156
|
"""Get or create the Snowflake service singleton."""
|
|
146
157
|
global _service
|
|
@@ -11,6 +11,7 @@ import logging
|
|
|
11
11
|
from mcp.server.fastmcp import FastMCP
|
|
12
12
|
|
|
13
13
|
from datasecops_mcp.connection import get_snowflake_service
|
|
14
|
+
from datasecops_cli.models.project_config import DEFAULT_BRANCH_FORMAT
|
|
14
15
|
|
|
15
16
|
logger = logging.getLogger(__name__)
|
|
16
17
|
|
|
@@ -25,7 +26,7 @@ def get_branching_rules() -> str:
|
|
|
25
26
|
"""Get the source control branching rules enforced by the framework.
|
|
26
27
|
|
|
27
28
|
Returns branch types (feature, hotfix, etc.), naming conventions,
|
|
28
|
-
environment branches, merge strategies
|
|
29
|
+
environment branches, and merge strategies.
|
|
29
30
|
Use this to understand how branches should be created and named.
|
|
30
31
|
"""
|
|
31
32
|
sf = get_snowflake_service()
|
|
@@ -35,14 +36,30 @@ def get_branching_rules() -> str:
|
|
|
35
36
|
|
|
36
37
|
result = {
|
|
37
38
|
"branch_types": raw.get("branch_types", []),
|
|
38
|
-
"
|
|
39
|
-
"branch_format": raw.get("branch_format", "{branch_type}/{branch_name}"),
|
|
39
|
+
"branch_format": raw.get("branch_format", DEFAULT_BRANCH_FORMAT),
|
|
40
40
|
"source_control_platform": raw.get("source_control_platform", "GitHub"),
|
|
41
41
|
"environments": raw.get("environments", []),
|
|
42
42
|
}
|
|
43
43
|
return json.dumps(result, indent=2)
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
@mcp.tool()
|
|
47
|
+
def get_work_items_config() -> str:
|
|
48
|
+
"""Get the work item tracking configuration from the framework.
|
|
49
|
+
|
|
50
|
+
Returns which ticket system is configured (Azure DevOps, Jira, or GitHub Issues),
|
|
51
|
+
whether ticket numbers are required in branch names, and the system-specific
|
|
52
|
+
connection details (org URL, project, etc.).
|
|
53
|
+
Auth tokens are managed per-user via the CLI — not stored here.
|
|
54
|
+
"""
|
|
55
|
+
sf = get_snowflake_service()
|
|
56
|
+
raw = sf.get_framework_config("WORK_ITEMS")
|
|
57
|
+
if not raw:
|
|
58
|
+
return json.dumps({"system": "", "ticket_number_required": False})
|
|
59
|
+
|
|
60
|
+
return json.dumps(raw, indent=2)
|
|
61
|
+
|
|
62
|
+
|
|
46
63
|
@mcp.tool()
|
|
47
64
|
def get_linting_rules() -> str:
|
|
48
65
|
"""Get the SQLFluff linting rules configured for this project.
|
|
@@ -313,8 +330,11 @@ def validate_branch_name(branch_name: str) -> str:
|
|
|
313
330
|
return "No source control config found - cannot validate"
|
|
314
331
|
|
|
315
332
|
branch_types = [bt.get("name", "") for bt in raw.get("branch_types", [])]
|
|
316
|
-
|
|
317
|
-
|
|
333
|
+
branch_format = raw.get("branch_format", DEFAULT_BRANCH_FORMAT)
|
|
334
|
+
|
|
335
|
+
# Check WORK_ITEMS for ticket requirement
|
|
336
|
+
work_items = sf.get_framework_config("WORK_ITEMS") or {}
|
|
337
|
+
ticket_required = work_items.get("ticket_number_required", False)
|
|
318
338
|
|
|
319
339
|
parts = branch_name.split("/", 1)
|
|
320
340
|
errors = []
|
|
@@ -528,6 +548,19 @@ def _find_project_dir() -> "Path":
|
|
|
528
548
|
|
|
529
549
|
def main():
|
|
530
550
|
"""Run the MCP server via stdio transport."""
|
|
551
|
+
import argparse
|
|
552
|
+
|
|
553
|
+
parser = argparse.ArgumentParser(description="DataSecOps MCP Server")
|
|
554
|
+
parser.add_argument("--connection-name", help="Snowflake connection name (overrides .datasecops.yml)")
|
|
555
|
+
parser.add_argument("--app-database", help="Native app database name (overrides .datasecops.yml)")
|
|
556
|
+
args = parser.parse_args()
|
|
557
|
+
|
|
558
|
+
if args.connection_name or args.app_database:
|
|
559
|
+
if not args.connection_name or not args.app_database:
|
|
560
|
+
parser.error("--connection-name and --app-database must both be provided")
|
|
561
|
+
from datasecops_mcp.connection import init_snowflake_service
|
|
562
|
+
init_snowflake_service(args.connection_name, args.app_database)
|
|
563
|
+
|
|
531
564
|
mcp.run(transport="stdio")
|
|
532
565
|
|
|
533
566
|
|
|
@@ -100,8 +100,7 @@ class TestSourceControl:
|
|
|
100
100
|
def test_defaults(self):
|
|
101
101
|
sc = SourceControl()
|
|
102
102
|
assert sc.branch_types == []
|
|
103
|
-
assert sc.
|
|
104
|
-
assert sc.branch_format == "{branch_type}/{branch_name}"
|
|
103
|
+
assert sc.branch_format == "{branch_type}/{ticket_name}_{name}"
|
|
105
104
|
assert sc.source_control_platform == "GitHub"
|
|
106
105
|
assert sc.environments == []
|
|
107
106
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.8"
|
|
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.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/bootstrap_service.py
RENAMED
|
File without changes
|
{datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/dbt_project_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/directory_scaffolder.py
RENAMED
|
File without changes
|
{datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/download_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/linting_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/src/datasecops_cli/services/snowflake_service.py
RENAMED
|
File without changes
|
{datasecops_cli-0.4.9 → datasecops_cli-0.5.1}/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
|