Translate code_source git and include_paths in convert-to-dabs - #6495
Conversation
Instead of rejecting a git-pinned or include_paths-narrowed code_source, convert
now emits a `tgz` artifact (the DABs artifact snapshotter): DABs builds the
tarball from the git ref / include subset at deploy, and code_source_path points
at the built tarball. Plain snapshots are unchanged — code_source_path stays the
source directory, packaged by the deploy-time aicode mutator.
- snapshot.git.{branch,commit} -> artifact git.{branch,commit}
- snapshot.include_paths -> artifact include (code-source-root-relative, matching
air CLI semantics)
- remote_volume is still rejected (not representable per code source).
This unblocks full-fidelity conversion of the cases convert previously refused.
Stacked on the artifacts tgz/git/include primitive (#6428).
Co-authored-by: Isaac <no-reply@databricks.com>
Match the artifact snapshotter's path-relative entry naming: emit the artifact `path` as the code dir's parent and `include` as basename-prefixed subpaths, so archive entries come out as "<basename>/..." — the layout the runtime extracts to /databricks/code_source/<dir>. - root_path ./src, no include_paths -> path ".", include ["src"] - root_path ./src, include_paths [x] -> path ".", include ["src/x"] Co-authored-by: Isaac <no-reply@databricks.com>
AI Runtime training runs are long, so the generated next-steps suggest `bundle run <job> --no-wait` — submit and return the terminal rather than blocking on a streamed run. Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
The launcher runs command.sh from the launch dir, but code_source extracts to /databricks/code_source/<dir>, so a bare command like `python train.py` couldn't find its files. Prepend a cd into that dir to the generated command.sh so relative paths resolve against the code — matching how the command reads under `air run`. Convert-only; native `air run` is unchanged. Co-authored-by: Isaac <no-reply@databricks.com>
| // environments[] spec, which the runtime installs from directly; no requirements.yaml | ||
| // is emitted. | ||
| // | ||
| // When the snapshot pins a git ref or narrows to include_paths, convert instead emits |
There was a problem hiding this comment.
if there's git: but not branch or commit what is the behavior? Do we error in CLI? Does translation fail?
There was a problem hiding this comment.
It would fail when user runs convert-to-dabs before any sort of dab command
There was a problem hiding this comment.
throws git: must specify either 'branch' or 'commit'
| const generatedArtifactsDir = "generated_artifacts" | ||
|
|
||
| // codeSourceArtifactKey names the `tgz` artifact convert emits for a git/include | ||
| // snapshot. codeSourceTgzArtifact is where DABs writes the built tarball — kept out of |
There was a problem hiding this comment.
can we make sure that include_paths tests directories? That is main use case not individual files
|
|
||
| // cdIntoCodeSource prepends a `cd` into the extracted code dir to the generated | ||
| // command.sh, so the user's command runs against the code rather than the launch dir. | ||
| func cdIntoCodeSource(artifacts []uploadItem, dirName string) { |
There was a problem hiding this comment.
I am confused by this. Why is it needed?
There was a problem hiding this comment.
Not clear to me what this is fixing
There was a problem hiding this comment.
The launcher runs command.sh from the launch directory, but code_source is extracted to /databricks/code_source/<dir>. If no cd, a relative command like python train.py runs from the launch dir and can't find the file.
I think running python train.py is what most users would assume and they would be confused why they have to cd into /databricks/code_source/<dir> before running.
^ This was a requirement raised by Pieter in the previous pr because we are making tgz a generic artifact type, we need to follow DABs semantics for what they do with the other artifact types which I agree with. This comment
There was a problem hiding this comment.
isn't that what we do currently in CLI? Why is there a difference with DABs
| if snap == nil || (snap.Git == nil && len(snap.IncludePaths) == 0) { | ||
| return nil | ||
| } | ||
| codeDirRel := strings.TrimPrefix(codeDirPath, "./") |
There was a problem hiding this comment.
from claude:
Issue: With root_path: ., codeDirRel is ".", so path.Base → "." and path.Dir → ".", yielding include: ["."] and an injected cd /databricks/code_source/..
Fix: Guard/normalize the root_path == "." case or confirm it's unreachable.
Guard convert against a git/include_paths snapshot whose root_path resolves to the bundle root: there is no basename to nest the archive under, and an include rooted at "." would sweep the bundle's own generated files into the tarball. Point root_path at a subdirectory instead. Also add a test covering directory include_paths (the primary use case). Co-authored-by: Isaac <no-reply@databricks.com>
The AIR runtime exports $CODE_SOURCE_PATH (and symlinks it under $HOME) but never cds into the code source — the user's command owns its own cd, as every air example does. So a working air command already cds via $CODE_SOURCE_PATH, and injecting cd /databricks/code_source/<dir> only rescued commands that would also fail under air run, making convert diverge from it. Copy the command verbatim instead. Verified end-to-end on e2-dogfood: a cd $CODE_SOURCE_PATH command runs to SUCCESS with no injection. Co-authored-by: Isaac <no-reply@databricks.com>
The fixture command now cds via $CODE_SOURCE_PATH (a real air command), and command.sh is emitted verbatim with no injected cd — matching the runtime contract. Regenerates the golden. Co-authored-by: Isaac <no-reply@databricks.com>
Summary
experimental air convert-to-dabspreviously rejected acode_source.snapshotthatpinned a git ref or narrowed to
include_paths— it errored and told the user to workaround it. Now it translates them into a
tgzartifact (added in #6428): DABs buildsthe tarball from the git ref / include subset at deploy, and
code_source_pathpointsat the built tarball.
This closes the conversion gaps for the two cases convert used to refuse.
Mapping
For a snapshot that pins a git ref and/or
include_paths, convert emits atgzartifact and points
code_source_pathat its output:snapshot.git.{branch,commit}→ artifactgit.{branch,commit}snapshot.include_paths→ artifactincludeArchive entries must nest under the code directory's basename (the runtime extracts to
/databricks/code_source/<dir>), so convert emitspath= the code dir's parent andinclude= basename-prefixed subpaths:root_path: ./src→path: ".", include: ["src"]root_path: ./src,include_paths: [foo]→path: ".", include: ["src/foo"]A plain snapshot (no git / include_paths) is unchanged:
code_source_pathstays thesource directory, packaged at deploy.
remote_volumeis still rejected — a per-sourcevolume isn't representable in a bundle (set
workspace.artifact_pathinstead).Testing
Unit tests assert the emitted artifact (
type,path,git/include,files) andthe rewritten
code_source_pathfor both the git-ref and include_paths cases; theplain-snapshot path is covered by the existing convert acceptance golden.
E2E test:
TEST INCLUDE
Setup
convert to dabs
investigate artifacts:
deploy:
run:
TEST GIT
Setup dirty git:
convert to dabs and investigate artifact:
deploy:
Prove we properly exclude dirty git:
run: