Skip to content

Commit 07ff6d3

Browse files
committed
Merge main into 1.12.latest (v1.12.0 re-cut)
2 parents caf2d48 + f91eb2a commit 07ff6d3

7 files changed

Lines changed: 96 additions & 20 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Installs dbt-databricks at lowest-direct dependency resolution and
2+
# runs `dbt parse`, catching constraint ranges that admit versions
3+
# the code doesn't support. Default test paths resolve to
4+
# highest-compatible versions, so lower bounds drift untested.
5+
#
6+
# Runs post-merge rather than on PRs because fork PRs cannot issue
7+
# the OIDC token required by the JFrog proxy and protected runner.
8+
# Acceptable compromise: a bad merge sits on the target branch
9+
# briefly before the gate fires, but well before any release tag.
10+
11+
name: Min-Deps Parse
12+
13+
on:
14+
push:
15+
branches:
16+
- "main"
17+
- "*.latest"
18+
workflow_dispatch:
19+
20+
permissions:
21+
id-token: write
22+
contents: read
23+
24+
defaults:
25+
run:
26+
shell: bash
27+
28+
jobs:
29+
parse-at-lowest-direct:
30+
name: dbt parse at lowest-direct resolution
31+
runs-on:
32+
group: databricks-protected-runner-group
33+
labels: linux-ubuntu-latest
34+
timeout-minutes: 5
35+
36+
steps:
37+
- name: Check out the repository
38+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
39+
40+
- name: Setup JFrog PyPI Proxy
41+
uses: ./.github/actions/setup-jfrog-pypi
42+
43+
- name: Set up Python
44+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
45+
with:
46+
python-version: "3.10"
47+
48+
- name: Install uv
49+
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
50+
with:
51+
cache-local-path: ~/.cache/uv
52+
53+
- name: Install dbt-databricks at lowest-direct dep resolution
54+
run: |
55+
uv venv --python 3.10
56+
uv pip install --resolution lowest-direct .
57+
58+
- name: dbt parse against min-deps fixture
59+
run: .venv/bin/dbt parse --project-dir tests/min_deps_smoke --profiles-dir tests/min_deps_smoke

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## dbt-databricks 1.12.0 (May 14, 2026)
1+
## dbt-databricks 1.12.0 (May 18, 2026)
22

33
### Features
44

@@ -14,6 +14,7 @@
1414
- Fix `metric_view` failing with `METRIC_VIEW_INVALID_VIEW_DEFINITION` when models use bare `{{ ref(...) }}` for the `source:` field ([#1361](https://github.com/databricks/dbt-databricks/issues/1361))
1515
- Fix `RefreshConfig.__eq__` self/other typo where two configs with the same `cron` but different `time_zone_value` compared equal
1616
- Fix streaming-table DROP-SCHEDULE path that was silently filtered out of the changeset
17+
- Use pydantic v1-compatible API in `refresh.py` so the adapter imports on environments shipping pydantic v1 ([#1461](https://github.com/databricks/dbt-databricks/pull/1461))
1718

1819
### Under the Hood
1920

dbt/adapters/databricks/relation_configs/refresh.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from dbt.adapters.contracts.relation import RelationConfig
66
from dbt.adapters.relation_configs.config_base import RelationResults
77
from dbt_common.exceptions import DbtRuntimeError
8-
from pydantic import model_validator
8+
from pydantic import root_validator
99

1010
from dbt.adapters.databricks.relation_configs import base
1111
from dbt.adapters.databricks.relation_configs.base import (
@@ -41,6 +41,9 @@ class RefreshMode(str, Enum):
4141
# these all canonicalize to the same UTC for equality.
4242
_UTC_ALIASES = {"UTC", "ETC/UTC"}
4343

44+
# Mutually-exclusive mode discriminators on RefreshConfig.
45+
_MODE_FIELDS = ("cron", "every", "on_update")
46+
4447

4548
def _canonical_tz(tz: Optional[str]) -> str:
4649
s = (tz or "UTC").upper()
@@ -98,33 +101,27 @@ class RefreshConfig(DatabricksComponentConfig):
98101
# affect identity.
99102
is_altered: bool = False
100103

101-
@model_validator(mode="after")
102-
def _validate_mode_fields(self) -> "RefreshConfig":
103-
modes_set = [name for name, value in self._mode_signals() if value]
104+
@root_validator(skip_on_failure=True)
105+
def _validate_mode_fields(cls, values: dict) -> dict:
106+
modes_set = [name for name in _MODE_FIELDS if values.get(name)]
104107
if len(modes_set) > 1:
105108
raise DbtRuntimeError(
106109
f"Refresh schedule must specify at most one of cron / every / on_update;"
107110
f" got {modes_set}."
108111
)
109-
if self.time_zone_value is not None and self.cron is None:
112+
if values.get("time_zone_value") is not None and values.get("cron") is None:
110113
raise DbtRuntimeError("`time_zone_value` is only valid when `cron` is set.")
111-
if self.at_most_every is not None:
112-
if not self.on_update:
114+
at_most_every = values.get("at_most_every")
115+
if at_most_every is not None:
116+
if not values.get("on_update"):
113117
raise DbtRuntimeError("`at_most_every` is only valid when `on_update` is True.")
114-
seconds = _interval_seconds(self.at_most_every)
118+
seconds = _interval_seconds(at_most_every)
115119
if seconds < 60:
116120
raise DbtRuntimeError(
117121
f"`at_most_every` must be at least 60 seconds (1 minute);"
118-
f" got {self.at_most_every!r} ({seconds}s)."
122+
f" got {at_most_every!r} ({seconds}s)."
119123
)
120-
return self
121-
122-
def _mode_signals(self) -> tuple[tuple[str, Any], ...]:
123-
return (
124-
("cron", self.cron),
125-
("every", self.every),
126-
("on_update", self.on_update),
127-
)
124+
return values
128125

129126
@property
130127
def mode(self) -> RefreshMode:
@@ -176,8 +173,8 @@ def get_diff(self, other: "RefreshConfig") -> Optional["RefreshConfig"]:
176173
if self == other:
177174
return None
178175
is_altered = self.mode != RefreshMode.MANUAL and other.mode != RefreshMode.MANUAL
179-
# model_construct skips re-validation; only is_altered changes, other fields stay valid.
180-
return self.model_construct(**{**self.model_dump(), "is_altered": is_altered})
176+
# copy() skips re-validation; only is_altered changes, other fields stay valid.
177+
return self.copy(update={"is_altered": is_altered})
181178

182179

183180
class RefreshProcessor(DatabricksComponentProcessor[RefreshConfig]):

tests/min_deps_smoke/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target/
2+
logs/
3+
dbt_packages/
4+
.user.yml
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
name: 'min_deps_smoke'
2+
version: '1.0.0'
3+
config-version: 2
4+
5+
profile: 'min_deps_smoke'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
select 1 as n

tests/min_deps_smoke/profiles.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
min_deps_smoke:
2+
target: dev
3+
outputs:
4+
dev:
5+
type: databricks
6+
schema: min_deps_smoke
7+
host: fake.databricks.test
8+
http_path: /sql/1.0/warehouses/fake
9+
token: fake-token

0 commit comments

Comments
 (0)