datasecops-cli 0.2.1__tar.gz → 0.2.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.2.1 → datasecops_cli-0.2.2}/CHANGELOG.md +13 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/PKG-INFO +1 -1
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/pyproject.toml +1 -1
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/setup.ps1 +48 -6
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/setup.sh +45 -6
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/download_service.py +129 -23
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/.github/workflows/publish-cli.yml +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/.gitignore +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/DEVELOPMENT.md +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/LICENSE +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/README.md +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/docs/getting-started.md +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/docs/legacy.md +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/docs/legacy_plan_of_action.md +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/docs/mcp-server.md +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/mcp-servers.json +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/__init__.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/config.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/main.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/menus/__init__.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/menus/development.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/menus/downloads.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/menus/git_operations.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/models/__init__.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/models/git_helpers.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/models/project_config.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/__init__.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/bootstrap_service.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/dbt_runner.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/git_service.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/linting_service.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/skill_service.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/snowflake_service.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/utilities/__init__.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/utilities/display.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/utilities/file_utils.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/utilities/yaml_utils.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_mcp/__init__.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_mcp/__main__.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_mcp/connection.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_mcp/server.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/tests/__init__.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/tests/test_config.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/tests/test_file_utils.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/tests/test_models.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/tests/test_version.py +0 -0
- {datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/tests/test_yaml_utils.py +0 -0
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to the DataSecOps CLI are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.2.2] - 2026-05-10
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Full SQLFluff config generation** — `download_sqlfluff_config()` now generates all sections from the native app config: core, templater, indentation, layout types, global rules, and all rule bundles (aliasing, ambiguous, capitalisation, convention, jinja, layout, references, structure)
|
|
10
|
+
- Previously only `core` and `indentation` sections were emitted, missing ~90% of the config
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Setup script connection selector** — `setup.ps1` and `setup.sh` now parse `~/.snowflake/connections.toml`, display a numbered list of connections with the default highlighted, and let the user select by number instead of typing the name manually
|
|
15
|
+
- Rule code to section name mapping (`RULE_SECTION_MAP`) for all sqlfluff rule bundles (AL01-AL09, AM01-AM08, CP01-CP05, CV01-CV12, JJ01, LT01-LT15, RF01-RF06, ST01-ST11)
|
|
16
|
+
- Value formatting helper (`_format_value`) for correct INI output of bools, lists, nulls, and empty strings
|
|
17
|
+
|
|
5
18
|
## [0.2.1] - 2026-05-10
|
|
6
19
|
|
|
7
20
|
### Fixed
|
|
@@ -48,18 +48,60 @@ Write-Host "--- Snowflake Connection ---" -ForegroundColor Green
|
|
|
48
48
|
Write-Host ""
|
|
49
49
|
|
|
50
50
|
$connectionsFile = Join-Path $env:USERPROFILE ".snowflake\connections.toml"
|
|
51
|
+
$connectionNames = @()
|
|
52
|
+
$defaultConnection = $null
|
|
53
|
+
|
|
51
54
|
if (Test-Path $connectionsFile) {
|
|
55
|
+
# Parse default connection name
|
|
56
|
+
$defaultLine = Get-Content $connectionsFile | Select-String '^default_connection_name\s*=' | Select-Object -First 1
|
|
57
|
+
if ($defaultLine) {
|
|
58
|
+
$defaultConnection = ($defaultLine -replace '.*=\s*', '').Trim().Trim('"').Trim("'")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Parse connection section names
|
|
62
|
+
$connectionNames = @(Get-Content $connectionsFile | Select-String '^\[([^\]]+)\]' | ForEach-Object { $_.Matches[0].Groups[1].Value })
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if ($connectionNames.Count -gt 0) {
|
|
52
66
|
Write-Host "Available connections:" -ForegroundColor Cyan
|
|
53
|
-
|
|
67
|
+
for ($i = 0; $i -lt $connectionNames.Count; $i++) {
|
|
68
|
+
$marker = ""
|
|
69
|
+
if ($connectionNames[$i] -eq $defaultConnection) { $marker = " (default)" }
|
|
70
|
+
Write-Host " [$($i + 1)] $($connectionNames[$i])$marker"
|
|
71
|
+
}
|
|
54
72
|
Write-Host ""
|
|
55
|
-
}
|
|
56
73
|
|
|
57
|
-
$
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
$defaultIndex = -1
|
|
75
|
+
if ($defaultConnection) {
|
|
76
|
+
$defaultIndex = [array]::IndexOf($connectionNames, $defaultConnection)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if ($defaultIndex -ge 0) {
|
|
80
|
+
$prompt = "Select connection [1-$($connectionNames.Count)] (default: $($defaultIndex + 1))"
|
|
81
|
+
} else {
|
|
82
|
+
$prompt = "Select connection [1-$($connectionNames.Count)]"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
$selection = Read-Host $prompt
|
|
86
|
+
if ([string]::IsNullOrWhiteSpace($selection) -and $defaultIndex -ge 0) {
|
|
87
|
+
$connectionName = $defaultConnection
|
|
88
|
+
} elseif ($selection -match '^\d+$' -and [int]$selection -ge 1 -and [int]$selection -le $connectionNames.Count) {
|
|
89
|
+
$connectionName = $connectionNames[[int]$selection - 1]
|
|
90
|
+
} else {
|
|
91
|
+
Write-Host "ERROR: Invalid selection" -ForegroundColor Red
|
|
92
|
+
exit 1
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
Write-Host "No connections found in $connectionsFile" -ForegroundColor Yellow
|
|
96
|
+
$connectionName = Read-Host "Enter connection name"
|
|
97
|
+
if ([string]::IsNullOrWhiteSpace($connectionName)) {
|
|
98
|
+
Write-Host "ERROR: Connection name is required" -ForegroundColor Red
|
|
99
|
+
exit 1
|
|
100
|
+
}
|
|
61
101
|
}
|
|
62
102
|
|
|
103
|
+
Write-Host "[OK] Using connection: $connectionName" -ForegroundColor Green
|
|
104
|
+
|
|
63
105
|
# --- Step 3: Enter native app database name ---
|
|
64
106
|
|
|
65
107
|
Write-Host ""
|
|
@@ -46,18 +46,57 @@ echo "--- Snowflake Connection ---"
|
|
|
46
46
|
echo ""
|
|
47
47
|
|
|
48
48
|
CONNECTIONS_FILE="$HOME/.snowflake/connections.toml"
|
|
49
|
+
CONNECTION_NAMES=()
|
|
50
|
+
DEFAULT_CONNECTION=""
|
|
51
|
+
|
|
49
52
|
if [ -f "$CONNECTIONS_FILE" ]; then
|
|
53
|
+
# Parse default connection name
|
|
54
|
+
DEFAULT_CONNECTION=$(grep '^default_connection_name' "$CONNECTIONS_FILE" | sed 's/.*=\s*//;s/[" ]//g' | head -1)
|
|
55
|
+
|
|
56
|
+
# Parse connection section names
|
|
57
|
+
while IFS= read -r name; do
|
|
58
|
+
CONNECTION_NAMES+=("$name")
|
|
59
|
+
done < <(grep '^\[' "$CONNECTIONS_FILE" | sed 's/\[//;s/\]//')
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if [ ${#CONNECTION_NAMES[@]} -gt 0 ]; then
|
|
50
63
|
echo "Available connections:"
|
|
51
|
-
|
|
64
|
+
DEFAULT_INDEX=-1
|
|
65
|
+
for i in "${!CONNECTION_NAMES[@]}"; do
|
|
66
|
+
marker=""
|
|
67
|
+
if [ "${CONNECTION_NAMES[$i]}" = "$DEFAULT_CONNECTION" ]; then
|
|
68
|
+
marker=" (default)"
|
|
69
|
+
DEFAULT_INDEX=$i
|
|
70
|
+
fi
|
|
71
|
+
echo " [$((i + 1))] ${CONNECTION_NAMES[$i]}${marker}"
|
|
72
|
+
done
|
|
52
73
|
echo ""
|
|
53
|
-
fi
|
|
54
74
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
if [ $DEFAULT_INDEX -ge 0 ]; then
|
|
76
|
+
read -rp "Select connection [1-${#CONNECTION_NAMES[@]}] (default: $((DEFAULT_INDEX + 1))): " SELECTION
|
|
77
|
+
else
|
|
78
|
+
read -rp "Select connection [1-${#CONNECTION_NAMES[@]}]: " SELECTION
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
if [ -z "$SELECTION" ] && [ $DEFAULT_INDEX -ge 0 ]; then
|
|
82
|
+
CONNECTION_NAME="$DEFAULT_CONNECTION"
|
|
83
|
+
elif [[ "$SELECTION" =~ ^[0-9]+$ ]] && [ "$SELECTION" -ge 1 ] && [ "$SELECTION" -le ${#CONNECTION_NAMES[@]} ]; then
|
|
84
|
+
CONNECTION_NAME="${CONNECTION_NAMES[$((SELECTION - 1))]}"
|
|
85
|
+
else
|
|
86
|
+
echo "ERROR: Invalid selection"
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
else
|
|
90
|
+
echo "No connections found in $CONNECTIONS_FILE"
|
|
91
|
+
read -rp "Enter connection name: " CONNECTION_NAME
|
|
92
|
+
if [ -z "$CONNECTION_NAME" ]; then
|
|
93
|
+
echo "ERROR: Connection name is required"
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
59
96
|
fi
|
|
60
97
|
|
|
98
|
+
echo "[OK] Using connection: $CONNECTION_NAME"
|
|
99
|
+
|
|
61
100
|
# --- Step 3: Enter native app database name ---
|
|
62
101
|
|
|
63
102
|
echo ""
|
{datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/download_service.py
RENAMED
|
@@ -12,10 +12,83 @@ from datasecops_cli.utilities.file_utils import write_file, ensure_dir
|
|
|
12
12
|
|
|
13
13
|
class DownloadService:
|
|
14
14
|
"""Downloads configurations from the native app."""
|
|
15
|
+
|
|
16
|
+
# Maps rule codes to their sqlfluff INI section names.
|
|
17
|
+
RULE_SECTION_MAP: dict[str, str] = {
|
|
18
|
+
# Aliasing
|
|
19
|
+
"AL01": "aliasing.table", "AL02": "aliasing.column", "AL03": "aliasing.expression",
|
|
20
|
+
"AL04": "aliasing.unique.table", "AL05": "aliasing.unused", "AL06": "aliasing.length",
|
|
21
|
+
"AL07": "aliasing.forbid", "AL08": "aliasing.unique.column", "AL09": "aliasing.self_alias",
|
|
22
|
+
# Ambiguous
|
|
23
|
+
"AM01": "ambiguous.distinct", "AM02": "ambiguous.union", "AM03": "ambiguous.order_by",
|
|
24
|
+
"AM04": "ambiguous.column_count", "AM05": "ambiguous.join",
|
|
25
|
+
"AM06": "ambiguous.column_references", "AM07": "ambiguous.set_columns",
|
|
26
|
+
"AM08": "ambiguous.union_type",
|
|
27
|
+
# Capitalisation
|
|
28
|
+
"CP01": "capitalisation.keywords", "CP02": "capitalisation.identifiers",
|
|
29
|
+
"CP03": "capitalisation.functions", "CP04": "capitalisation.literals",
|
|
30
|
+
"CP05": "capitalisation.types",
|
|
31
|
+
# Convention
|
|
32
|
+
"CV01": "convention.not_equal", "CV02": "convention.coalesce",
|
|
33
|
+
"CV03": "convention.select_trailing_comma", "CV04": "convention.count_rows",
|
|
34
|
+
"CV05": "convention.is_null", "CV06": "convention.terminator",
|
|
35
|
+
"CV07": "convention.statement_brackets", "CV08": "convention.left_join",
|
|
36
|
+
"CV09": "convention.blocked_words", "CV10": "convention.quoted_literals",
|
|
37
|
+
"CV11": "convention.casting_style", "CV12": "convention.semicolon",
|
|
38
|
+
# Jinja
|
|
39
|
+
"JJ01": "jinja.padding",
|
|
40
|
+
# Layout
|
|
41
|
+
"LT01": "layout.spacing", "LT02": "layout.indent", "LT03": "layout.operators",
|
|
42
|
+
"LT04": "layout.commas", "LT05": "layout.long_lines", "LT06": "layout.functions",
|
|
43
|
+
"LT07": "layout.cte_bracket", "LT08": "layout.cte_newline",
|
|
44
|
+
"LT09": "layout.select_targets", "LT10": "layout.select_modifiers",
|
|
45
|
+
"LT11": "layout.set_operators", "LT12": "layout.end-of-file",
|
|
46
|
+
"LT13": "layout.start_of_file", "LT14": "layout.one_line", "LT15": "layout.newlines",
|
|
47
|
+
# References
|
|
48
|
+
"RF01": "references.from", "RF02": "references.qualification",
|
|
49
|
+
"RF03": "references.consistent", "RF04": "references.keywords",
|
|
50
|
+
"RF05": "references.special_chars", "RF06": "references.quoting",
|
|
51
|
+
# Structure
|
|
52
|
+
"ST01": "structure.else_null", "ST02": "structure.simple_case",
|
|
53
|
+
"ST03": "structure.unused_cte", "ST04": "structure.nested_case",
|
|
54
|
+
"ST05": "structure.subquery", "ST06": "structure.column_order",
|
|
55
|
+
"ST07": "structure.using", "ST08": "structure.distinct",
|
|
56
|
+
"ST09": "structure.join_condition_order", "ST10": "structure.cte_definition_order",
|
|
57
|
+
"ST11": "structure.test_query",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Core sub-keys that map to specific INI sections.
|
|
61
|
+
CORE_SECTION_MAP: dict[str, str] = {
|
|
62
|
+
"sqlfluff": "sqlfluff",
|
|
63
|
+
"dbt": "sqlfluff:templater:dbt",
|
|
64
|
+
"jinja": "sqlfluff:templater:jinja",
|
|
65
|
+
"templater": "sqlfluff:templater",
|
|
66
|
+
}
|
|
15
67
|
|
|
16
68
|
def __init__(self, snowflake_service: SnowflakeService, project_dir: Path):
|
|
17
69
|
self.sf = snowflake_service
|
|
18
70
|
self.project_dir = project_dir
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _format_value(val) -> str:
|
|
74
|
+
"""Format a JSON value for sqlfluff INI output."""
|
|
75
|
+
if isinstance(val, bool):
|
|
76
|
+
return str(val)
|
|
77
|
+
if isinstance(val, list):
|
|
78
|
+
if not val:
|
|
79
|
+
return "None"
|
|
80
|
+
return ", ".join(str(v) for v in val)
|
|
81
|
+
if val is None or val == "":
|
|
82
|
+
return "None"
|
|
83
|
+
return str(val)
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def _emit_section(lines: list[str], header: str, options: dict) -> None:
|
|
87
|
+
"""Append an INI section with its options to lines."""
|
|
88
|
+
lines.append(f"[{header}]")
|
|
89
|
+
for key, val in options.items():
|
|
90
|
+
lines.append(f"{key} = {DownloadService._format_value(val)}")
|
|
91
|
+
lines.append("")
|
|
19
92
|
|
|
20
93
|
def download_sqlfluff_config(self, profiles_dir: str = None) -> bool:
|
|
21
94
|
info_line("Downloading SQLFluff configuration...")
|
|
@@ -24,31 +97,64 @@ class DownloadService:
|
|
|
24
97
|
error_line("No SQLFluff configuration found in native app")
|
|
25
98
|
return False
|
|
26
99
|
|
|
27
|
-
lines
|
|
28
|
-
|
|
100
|
+
lines: list[str] = []
|
|
101
|
+
|
|
102
|
+
# --- Core sections ([sqlfluff], [sqlfluff:templater:dbt], etc.) ---
|
|
29
103
|
core = raw.get("core", {})
|
|
30
|
-
for
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
104
|
+
for key, section_header in self.CORE_SECTION_MAP.items():
|
|
105
|
+
entry = core.get(key)
|
|
106
|
+
if not isinstance(entry, dict) or not entry.get("enabled", True):
|
|
107
|
+
continue
|
|
108
|
+
opts = dict(entry.get("options", {}))
|
|
109
|
+
if key == "dbt":
|
|
110
|
+
# Override profiles_dir / project_dir with local values
|
|
111
|
+
opts["project_dir"] = "."
|
|
112
|
+
if profiles_dir:
|
|
113
|
+
opts["profiles_dir"] = profiles_dir
|
|
114
|
+
elif "profiles_dir" in opts:
|
|
115
|
+
del opts["profiles_dir"]
|
|
116
|
+
if opts:
|
|
117
|
+
self._emit_section(lines, section_header, opts)
|
|
118
|
+
|
|
119
|
+
# --- Indentation ---
|
|
44
120
|
indentation = raw.get("indentation", {})
|
|
45
|
-
for
|
|
46
|
-
if isinstance(
|
|
47
|
-
opts =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
121
|
+
for _code, entry in indentation.items():
|
|
122
|
+
if isinstance(entry, dict) and entry.get("enabled", True):
|
|
123
|
+
opts = entry.get("options", {})
|
|
124
|
+
if opts:
|
|
125
|
+
self._emit_section(lines, "sqlfluff:indentation", opts)
|
|
126
|
+
|
|
127
|
+
# --- Layout sections ([sqlfluff:layout:type:<code>]) ---
|
|
128
|
+
layout = raw.get("layout", {})
|
|
129
|
+
for code, entry in layout.items():
|
|
130
|
+
if not isinstance(entry, dict) or not entry.get("enabled", True):
|
|
131
|
+
continue
|
|
132
|
+
opts = entry.get("options", {})
|
|
133
|
+
if opts:
|
|
134
|
+
self._emit_section(lines, f"sqlfluff:layout:type:{code}", opts)
|
|
135
|
+
|
|
136
|
+
# --- Global rules ([sqlfluff:rules]) ---
|
|
137
|
+
rules = raw.get("rules", {})
|
|
138
|
+
for _code, entry in rules.items():
|
|
139
|
+
if isinstance(entry, dict) and entry.get("enabled", True):
|
|
140
|
+
opts = entry.get("options", {})
|
|
141
|
+
if opts:
|
|
142
|
+
self._emit_section(lines, "sqlfluff:rules", opts)
|
|
143
|
+
|
|
144
|
+
# --- Rule bundle sections (rules_aliasing, rules_capitalisation, etc.) ---
|
|
145
|
+
for section_key, entries in raw.items():
|
|
146
|
+
if not section_key.startswith("rules_") or not isinstance(entries, dict):
|
|
147
|
+
continue
|
|
148
|
+
for code, entry in entries.items():
|
|
149
|
+
if not isinstance(entry, dict) or not entry.get("enabled", True):
|
|
150
|
+
continue
|
|
151
|
+
section_name = self.RULE_SECTION_MAP.get(code)
|
|
152
|
+
if not section_name:
|
|
153
|
+
continue
|
|
154
|
+
opts = entry.get("options", {})
|
|
155
|
+
self._emit_section(lines, f"sqlfluff:rules:{section_name}", opts)
|
|
156
|
+
|
|
157
|
+
content = "\n".join(lines)
|
|
52
158
|
dest = self.project_dir / ".sqlfluff"
|
|
53
159
|
write_file(dest, content)
|
|
54
160
|
success_line(f"SQLFluff config written to {dest}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/bootstrap_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/linting_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{datasecops_cli-0.2.1 → datasecops_cli-0.2.2}/src/datasecops_cli/services/snowflake_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|