Skip to content

Fix sync --dry-run mutating the remote workspace#5495

Open
simonfaltum wants to merge 5 commits into
mainfrom
simonfaltum/b31-sync-dryrun
Open

Fix sync --dry-run mutating the remote workspace#5495
simonfaltum wants to merge 5 commits into
mainfrom
simonfaltum/b31-sync-dryrun

Conversation

@simonfaltum

Copy link
Copy Markdown
Member

Why

Found during a full-repo review of the CLI. databricks sync --dry-run (and bundle sync --dry-run) promises to make no changes, but it still mutated the workspace: if the remote directory did not exist, the sync setup created it. Separately, the dry-run upload path opened every local file before checking the dry-run flag, so a file deleted between listing and upload failed the preview for no reason.

Changes

Before, a dry run created the missing remote directory and opened every local upload candidate; now a dry run performs only read-only API calls and never touches local file contents.

  • EnsureRemotePathIsUsable takes a dryRun parameter. Read-only validation (path lookup, repo existence check) still runs, but creating a missing remote directory is skipped in dry-run mode.
  • applyPut opens the local file inside the !DryRun branch, next to the write it feeds.

Test plan

  • New acceptance test acceptance/cmd/sync-dryrun-missing-remote records HTTP requests and asserts that a dry run against a missing remote path records no mkdirs or import requests, while the same sync without --dry-run records them.
  • New unit test TestApplyPutDryRunSkipsLocalRead covers the missing-local-file case in dry-run mode.
  • Existing sync acceptance tests pass (cmd/sync, cmd/sync/dryrun, cmd/sync-from-file, cmd/sync-without-args, bundle/sync).
  • libs/sync unit tests pass; integration test package compiles.
  • ./task fmt-q, ./task lint-q, and ./task checks pass.

This pull request and its description were written by Isaac.

A dry-run sync against a missing remote path created the directory, and applyPut opened local files before checking DryRun, so a file vanishing mid-run failed the preview. Skip the mkdir and the local open in dry-run mode.

Co-authored-by: Isaac
@simonfaltum simonfaltum requested a review from pietern June 9, 2026 19:57
@simonfaltum simonfaltum temporarily deployed to test-trigger-is June 9, 2026 19:58 — with GitHub Actions Inactive
@simonfaltum simonfaltum temporarily deployed to test-trigger-is June 9, 2026 19:58 — with GitHub Actions Inactive
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Commit: b8d71bf

Run: 27290548541

Env 🟨​KNOWN 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🟨​ aws linux 7 15 261 970 7:55
🟨​ aws windows 7 15 263 968 15:49
💚​ aws-ucws linux 7 15 357 884 6:59
💚​ aws-ucws windows 7 15 359 882 12:16
💚​ azure linux 1 17 264 968 7:48
💚​ azure windows 1 17 266 966 12:21
💚​ azure-ucws linux 1 17 362 880 8:30
💚​ azure-ucws windows 1 17 364 878 12:14
💚​ gcp linux 1 17 260 971 8:48
💚​ gcp windows 1 17 262 969 12:36
22 interesting tests: 15 SKIP, 7 KNOWN
Test Name aws linux aws windows aws-ucws linux aws-ucws windows azure linux azure windows azure-ucws linux azure-ucws windows gcp linux gcp windows
🟨​ TestAccept 🟨​K 🟨​K 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/permissions 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions 🟨​K 🟨​K 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions 🟨​K 🟨​K 💚​R 💚​R 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct 🟨​K 🟨​K 💚​R 💚​R
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform 🟨​K 🟨​K 💚​R 💚​R
🙈​ TestAccept/bundle/resources/postgres_branches/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/replace_existing 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/update_protected 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_branches/without_branch_id 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_endpoints/recreate 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/postgres_projects/update_display_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/synced_database_tables/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/grants/select 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
Top 28 slowest tests (at least 2 minutes):
duration env testname
7:36 azure windows TestAccept
6:07 azure-ucws windows TestAccept
6:05 aws-ucws windows TestAccept
5:51 gcp windows TestAccept
4:58 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:48 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:42 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:31 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:51 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:37 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:36 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:33 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:04 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:57 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:55 gcp linux TestAccept
2:55 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:54 azure linux TestAccept
2:51 azure-ucws linux TestAccept
2:46 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:45 aws-ucws linux TestAccept
2:41 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:38 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:38 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:36 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:33 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:29 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:29 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:28 azure linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct

Co-authored-by: Isaac
Git Bash rewrites the /Users/... argument to C:/Program Files/Git/Users/... before the CLI sees it, so the recorded request paths diverged; MSYS_NO_PATHCONV=1 disables that conversion. Also force LF on committed .py fixtures so the uploaded raw_body does not gain a CR on Windows checkouts.

Co-authored-by: Isaac
Comment thread acceptance/.gitattributes Outdated
# uploading the file's content to a workspace.
*.txt text eol=lf
*.lvdash.json text eol=lf
*.py text eol=lf

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in b8d71bf. With the request bodies filtered out of the recorded output, the line ending of the .py fixture no longer matters.

Uploaded .gitignore
Uploaded hello.py

>>> print_requests.py --sort //api/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use jq to filter to the method and path to omit the body from the output.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in b8d71bf, the script now pipes print_requests.py through jq '{method, path}' so the body is omitted.

@@ -0,0 +1 @@
print("hello")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test should be nested under cmd/sync/<test name>.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to acceptance/cmd/sync/dryrun-missing-remote in b8d71bf. The parent cmd/sync test syncs its own directory, so its output now also lists the nested test files, same as the existing dryrun/out.test.toml entry.

# A dry run must not create the missing remote dir or upload anything.
# Note: output line starting with Action lists files in non-deterministic order so we filter it out
# MSYS_NO_PATHCONV=1 prevents Git Bash on Windows from converting /Users/... to C:/Program Files/Git/Users/...
MSYS_NO_PATHCONV=1 trace $CLI sync . /Users/$CURRENT_USER_NAME/missing-dir --dry-run | grep -v "^Action" | sort

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concurrent tests will interfere. Tip: use ephemeral remote directory and clean it up on exit.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in b8d71bf. The test now syncs to /Users//missing-dir-$UNIQUE_NAME and the cleanup trap deletes the remote directory on exit.

…dryrun

# Conflicts:
#	acceptance/.gitattributes
Move the test under acceptance/cmd/sync/dryrun-missing-remote, use an
ephemeral remote directory based on UNIQUE_NAME with cleanup on exit,
and filter recorded requests to method and path via jq so request
bodies stay out of the output. Drop the .gitattributes *.py entry;
with bodies omitted the line ending of the uploaded fixture no longer
affects the output. Regenerate the parent cmd/sync output which now
lists the nested test directory files.

Co-authored-by: Isaac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants