Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
e4be796
SNOW-2306184: config refactory entry point guarded by env
sfc-gh-mraba Oct 2, 2025
6df5d6c
SNOW-2306184: config refactory entry point guarded by env - fix snapshot
sfc-gh-mraba Oct 3, 2025
dbcebc3
SNOW-2306184: config refactory - core abstraction
sfc-gh-mraba Oct 3, 2025
248b286
SNOW-2306184: config refactory - top tier configuration sources
sfc-gh-mraba Oct 3, 2025
0fb6eb6
SNOW-2306184: config refactory - env vars discovery
sfc-gh-mraba Oct 6, 2025
4749dd9
SNOW-2306184: config refactory - move key mappings to module level
sfc-gh-mraba Oct 6, 2025
2a7385f
SNOW-2306184: config refactory - SnowSQL config files behaviour
sfc-gh-mraba Oct 6, 2025
64d715b
SNOW-2306184: config refactory - resolver history
sfc-gh-mraba Oct 6, 2025
3657305
SNOW-2306184: config refactory - integrate new config
sfc-gh-mraba Oct 7, 2025
a0628a2
SNOW-2306184: config refactory - helpers command integration
sfc-gh-mraba Oct 7, 2025
482750e
SNOW-2306184: config refactory - config testing setup
sfc-gh-mraba Oct 8, 2025
f633582
SNOW-2306184: config refactory - fix snowsql config format parsing
sfc-gh-mraba Oct 8, 2025
c52e141
SNOW-2306184: config refactory - drop tests for language functionality
sfc-gh-mraba Oct 8, 2025
6128802
SNOW-2306184: config refactory - drop tests for language functionalit…
sfc-gh-mraba Oct 8, 2025
5664e46
SNOW-2306184: config refactory - cleanup source config handlers naming
sfc-gh-mraba Oct 8, 2025
2c89535
SNOW-2306184: config refactory - cleanup source ConfigValue creation
sfc-gh-mraba Oct 8, 2025
cc63fd7
SNOW-2306184: config refactor - simplify abstractions
sfc-gh-mraba Oct 8, 2025
79b6c7f
SNOW-2306184: config refactor - simplified implementation
sfc-gh-mraba Oct 9, 2025
a0290b1
SNOW-2306184: config refactor - gh workflows
sfc-gh-mraba Oct 9, 2025
8242756
SNOW-2306184: config refactor - cli env update
sfc-gh-mraba Oct 9, 2025
2ab5502
SNOW-2306184: config refactor - snowsql env support
sfc-gh-mraba Oct 9, 2025
aa7853b
SNOW-2306184: config refactor - restore temp conn
sfc-gh-mraba Oct 9, 2025
1ced02d
SNOW-2306184: config refactor - tests-ng fix
sfc-gh-mraba Oct 9, 2025
1b13208
SNOW-2306184: config refactor - integrations ng
sfc-gh-mraba Oct 9, 2025
c04a7cc
SNOW-2306184: config refactor - old & new unit tests pass
sfc-gh-mraba Oct 9, 2025
005e3db
SNOW-2306184: config refactor - old & new unit tests
sfc-gh-mraba Oct 10, 2025
2ae4238
SNOW-2306184: config refactor - plugin tests in JSON format
sfc-gh-mraba Oct 10, 2025
00daf6a
SNOW-2306184: config refactor - e2e fix attempt
sfc-gh-mraba Oct 10, 2025
231f1b2
SNOW-2306184: config refactor - e2e fix attempt 3
sfc-gh-mraba Oct 10, 2025
16c6900
SNOW-2306184: config refactor - e2e fix attempt 4
sfc-gh-mraba Oct 10, 2025
8a484f3
SNOW-2306184: config refactor - e2e fix attempt 5
sfc-gh-mraba Oct 10, 2025
93cb1e2
SNOW-2306184: config refactor - e2e fix attempt 5a
sfc-gh-mraba Oct 10, 2025
2db49fb
SNOW-2306184: config refactor - e2e fix attempt 5b
sfc-gh-mraba Oct 10, 2025
18a52ba
SNOW-2306184: config refactor - config_snapshot to distingush between…
sfc-gh-mraba Oct 13, 2025
1947e9d
SNOW-2306184: config refactor - invalidate singleton between tests
sfc-gh-mraba Oct 13, 2025
f408374
SNOW-2306184: config refactor - list connections --all
sfc-gh-mraba Oct 13, 2025
79f5627
SNOW-2306184: config refactor - snapshot COLUMNS=200
sfc-gh-mraba Oct 13, 2025
58133fc
SNOW-2306184: config refactor - snapshot fix 2
sfc-gh-mraba Oct 13, 2025
e11a531
SNOW-2306184: config refactor - snapshot fix 3
sfc-gh-mraba Oct 13, 2025
9228ec5
SNOW-2306184: config refactor - show-config-sources tests
sfc-gh-mraba Oct 13, 2025
1d54c4e
SNOW-2306184: config refactor - more merging tests
sfc-gh-mraba Oct 14, 2025
3a6a298
SNOW-2306184: config refactor - connections toml merging split
sfc-gh-mraba Oct 15, 2025
ad0a8bb
SNOW-2306184: config refactor - cleanup 1
sfc-gh-mraba Oct 15, 2025
bef03fa
SNOW-2306184: config refactor - cleanup 2
sfc-gh-mraba Oct 15, 2025
5adb52a
SNOW-2306184: config refactor - cleanup 3
sfc-gh-mraba Oct 15, 2025
7edbdc7
SNOW-2306184: config refactor - cleanup 4
sfc-gh-mraba Oct 15, 2025
6c71735
SNOW-2306184: config refactor - cleanup 5
sfc-gh-mraba Oct 15, 2025
fd3d744
SNOW-2306184: config refactor - connections.toml legacy behaviour
sfc-gh-mraba Oct 15, 2025
c081f94
SNOW-2306184: config refactor - improve resolution raport
sfc-gh-mraba Oct 15, 2025
66af182
SNOW-2306184: config refactor - improve connections env parsing
sfc-gh-mraba Oct 15, 2025
d4ce1e9
SNOW-2306184: config refactor - clean tmp files
sfc-gh-mraba Oct 15, 2025
7b188a5
SNOW-2306184: config refactor - connection level overwrite for config…
sfc-gh-mraba Oct 16, 2025
d94d29a
SNOW-2306184: config refactor - variables merge
sfc-gh-mraba Oct 16, 2025
55376b3
SNOW-2306184: config refactor - separate parsing from reading and mov…
sfc-gh-mraba Oct 20, 2025
5a9b74a
SNOW-2306184: config refactor - Release Notes
sfc-gh-mraba Oct 21, 2025
62db9ab
SNOW-2306184: config refactor - telemetry
sfc-gh-mraba Oct 22, 2025
51f7f9d
SNOW-2306184: config refactor - telemetry test update
sfc-gh-mraba Oct 22, 2025
7683f22
SNOW-2306184: config refactor - allow empty connections
sfc-gh-mraba Oct 23, 2025
aee3874
SNOW-2306184: config refactor - allow empty connections test fix
sfc-gh-mraba Oct 23, 2025
f3effcc
SNOW-2306184: config refactor - after rebase fixes
sfc-gh-mraba Oct 23, 2025
daa81f4
SNOW-2306184: config refactor - after rebase fixes 2
sfc-gh-mraba Oct 23, 2025
f975034
SNOW-2306184: config refactor - remove comments
sfc-gh-mraba Oct 24, 2025
610a8da
SNOW-2306184: config refactor - value masking for ResolutionHistory
sfc-gh-mraba Nov 17, 2025
acdfb84
SNOW-2306184: config refactor - in memory pk
sfc-gh-mraba Nov 18, 2025
995f91a
SNOW-2306184: config refactor - ensure file permissions checks
sfc-gh-mraba Nov 18, 2025
b849f52
SNOW-2306184: config refactor - tests for value masking
sfc-gh-mraba Nov 18, 2025
d1cb6f5
SNOW-2306184: config refactor - oauth typo
sfc-gh-mraba Nov 18, 2025
13819cb
SNOW-2306184: config refactor - thread safe singleton
sfc-gh-mraba Nov 18, 2025
fb37a23
SNOW-2306184: config refactor - try_cast_to_bool compatibility added
sfc-gh-mraba Nov 18, 2025
3e2ea59
SNOW-2306184: config refactor - sanitize logging
sfc-gh-mraba Nov 18, 2025
e494653
SNOW-2306184: config refactor - remove tomli conditional import
sfc-gh-mraba Nov 18, 2025
236680f
SNOW-2306184: config refactor - test fix
sfc-gh-mraba Nov 18, 2025
c9bca4b
SNOW-2306184: config refactor - test fix 2
sfc-gh-mraba Nov 18, 2025
99eb8d5
SNOW-2306184: config refactor - ensure file permissions checks - plug…
sfc-gh-mraba Nov 19, 2025
e429407
SNOW-2306184: config refactor - top level conn params
sfc-gh-mraba Nov 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
- features/*

env:
TERM: unknown # Disables colors in rich
TERM: unknown # Disables colors in rich

permissions:
contents: read
Expand Down Expand Up @@ -43,3 +43,29 @@ jobs:
- name: Test with hatch
run: hatch run test-cov
- uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24

tests-config-ng:
needs: define-matrix
strategy:
fail-fast: true
matrix:
os: ${{ fromJSON(needs.define-matrix.outputs.os) }}
python-version: ${{ fromJSON(needs.define-matrix.outputs.python) }}
runs-on: ${{ matrix.os }}
env:
SNOWFLAKE_CLI_CONFIG_V2_ENABLED: 1
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install hatch
run: |
pip install -U click==8.2.1 hatch
hatch env create default
- name: Test with hatch
run: hatch run test-cov
- uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24
28 changes: 28 additions & 0 deletions .github/workflows/test_trusted.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,31 @@ jobs:
SNOWFLAKE_CONNECTIONS_INTEGRATION_DATABASE: ${{ secrets.SNOWFLAKE_DATABASE }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_PRIVATE_KEY_RAW: ${{ secrets.SNOWFLAKE_PRIVATE_KEY_RAW }}
run: python -m hatch run ${{ inputs.hatch-run }}

tests-trusted-ng:
runs-on: ${{ inputs.runs-on }}
env:
SNOWFLAKE_CLI_CONFIG_V2_ENABLED: 1
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip click==8.2.1 hatch
python -m hatch env create ${{ inputs.python-env }}
- name: Run integration tests
env:
GH_TOKEN: ${{ secrets.SNOWFLAKE_GITHUB_TOKEN }}
TERM: unknown
SNOWFLAKE_CONNECTIONS_INTEGRATION_AUTHENTICATOR: SNOWFLAKE_JWT
SNOWFLAKE_CONNECTIONS_INTEGRATION_HOST: ${{ secrets.SNOWFLAKE_HOST }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_DATABASE: ${{ secrets.SNOWFLAKE_DATABASE }}
SNOWFLAKE_CONNECTIONS_INTEGRATION_PRIVATE_KEY_RAW: ${{ secrets.SNOWFLAKE_PRIVATE_KEY_RAW }}
run: python -m hatch run ${{ inputs.hatch-run }}
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
-->
# Unreleased version
## Backward incompatibility
* **Configuration System (NG)**: File-based configuration sources (`snowsql_config`, `cli_config_toml`, `connections_toml`) now use **connection-level replacement** instead of field-level merging. When a later file source defines a connection, it completely replaces the entire connection from earlier file sources - fields are NOT inherited. Environment variables and CLI arguments continue to overlay per-field on top of the file-derived connection. This provides more predictable configuration behavior where file-defined connections are atomic units.

## Deprecations

Expand Down
51 changes: 51 additions & 0 deletions src/snowflake/cli/_app/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ class CLITelemetryField(Enum):
COMMAND_CI_ENVIRONMENT = "command_ci_environment"
# Configuration
CONFIG_FEATURE_FLAGS = "config_feature_flags"
CONFIG_PROVIDER_TYPE = "config_provider_type"
CONFIG_SOURCES_USED = "config_sources_used"
CONFIG_SOURCE_WINS = "config_source_wins"
CONFIG_TOTAL_KEYS_RESOLVED = "config_total_keys_resolved"
CONFIG_KEYS_WITH_OVERRIDES = "config_keys_with_overrides"
# Metrics
COUNTERS = "counters"
SPANS = "spans"
Expand Down Expand Up @@ -219,6 +224,51 @@ def python_version() -> str:
return f"{py_ver.major}.{py_ver.minor}.{py_ver.micro}"


def _get_config_telemetry() -> TelemetryDict:
"""Get configuration resolution telemetry data."""
try:
from snowflake.cli.api.config_provider import (
AlternativeConfigProvider,
get_config_provider_singleton,
)

provider = get_config_provider_singleton()

# Identify which config provider is being used
provider_type = (
"ng" if isinstance(provider, AlternativeConfigProvider) else "legacy"
)

result: TelemetryDict = {CLITelemetryField.CONFIG_PROVIDER_TYPE: provider_type}

# Get detailed telemetry if using ng config
if isinstance(provider, AlternativeConfigProvider):
payload = provider.resolution_summary

# Map payload keys to telemetry fields
if payload:
if "config_sources_used" in payload:
result[CLITelemetryField.CONFIG_SOURCES_USED] = payload[
"config_sources_used"
]
if "config_source_wins" in payload:
result[CLITelemetryField.CONFIG_SOURCE_WINS] = payload[
"config_source_wins"
]
if "config_total_keys_resolved" in payload:
result[CLITelemetryField.CONFIG_TOTAL_KEYS_RESOLVED] = payload[
"config_total_keys_resolved"
]
if "config_keys_with_overrides" in payload:
result[CLITelemetryField.CONFIG_KEYS_WITH_OVERRIDES] = payload[
"config_keys_with_overrides"
]

return result
except Exception:
return {}


class CLITelemetryClient:
@property
def _ctx(self) -> _CliGlobalContextAccess:
Expand All @@ -239,6 +289,7 @@ def generate_telemetry_data_dict(
k: str(v) for k, v in get_feature_flags_section().items()
},
**_find_command_info(),
**_get_config_telemetry(),
**telemetry_payload,
}
# To map Enum to string, so we don't have to use .value every time
Expand Down
39 changes: 30 additions & 9 deletions src/snowflake/cli/_plugins/connection/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
set_config_value,
unset_config_value,
)
from snowflake.cli.api.config_ng.masking import mask_sensitive_value
from snowflake.cli.api.console import cli_console
from snowflake.cli.api.constants import ObjectType
from snowflake.cli.api.output.types import (
Expand All @@ -85,25 +86,45 @@ def __repr__(self):
return "optional"


def _mask_sensitive_parameters(connection_params: dict):
if "password" in connection_params:
connection_params["password"] = "****"
if "oauth_client_secret" in connection_params:
connection_params["oauth_client_secret"] = "****"
return connection_params
def mask_sensitive_parameters(connection_params: dict):
return {
key: mask_sensitive_value(key, value)
for key, value in connection_params.items()
}


@app.command(name="list")
def list_connections(**options) -> CommandResult:
def list_connections(
all_sources: bool = typer.Option(
False,
"--all",
"-a",
help="Include connections from all sources (environment variables, SnowSQL config). "
"By default, only shows connections from configuration files.",
),
**options,
) -> CommandResult:
"""
Lists configured connections.
"""
connections = get_all_connections()
from snowflake.cli.api.config_provider import (
get_config_provider_singleton,
is_alternative_config_enabled,
)

# Use provider directly for config_ng to pass the flag
if is_alternative_config_enabled():
provider = get_config_provider_singleton()
connections = provider.get_all_connections(include_env_connections=all_sources)
else:
# Legacy provider ignores the flag
connections = get_all_connections()

default_connection = get_default_connection_name()
result = (
{
"connection_name": connection_name,
"parameters": _mask_sensitive_parameters(
"parameters": mask_sensitive_parameters(
connection_config.to_dict_of_known_non_empty_values()
),
"is_default": connection_name == default_connection,
Expand Down
10 changes: 8 additions & 2 deletions src/snowflake/cli/_plugins/dcm/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import yaml
from snowflake.cli._plugins.stage.manager import StageManager
from snowflake.cli.api.artifacts.upload import sync_artifacts_with_stage
from snowflake.cli.api.commands.utils import parse_key_value_variables
from snowflake.cli.api.console.console import cli_console
from snowflake.cli.api.constants import (
DEFAULT_SIZE_LIMIT_MB,
Expand Down Expand Up @@ -102,8 +101,15 @@ def execute(
if configuration:
query += f" CONFIGURATION {configuration}"
if variables:
from snowflake.cli.api.commands.common import Variable
from snowflake.cli.api.config_ng import get_merged_variables

# Get merged variables from SnowSQL config and CLI -D parameters
merged_vars_dict = get_merged_variables(variables)
# Convert dict to List[Variable] for compatibility with parse_execute_variables
parsed_variables = [Variable(k, v) for k, v in merged_vars_dict.items()]
query += StageManager.parse_execute_variables(
parse_key_value_variables(variables)
parsed_variables
).removeprefix(" using")
stage_path = StagePath.from_stage_str(from_stage)
query += f" FROM {stage_path.absolute_path()}"
Expand Down
87 changes: 87 additions & 0 deletions src/snowflake/cli/_plugins/helpers/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
get_all_connections,
set_config_value,
)
from snowflake.cli.api.config_provider import ALTERNATIVE_CONFIG_ENV_VAR
from snowflake.cli.api.console import cli_console
from snowflake.cli.api.output.types import (
CollectionResult,
Expand Down Expand Up @@ -317,3 +318,89 @@ def check_snowsql_env_vars(**options):

results.append(MessageResult(summary))
return MultipleResults(results)


@app.command(
name="show-config-sources",
requires_connection=False,
hidden=os.environ.get(ALTERNATIVE_CONFIG_ENV_VAR, "").lower()
not in ("1", "true", "yes", "on"),
)
def show_config_sources(
key: Optional[str] = typer.Argument(
None,
help="Specific configuration key to show resolution for (e.g., 'account', 'user'). If not provided, shows summary for all keys.",
),
show_details: bool = typer.Option(
False,
"--show-details",
"-d",
help="Show detailed resolution chains for all sources consulted.",
),
export_file: Optional[Path] = typer.Option(
None,
"--export",
"-e",
help="Export complete resolution history to JSON file for support or debugging.",
file_okay=True,
dir_okay=False,
),
**options,
) -> CommandResult:
"""
Show where configuration values come from.

This command displays the configuration resolution process, showing which
source (CLI arguments, environment variables, or config files) provided
each configuration value. Useful for debugging configuration issues.

Examples:

# Show summary of all configuration resolution
snow helpers show-config-sources

# Show detailed resolution for all keys
snow helpers show-config-sources --show-details

# Show resolution for a specific key
snow helpers show-config-sources account

# Show detailed resolution for a specific key
snow helpers show-config-sources account --show-details

# Export complete resolution history to file
snow helpers show-config-sources --export config_debug.json

Note: This command requires the enhanced configuration system to be enabled.
Set SNOWFLAKE_CLI_CONFIG_V2_ENABLED=true to enable it.
"""
from snowflake.cli.api.config_ng import (
export_resolution_history,
is_resolution_logging_available,
)
from snowflake.cli.api.config_ng.resolution_logger import (
get_configuration_explanation_results,
)

if not is_resolution_logging_available():
return MessageResult(
f"⚠️ Configuration resolution logging is not available.\n\n"
f"To enable it, set the environment variable:\n"
f" export {ALTERNATIVE_CONFIG_ENV_VAR}=true\n\n"
f"Then run this command again to see where configuration values come from."
)

# Export if requested
if export_file:
success = export_resolution_history(export_file)
if not success:
return MessageResult(
f"❌ Failed to export resolution history to {export_file}"
)
return MessageResult(
f"✅ Resolution history exported to: {export_file}\n\n"
f"This file contains complete details about configuration resolution "
f"and can be attached to support tickets."
)

return get_configuration_explanation_results(key=key, verbose=show_details)
7 changes: 3 additions & 4 deletions src/snowflake/cli/_plugins/sql/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
)
from snowflake.cli.api.commands.overrideable_parameter import OverrideableOption
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
from snowflake.cli.api.commands.utils import parse_key_value_variables
from snowflake.cli.api.exceptions import CliArgumentError
from snowflake.cli.api.output.types import (
CommandResult,
Expand Down Expand Up @@ -136,9 +135,9 @@ def execute_sql(
The command supports variable substitution that happens on client-side.
"""

data = {}
if data_override:
data = {v.key: v.value for v in parse_key_value_variables(data_override)}
from snowflake.cli.api.config_ng import get_merged_variables

data = get_merged_variables(data_override)

template_syntax_config = _parse_template_syntax_config(enabled_templating)

Expand Down
8 changes: 6 additions & 2 deletions src/snowflake/cli/_plugins/stage/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
OnErrorType,
Variable,
)
from snowflake.cli.api.commands.utils import parse_key_value_variables
from snowflake.cli.api.console import cli_console
from snowflake.cli.api.constants import PYTHON_3_12
from snowflake.cli.api.exceptions import CliError
Expand Down Expand Up @@ -608,7 +607,12 @@ def execute(
filtered_file_list, key=lambda f: (path.dirname(f), path.basename(f))
)

parsed_variables = parse_key_value_variables(variables)
from snowflake.cli.api.config_ng import get_merged_variables

# Get merged variables from SnowSQL config and CLI -D parameters
merged_vars_dict = get_merged_variables(variables)
# Convert dict back to List[Variable] for compatibility with existing methods
parsed_variables = [Variable(k, v) for k, v in merged_vars_dict.items()]
sql_variables = self.parse_execute_variables(parsed_variables)
python_variables = self._parse_python_variables(parsed_variables)
results = []
Expand Down
Loading