Skip to content

Support tgz artifacts built from a directory or git ref - #6428

Merged
vinchenzo-db merged 10 commits into
mainfrom
air-artifacts-code-source
Sep 2, 2026
Merged

Support tgz artifacts built from a directory or git ref#6428
vinchenzo-db merged 10 commits into
mainfrom
air-artifacts-code-source

Conversation

@vinchenzo-db

@vinchenzo-db vinchenzo-db commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Adds a tgz artifact type that packages source files into a gzipped tarball as part of bundle deploy, alongside the existing whl and jar types. Unlike those, DABs builds the tarball itself — there is no user build command.
Two sources are supported:

  • include: pack paths under the artifact's path from the working tree (.gitignore honored).
  • git: snapshot a committed ref (branch/commit) via git archive.

Archive entries are named relative to the artifact's path, so the layout is controlled by config. The built tarball is uploaded and referenced like any other artifact file.

artifacts:
  code:
     type: tgz
     path: .
     include: [src]
     files:
       - source: ./dist/code.tgz

Co-authored-by: Isaac

Changes

Why

Tests

Show that the include field is respected, we notice the ./dist/code.tgz is properly uploaded.

 v.chen at https://dbc-04ac0685-8857 in ~/.worktrees/cli-artifacts (git:air-artifacts-code-source) [20:04:39]
$ rm -rf /tmp/demo-include && mkdir -p /tmp/demo-include/src && cd /tmp/demo-include
printf 'print("hello from packaged code")\n' > src/train.py
printf '#!/bin/bash\ncd "$(dirname "$0")"\npython src/train.py\n' > command.sh
cat > databricks.yml <<YAML
bundle: {name: demo-include}
targets: {dev: {mode: development, default: true, workspace: {host: $HOST}}}
artifacts:
  code_source:
    type: tgz
    include: [src]
    files: [{source: ./dist/code.tgz}]
resources:
  jobs:
    demo_include:
      name: demo-include
      tasks:
        - task_key: train
          environment_key: default
          ai_runtime_task:
            experiment: demo-include
            deployments: [{command_path: ./command.sh, compute: {accelerator_type: GPU_1xA10, accelerator_count: 1}}]
            code_source_path: ./dist/code.tgz
      environments: [{environment_key: default, spec: {environment_version: "5", dependencies: []}}]
YAML

$CLI bundle deploy -t dev                       # → "Building code_source..." → "Uploading dist/code.tgz..."
tar tzf dist/code.tgz                            # → src/train.py   (DABs built it from ./src)
JID=$($CLI bundle summary -t dev | grep -oE 'jobs/[0-9]+' | head -1 | cut -d/ -f2)
$CLI jobs get $JID | grep code_source_path       # → rewritten to /Workspace/.../artifacts/.internal/code.tgz
$CLI bundle destroy -t dev --auto-approve

Building code_source...
Uploading dist/code.tgz...
Uploading bundle files to /Workspace/Users/v.chen@databricks.com/.bundle/demo-include/dev/files...
Created jobs.demo_include
Files: 4 uploaded, 0 deleted
Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
src/train.py
          "code_source_path": "/Workspace/Users/v.chen@databricks.com/.bundle/demo-include/dev/artifacts/.internal/code.tgz",
The following resources will be deleted:
  delete resources.jobs.demo_include

All files and directories at the following location will be deleted: /Workspace/Users/v.chen@databricks.com/.bundle/demo-include/dev

Destroy: 1 deleted

Show that git refs are respected:

# v.chen at https://dbc-04ac0685-8857 in /tmp/demo-include (git:) [20:04:57]
$ rm -rf /tmp/demo-git && mkdir -p /tmp/demo-git/src && cd /tmp/demo-git
git init -q && git config user.email t@t.co && git config user.name t
printf 'print("committed code")\n' > src/train.py
printf '#!/bin/bash\ncd "$(dirname "$0")"\npython src/train.py\n' > command.sh
git add -A && git commit -qm init
BR=$(git rev-parse --abbrev-ref HEAD)
cat > databricks.yml <<YAML
bundle: {name: demo-git}
targets: {dev: {mode: development, default: true, workspace: {host: $HOST}}}
artifacts:
  code_source:
    type: tgz
    git: {branch: $BR}          # or  commit: <sha>
    include: [src]
    files: [{source: ./dist/code.tgz}]
resources:
  jobs:
    demo_git:
      name: demo-git
      tasks:
        - task_key: train
          environment_key: default
          ai_runtime_task:
            experiment: demo-git
            deployments: [{command_path: ./command.sh, compute: {accelerator_type: GPU_1xA10, accelerator_count: 1}}]
            code_source_path: ./dist/code.tgz
      environments: [{environment_key: default, spec: {environment_version: "5", dependencies: []}}]
YAML

$CLI bundle deploy -t dev
tar tzf dist/code.tgz                            # → src/train.py   (from `git archive` of the ref)
$CLI bundle destroy -t dev --auto-approve
Databricks pre-commit Git Hook V2.5.0
Running secret scanning on changes staged for commit.
secret-scan hook completed in 126 ms
Unknown project name: None, skipping linting.
pre-commit-total hook completed in 153 ms
Databricks commit-msg Git Hook V2.5.0
Running secret scanning on commit message.
secret-scan hook completed in 40 ms
commit-msg-total hook completed in 50 ms
Building code_source...
Uploading dist/code.tgz...
Uploading bundle files to /Workspace/Users/v.chen@databricks.com/.bundle/demo-git/dev/files...
Created jobs.demo_git
Files: 4 uploaded, 0 deleted
Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
src/
src/train.py
The following resources will be deleted:
  delete resources.jobs.demo_git

All files and directories at the following location will be deleted: /Workspace/Users/v.chen@databricks.com/.bundle/demo-git/dev

Destroy: 1 deleted

…git ref

Proposal for AIR-on-DABs code_source parity (see DABs x AIR CLI Alignment).
Extends the existing `artifacts` block so DABs can build the code tarball itself
instead of only uploading a user build command's output:

  artifacts:
    code_source:
      type: tgz            # new ArtifactType (was whl|jar)
      include: [src/...]   # subpaths to pack, gitignore-honored
      git: {branch|commit} # snapshot a ref instead of the working tree
      files: [{source: ./dist/code.tgz}]

`build` and `git`/`include` are mutually exclusive: either the user's command
produces the tarball (today's behavior) or DABs does. The produced file flows
through the existing artifact upload path, so an ai_runtime_task's
code_source_path pointing at it is uploaded and rewritten to the remote path
with no new reference syntax.

- include: reuses the bundle sync walker (matches file-sync filtering)
- git: shells out to `git archive`
Verified end-to-end on staging (both modes deploy; code_source_path rewritten).

Open for review: this is a proposal to react to, not a finished feature.
jsonschema annotations for the new fields are a follow-up.

Co-authored-by: Isaac
@vinchenzo-db vinchenzo-db changed the title [POC] Native tgz artifacts: build code_source from include paths / a … [POC] DABs AIR native fields Aug 28, 2026
- annotations.yml: descriptions for the new `include`/`git` (+ branch/commit)
  fields and `tgz` type, so TestRequiredAnnotationsForNewFields passes.
- jsonschema.json: regenerated (adds include/git/ArtifactGit/tgz; scoped diff).
- validate/strict acceptance golden: artifact type enum now [whl jar tgz].

All bundle/... unit tests, go vet, and the artifacts+validate acceptance
subsets pass.

Co-authored-by: Isaac
- tarball.go: use errors.New for the no-args git error (perfsprint lint).
- jsonschema.json: regenerated post-merge; picks up the variable-ref regex
  the newer generator emits (validate-generated).

Co-authored-by: Isaac
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: f2a8fd9

Run: 33203627766

Env 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 1 1 274 1207 3:59
💚​ aws windows 1 1 276 1205 4:06
💚​ azure linux 1 1 273 1207 3:59
💚​ azure windows 1 1 275 1205 3:38
💚​ gcp linux 1 1 274 1207 4:18
💚​ gcp windows 1 1 276 1205 3:44
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
Top 3 slowest tests (at least 2 minutes):
duration env testname
4:01 aws windows TestAccept
3:39 gcp windows TestAccept
3:32 azure windows TestAccept

@ben-hansen-db ben-hansen-db left a comment

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.

Looks good, let's follow the file generation flow for the autogen files


// tarballFromGit snapshots a git ref via `git archive`, so the archive reflects the
// committed tree at that ref rather than the working tree. Commit wins over Branch.
func tarballFromGit(ctx context.Context, b *bundle.Bundle, a *config.Artifact, w io.Writer) error {

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.

have we tested equivalence of tarballs from here vs CLI? Note also the tarball changes that landed in CLI recently to respect gitignore

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Made them share code instead of testing equivalence

b.Metrics.AddBoolValue(metrics.ArtifactFilesIsSet, len(artifact.Files) != 0)

// A `tgz` artifact with `include`/`git` is built by DABs itself in the build
// phase (see artifacts.Build). `build` and `git`/`include` are mutually

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.

is semantics for include the same as in air cli? There it is relative to code source root, is include relative to bundle root in DABs? Can we make sure that is documented well somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

include is now relative to the code-source root, same as the air CLI. The reason I decided this is so that convert-to-dabs can map air CLI's include_paths straight across later.

var EnumFields = map[string][]string{
"artifacts.*.executable": {"bash", "sh", "cmd"},
"artifacts.*.type": {"whl", "jar"},
"artifacts.*.type": {"whl", "jar", "tgz"},

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.

above it says this is autogenerated. Did claude update this file or was it infact autogenerated?

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.

there's probably a flow for autogenerating this file to follow

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

^ claude ran the autogeneration script

Comment thread bundle/artifacts/tarball.go Outdated
Comment thread bundle/artifacts/tarball.go Outdated
vinchenzo-db and others added 5 commits August 31, 2026 21:56
- buildTarballArtifact writes to a temp file and renames on success, so a failed
  git archive / pack no longer leaves a partial tarball at the output path.
- Drop the unreachable `.` fallback in tarballFromInclude; include mode is only
  entered with a non-empty a.Include.

Co-authored-by: Isaac <no-reply@databricks.com>
Clarify that DABs `include` is bundle-root-relative and composes files from
anywhere in the bundle (e.g. a code dir plus a sibling env file), intentionally
broader than the air CLI's code-source-root-relative include. Update the field
doc and schema annotation, and add a packing-layer test showing a tarball
composed across two directories keeps both entries at bundle-root-relative paths.

Co-authored-by: Isaac <no-reply@databricks.com>
The AI Runtime extracts a code_source tarball to /databricks/code_source/<dir>,
so the archive must have a single load-bearing top-level directory. The previous
include implementation packed bundle-root-relative entries with no such prefix,
diverging from both the air CLI and DABs' own aicode directory packer.

- Export aicode.BuildCodeSnapshot and reuse it for include mode, so entries nest
  under the code-source root's directory name (relBase + prefix), gitignore-aware
  and reproducible — identical layout to a directory code_source_path.
- include now selects subpaths of the code-source root (`path`), relative to it,
  matching the air CLI's include_paths (so convert-to-dabs can translate 1:1).
- git mode adds --prefix=<dir>/ so git archive produces the same top-level layout.
- Replace the bundle-root-relative field docs/test from the prior commit with the
  code-source-root semantics and a git-mode prefix test.

Co-authored-by: Isaac <no-reply@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Disable core.autocrlf in the temp repo and compare trimmed content, so the
content assertion doesn't depend on the Windows runner's autocrlf default.

Co-authored-by: Isaac <no-reply@databricks.com>

@ben-hansen-db ben-hansen-db left a comment

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.

LGTM, nice work!

Comment thread bundle/artifacts/tarball.go Outdated
Comment thread bundle/artifacts/tarball.go Outdated
Comment thread bundle/artifacts/tarball.go Outdated
Comment thread bundle/artifacts/tarball.go Outdated
}

// codeRoot returns the artifact's code-source root as a sync-root-relative path plus
// its directory name. dirName is the load-bearing top-level entry the runtime extracts

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.

load-bearing? 😂

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed, lol

if len(a.Files) != 1 {
return fmt.Errorf("artifact %q: a tgz artifact needs exactly one `files` entry naming the output path", name)
}
out := a.Files[0].Source // made absolute by artifacts.Prepare

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.

Why do we need the output path to be specified?

We can interpolate a temporary path, under .databricks for example, such that this file is never included in the archive it produces on the second run.

Artifacts are uploaded separately from regular files. The places that refer to artifacts do path rewriting to make sure the final path we use in the workspace is the right one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For now I think this is still necessary. We can mark it as a follow up if you feel it shouldn't be, but right now the BYOT API specifies code_source_path, which is where the user runs their code. Without this output path I think it becomes a little opaque to the user that code_source_path would still say ./dist/code.tgz, but the artifact now lives somewhere else and nothing links them.

I asked claude what it would take to bridge this gap and we would need to let code_source_path reference the artifact by name, but alledgely its not a very easy fix.

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.

Thanks, yeah I agree with the opaque bit, and that referencing via interpolation is the right solution.

You'd get something like this:

code_source_path: ${artifacts.air_code_source.output}

Comment thread bundle/artifacts/tarball.go
- Make tarball creation independent of the aicode package: inline the tar/gzip
  packing in bundle/artifacts and revert the aicode export.
- Drop the injected code-dir basename prefix. Archive entries are now named
  relative to the artifact's `path`, so `path: src` + `include: [foo]` and
  `path: .` + `include: [src/foo]` control the layout explicitly.
- Do not apply the bundle-wide sync.include/exclude when packing; only .gitignore
  filters the walk.
- Simplify the temp-file cleanup (drop the redundant renamed flag).

Co-authored-by: Isaac <no-reply@databricks.com>
@vinchenzo-db vinchenzo-db changed the title [POC] DABs AIR native fields Support tgz artifacts built from a directory or git ref Sep 2, 2026
@vinchenzo-db
vinchenzo-db requested a review from pietern September 2, 2026 08:35
if len(a.Files) != 1 {
return fmt.Errorf("artifact %q: a tgz artifact needs exactly one `files` entry naming the output path", name)
}
out := a.Files[0].Source // made absolute by artifacts.Prepare

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.

Thanks, yeah I agree with the opaque bit, and that referencing via interpolation is the right solution.

You'd get something like this:

code_source_path: ${artifacts.air_code_source.output}

@vinchenzo-db
vinchenzo-db added this pull request to the merge queue Sep 2, 2026
Merged via the queue into main with commit dc739cc Sep 2, 2026
38 checks passed
@vinchenzo-db
vinchenzo-db deleted the air-artifacts-code-source branch September 2, 2026 16:50
vinchenzo-db added a commit that referenced this pull request Sep 2, 2026
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>
github-merge-queue Bot pushed a commit that referenced this pull request Sep 3, 2026
## Summary

`experimental air convert-to-dabs` previously rejected a
`code_source.snapshot` that
pinned a git ref or narrowed to `include_paths` — it errored and told
the user to work
around it. Now it translates them into a `tgz` artifact (added in
#6428): DABs builds
the tarball from the git ref / include subset at deploy, and
`code_source_path` points
at 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
a `tgz`
artifact and points `code_source_path` at its output:

- `snapshot.git.{branch,commit}` → artifact `git.{branch,commit}`
- `snapshot.include_paths` → artifact `include`

Archive entries must nest under the code directory's basename (the
runtime extracts to
`/databricks/code_source/<dir>`), so convert emits `path` = the code
dir's parent and
`include` = 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_path` stays the
source directory, packaged at deploy. `remote_volume` is still rejected
— a per-source
volume isn't representable in a bundle (set `workspace.artifact_path`
instead).

## Testing

Unit tests assert the emitted artifact (`type`, `path`, `git`/`include`,
`files`) and
the rewritten `code_source_path` for both the git-ref and include_paths
cases; the
plain-snapshot path is covered by the existing convert acceptance
golden.

E2E test:
TEST INCLUDE
Setup
```
$ rm -rf /tmp/pr2-include && mkdir -p /tmp/pr2-include/src && cd /tmp/pr2-include
printf 'print("include ok")\n' > src/train.py
printf 'python train.py\n'      > src/command.sh
printf 'excluded\n'             > src/extra.txt
cat > train.yaml <<'YAML'
experiment_name: pr2_include_demo
command: python train.py
compute: {accelerator_type: GPU_1xA10, num_accelerators: 1}
code_source:
  type: snapshot
  snapshot:
    root_path: ./src
    include_paths: [train.py, command.sh]
YAML
```
convert to dabs
```
$ /tmp/cli-pr2 experimental air convert-to-dabs train.yaml
Wrote a Databricks Asset Bundle to .:
  databricks.yml
  generated_artifacts/training_config.yaml
  generated_artifacts/command.sh

To deploy and run this workload as a bundle:
  1. /tmp/cli-pr2 bundle validate
  2. /tmp/cli-pr2 bundle deploy
  3. /tmp/cli-pr2 bundle run pr2_include_demo --no-wait

bundle deploy uploads the code source and launch scripts automatically.
To see what it deployed and where: /tmp/cli-pr2 bundle summary

Unlike `air run` (which submits an ephemeral run), bundle deploy creates a
persistent job that is not garbage-collected. When you are done, remove the
job and its uploaded files with:
  /tmp/cli-pr2 bundle destroy
```
investigate artifacts:
```
$ cat databricks.yml
cat generated_artifacts/command.sh
bundle:
  name: pr2_include_demo
sync:
  paths:
    - generated_artifacts
artifacts:
  code_source:
    type: tgz
    path: .
    include:
      - src/train.py
      - src/command.sh
    files:
      - source: ./dist/code_source.tgz
targets:
  dev:
    mode: development
    default: true
resources:
  jobs:
    pr2_include_demo:
      name: pr2_include_demo
      tasks:
        - task_key: pr2_include_demo
          environment_key: default
          max_retries: 3
          ai_runtime_task:
            experiment: pr2_include_demo
            deployments:
              - command_path: ./generated_artifacts/command.sh
                compute:
                  accelerator_type: GPU_1xA10
                  accelerator_count: 1
            code_source_path: ./dist/code_source.tgz
      environments:
        - environment_key: default
          spec:
            environment_version: "4"
cd /databricks/code_source/src
python train.py%  
```
deploy:
```
$ /tmp/cli-pr2 bundle validate && /tmp/cli-pr2 bundle deploy
Name: pr2_include_demo
Target: dev
Workspace:
  User: v.chen@databricks.com
  Path: /Workspace/Users/v.chen@databricks.com/.bundle/pr2_include_demo/dev

Validation OK!
Building code_source...
Uploading dist/code_source.tgz...
Uploading bundle files to /Workspace/Users/v.chen@databricks.com/.bundle/pr2_include_demo/dev/files...
Files: 2 uploaded, 0 deleted
Resources: 0 created, 0 changed, 0 deleted, 1 unchanged
# v.chen at ip-10-90-20-219 in /tmp/pr2-include (git:) [19:22:29]
$ tar tzf dist/code_source.tgz
src/command.sh
src/train.py
```
run:
```
# v.chen at ip-10-90-20-219 in /tmp/pr2-include (git:) [19:22:34]
$ /tmp/cli-pr2 bundle summary
Name: pr2_include_demo
Target: dev
Workspace:
  User: v.chen@databricks.com
  Path: /Workspace/Users/v.chen@databricks.com/.bundle/pr2_include_demo/dev
Resources:
  Jobs:
    pr2_include_demo:
      Name: [dev v_chen] pr2_include_demo
      URL:  https://e2-dogfood.staging.cloud.databricks.com/jobs/212600874211444?w=6051921418418893
# v.chen at ip-10-90-20-219 in /tmp/pr2-include (git:) [19:22:39]
$ /tmp/cli-pr2 bundle run pr2_include_demo --no-wait
Run URL: https://e2-dogfood.staging.cloud.databricks.com/jobs/212600874211444/runs/882449706897243?o=6051921418418893
```
<img width="1421" height="510" alt="image"
src="https://github.com/user-attachments/assets/ef824a46-9288-4ef3-aee0-537231949a6e"
/>
<img width="1043" height="530" alt="image"
src="https://github.com/user-attachments/assets/ce9fbb9d-496f-4736-95bb-211a9ee0fe36"
/>


TEST GIT
Setup dirty git:
```
# v.chen at ip-10-90-20-219 in /tmp/pr2-include (git:) [19:22:48]
$ rm -rf /tmp/pr2-git && mkdir -p /tmp/pr2-git/src && cd /tmp/pr2-git
git init -q -b main
printf 'print("git v1 committed")\n' > src/train.py
printf 'python train.py\n'           > src/command.sh
git add -A && git commit -qm "v1"
Databricks pre-commit Git Hook V2.5.0
Running secret scanning on changes staged for commit.
secret-scan hook completed in 74 ms
Unknown project name: None, skipping linting.
pre-commit-total hook completed in 90 ms
Databricks commit-msg Git Hook V2.5.0
Running secret scanning on commit message.
secret-scan hook completed in 23 ms
commit-msg-total hook completed in 29 ms
# v.chen at ip-10-90-20-219 in /tmp/pr2-git (git:main) [19:26:30]
$ printf 'print("v2 UNCOMMITTED - should NOT appear")\n' > src/train.py

cat > train.yaml <<'YAML'
experiment_name: pr2_git_demo
command: python train.py
compute: {accelerator_type: GPU_1xA10, num_accelerators: 1}
code_source:
  type: snapshot
  snapshot:
    root_path: ./src
    git: {branch: main}
YAML

```
convert to dabs and investigate artifact:
```
$ /tmp/cli-pr2 experimental air convert-to-dabs train.yaml
cat databricks.yml
Wrote a Databricks Asset Bundle to .:
  databricks.yml
  generated_artifacts/training_config.yaml
  generated_artifacts/command.sh

To deploy and run this workload as a bundle:
  1. /tmp/cli-pr2 bundle validate
  2. /tmp/cli-pr2 bundle deploy
  3. /tmp/cli-pr2 bundle run pr2_git_demo --no-wait

bundle deploy uploads the code source and launch scripts automatically.
To see what it deployed and where: /tmp/cli-pr2 bundle summary

Unlike `air run` (which submits an ephemeral run), bundle deploy creates a
persistent job that is not garbage-collected. When you are done, remove the
job and its uploaded files with:
  /tmp/cli-pr2 bundle destroy
bundle:
  name: pr2_git_demo
sync:
  paths:
    - generated_artifacts
artifacts:
  code_source:
    type: tgz
    path: .
    git:
      branch: main
    include:
      - src
    files:
      - source: ./dist/code_source.tgz
targets:
  dev:
    mode: development
    default: true
resources:
  jobs:
    pr2_git_demo:
      name: pr2_git_demo
      tasks:
        - task_key: pr2_git_demo
          environment_key: default
          max_retries: 3
          ai_runtime_task:
            experiment: pr2_git_demo
            deployments:
              - command_path: ./generated_artifacts/command.sh
                compute:
                  accelerator_type: GPU_1xA10
                  accelerator_count: 1
            code_source_path: ./dist/code_source.tgz
      environments:
        - environment_key: default
          spec:
            environment_version: "4"
```
deploy:
```
$ /tmp/cli-pr2 bundle validate && /tmp/cli-pr2 bundle deploy
Name: pr2_git_demo
Target: dev
Workspace:
  User: v.chen@databricks.com
  Path: /Workspace/Users/v.chen@databricks.com/.bundle/pr2_git_demo/dev

Validation OK!
Building code_source...
Uploading dist/code_source.tgz...
Uploading bundle files to /Workspace/Users/v.chen@databricks.com/.bundle/pr2_git_demo/dev/files...
Created jobs.pr2_git_demo
Files: 2 uploaded, 0 deleted
Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
```
Prove we properly exclude dirty git:
```
# v.chen at ip-10-90-20-219 in /tmp/pr2-git (git:main) [19:28:02]
$ mkdir -p /tmp/pr2-git-check && tar xzf dist/code_source.tgz -C /tmp/pr2-git-check
grep -R "committed" /tmp/pr2-git-check/src/train.py    # -> "git v1 committed"
grep -R "UNCOMMITTED" /tmp/pr2-git-check/src/train.py || echo "OK: uncommitted change correctly excluded"
print("git v1 committed")
OK: uncommitted change correctly excluded
```

run:
```
$ /tmp/cli-pr2 bundle summary
Name: pr2_git_demo
Target: dev
Workspace:
  User: v.chen@databricks.com
  Path: /Workspace/Users/v.chen@databricks.com/.bundle/pr2_git_demo/dev
Resources:
  Jobs:
    pr2_git_demo:
      Name: [dev v_chen] pr2_git_demo
      URL:  https://e2-dogfood.staging.cloud.databricks.com/jobs/235795448342543?w=6051921418418893
# v.chen at ip-10-90-20-219 in /tmp/pr2-git (git:main) [19:28:47]
$ /tmp/cli-pr2 bundle run pr2_git_demo --no-wait
Run URL: https://e2-dogfood.staging.cloud.databricks.com/jobs/235795448342543/runs/137864060324526?o=6051921418418893
```
<img width="1437" height="539" alt="image"
src="https://github.com/user-attachments/assets/0474aba1-2b4c-4cf1-b646-34a785efdc88"
/>
<img width="1035" height="494" alt="image"
src="https://github.com/user-attachments/assets/5a72ec33-03e7-4df1-9385-cdd942cfadf4"
/>

---------

Co-authored-by: Isaac <no-reply@databricks.com>
shreyas-goenka pushed a commit to shreyas-goenka/cli that referenced this pull request Sep 3, 2026
…databricks#6494)

databricks#6110 <- basically reverting this
pr

A local-directory code_source_path is now turned into a `tgz` artifact
and built and uploaded through the standard artifact path, instead of
the aicode mutator splicing a content-addressed tarball onto the sync
root. The tgz artifact is the single packing mechanism.

- aicode.PackageCodeSource now synthesizes a `tgz` artifact per
local-dir code_source_path (path = the dir's parent, include = its
basename, so archive entries nest under the basename for the
/databricks/code_source/<dir> layout) and rewrites code_source_path to
the built tarball. It runs before artifacts.Prepare (initialize) instead
of in the build phase.
- Remove the sync-root overlay, the content-addressed packer
(buildCodeSnapshot), bundle.HasAiRuntimeCodeSnapshot,
bundle.AiCodeSnapshotDir, and the validateSnapshotDir guards that only
existed for the overlay.
- Behavior change: only .gitignore filters the packaged files now; the
bundle-wide sync.include/exclude no longer apply to a code artifact.
- Keep the git_source / immutable-folder / source-linked / for_each
rejections.

## Why
databricks#6428 We
recently merged a DABs native uploader, which supercedes this mutator
workaround

## Tests
Unit tests

E2E test:
Setup:
```
# v.chen at ip-10-90-20-219 in /tmp/pr2-e2e (git:) [18:59:50]
$ cd ~/.worktrees/cli-rmaicode && go build -o /tmp/cli-pr3 . && cd -

rm -rf /tmp/pr3-e2e && mkdir -p /tmp/pr3-e2e/src && cd /tmp/pr3-e2e
printf 'print("train ok")\n' > src/train.py
# hand-authored command.sh cds into the extracted code dir itself
printf 'cd /databricks/code_source/src\npython train.py\n' > src/command.sh
cat > databricks.yml <<'YAML'
bundle:
  name: pr3-code-source-demo
resources:
  jobs:
    train:
      name: "[${bundle.target}] pr3 demo"
      tasks:
        - task_key: train
          environment_key: default
          ai_runtime_task:
            experiment: pr3_demo
            code_source_path: ./src
            deployments:
              - command_path: src/command.sh
                compute: {accelerator_type: GPU_1xA10, accelerator_count: 1}
      environments:
        - environment_key: default
          spec:
            environment_version: "5"
targets:
  dev: {mode: development, default: true}
YAML

export DATABRICKS_CONFIG_PROFILE=e2-dogfood DATA
/tmp/pr2-e2e
# v.chen at ip-10-90-20-219 in /tmp/pr3-e2e (git:) [18:59:59]
$ /tmp/cli-pr3 bundle deploy
Building air_code_source_src...
Uploading .databricks/air_code_source/air_code_source_src.tar.gz...
Uploading bundle files to /Workspace/Users/v.chen@databricks.com/.bundle/pr3-code-source-demo/dev/files...
Created jobs.train
Files: 3 uploaded, 0 deleted
Resources: 1 created, 0 changed, 0 deleted, 0 unchanged
```
Show deploy works:
```
# v.chen at ip-10-90-20-219 in /tmp/pr3-e2e (git:) [19:00:14]
$ /tmp/cli-pr3 bundle deploy
Building air_code_source_src...
Uploading .databricks/air_code_source/air_code_source_src.tar.gz...
Uploading bundle files to /Workspace/Users/v.chen@databricks.com/.bundle/pr3-code-source-demo/dev/files...
Files: 0 uploaded, 0 deleted
Resources: 0 created, 0 changed, 0 deleted, 1 unchanged
# v.chen at ip-10-90-20-219 in /tmp/pr3-e2e (git:) [19:01:00]
```
Investigate what is in tar:
```
$ tar tzf .databricks/air_code_source/air_code_source_src.tar.gz
src/command.sh
src/train.py
# v.chen at ip-10-90-20-219 in /tmp/pr3-e2e (git:) [19:01:29]
$ 
```
bundle summary:
```
$ /tmp/cli-pr3 bundle summary
Name: pr3-code-source-demo
Target: dev
Workspace:
  User: v.chen@databricks.com
  Path: /Workspace/Users/v.chen@databricks.com/.bundle/pr3-code-source-demo/dev
Resources:
  Jobs:
    train:
      Name: [dev v_chen] [dev] pr3 demo
      URL:  https://e2-dogfood.staging.cloud.databricks.com/jobs/1015159229445184?w=6051921418418893
# v.chen at ip-10-90-20-219 in /tmp/pr3-e2e (git:) [19:01:51]
$ 
```
bundle run:
```
$ /tmp/cli-pr3 bundle run train --no-wait
Run URL: https://e2-dogfood.staging.cloud.databricks.com/jobs/1015159229445184/runs/174551477706538?o=6051921418418893
```

Run details:
<img width="1512" height="821" alt="image"
src="https://github.com/user-attachments/assets/b18e41ce-f861-42be-b247-b5af50b6ce43"
/>
<img width="1495" height="672" alt="image"
src="https://github.com/user-attachments/assets/193f8e8e-97c0-4e0b-a12f-e5671c19f102"
/>

---------

Co-authored-by: Isaac <no-reply@databricks.com>
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.

4 participants