Skip to content

Detect notebooks with the workspace's first-line rule - #7290

Open
Sankalp-Mittal wants to merge 2 commits into
mainfrom
sankalp-mittal/notebook-deletion-badness
Open

Sankalp-Mittal wants to merge 2 commits into
mainfrom
sankalp-mittal/notebook-deletion-badness

Conversation

@Sankalp-Mittal

@Sankalp-Mittal Sankalp-Mittal commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Changes

libs/notebook now classifies a source file the way the workspace's import-file endpoint does (NotebookUtils.checkSourceFileNotebook in universe): a .py, .sql, .scala or .r file is a notebook when the first line of its leading 200 bytes contains Databricks notebook source. Previously the CLI required the first line to equal the language-specific header exactly, so a leading byte order mark, or a different comment prefix, made the CLI treat a file the workspace imports as a notebook as a plain file.

The CLI only uses this classification to predict the remote name the workspace will give a file: in the sync snapshot, translate_paths, deployment state and workspace import-dir. When the prediction disagrees with the server, the snapshot tracks bom.sql while the workspace stored notebook bom. Removing the file locally then deletes a path that doesn't exist, which sync tolerates, and the notebook is orphaned. This is the badness documented in #7247.

Why

Since #7218 the server classifies uploads again, so the CLI's local rule has to match the server's for the snapshot to address the right remote object.

Tests

  • bundle/sync/replace-node-type now also removes both local files and syncs again; both bom and plain.sql are deleted. Passes locally and on aws-cli in both DMS variants. On the real workspace the old code's bom.sql delete 404'd and left bom behind. This supersedes the test extension in Add acceptance coverage for BOM notebook deletion badness #7247.
  • TestDetectBytes gains cases for the marker mid-line, another language's comment prefix, the marker on the second line, and the marker past the 200-byte buffer.
  • Unit tests pass for every consumer of libs/notebook: libs/sync, libs/fileset, libs/filer, bundle/config/mutator, bundle/deploy, bundle/generate, cmd/workspace/workspace.
  • Full local acceptance suite: 1491 of 1494 directories run. The only failures are the 25 Python wheel builds (templates/default-python/*, templates/pydabs/deploy-classic, artifacts/issue_3109, migrate/command/default-python), all because uv can't resolve hatchling through the internal proxy in my environment. CI covers them.

This pull request and its description were written by Isaac.

Sankalp-Mittal and others added 2 commits November 2, 2026 15:39
The CLI classifies a local source file as a notebook only to predict the
remote name the workspace will give it (sync snapshot, translate_paths,
deployment state, workspace import-dir). It required the first line to
equal the language-specific header exactly, while the import-file endpoint
only requires the first line of the leading 200 bytes to contain
"Databricks notebook source" (NotebookUtils.checkSourceFileNotebook).

A BOM-prefixed SQL notebook therefore landed as notebook "bom" while the
sync snapshot tracked "bom.sql"; removing it locally deleted a path that
did not exist and left the notebook behind. detectSource now applies the
server's rule.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
@github-actions github-actions Bot added the DABs DABs related issues label Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Commit: b4834797d | Workflow run

Regression test report

Selected 1 changed tests (limit=20, 0 not selected)

2 tests: regression=2 unreleased=0 coverage=0 failing=0

test branch main (df1a491) latest (v1.23.0)
TestAccept/bundle/sync/replace-node-type/DMS= ✅ ❌ ❌
TestAccept/bundle/sync/replace-node-type/DMS=true ✅ ❌ ❌
TestAccept/bundle/sync/replace-node-type/DMS=: branch ✅ | main (df1a491) ❌ | latest (v1.23.0) ❌

main (df1a491):

acceptance_test.go:1341: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1341
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1082
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:659
        	Error:      	Not equal: 
        	            	expected: "\n=== A file holds the name of notebook bom.sql, a notebook holds the name of file plain.sql\n\n>>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/replace-node-type-[UNIQUE_NAME]/default/files/bom.sql --file bom.sql --format RAW\n\n>>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/replace-node-type-[UNIQUE_NAME]/default/files/plain --file plain.sql --format SOURCE --language SQL\n\n>>> list\nFILE bom.sql\nNOTEBOOK plain\n\n=== Sync replaces both\n\n>>> [CLI] bundle sync --output text\nAction: PUT: bom.sql, plain.sql\nInitial Sync Complete\nUploaded bom.sql\nUploaded plain.sql\n\n>>> list\nNOTEBOOK bom\nFILE plain.sql\n\n=== Remove both local files and sync again\n\n>>> [CLI] bundle sync --output text\nAction: DELETE: bom, plain.sql\nDeleted bom\nDeleted plain.sql\nInitial Sync Complete\n\n>>> list\n"
        	            	actual  : "\n=== A file holds the name of notebook bom.sql, a notebook holds the name of file plain.sql\n\n>>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/replace-node-type-[UNIQUE_NAME]/default/files/bom.sql --file bom.sql --format RAW\n\n>>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/replace-node-type-[UNIQUE_NAME]/default/files/plain --file plain.sql --format SOURCE --language SQL\n\n>>> list\nFILE bom.sql\nNOTEBOOK plain\n\n=== Sync replaces both\n\n>>> [CLI] bundle sync --output text\nAction: PUT: bom.sql, plain.sql\nInitial Sync Complete\nUploaded bom.sql\nUploaded plain.sql\n\n>>> list\nNOTEBOOK bom\nFILE plain.sql\n\n=== Remove both local files and sync again\n\n>>> [CLI] bundle sync --output text\nAction: DELETE: bom.sql, plain.sql\nDeleted bom.sql\nDeleted plain.sql\nInitial Sync Complete\n\n>>> list\nNOTEBOOK bom\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -26,4 +26,4 @@
        	            	 >>> [CLI] bundle sync --output text
        	            	-Action: DELETE: bom, plain.sql
        	            	-Deleted bom
        	            	+Action: DELETE: bom.sql, plain.sql
        	            	+Deleted bom.sql
        	            	 Deleted plain.sql
        	            	@@ -32,2 +32,3 @@
        	            	 >>> list
        	            	+NOTEBOOK bom
        	            	 
        	Test:       	TestAccept/bundle/sync/replace-node-type/DMS=
        	Messages:   	bundle/sync/replace-node-type/output.txt vs /tmp/TestAcceptbundlesyncreplace-node-typeDMS=3802481021/002/output.txt
FAIL

latest (v1.23.0): same output as main (df1a491)

TestAccept/bundle/sync/replace-node-type/DMS=true: branch ✅ | main (df1a491) ❌ | latest (v1.23.0) ❌

main (df1a491):

acceptance_test.go:1341: 
        	Error Trace:	/home/runner/work/cli/cli/libs/testdiff/testdiff.go:22
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1341
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:1082
        	            				/home/runner/work/cli/cli/acceptance/acceptance_test.go:659
        	Error:      	Not equal: 
        	            	expected: "\n=== A file holds the name of notebook bom.sql, a notebook holds the name of file plain.sql\n\n>>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/replace-node-type-[UNIQUE_NAME]/default/files/bom.sql --file bom.sql --format RAW\n\n>>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/replace-node-type-[UNIQUE_NAME]/default/files/plain --file plain.sql --format SOURCE --language SQL\n\n>>> list\nFILE bom.sql\nNOTEBOOK plain\n\n=== Sync replaces both\n\n>>> [CLI] bundle sync --output text\nAction: PUT: bom.sql, plain.sql\nInitial Sync Complete\nUploaded bom.sql\nUploaded plain.sql\n\n>>> list\nNOTEBOOK bom\nFILE plain.sql\n\n=== Remove both local files and sync again\n\n>>> [CLI] bundle sync --output text\nAction: DELETE: bom, plain.sql\nDeleted bom\nDeleted plain.sql\nInitial Sync Complete\n\n>>> list\n"
        	            	actual  : "\n=== A file holds the name of notebook bom.sql, a notebook holds the name of file plain.sql\n\n>>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/replace-node-type-[UNIQUE_NAME]/default/files/bom.sql --file bom.sql --format RAW\n\n>>> [CLI] workspace import /Workspace/Users/[USERNAME]/.bundle/replace-node-type-[UNIQUE_NAME]/default/files/plain --file plain.sql --format SOURCE --language SQL\n\n>>> list\nFILE bom.sql\nNOTEBOOK plain\n\n=== Sync replaces both\n\n>>> [CLI] bundle sync --output text\nAction: PUT: bom.sql, plain.sql\nInitial Sync Complete\nUploaded bom.sql\nUploaded plain.sql\n\n>>> list\nNOTEBOOK bom\nFILE plain.sql\n\n=== Remove both local files and sync again\n\n>>> [CLI] bundle sync --output text\nAction: DELETE: bom.sql, plain.sql\nDeleted bom.sql\nDeleted plain.sql\nInitial Sync Complete\n\n>>> list\nNOTEBOOK bom\n"
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -26,4 +26,4 @@
        	            	 >>> [CLI] bundle sync --output text
        	            	-Action: DELETE: bom, plain.sql
        	            	-Deleted bom
        	            	+Action: DELETE: bom.sql, plain.sql
        	            	+Deleted bom.sql
        	            	 Deleted plain.sql
        	            	@@ -32,2 +32,3 @@
        	            	 >>> list
        	            	+NOTEBOOK bom
        	            	 
        	Test:       	TestAccept/bundle/sync/replace-node-type/DMS=true
        	Messages:   	bundle/sync/replace-node-type/output.txt vs /tmp/TestAcceptbundlesyncreplace-node-typeDMS=true2508812133/002/output.txt

latest (v1.23.0): same output as main (df1a491)

@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: b483479

Run: 41474072605

Env ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 284 15 5:38
✅​ aws-windows-latest-4core-16gb 286 13 7:54
✅​ azure linux-2core-8gb 283 15 5:42
✅​ azure-windows-latest-4core-16gb 285 13 7:32
✅​ gcp linux-2core-8gb 284 15 5:34
✅​ gcp-windows-latest-4core-16gb 286 13 7:38
Top 6 slowest tests (at least 2 minutes):
duration env testname
7:24 aws-windows-latest-4core-16gb TestAccept
7:14 gcp-windows-latest-4core-16gb TestAccept
7:05 azure-windows-latest-4core-16gb TestAccept
4:18 azure linux-2core-8gb TestAccept
4:11 aws linux-2core-8gb TestAccept
4:07 gcp linux-2core-8gb TestAccept

@Sankalp-Mittal
Sankalp-Mittal marked this pull request as ready for review November 2, 2026 16:15
@Sankalp-Mittal
Sankalp-Mittal requested review from a team as code owners November 2, 2026 16:15

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DABs DABs related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants