From b694a9272490aba31bee3d88003df4737a6bbc5c 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:01 +0000 Subject: [PATCH 1/4] 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 --- integration_tests/profiles/generate_profiles.py | 4 ++++ integration_tests/profiles/profiles.yml.j2 | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/integration_tests/profiles/generate_profiles.py b/integration_tests/profiles/generate_profiles.py index c24901865..dc105df5a 100644 --- a/integration_tests/profiles/generate_profiles.py +++ b/integration_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/integration_tests/profiles/profiles.yml.j2 b/integration_tests/profiles/profiles.yml.j2 index a081ebf8b..2d29989a5 100644 --- a/integration_tests/profiles/profiles.yml.j2 +++ b/integration_tests/profiles/profiles.yml.j2 @@ -107,7 +107,10 @@ 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 23f76d60d74c67d09b16bde03c4c26e069548437 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:41:21 +0000 Subject: [PATCH 2/4] chore: apply prettier auto-fix to README.md (unrelated CI fix) The prettier hook on master flags a missing blank line after the 'Elementary Tables' heading (pre-existing). Apply the auto-fix so that this PR's CI can pass. Co-Authored-By: Itamar Hartstein --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 19e86f676..3be912b5f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ The package has two core components: **1. Elementary Tables** Using dbt's on-run-end hook, the package automatically parses your dbt artifacts and run results and loads them as structured tables into your warehouse. This includes: + - **Metadata tables** — models, tests, sources, exposures, columns, seeds, snapshots, and more - **Run results tables** — invocations, model run results, test results, source freshness, and job-level outcomes From f9fabdc4f112658b356a0f77f76b95e00d06006a 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:46:56 +0000 Subject: [PATCH 3/4] 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 --- integration_tests/profiles/generate_profiles.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/integration_tests/profiles/generate_profiles.py b/integration_tests/profiles/generate_profiles.py index dc105df5a..0f86351ad 100644 --- a/integration_tests/profiles/generate_profiles.py +++ b/integration_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 f6c5bd6043d94c7f8992ba9f3c92689c6818427e 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:56 +0000 Subject: [PATCH 4/4] 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 --- integration_tests/profiles/generate_profiles.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration_tests/profiles/generate_profiles.py b/integration_tests/profiles/generate_profiles.py index 0f86351ad..1ea3115c5 100644 --- a/integration_tests/profiles/generate_profiles.py +++ b/integration_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