From 16fbaf892536c85c4226e6ba6f8fd8ac84696d0c Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Thu, 11 Jun 2026 11:48:41 +0200 Subject: [PATCH 01/14] Reproduce stale-published-content bug as acceptance test Co-authored-by: Denis Bilenko --- .../dashboard.lvdash.json | 1 + .../databricks.yml.tmpl | 9 ++ .../out.test.toml | 4 + .../publish-failure-stale-content/output.txt | 90 +++++++++++++++++++ .../publish-failure-stale-content/script | 42 +++++++++ .../publish-failure-stale-content/test.toml | 10 +++ 6 files changed, 156 insertions(+) create mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/dashboard.lvdash.json create mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml create mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt create mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/script create mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/dashboard.lvdash.json b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/dashboard.lvdash.json new file mode 100644 index 00000000000..0bfc5797ff0 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/dashboard.lvdash.json @@ -0,0 +1 @@ +{"pages":[{"name":"test-page","displayName":"Test Dashboard"}]} diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/databricks.yml.tmpl b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/databricks.yml.tmpl new file mode 100644 index 00000000000..25e359dd010 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/databricks.yml.tmpl @@ -0,0 +1,9 @@ +bundle: + name: update-publish-failure-stale-content + +resources: + dashboards: + dashboard1: + display_name: my dashboard + warehouse_id: $TEST_DEFAULT_WAREHOUSE_ID + file_path: ./dashboard.lvdash.json diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml new file mode 100644 index 00000000000..6072bc71acd --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml @@ -0,0 +1,4 @@ +Local = true +Cloud = true +RequiresWarehouse = true +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt new file mode 100644 index 00000000000..2617e5c820e --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -0,0 +1,90 @@ + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard", + "etag": [ETAG] +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard" +} + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... +Deploying resources... +Error: cannot update resources.dashboards.dashboard1: updating id=[DASHBOARD1_ID]: Fault injected by test. (400 INJECTED) + +Endpoint: POST [DATABRICKS_URL]/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published +HTTP Status: 400 Bad Request +API error_code: INJECTED +API message: Fault injected by test. + +Updating deployment state... + +Exit code: 1 + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed", + "etag": [ETAG] +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard" +} + +>>> print_requests.py //lakeview/dashboards +{ + "method": "PATCH", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]", + "body": { + "display_name": "my dashboard renamed", + "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", + "serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n", + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} +{ + "method": "POST", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published", + "body": { + "embed_credentials": false, + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed", + "etag": [ETAG] +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard" +} + +>>> print_requests.py //lakeview/dashboards + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete resources.dashboards.dashboard1 + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script new file mode 100644 index 00000000000..f5c9cbd3668 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -0,0 +1,42 @@ +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +unset MSYS_NO_PATHCONV + +# First deploy: dashboard is created and published successfully. +trace $CLI bundle deploy +replace_ids.py +DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards.dashboard1.id') +trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' +trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' +rm out.requests.txt + +# Inject a single publish failure for the update below. +fault.py "POST /api/2.0/lakeview/dashboards/*" 400 0 1 + +# Rename the dashboard to trigger an Update. +update_file.py databricks.yml "my dashboard" "my dashboard renamed" + +# Deploy: Update (PATCH) succeeds but publish fails. +errcode trace $CLI bundle deploy +trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' +trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' +trace print_requests.py //lakeview/dashboards + +# Bug: the next deploy should republish "my dashboard renamed" but it skips the +# publish entirely. The previously-published version ("my dashboard") is still +# visible via GetPublished, so remote.published=true matches desired.published=true +# and the planner marks the field skip(remote_already_set) — even though the +# published content is now stale. "Deployment complete!" is printed but no +# POST /published is issued. +trace $CLI bundle deploy +trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' +trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' + +# No PATCH and no POST /published — the stale published version is never updated. +trace print_requests.py //lakeview/dashboards diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml new file mode 100644 index 00000000000..8475ce8f45c --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml @@ -0,0 +1,10 @@ +Cloud = true +Local = true +RecordRequests = true + +[EnvMatrix] +DATABRICKS_BUNDLE_ENGINE = ["direct"] + +[[Repls]] +Old = '\?[ow]=\d+' +New = "?[WSPARAM]=[NUMID]" From 2fa12b95dd6151d3727e9fa2557cee186e5f0fae Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Thu, 11 Jun 2026 12:15:06 +0200 Subject: [PATCH 02/14] dashboard tests: replace generic ETAG regex with specific add_repl.py calls Co-authored-by: Denis Bilenko --- .../out.patch.requests.terraform.txt | 2 +- .../resources/dashboards/change-embed-credentials/script | 1 + .../change-name/out.patch.requests.terraform.txt | 2 +- .../bundle/resources/dashboards/change-name/script | 1 + .../dataset-catalog-schema/out.plan.direct.json | 6 +++--- .../resources/dashboards/dataset-catalog-schema/script | 1 + .../delete-trashed-out-of-band/out.plan.direct.json | 2 +- .../dashboards/delete-trashed-out-of-band/script | 1 + .../dashboards/detect-change/out.plan.direct.json | 6 +++--- .../bundle/resources/dashboards/detect-change/output.txt | 2 +- .../bundle/resources/dashboards/detect-change/script | 2 ++ .../dashboards/publish-failure-stale-content/output.txt | 6 +++--- .../dashboards/publish-failure-stale-content/script | 1 + .../resources/dashboards/simple/out.plan.direct.json | 6 +++--- acceptance/bundle/resources/dashboards/simple/script | 1 + acceptance/bundle/resources/dashboards/test.toml | 9 --------- .../unpublish-out-of-band/out.plan.direct.json | 6 +++--- .../resources/dashboards/unpublish-out-of-band/script | 1 + 18 files changed, 28 insertions(+), 28 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/change-embed-credentials/out.patch.requests.terraform.txt b/acceptance/bundle/resources/dashboards/change-embed-credentials/out.patch.requests.terraform.txt index 761d19479b9..7697866cf48 100644 --- a/acceptance/bundle/resources/dashboards/change-embed-credentials/out.patch.requests.terraform.txt +++ b/acceptance/bundle/resources/dashboards/change-embed-credentials/out.patch.requests.terraform.txt @@ -5,7 +5,7 @@ "create_time": "[TIMESTAMP]", "dashboard_id": "[DASHBOARD_ID]", "display_name": "test dashboard", - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Users/[USERNAME]/.bundle/change-embed-credentials-[UNIQUE_NAME]/default/resources", "path": "/Users/[USERNAME]/.bundle/change-embed-credentials-[UNIQUE_NAME]/default/resources/test dashboard.lvdash.json", diff --git a/acceptance/bundle/resources/dashboards/change-embed-credentials/script b/acceptance/bundle/resources/dashboards/change-embed-credentials/script index 640ca7da1ef..2fb2c7655f8 100755 --- a/acceptance/bundle/resources/dashboards/change-embed-credentials/script +++ b/acceptance/bundle/resources/dashboards/change-embed-credentials/script @@ -25,6 +25,7 @@ deploy_dashboard # Capture the dashboard ID as a replacement. dashboard_id=$($CLI bundle summary --output json | jq -r '.resources.dashboards.my_dashboard.id') echo "$dashboard_id:DASHBOARD_ID" >> ACC_REPLS +add_repl.py "$($CLI lakeview get $dashboard_id | jq -r '.etag')" ETAG # Change embed_credentials to true - this should trigger an update export EMBED_CREDENTIALS="true" diff --git a/acceptance/bundle/resources/dashboards/change-name/out.patch.requests.terraform.txt b/acceptance/bundle/resources/dashboards/change-name/out.patch.requests.terraform.txt index 2e2e8bc108c..1a3df84f499 100644 --- a/acceptance/bundle/resources/dashboards/change-name/out.patch.requests.terraform.txt +++ b/acceptance/bundle/resources/dashboards/change-name/out.patch.requests.terraform.txt @@ -5,7 +5,7 @@ "create_time": "[TIMESTAMP]", "dashboard_id": "[DASHBOARD_ID]", "display_name": "dashboard2", - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Users/[USERNAME]/.bundle/change-name-[UNIQUE_NAME]/default/resources", "path": "/Users/[USERNAME]/.bundle/change-name-[UNIQUE_NAME]/default/resources/dashboard1.lvdash.json", diff --git a/acceptance/bundle/resources/dashboards/change-name/script b/acceptance/bundle/resources/dashboards/change-name/script index 5a20f19f7a3..490993ece92 100644 --- a/acceptance/bundle/resources/dashboards/change-name/script +++ b/acceptance/bundle/resources/dashboards/change-name/script @@ -21,6 +21,7 @@ deploy_dashboard # Capture the dashboard ID as a replacement. dashboard_id=$($CLI bundle summary --output json | jq -r '.resources.dashboards.my_dashboard.id') echo "$dashboard_id:DASHBOARD_ID" >> ACC_REPLS +add_repl.py "$($CLI lakeview get $dashboard_id | jq -r '.etag')" ETAG # Change the name export NAME="dashboard2" diff --git a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/out.plan.direct.json b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/out.plan.direct.json index dd36d61502f..96ec1597f9e 100644 --- a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/out.plan.direct.json @@ -11,7 +11,7 @@ "dashboard_id": "[DASHBOARD_ID]", "display_name": "test bundle-deploy-dashboard-dataset [UUID]", "embed_credentials": true, - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Workspace/Users/[USERNAME]", "path": "/Users/[USERNAME]/test bundle-deploy-dashboard-dataset [UUID].lvdash.json", @@ -36,8 +36,8 @@ "etag": { "action": "skip", "reason": "custom", - "old": [ETAG], - "remote": [ETAG] + "old": "[ETAG]", + "remote": "[ETAG]" }, "serialized_dashboard": { "action": "skip", diff --git a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script index e4c6e6dfec7..994bd1e0c36 100755 --- a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script +++ b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script @@ -19,6 +19,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path, path}' diff --git a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/out.plan.direct.json b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/out.plan.direct.json index dcc05760f7a..63f02f163f2 100644 --- a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/out.plan.direct.json @@ -20,7 +20,7 @@ "etag": { "action": "update", "reason": "custom", - "old": [ETAG] + "old": "[ETAG]" } } } diff --git a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script index 0868adbc13b..67994ce485c 100755 --- a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script +++ b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script @@ -13,6 +13,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG # Verify dashboard was deployed trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path}' diff --git a/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json b/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json index 7af15357c25..1912d5188c1 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json @@ -21,7 +21,7 @@ "dashboard_id": "[DASHBOARD_ID]", "display_name": "test-dashboard-[UNIQUE_NAME]", "embed_credentials": false, - "etag": [ETAG], + "etag": "[ETAG_2]", "lifecycle_state": "ACTIVE", "parent_path": "/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources", "path": "/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources/test-dashboard-[UNIQUE_NAME].lvdash.json", @@ -33,8 +33,8 @@ "changes": { "etag": { "action": "update", - "old": [ETAG], - "remote": [ETAG] + "old": "[ETAG]", + "remote": "[ETAG_2]" }, "serialized_dashboard": { "action": "skip", diff --git a/acceptance/bundle/resources/dashboards/detect-change/output.txt b/acceptance/bundle/resources/dashboards/detect-change/output.txt index 8e06bc10e80..a26851efaac 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/output.txt +++ b/acceptance/bundle/resources/dashboards/detect-change/output.txt @@ -30,7 +30,7 @@ Deployment complete! { "display_name": "test-dashboard-[UNIQUE_NAME]", "embed_credentials": false, - "etag": [ETAG], + "etag": "[ETAG]", "file_path": "dashboard.lvdash.json", "id": "[DASHBOARD_ID]", "parent_path": "/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources", diff --git a/acceptance/bundle/resources/dashboards/detect-change/script b/acceptance/bundle/resources/dashboards/detect-change/script index ccf26142303..b1cbf43e3d5 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/script +++ b/acceptance/bundle/resources/dashboards/detect-change/script @@ -22,6 +22,7 @@ DASHBOARD_ID=$(jq -r '.id' out.summary.json) # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS +add_repl.py "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag')" ETAG rm out.summary.json title "Load the dashboard by its ID and confirm its display name: " @@ -31,6 +32,7 @@ title "Make an out of band modification to the dashboard and confirm that it is RESOURCE_ID=$($CLI workspace get-status "${DASHBOARD_PATH}" | jq -r '.resource_id') DASHBOARD_JSON="{\"serialized_dashboard\": \"{}\", \"warehouse_id\": \"$TEST_DEFAULT_WAREHOUSE_ID\"}" $CLI lakeview update "${RESOURCE_ID}" --json "${DASHBOARD_JSON}" | jq '{lifecycle_state}' +add_repl.py "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag')" ETAG title "Try to redeploy the bundle and confirm that the out of band modification is detected:" trace $CLI bundle plan diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt index 2617e5c820e..e775c26538e 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -8,7 +8,7 @@ Deployment complete! >>> [CLI] lakeview get [DASHBOARD1_ID] { "display_name": "my dashboard", - "etag": [ETAG] + "etag": "[ETAG]" } >>> [CLI] lakeview get-published [DASHBOARD1_ID] @@ -33,7 +33,7 @@ Exit code: 1 >>> [CLI] lakeview get [DASHBOARD1_ID] { "display_name": "my dashboard renamed", - "etag": [ETAG] + "etag": "[ETAG]" } >>> [CLI] lakeview get-published [DASHBOARD1_ID] @@ -70,7 +70,7 @@ Deployment complete! >>> [CLI] lakeview get [DASHBOARD1_ID] { "display_name": "my dashboard renamed", - "etag": [ETAG] + "etag": "[ETAG]" } >>> [CLI] lakeview get-published [DASHBOARD1_ID] diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script index f5c9cbd3668..ea7bf779170 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -12,6 +12,7 @@ unset MSYS_NO_PATHCONV trace $CLI bundle deploy replace_ids.py DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards.dashboard1.id') +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' rm out.requests.txt diff --git a/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json b/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json index 59ab070e00e..f0e8fb6f047 100644 --- a/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/simple/out.plan.direct.json @@ -11,7 +11,7 @@ "dashboard_id": "[DASHBOARD_ID]", "display_name": "test bundle-deploy-dashboard [UUID]", "embed_credentials": true, - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Workspace/Users/[USERNAME]", "path": "/Users/[USERNAME]/test bundle-deploy-dashboard [UUID].lvdash.json", @@ -24,8 +24,8 @@ "etag": { "action": "skip", "reason": "custom", - "old": [ETAG], - "remote": [ETAG] + "old": "[ETAG]", + "remote": "[ETAG]" }, "serialized_dashboard": { "action": "skip", diff --git a/acceptance/bundle/resources/dashboards/simple/script b/acceptance/bundle/resources/dashboards/simple/script index f834edbeadd..9aa5b7941ea 100644 --- a/acceptance/bundle/resources/dashboards/simple/script +++ b/acceptance/bundle/resources/dashboards/simple/script @@ -17,6 +17,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path, path, serialized_dashboard}' diff --git a/acceptance/bundle/resources/dashboards/test.toml b/acceptance/bundle/resources/dashboards/test.toml index d932390098f..a0d0d5ea3f1 100644 --- a/acceptance/bundle/resources/dashboards/test.toml +++ b/acceptance/bundle/resources/dashboards/test.toml @@ -10,12 +10,3 @@ RequiresWarehouse = true # C:/Program Files/Git/Users/$username/UNIQUE_NAME before passing it to the CLI # Setting this environment variable prevents that conversion on windows. MSYS_NO_PATHCONV = "1" - -# Etag can be both negative and positive. -[[Repls]] -Old = "\"[-0-9]{8,}\"" -New = "[ETAG]" - -[[Repls]] -Old = "\"[0-9]{8,}\"" -New = "[ETAG]" diff --git a/acceptance/bundle/resources/dashboards/unpublish-out-of-band/out.plan.direct.json b/acceptance/bundle/resources/dashboards/unpublish-out-of-band/out.plan.direct.json index 558a4ddcfd8..7b3761ca39e 100644 --- a/acceptance/bundle/resources/dashboards/unpublish-out-of-band/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/unpublish-out-of-band/out.plan.direct.json @@ -21,7 +21,7 @@ "dashboard_id": "[DASHBOARD1_ID]", "display_name": "test bundle-deploy-dashboard [UNIQUE_NAME]", "embed_credentials": false, - "etag": [ETAG], + "etag": "[ETAG]", "lifecycle_state": "ACTIVE", "parent_path": "/Workspace/Users/[USERNAME]/.bundle/unpublish-out-of-band-[UNIQUE_NAME]/default/resources", "path": "/Users/[USERNAME]/.bundle/unpublish-out-of-band-[UNIQUE_NAME]/default/resources/test bundle-deploy-dashboard [UNIQUE_NAME].lvdash.json", @@ -34,8 +34,8 @@ "etag": { "action": "skip", "reason": "custom", - "old": [ETAG], - "remote": [ETAG] + "old": "[ETAG]", + "remote": "[ETAG]" }, "published": { "action": "update", diff --git a/acceptance/bundle/resources/dashboards/unpublish-out-of-band/script b/acceptance/bundle/resources/dashboards/unpublish-out-of-band/script index 3e75d6d0a9a..2dae2d0bf24 100644 --- a/acceptance/bundle/resources/dashboards/unpublish-out-of-band/script +++ b/acceptance/bundle/resources/dashboards/unpublish-out-of-band/script @@ -11,6 +11,7 @@ trace $CLI bundle plan -o json > out.plan_initial.$DATABRICKS_BUNDLE_ENGINE.json # Deploy the dashboard trace $CLI bundle deploy DASHBOARD_ID=$(read_id.py dashboard1) +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG trace print_state.py | grep publish > out.state.$DATABRICKS_BUNDLE_ENGINE.json From 4e0e059aa187c38c5de0d8741f02e56efb133bcb Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Thu, 11 Jun 2026 12:33:52 +0200 Subject: [PATCH 03/14] clean up[ --- .../publish-failure-stale-content/out.test.toml | 2 +- .../dashboards/publish-failure-stale-content/test.toml | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml index 6072bc71acd..a29f11b9ab2 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.test.toml @@ -1,4 +1,4 @@ Local = true -Cloud = true +Cloud = false RequiresWarehouse = true EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml index 8475ce8f45c..551ea7a1f44 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml @@ -1,10 +1,4 @@ -Cloud = true Local = true +Cloud = false RecordRequests = true - -[EnvMatrix] -DATABRICKS_BUNDLE_ENGINE = ["direct"] - -[[Repls]] -Old = '\?[ow]=\d+' -New = "?[WSPARAM]=[NUMID]" +EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] From 11e77121ee0ca16b90ab10ce6ee28d30ebf74a7d Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Thu, 11 Jun 2026 22:11:55 +0200 Subject: [PATCH 04/14] testserver: bump dashboard etag on every PATCH, matching cloud behavior Previously the fake workspace only bumped the etag when SerializedDashboard changed. Cloud bumps it on every write. Update the test server and regenerate the stale-content acceptance test output accordingly. Co-authored-by: Isaac --- .../publish-failure-stale-content/output.txt | 28 +++++++++---------- .../publish-failure-stale-content/script | 2 ++ .../publish-failure-stale-content/test.toml | 15 ++++++++-- libs/testserver/dashboards.go | 24 ++++++++-------- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt index e775c26538e..5c50da91340 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -33,7 +33,7 @@ Exit code: 1 >>> [CLI] lakeview get [DASHBOARD1_ID] { "display_name": "my dashboard renamed", - "etag": "[ETAG]" + "etag": "[ETAG_2]" } >>> [CLI] lakeview get-published [DASHBOARD1_ID] @@ -62,23 +62,19 @@ Exit code: 1 } >>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... -Deploying resources... -Updating deployment state... -Deployment complete! +Error: dashboard "dashboard1" has been modified remotely + at resources.dashboards.dashboard1 + in databricks.yml:7:7 ->>> [CLI] lakeview get [DASHBOARD1_ID] -{ - "display_name": "my dashboard renamed", - "etag": "[ETAG]" -} +This dashboard has been modified remotely since the last bundle deployment. +These modifications are untracked and will be overwritten on deploy. ->>> [CLI] lakeview get-published [DASHBOARD1_ID] -{ - "display_name": "my dashboard" -} +Make sure that the local dashboard definition matches what you intend to deploy +before proceeding with the deployment. + +To overwrite the remote changes with your local version, use --force. +The remote modifications will be lost. ->>> print_requests.py //lakeview/dashboards >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: @@ -88,3 +84,5 @@ All files and directories at the following location will be deleted: /Workspace/ Deleting files... Destroy complete! + +Exit code: 1 diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script index ea7bf779170..a1f22774326 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -24,7 +24,9 @@ fault.py "POST /api/2.0/lakeview/dashboards/*" 400 0 1 update_file.py databricks.yml "my dashboard" "my dashboard renamed" # Deploy: Update (PATCH) succeeds but publish fails. +# SaveStateWith records published=false in state. errcode trace $CLI bundle deploy +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' trace print_requests.py //lakeview/dashboards diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml index 551ea7a1f44..41a8ad88f69 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml @@ -1,4 +1,15 @@ -Local = true Cloud = false +Local = true RecordRequests = true -EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] + +Ignore = [ + ".databricks", + "databricks.yml", +] + +[EnvMatrix] +DATABRICKS_BUNDLE_ENGINE = ["direct"] + +[[Repls]] +Old = '\?[ow]=\d+' +New = "?[WSPARAM]=[NUMID]" diff --git a/libs/testserver/dashboards.go b/libs/testserver/dashboards.go index eab45bdbdc1..10cc03a477c 100644 --- a/libs/testserver/dashboards.go +++ b/libs/testserver/dashboards.go @@ -166,21 +166,19 @@ func (s *FakeWorkspace) DashboardUpdate(req Request) Response { } } - if updateReq.SerializedDashboard != dashboard.InputSerializedDashboard { - // Update etag. - prevEtag, err := strconv.Atoi(dashboard.Etag) - if err != nil { - return Response{ - Body: map[string]string{ - "message": "Invalid etag: " + dashboard.Etag, - }, - StatusCode: 400, - } + // Bump etag on every write, matching cloud behavior. + prevEtag, err := strconv.Atoi(dashboard.Etag) + if err != nil { + return Response{ + Body: map[string]string{ + "message": "Invalid etag: " + dashboard.Etag, + }, + StatusCode: 400, } - nextEtag := prevEtag + 1 - dashboard.Etag = strconv.Itoa(nextEtag) + } + dashboard.Etag = strconv.Itoa(prevEtag + 1) - // Update the input serialized dashboard. + if updateReq.SerializedDashboard != dashboard.InputSerializedDashboard { dashboard.InputSerializedDashboard = updateReq.SerializedDashboard } From c45317ecf527b75bef71eaceaa9fda3e11a59939 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 08:59:23 -0700 Subject: [PATCH 05/14] acceptance: fix comments and show stale content after failed re-deploy SaveState is only called on success; the failing second deploy leaves state with the pre-PATCH etag. The third deploy detects the etag mismatch and errors with "modified remotely" instead of silently skipping publish. Wrap the third deploy in errcode so the subsequent get/get-published calls run and show the draft renamed while the published version is still stale. Co-authored-by: Isaac --- .../publish-failure-stale-content/output.txt | 17 +++++++++++++++-- .../publish-failure-stale-content/script | 15 ++++++--------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt index 5c50da91340..4ace75fe4c6 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -76,6 +76,21 @@ To overwrite the remote changes with your local version, use --force. The remote modifications will be lost. +Exit code: 1 + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed", + "etag": "[ETAG_2]" +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard" +} + +>>> print_requests.py //lakeview/dashboards + >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: delete resources.dashboards.dashboard1 @@ -84,5 +99,3 @@ All files and directories at the following location will be deleted: /Workspace/ Deleting files... Destroy complete! - -Exit code: 1 diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script index a1f22774326..8d34a91b32a 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -23,21 +23,18 @@ fault.py "POST /api/2.0/lakeview/dashboards/*" 400 0 1 # Rename the dashboard to trigger an Update. update_file.py databricks.yml "my dashboard" "my dashboard renamed" -# Deploy: Update (PATCH) succeeds but publish fails. -# SaveStateWith records published=false in state. +# Deploy: PATCH succeeds (bumping the remote etag) but publish fails. +# SaveState is only called on success, so state retains the pre-PATCH etag. errcode trace $CLI bundle deploy add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' trace print_requests.py //lakeview/dashboards -# Bug: the next deploy should republish "my dashboard renamed" but it skips the -# publish entirely. The previously-published version ("my dashboard") is still -# visible via GetPublished, so remote.published=true matches desired.published=true -# and the planner marks the field skip(remote_already_set) — even though the -# published content is now stale. "Deployment complete!" is printed but no -# POST /published is issued. -trace $CLI bundle deploy +# Bug: re-running deploy fails with "modified remotely" because the stored etag +# (pre-PATCH) no longer matches the remote etag (bumped by the PATCH above). +# No API writes are attempted — the stale published content is stuck. +errcode trace $CLI bundle deploy trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' From 5907889cbaa8bd9ab99a9b5d2c92f27e97907c4f Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 09:12:05 -0700 Subject: [PATCH 06/14] acceptance: use explicit ETAG_1/ETAG_2 labels in publish-failure-stale-content Co-authored-by: Isaac --- .../dashboards/publish-failure-stale-content/output.txt | 2 +- .../resources/dashboards/publish-failure-stale-content/script | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt index 4ace75fe4c6..0b38d53ffa9 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -8,7 +8,7 @@ Deployment complete! >>> [CLI] lakeview get [DASHBOARD1_ID] { "display_name": "my dashboard", - "etag": "[ETAG]" + "etag": "[ETAG_1]" } >>> [CLI] lakeview get-published [DASHBOARD1_ID] diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script index 8d34a91b32a..009a260757d 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -12,7 +12,7 @@ unset MSYS_NO_PATHCONV trace $CLI bundle deploy replace_ids.py DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards.dashboard1.id') -add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_1 trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' rm out.requests.txt @@ -26,7 +26,7 @@ update_file.py databricks.yml "my dashboard" "my dashboard renamed" # Deploy: PATCH succeeds (bumping the remote etag) but publish fails. # SaveState is only called on success, so state retains the pre-PATCH etag. errcode trace $CLI bundle deploy -add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_2 trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' trace print_requests.py //lakeview/dashboards From d3e942b0ad9ad509a50bd3f8ecf518d03aafee7f Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 09:13:47 -0700 Subject: [PATCH 07/14] acceptance: use explicit ETAG_1/ETAG_2 labels in detect-change Co-authored-by: Isaac --- .../resources/dashboards/detect-change/out.plan.direct.json | 2 +- .../bundle/resources/dashboards/detect-change/output.txt | 2 +- acceptance/bundle/resources/dashboards/detect-change/script | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json b/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json index 1912d5188c1..b113b73d33a 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json +++ b/acceptance/bundle/resources/dashboards/detect-change/out.plan.direct.json @@ -33,7 +33,7 @@ "changes": { "etag": { "action": "update", - "old": "[ETAG]", + "old": "[ETAG_1]", "remote": "[ETAG_2]" }, "serialized_dashboard": { diff --git a/acceptance/bundle/resources/dashboards/detect-change/output.txt b/acceptance/bundle/resources/dashboards/detect-change/output.txt index a26851efaac..fe3f8962ec7 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/output.txt +++ b/acceptance/bundle/resources/dashboards/detect-change/output.txt @@ -30,7 +30,7 @@ Deployment complete! { "display_name": "test-dashboard-[UNIQUE_NAME]", "embed_credentials": false, - "etag": "[ETAG]", + "etag": "[ETAG_1]", "file_path": "dashboard.lvdash.json", "id": "[DASHBOARD_ID]", "parent_path": "/Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/resources", diff --git a/acceptance/bundle/resources/dashboards/detect-change/script b/acceptance/bundle/resources/dashboards/detect-change/script index b1cbf43e3d5..6376710caf2 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/script +++ b/acceptance/bundle/resources/dashboards/detect-change/script @@ -22,7 +22,7 @@ DASHBOARD_ID=$(jq -r '.id' out.summary.json) # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS -add_repl.py "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag')" ETAG +add_repl.py "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag')" ETAG_1 rm out.summary.json title "Load the dashboard by its ID and confirm its display name: " @@ -32,7 +32,7 @@ title "Make an out of band modification to the dashboard and confirm that it is RESOURCE_ID=$($CLI workspace get-status "${DASHBOARD_PATH}" | jq -r '.resource_id') DASHBOARD_JSON="{\"serialized_dashboard\": \"{}\", \"warehouse_id\": \"$TEST_DEFAULT_WAREHOUSE_ID\"}" $CLI lakeview update "${RESOURCE_ID}" --json "${DASHBOARD_JSON}" | jq '{lifecycle_state}' -add_repl.py "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag')" ETAG +add_repl.py "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag')" ETAG_2 title "Try to redeploy the bundle and confirm that the out of band modification is detected:" trace $CLI bundle plan From 9c50bd07b571b6d10ed82e9303b7ec5b3553f732 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 09:18:06 -0700 Subject: [PATCH 08/14] acceptance: show that --force recovers from stale-content state Co-authored-by: Isaac --- .../publish-failure-stale-content/output.txt | 37 +++++++++++++++++++ .../publish-failure-stale-content/script | 9 +++++ 2 files changed, 46 insertions(+) diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt index 0b38d53ffa9..d9210691c72 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -91,6 +91,43 @@ Exit code: 1 >>> print_requests.py //lakeview/dashboards +>>> [CLI] bundle deploy --force +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed", + "etag": "[ETAG_3]" +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed" +} + +>>> print_requests.py //lakeview/dashboards +{ + "method": "PATCH", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]", + "body": { + "display_name": "my dashboard renamed", + "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", + "serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n", + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} +{ + "method": "POST", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published", + "body": { + "embed_credentials": false, + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} + >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: delete resources.dashboards.dashboard1 diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script index 009a260757d..4fef35da8be 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -40,3 +40,12 @@ trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' # No PATCH and no POST /published — the stale published version is never updated. trace print_requests.py //lakeview/dashboards + +# --force bypasses the etag check and forces a full re-deploy (PATCH + POST /published), +# which fixes the stale published content. +trace $CLI bundle deploy --force +add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_3 +trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' +trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' + +trace print_requests.py //lakeview/dashboards From 74eb86ba05c81c2007c823a164a194cf08bf5540 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 09:20:52 -0700 Subject: [PATCH 09/14] acceptance: record JSON plans after first and second deploys Co-authored-by: Isaac --- .../out.plan.1.json | 40 +++++++++++++ .../out.plan.2.json | 56 +++++++++++++++++++ .../publish-failure-stale-content/script | 4 ++ 3 files changed, 100 insertions(+) create mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.1.json create mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.2.json diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.1.json b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.1.json new file mode 100644 index 00000000000..e48ee7e6ae2 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.1.json @@ -0,0 +1,40 @@ +{ + "plan_version": 2, + "cli_version": "[DEV_VERSION]", + "lineage": "[UUID]", + "serial": 1, + "plan": { + "resources.dashboards.dashboard1": { + "action": "skip", + "remote_state": { + "create_time": "[TIMESTAMP]", + "dashboard_id": "[DASHBOARD1_ID]", + "display_name": "my dashboard", + "embed_credentials": false, + "etag": "[ETAG_1]", + "lifecycle_state": "ACTIVE", + "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", + "path": "/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources/my dashboard.lvdash.json", + "published": true, + "serialized_dashboard": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}", + "update_time": "[TIMESTAMP]", + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + }, + "changes": { + "etag": { + "action": "skip", + "reason": "custom", + "old": "[ETAG_1]", + "remote": "[ETAG_1]" + }, + "serialized_dashboard": { + "action": "skip", + "reason": "etag_based", + "old": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", + "new": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", + "remote": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}" + } + } + } + } +} diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.2.json b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.2.json new file mode 100644 index 00000000000..746ae2cf7a1 --- /dev/null +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.2.json @@ -0,0 +1,56 @@ +{ + "plan_version": 2, + "cli_version": "[DEV_VERSION]", + "lineage": "[UUID]", + "serial": 1, + "plan": { + "resources.dashboards.dashboard1": { + "action": "update", + "new_state": { + "value": { + "display_name": "my dashboard renamed", + "embed_credentials": false, + "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", + "published": true, + "serialized_dashboard": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } + }, + "remote_state": { + "create_time": "[TIMESTAMP]", + "dashboard_id": "[DASHBOARD1_ID]", + "display_name": "my dashboard renamed", + "embed_credentials": false, + "etag": "[ETAG_2]", + "lifecycle_state": "ACTIVE", + "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", + "path": "/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources/my dashboard renamed.lvdash.json", + "published": true, + "serialized_dashboard": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}", + "update_time": "[TIMESTAMP]", + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + }, + "changes": { + "display_name": { + "action": "skip", + "reason": "remote_already_set", + "old": "my dashboard", + "new": "my dashboard renamed", + "remote": "my dashboard renamed" + }, + "etag": { + "action": "update", + "old": "[ETAG_1]", + "remote": "[ETAG_2]" + }, + "serialized_dashboard": { + "action": "skip", + "reason": "etag_based", + "old": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", + "new": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", + "remote": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}" + } + } + } + } +} diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script index 4fef35da8be..23f2dfac350 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -15,6 +15,8 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_1 trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' +$CLI bundle plan -o json > out.plan.1.json +json_in_json_normalize.py out.plan.1.json rm out.requests.txt # Inject a single publish failure for the update below. @@ -29,6 +31,8 @@ errcode trace $CLI bundle deploy add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_2 trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' +errcode $CLI bundle plan -o json > out.plan.2.json +json_in_json_normalize.py out.plan.2.json trace print_requests.py //lakeview/dashboards # Bug: re-running deploy fails with "modified remotely" because the stored etag From f27d76f6402319af685864b6f26b7d5bd29c1214 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 09:30:21 -0700 Subject: [PATCH 10/14] acceptance: inline plans via gron.py | grep etag|published Co-authored-by: Isaac --- .../out.plan.1.json | 40 ------------- .../out.plan.2.json | 56 ------------------- .../publish-failure-stale-content/output.txt | 31 ++++++++++ .../publish-failure-stale-content/script | 6 +- 4 files changed, 33 insertions(+), 100 deletions(-) delete mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.1.json delete mode 100644 acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.2.json diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.1.json b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.1.json deleted file mode 100644 index e48ee7e6ae2..00000000000 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.1.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "plan_version": 2, - "cli_version": "[DEV_VERSION]", - "lineage": "[UUID]", - "serial": 1, - "plan": { - "resources.dashboards.dashboard1": { - "action": "skip", - "remote_state": { - "create_time": "[TIMESTAMP]", - "dashboard_id": "[DASHBOARD1_ID]", - "display_name": "my dashboard", - "embed_credentials": false, - "etag": "[ETAG_1]", - "lifecycle_state": "ACTIVE", - "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", - "path": "/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources/my dashboard.lvdash.json", - "published": true, - "serialized_dashboard": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}", - "update_time": "[TIMESTAMP]", - "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" - }, - "changes": { - "etag": { - "action": "skip", - "reason": "custom", - "old": "[ETAG_1]", - "remote": "[ETAG_1]" - }, - "serialized_dashboard": { - "action": "skip", - "reason": "etag_based", - "old": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", - "new": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", - "remote": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}" - } - } - } - } -} diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.2.json b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.2.json deleted file mode 100644 index 746ae2cf7a1..00000000000 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/out.plan.2.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "plan_version": 2, - "cli_version": "[DEV_VERSION]", - "lineage": "[UUID]", - "serial": 1, - "plan": { - "resources.dashboards.dashboard1": { - "action": "update", - "new_state": { - "value": { - "display_name": "my dashboard renamed", - "embed_credentials": false, - "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", - "published": true, - "serialized_dashboard": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", - "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" - } - }, - "remote_state": { - "create_time": "[TIMESTAMP]", - "dashboard_id": "[DASHBOARD1_ID]", - "display_name": "my dashboard renamed", - "embed_credentials": false, - "etag": "[ETAG_2]", - "lifecycle_state": "ACTIVE", - "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", - "path": "/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources/my dashboard renamed.lvdash.json", - "published": true, - "serialized_dashboard": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}", - "update_time": "[TIMESTAMP]", - "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" - }, - "changes": { - "display_name": { - "action": "skip", - "reason": "remote_already_set", - "old": "my dashboard", - "new": "my dashboard renamed", - "remote": "my dashboard renamed" - }, - "etag": { - "action": "update", - "old": "[ETAG_1]", - "remote": "[ETAG_2]" - }, - "serialized_dashboard": { - "action": "skip", - "reason": "etag_based", - "old": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", - "new": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\"}]}", - "remote": "{\"pages\":[{\"displayName\":\"Test Dashboard\",\"name\":\"test-page\",\"pageType\":\"PAGE_TYPE_CANVAS\"}]}" - } - } - } - } -} diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt index d9210691c72..52668e9f8a0 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -16,6 +16,15 @@ Deployment complete! "display_name": "my dashboard" } +>>> [CLI] bundle plan -o json +json.plan.resources.dashboards.dashboard1.remote_state.etag = "[ETAG_1]"; +json.plan.resources.dashboards.dashboard1.remote_state.published = true; +json.plan.resources.dashboards.dashboard1.changes.etag.action = "skip"; +json.plan.resources.dashboards.dashboard1.changes.etag.reason = "custom"; +json.plan.resources.dashboards.dashboard1.changes.etag.old = "[ETAG_1]"; +json.plan.resources.dashboards.dashboard1.changes.etag.remote = "[ETAG_1]"; +json.plan.resources.dashboards.dashboard1.changes.serialized_dashboard.reason = "etag_based"; + >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... Deploying resources... @@ -41,6 +50,28 @@ Exit code: 1 "display_name": "my dashboard" } +>>> [CLI] bundle plan -o json +Warning: dashboard "dashboard1" has been modified remotely + at resources.dashboards.dashboard1 + in databricks.yml:7:7 + +This dashboard has been modified remotely since the last bundle deployment. +These modifications are untracked and will be overwritten on deploy. + +Make sure that the local dashboard definition matches what you intend to deploy +before proceeding with the deployment. + +To overwrite the remote changes with your local version, use --force. +The remote modifications will be lost. + +json.plan.resources.dashboards.dashboard1.new_state.value.published = true; +json.plan.resources.dashboards.dashboard1.remote_state.etag = "[ETAG_2]"; +json.plan.resources.dashboards.dashboard1.remote_state.published = true; +json.plan.resources.dashboards.dashboard1.changes.etag.action = "update"; +json.plan.resources.dashboards.dashboard1.changes.etag.old = "[ETAG_1]"; +json.plan.resources.dashboards.dashboard1.changes.etag.remote = "[ETAG_2]"; +json.plan.resources.dashboards.dashboard1.changes.serialized_dashboard.reason = "etag_based"; + >>> print_requests.py //lakeview/dashboards { "method": "PATCH", diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script index 23f2dfac350..25f9f190098 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -15,8 +15,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_1 trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' -$CLI bundle plan -o json > out.plan.1.json -json_in_json_normalize.py out.plan.1.json +trace $CLI bundle plan -o json | gron.py | grep -E "etag|published" rm out.requests.txt # Inject a single publish failure for the update below. @@ -31,8 +30,7 @@ errcode trace $CLI bundle deploy add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_2 trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' -errcode $CLI bundle plan -o json > out.plan.2.json -json_in_json_normalize.py out.plan.2.json +trace $CLI bundle plan -o json | gron.py | grep -E "etag|published" trace print_requests.py //lakeview/dashboards # Bug: re-running deploy fails with "modified remotely" because the stored etag From d0c4fc61e983b3a48981e4d43edb4ac52fe80bce Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 09:37:14 -0700 Subject: [PATCH 11/14] acceptance: move print_requests after each deploy; add final plan check Co-authored-by: Isaac --- .../publish-failure-stale-content/output.txt | 69 ++++++++++--------- .../publish-failure-stale-content/script | 10 ++- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt index 52668e9f8a0..394da5bea52 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/output.txt @@ -39,6 +39,26 @@ Updating deployment state... Exit code: 1 +>>> print_requests.py //lakeview/dashboards +{ + "method": "PATCH", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]", + "body": { + "display_name": "my dashboard renamed", + "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", + "serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n", + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} +{ + "method": "POST", + "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published", + "body": { + "embed_credentials": false, + "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" + } +} + >>> [CLI] lakeview get [DASHBOARD1_ID] { "display_name": "my dashboard renamed", @@ -72,26 +92,6 @@ json.plan.resources.dashboards.dashboard1.changes.etag.old = "[ETAG_1]"; json.plan.resources.dashboards.dashboard1.changes.etag.remote = "[ETAG_2]"; json.plan.resources.dashboards.dashboard1.changes.serialized_dashboard.reason = "etag_based"; ->>> print_requests.py //lakeview/dashboards -{ - "method": "PATCH", - "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]", - "body": { - "display_name": "my dashboard renamed", - "parent_path": "/Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/resources", - "serialized_dashboard": "{\"pages\":[{\"name\":\"test-page\",\"displayName\":\"Test Dashboard\"}]}\n", - "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" - } -} -{ - "method": "POST", - "path": "/api/2.0/lakeview/dashboards/[DASHBOARD1_ID]/published", - "body": { - "embed_credentials": false, - "warehouse_id": "[TEST_DEFAULT_WAREHOUSE_ID]" - } -} - >>> [CLI] bundle deploy Error: dashboard "dashboard1" has been modified remotely at resources.dashboards.dashboard1 @@ -109,6 +109,8 @@ The remote modifications will be lost. Exit code: 1 +>>> print_requests.py //lakeview/dashboards + >>> [CLI] lakeview get [DASHBOARD1_ID] { "display_name": "my dashboard renamed", @@ -120,25 +122,12 @@ Exit code: 1 "display_name": "my dashboard" } ->>> print_requests.py //lakeview/dashboards - >>> [CLI] bundle deploy --force Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/update-publish-failure-stale-content/default/files... Deploying resources... Updating deployment state... Deployment complete! ->>> [CLI] lakeview get [DASHBOARD1_ID] -{ - "display_name": "my dashboard renamed", - "etag": "[ETAG_3]" -} - ->>> [CLI] lakeview get-published [DASHBOARD1_ID] -{ - "display_name": "my dashboard renamed" -} - >>> print_requests.py //lakeview/dashboards { "method": "PATCH", @@ -159,6 +148,20 @@ Deployment complete! } } +>>> [CLI] lakeview get [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed", + "etag": "[ETAG_3]" +} + +>>> [CLI] lakeview get-published [DASHBOARD1_ID] +{ + "display_name": "my dashboard renamed" +} + +>>> [CLI] bundle plan +Plan: 0 to add, 0 to change, 0 to delete, 1 unchanged + >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: delete resources.dashboards.dashboard1 diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script index 25f9f190098..500d87c5d76 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/script @@ -27,27 +27,25 @@ update_file.py databricks.yml "my dashboard" "my dashboard renamed" # Deploy: PATCH succeeds (bumping the remote etag) but publish fails. # SaveState is only called on success, so state retains the pre-PATCH etag. errcode trace $CLI bundle deploy +trace print_requests.py //lakeview/dashboards add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_2 trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' trace $CLI bundle plan -o json | gron.py | grep -E "etag|published" -trace print_requests.py //lakeview/dashboards # Bug: re-running deploy fails with "modified remotely" because the stored etag # (pre-PATCH) no longer matches the remote etag (bumped by the PATCH above). # No API writes are attempted — the stale published content is stuck. errcode trace $CLI bundle deploy +trace print_requests.py //lakeview/dashboards trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' -# No PATCH and no POST /published — the stale published version is never updated. -trace print_requests.py //lakeview/dashboards - # --force bypasses the etag check and forces a full re-deploy (PATCH + POST /published), # which fixes the stale published content. trace $CLI bundle deploy --force +trace print_requests.py //lakeview/dashboards add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG_3 trace $CLI lakeview get $DASHBOARD_ID | jq '{display_name, etag}' trace $CLI lakeview get-published $DASHBOARD_ID | jq '{display_name}' - -trace print_requests.py //lakeview/dashboards +trace $CLI bundle plan From 554c55d8938664ea2cfa804fb4e3be4dd6b0dd22 Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 13:02:01 -0700 Subject: [PATCH 12/14] dresources: fix TestAll to handle server-generated field changes after DoUpdate DoUpdate may mutate newState in place (e.g. etag) and server-generated fields may change on every write. Update the test to compare DoUpdate's return value against a fresh DoRead, and update remappedState to the post-update value so the subsequent field checks stay consistent. Co-authored-by: Isaac --- bundle/direct/dresources/all_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/bundle/direct/dresources/all_test.go b/bundle/direct/dresources/all_test.go index 30adb4640cc..4f8f8f5e269 100644 --- a/bundle/direct/dresources/all_test.go +++ b/bundle/direct/dresources/all_test.go @@ -889,8 +889,18 @@ func testCRUD(t *testing.T, group string, adapter *Adapter, client *databricks.W if remoteStateFromUpdate != nil { remappedStateFromUpdate, err := adapter.RemapState(remoteStateFromUpdate) require.NoError(t, err) - ignoreFilter.requireEqual(t, remappedState, remappedStateFromUpdate, + // Compare DoUpdate's result against a fresh DoRead: server-generated + // fields (e.g. etag) may change on any write, so DoUpdate's return + // value must match what DoRead returns right after. + remotePostUpdate, err := adapter.DoRead(ctx, createdID) + require.NoError(t, err) + remappedPostUpdate, err := adapter.RemapState(remotePostUpdate) + require.NoError(t, err) + ignoreFilter.requireEqual(t, remappedPostUpdate, remappedStateFromUpdate, "unexpected differences between remappedState and remappedStateFromUpdate") + // DoUpdate may mutate newState in place (e.g. etag), so update remappedState + // to match the post-update server state for the field checks below. + remappedState = remappedStateFromUpdate } remoteStateFromWaitUpdate, err := adapter.WaitAfterUpdate(ctx, createdID, newState) From 39aa4fcf45c2f56f9503cb0e277f3325b89159ce Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Sun, 14 Jun 2026 15:40:27 -0700 Subject: [PATCH 13/14] acceptance: fix Windows path issue in dashboard tests Replace add_repl.py calls with direct echo in tests that run with MSYS_NO_PATHCONV=1. When this env var is set, MSYS does not convert Unix-style paths to Windows format before passing them to Python.exe, causing the shebang invocation to fail with the wrong path. Direct echo to ACC_REPLS avoids Python entirely and works on all platforms. Co-authored-by: Denis Bilenko --- .../resources/dashboards/change-embed-credentials/script | 2 +- acceptance/bundle/resources/dashboards/change-name/script | 2 +- .../bundle/resources/dashboards/dataset-catalog-schema/script | 2 +- .../resources/dashboards/delete-trashed-out-of-band/script | 2 +- acceptance/bundle/resources/dashboards/detect-change/script | 4 ++-- acceptance/bundle/resources/dashboards/simple/script | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/acceptance/bundle/resources/dashboards/change-embed-credentials/script b/acceptance/bundle/resources/dashboards/change-embed-credentials/script index 2fb2c7655f8..d1a8743c75e 100755 --- a/acceptance/bundle/resources/dashboards/change-embed-credentials/script +++ b/acceptance/bundle/resources/dashboards/change-embed-credentials/script @@ -25,7 +25,7 @@ deploy_dashboard # Capture the dashboard ID as a replacement. dashboard_id=$($CLI bundle summary --output json | jq -r '.resources.dashboards.my_dashboard.id') echo "$dashboard_id:DASHBOARD_ID" >> ACC_REPLS -add_repl.py "$($CLI lakeview get $dashboard_id | jq -r '.etag')" ETAG +echo "$($CLI lakeview get $dashboard_id | jq -r '.etag'):ETAG" >> ACC_REPLS # Change embed_credentials to true - this should trigger an update export EMBED_CREDENTIALS="true" diff --git a/acceptance/bundle/resources/dashboards/change-name/script b/acceptance/bundle/resources/dashboards/change-name/script index 490993ece92..93a90794786 100644 --- a/acceptance/bundle/resources/dashboards/change-name/script +++ b/acceptance/bundle/resources/dashboards/change-name/script @@ -21,7 +21,7 @@ deploy_dashboard # Capture the dashboard ID as a replacement. dashboard_id=$($CLI bundle summary --output json | jq -r '.resources.dashboards.my_dashboard.id') echo "$dashboard_id:DASHBOARD_ID" >> ACC_REPLS -add_repl.py "$($CLI lakeview get $dashboard_id | jq -r '.etag')" ETAG +echo "$($CLI lakeview get $dashboard_id | jq -r '.etag'):ETAG" >> ACC_REPLS # Change the name export NAME="dashboard2" diff --git a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script index 994bd1e0c36..f4a5750e820 100755 --- a/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script +++ b/acceptance/bundle/resources/dashboards/dataset-catalog-schema/script @@ -19,7 +19,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS -add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG +echo "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag'):ETAG" >> ACC_REPLS trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path, path}' diff --git a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script index 67994ce485c..bc67448e8a6 100755 --- a/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script +++ b/acceptance/bundle/resources/dashboards/delete-trashed-out-of-band/script @@ -13,7 +13,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS -add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG +echo "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag'):ETAG" >> ACC_REPLS # Verify dashboard was deployed trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path}' diff --git a/acceptance/bundle/resources/dashboards/detect-change/script b/acceptance/bundle/resources/dashboards/detect-change/script index 6376710caf2..36649c77cad 100644 --- a/acceptance/bundle/resources/dashboards/detect-change/script +++ b/acceptance/bundle/resources/dashboards/detect-change/script @@ -22,7 +22,7 @@ DASHBOARD_ID=$(jq -r '.id' out.summary.json) # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS -add_repl.py "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag')" ETAG_1 +echo "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag'):ETAG_1" >> ACC_REPLS rm out.summary.json title "Load the dashboard by its ID and confirm its display name: " @@ -32,7 +32,7 @@ title "Make an out of band modification to the dashboard and confirm that it is RESOURCE_ID=$($CLI workspace get-status "${DASHBOARD_PATH}" | jq -r '.resource_id') DASHBOARD_JSON="{\"serialized_dashboard\": \"{}\", \"warehouse_id\": \"$TEST_DEFAULT_WAREHOUSE_ID\"}" $CLI lakeview update "${RESOURCE_ID}" --json "${DASHBOARD_JSON}" | jq '{lifecycle_state}' -add_repl.py "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag')" ETAG_2 +echo "$($CLI lakeview get "$DASHBOARD_ID" | jq -r '.etag'):ETAG_2" >> ACC_REPLS title "Try to redeploy the bundle and confirm that the out of band modification is detected:" trace $CLI bundle plan diff --git a/acceptance/bundle/resources/dashboards/simple/script b/acceptance/bundle/resources/dashboards/simple/script index 9aa5b7941ea..0b1adec55d6 100644 --- a/acceptance/bundle/resources/dashboards/simple/script +++ b/acceptance/bundle/resources/dashboards/simple/script @@ -17,7 +17,7 @@ DASHBOARD_ID=$($CLI bundle summary --output json | jq -r '.resources.dashboards. # Capture the dashboard ID as a replacement. echo "$DASHBOARD_ID:DASHBOARD_ID" >> ACC_REPLS -add_repl.py "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag')" ETAG +echo "$($CLI lakeview get $DASHBOARD_ID | jq -r '.etag'):ETAG" >> ACC_REPLS trace $CLI lakeview get $DASHBOARD_ID | jq '{lifecycle_state, parent_path, path, serialized_dashboard}' From 0133f23682d060c4d5981e64ce32bb02a8d4bbae Mon Sep 17 00:00:00 2001 From: Denis Bilenko Date: Mon, 15 Jun 2026 06:41:53 -0700 Subject: [PATCH 14/14] acceptance: document stale-content bug in Badness field Co-authored-by: Denis Bilenko --- .../resources/dashboards/publish-failure-stale-content/test.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml index 41a8ad88f69..ee9d6ba03ae 100644 --- a/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml +++ b/acceptance/bundle/resources/dashboards/publish-failure-stale-content/test.toml @@ -1,3 +1,4 @@ +Badness = "after publish failure, re-deploy fails with 'modified remotely' instead of republishing; --force is required as a workaround" Cloud = false Local = true RecordRequests = true