From b4b35586aefc2af83a30b02d8031f916ee2348a1 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 13:38:02 +0000 Subject: [PATCH 1/3] CORE-742: switch Snowflake CI auth from password to private key The 'snowflake' target in profiles.yml.j2 now uses key-pair authentication (private_key, with optional private_key_passphrase) instead of password. generate_profiles.py's _yaml_inline filter is updated to safely render multi-line strings (e.g. PEM private keys) as a double-quoted YAML scalar with escaped newlines, so the rendered profiles.yml stays parseable inline. The CI_WAREHOUSE_SECRETS GitHub Actions secret needs a coordinated update: - remove key 'snowflake_password' - add key 'snowflake_private_key' (PEM or base64-encoded DER) - optional 'snowflake_private_key_passphrase' for encrypted keys The CI service user's RSA_PUBLIC_KEY must also be configured in Snowflake. Co-Authored-By: Itamar Hartstein --- tests/profiles/generate_profiles.py | 4 ++++ tests/profiles/profiles.yml.j2 | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/profiles/generate_profiles.py b/tests/profiles/generate_profiles.py index c24901865..dc105df5a 100644 --- a/tests/profiles/generate_profiles.py +++ b/tests/profiles/generate_profiles.py @@ -32,6 +32,8 @@ def _yaml_inline(value: Any) -> str: """Render *value* for inline YAML. * Dicts (e.g. bigquery keyfile) → compact ``{key: val, …}`` + * Multi-line strings (e.g. PEM private keys) → double-quoted scalar with + escaped newlines so the rendered profiles.yml stays parseable inline. * Undefined (docker-only, no secrets) → empty string ``''`` * Everything else → pass through as-is """ @@ -39,6 +41,8 @@ def _yaml_inline(value: Any) -> str: return "''" if isinstance(value, dict): return yaml.dump(value, default_flow_style=True).strip() + if isinstance(value, str) and "\n" in value: + return yaml.dump(value, default_flow_style=True, default_style='"').strip() return value diff --git a/tests/profiles/profiles.yml.j2 b/tests/profiles/profiles.yml.j2 index 32167402f..4bb0b4609 100644 --- a/tests/profiles/profiles.yml.j2 +++ b/tests/profiles/profiles.yml.j2 @@ -104,7 +104,10 @@ elementary_tests: type: snowflake account: {{ snowflake_account | toyaml }} user: {{ snowflake_user | toyaml }} - password: {{ snowflake_password | toyaml }} + private_key: {{ snowflake_private_key | toyaml }} + {%- if snowflake_private_key_passphrase is defined and snowflake_private_key_passphrase %} + private_key_passphrase: {{ snowflake_private_key_passphrase | toyaml }} + {%- endif %} role: {{ snowflake_role | toyaml }} database: {{ snowflake_database | toyaml }} warehouse: {{ snowflake_warehouse | toyaml }} From 33d70b435211e487c9c91a23758b7aced3cf60dc Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 13:47:03 +0000 Subject: [PATCH 2/3] CORE-742: always quote string values in _yaml_inline (CodeRabbit feedback) Quoting only multi-line strings still allowed single-line string secrets like 'true', 'null', or '123' to be emitted unquoted and mis-parsed as booleans/None/integers when the rendered profiles.yml is loaded by dbt. Always quote string values for safety; non-string values (Undefined, dict, etc.) keep their previous behavior. Co-Authored-By: Itamar Hartstein --- tests/profiles/generate_profiles.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/profiles/generate_profiles.py b/tests/profiles/generate_profiles.py index dc105df5a..0f86351ad 100644 --- a/tests/profiles/generate_profiles.py +++ b/tests/profiles/generate_profiles.py @@ -32,8 +32,9 @@ def _yaml_inline(value: Any) -> str: """Render *value* for inline YAML. * Dicts (e.g. bigquery keyfile) → compact ``{key: val, …}`` - * Multi-line strings (e.g. PEM private keys) → double-quoted scalar with - escaped newlines so the rendered profiles.yml stays parseable inline. + * Strings (incl. multi-line PEM private keys) → double-quoted scalar with + escaped newlines so the rendered profiles.yml stays parseable inline + and values like ``"true"``/``"null"``/``"123"`` aren't type-coerced. * Undefined (docker-only, no secrets) → empty string ``''`` * Everything else → pass through as-is """ @@ -41,7 +42,7 @@ def _yaml_inline(value: Any) -> str: return "''" if isinstance(value, dict): return yaml.dump(value, default_flow_style=True).strip() - if isinstance(value, str) and "\n" in value: + if isinstance(value, str): return yaml.dump(value, default_flow_style=True, default_style='"').strip() return value From 88f87157d26e65ab049c37a60f6136f6d5b94c3a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 14:07:58 +0000 Subject: [PATCH 3/3] CORE-742: only quote multi-line strings in _yaml_inline The previous change to always quote string values broke YAML type coercion for fields like redshift_port that come in as JSON strings but need to be rendered as bare YAML scalars so the dbt adapter parses them as integers. Revert to the original behavior: only multi-line strings (e.g. PEM private keys) are double-quoted with escaped newlines; single-line strings pass through unchanged so port/host/etc. continue to type-coerce correctly. Co-Authored-By: Itamar Hartstein --- tests/profiles/generate_profiles.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/profiles/generate_profiles.py b/tests/profiles/generate_profiles.py index 0f86351ad..1ea3115c5 100644 --- a/tests/profiles/generate_profiles.py +++ b/tests/profiles/generate_profiles.py @@ -32,17 +32,17 @@ def _yaml_inline(value: Any) -> str: """Render *value* for inline YAML. * Dicts (e.g. bigquery keyfile) → compact ``{key: val, …}`` - * Strings (incl. multi-line PEM private keys) → double-quoted scalar with - escaped newlines so the rendered profiles.yml stays parseable inline - and values like ``"true"``/``"null"``/``"123"`` aren't type-coerced. + * Multi-line strings (e.g. PEM private keys) → double-quoted scalar with + escaped newlines so the rendered profiles.yml stays parseable inline. * Undefined (docker-only, no secrets) → empty string ``''`` - * Everything else → pass through as-is + * Everything else (incl. single-line strings) → pass through as-is so + adapter-side type coercion still works (e.g. ``port: 5439`` stays an int). """ if isinstance(value, Undefined): return "''" if isinstance(value, dict): return yaml.dump(value, default_flow_style=True).strip() - if isinstance(value, str): + if isinstance(value, str) and "\n" in value: return yaml.dump(value, default_flow_style=True, default_style='"').strip() return value