job_runs: add lifecycle.triggers.on_file_change - #6309
Merged
Conversation
Collaborator
Integration test reportCommit: f964f6f
Top 3 slowest tests (at least 2 minutes):
|
radakam
marked this pull request as ready for review
August 19, 2026 08:04
Base automatically changed from
deco-job-runs-lifecycle-on-bundle-deploy
to
main
August 19, 2026 09:41
radakam
force-pushed
the
deco-job-runs-lifecycle-on-file-change
branch
from
August 19, 2026 09:47
b8c20f6 to
8744e5e
Compare
radakam
force-pushed
the
deco-job-runs-lifecycle-on-file-change
branch
from
August 19, 2026 11:39
8744e5e to
47994d8
Compare
denik
reviewed
Aug 19, 2026
denik
reviewed
Aug 19, 2026
denik
reviewed
Aug 20, 2026
denik
reviewed
Aug 20, 2026
denik
reviewed
Aug 21, 2026
denik
reviewed
Aug 21, 2026
radakam
force-pushed
the
deco-job-runs-lifecycle-on-file-change
branch
from
August 24, 2026 13:24
0756969 to
4e7b960
Compare
denik
reviewed
Aug 24, 2026
radakam
force-pushed
the
deco-job-runs-lifecycle-on-file-change
branch
from
August 25, 2026 08:12
17857f9 to
45c4ccd
Compare
radakam
changed the base branch from
main
to
job-runs-drop-unit-tests-covered-by-acc
August 25, 2026 08:13
Base automatically changed from
job-runs-drop-unit-tests-covered-by-acc
to
main
August 25, 2026 10:05
radakam
force-pushed
the
deco-job-runs-lifecycle-on-file-change
branch
from
August 25, 2026 10:12
d65e640 to
b0437a3
Compare
jariaspdev-lab
pushed a commit
to jariaspdev-lab/cli
that referenced
this pull request
Aug 25, 2026
## Changes Drop `job_run` unit tests that duplicate existing acceptance coverage, and add `destroy_unfinished_run` so cancel-before-delete is asserted through the same path a user destroy takes. Kept the wait/GetRun stubs that acceptance cannot reach without faking the Jobs API. ## Why Split from review on databricks#6309: if a unit test is already covered by acc, keep the acc test. ## Tests - New acc: `acceptance/bundle/resources/job_runs/destroy_unfinished_run` (faulted wait, then destroy cancel + delete order)
denik
reviewed
Aug 27, 2026
denik
force-pushed
the
deco-job-runs-lifecycle-on-file-change
branch
3 times, most recently
from
September 4, 2026 10:05
0ad6021 to
5e8ec14
Compare
syncableRelPaths re-derived HasOnFileChange over the job_runs map purely so the mutator stayed cheap to call from more than one place. With a single call site, collect the matching names once and list the sync tree only when that set is non-empty. Sorting the names also keeps diagnostics from several job_runs out of Go map order. Co-authored-by: Isaac
…state Removing a trigger downgraded the change to skip and wrote the cleared fingerprint, so re-adding the trigger re-fired the run even when the watched file had not changed since it last ran. Drop the change instead: state keeps the last hash, and re-adding compares against it, so the run fires only when the file changed while the trigger was disarmed. That removes the only producer of PersistState, so the plan entry field, the change field and the persist-state branches in plan and apply all go away. lifecycle.triggers state no longer needs anything from the shared engine: deployplan, bundle_plan.go and bundle_apply.go are back to their state on main, and skip once again always means no state write. Co-authored-by: Isaac
…ide table The knownMissingInRemoteType entry for job_runs was moved and its comment reworded for no reason; the map is not sorted either way, so put it back untouched. Only the knownMissingInStateType path has to change: state renamed triggers to triggers_state, so the whole lifecycle.triggers subtree is now missing from StateType rather than just its elements. Write each TestJobRunOverrideChangeDescTriggerRemoved case one field per line with named fields. Co-authored-by: Isaac
Keep the assertion at each call site and hoist only the expected substring, which is the one literal inlining would have repeated five times. Co-authored-by: Isaac
This reverts 364ff57. plan_no_changes was not the helper meant to be inlined. Co-authored-by: Isaac
The script writes out.plan.create.json, so the file has to be checked in: with it missing the run fails with "Test produced unexpected files". Co-authored-by: Isaac
Build each diagnostic at its call site so severity and locations are visible there, keeping only the shared summary prefix as a constant. Co-authored-by: Isaac
filepath.IsAbs only recognises the host's flavour, so "C:\watched.txt" was rejected on Windows but treated as a relative "C:" directory on macOS and Linux, where it then warned about matching no files. The same bundle now fails validation the same way wherever it is deployed from. Co-authored-by: Isaac
The emptiness rule enumerates every field of the struct, so keep it next to the struct rather than in PrepareState in another package: a fingerprint added later without extending it would be silently dropped instead of persisted. Co-authored-by: Isaac
sync drops files whose notebook type it cannot determine, so a malformed .ipynb is hashed but never uploaded. That is intended - editing one is when the run should re-fire - but it is worth saying so where the set is chosen. Co-authored-by: Isaac
The four fixed cases were spelled as parsed-path Len()+HasPrefix comparisons, which is exact equality written the long way; every other resource's OverrideChangeDesc switches on path.String(). Do the same and drop the four package-level path vars. The single on_file_change pattern entry has no fixed string, so it stays a Parent() check in the default branch. Co-authored-by: Isaac
lifecycle.go is for lifecycle settings common to all resources; the job-run trigger types belong next to JobRun. Pure move, no behaviour change. Co-authored-by: Isaac
main now autogenerates the job_runs pydabs models, so on_file_change has to be regenerated into job_run_trigger.py and the refschema golden. Also update the knownMissingInStateType entry: state renamed triggers to triggers_state, so the whole lifecycle.triggers subtree is missing from StateType, not just its elements. Co-authored-by: Isaac
main added a [PLAN_VERSION] replacement, so the committed out.plan.create.json needs the masked value in place of the literal. Co-authored-by: Isaac
Rebasing onto main pulled in DMS state recording (#6094), which stamps a version_id into the deployment block under DMS. out.plan.create.json served double duty as both the compared golden and the --plan deploy input, so it could not simply be masked: deploy from the raw plan (keeps version_id) and nostamp a separate copy for the golden. Also regenerate the refschema for the on_file_change / triggers_state fields.
…neric check A fired trigger recreates the run, and checkForPreventDestroy already rejects a recreate on a prevent_destroy resource generically (bundle/phases/plan.go), so the special-case validation and its schema-doc note were redundant. Removing them lets the combo create once and error only when a re-fire actually plans a recreate, matching every other recreate field.
…comment The AI Runtime sync overlay is gone (tgz artifact now); build and the predeploy script are the only steps that produce a watched file here.
Write the watched file to disk and hash it directly instead of layering an in-memory overlay over the sync root.
…troy note validate-generated failed because the pydabs job_run_trigger.py docstrings still carried "Incompatible with lifecycle.prevent_destroy"; only annotations.yml and jsonschema.json had been regenerated.
denik
force-pushed
the
deco-job-runs-lifecycle-on-file-change
branch
from
September 10, 2026 12:52
24f9f1f to
6391342
Compare
Drop the hand-rolled isWindowsAbs cross-OS check: it rejected "c:foo" and "\foo" which are valid relative filenames on POSIX, with a misleading "must be relative" error. filepath.IsAbs is already OS-aware, so a path rooted only on the other OS falls through to the sync-root containment check, which is what actually guards against escaping the tree. Reported by shreyas-goenka.
| @@ -0,0 +1 @@ | |||
| * direct: resources.job\_runs: new lifecycle.triggers.on\_file\_change setting to restart the run when monitored files change. Can be set to a series of paths or globs. ([#6309](https://github.com/databricks/cli/pull/6309)) | |||
Member
There was a problem hiding this comment.
Suggested change
| * direct: resources.job\_runs: new lifecycle.triggers.on\_file\_change setting to restart the run when monitored files change. Can be set to a series of paths or globs. ([#6309](https://github.com/databricks/cli/pull/6309)) | |
| * direct: `resources.job_runs`: new `lifecycle.triggers.on_file_change` setting to restart the run when monitored files change. Can be set to a series of paths or globs. ([#6309](https://github.com/databricks/cli/pull/6309)) |
Comment on lines
+63
to
+65
| `C:\watched.txt`, | ||
| "C:/watched.txt", | ||
| "c:watched.txt", |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
Add
lifecycle.triggers.on_file_changeforresources.job_runs(direct engine).bundle plan/bundle validate) and after predeploy (forbundle deploy). Hash through the sameSyncRootVFS used by sync, so WSFS notebooks and generated overlays are included. An mtime-onlytouchdoes not re-fire.libs/sync.FileList+path.Match), including sync's generated include patterns..gitignoreandsync.excludeare honored. Directories never appear in that list, somigrations/*hashes only regular files.lifecycle.triggers_stateas normalized-pattern → aggregate-hash entries, plus a UUID foron_bundle_deploy. Each aggregate hashes the sorted matched paths and their content hashes. Unarmed runs omitlifecyclefrom state. A no-match pattern stores the empty-set hash under the same pattern key used once files appear.**(path.Matchtreats it as two ordinary stars, not recursive), and combining a trigger withprevent_destroy. Warn when no synced files match; the empty-set fingerprint still re-arms once a matching file appears. Pattern errors are reported at validate time; hashing runs only when planning or deploying.Why
Users need to re-run a job when local inputs change (SQL migrations, notebooks, configs) without re-firing on every deploy. Content hashes keep the trigger stable under sync noise while still catching real edits.
Fingerprints must be computed after build and predeploy scripts, otherwise a trigger on a generated file fires one deploy late. Hashing through
SyncRootkeeps notebook and overlay content consistent with what sync uploads.A pattern that hashes nothing used to look like an unarmed trigger. Recording an empty-set fingerprint under a stable pattern key makes those cases re-arm later, so they are warnings rather than deploy failures.
Fingerprints live on
triggers_state, not on the YAMLtriggerslist, so user config and planner state stay distinct. Clearing a trigger must persist that omission; otherwise re-adding it looks unchanged and never fires.Tests
SyncRootoverlay reads, absolute-pattern rejection, and sync-root-relative missing-pattern keys; trigger validation for empty and invalid entries andprevent_destroyconflicts; state-only classification when a trigger is removed; omission of emptylifecyclefrom serialized state.on_file_change— first deploy fires; unchanged redeploy andtouchplan nothing; an edit recreates (including READPLAN); deleting and restoring the file each re-fire; removing the trigger performs norun-now, clears its state through both live and serialized plans, and re-adding it re-fires.on_file_change_build— a prebuild script rewriting a watched file re-fires the run in that same deploy.on_file_change_glob—migrations/*plusmysubdir/*.txt; matches appearing and disappearing re-fire; files below the glob and gitignored files do not;**is rejected; READPLAN.on_file_change_errors— pattern outside the sync root (error); directory-only, directory plus sync-excluded, only sync-excluded, and no-match cases (warnings); unsupported**and an absolute pattern (errors).on_file_change_include_sync_root— patterns from included YAML resolve next to the include, at the bundle root (../), and at the sync root (../../); each edit recreates only the corresponding pattern entry.