Skip to content

apps: git-back scaffolded apps when init runs inside a Git repo - #6406

Closed
atreyadbrx wants to merge 2 commits into
mainfrom
apps-init-git-autodetect
Closed

apps: git-back scaffolded apps when init runs inside a Git repo#6406
atreyadbrx wants to merge 2 commits into
mainfrom
apps-init-git-autodetect

Conversation

@atreyadbrx

Copy link
Copy Markdown
Collaborator

Changes

databricks apps init now detects the Git repository that will contain the new app and scaffolds a git-backed databricks.yml — a git_repository (origin URL + inferred provider) and git_source (current branch + repo-relative source_code_path) — instead of a plain source_code_path upload.

  • New cmd/apps/gitsource.go: detectGitScaffoldSource walks up from the scaffold destination via git.FetchRepositoryInfo, normalizes the origin remote to an https URL, and maps the host to a Databricks git provider (gitHub, gitLab, bitbucketCloud, azureDevOpsServices).
  • cmd/apps/init.go: the detected fields are threaded into the template context (a git map), so the AppKit databricks.yml.tmpl can render the block conditionally.

Detection is conservative and falls back to source_code_path when there is nothing safe to point a deploy at: no repo, no origin remote, an unrecognized/self-hosted provider host, a detached HEAD, or a destination outside the repo. It therefore never emits an unusable git block.

Why

Git-backed deployment (deploy from a repo/ref rather than uploading local files) is the recommended path for Databricks Apps — it is reproducible, reviewable, and rollback-able. Today a scaffolded app defaults to a local source_code_path and the git block is a commented placeholder the user must fill in by hand, so almost nobody adopts it. Auto-detecting the repo the user is already standing in makes git-backed the default with zero extra steps, while degrading cleanly to the old behavior everywhere else.

Pairs with the AppKit template change in databricks/appkit#556 (the template renders the git_repository/git_source block from these fields). Backward compatible: an older CLI renders the git fields empty and the template keeps source_code_path.

Tests

  • Unit tests in cmd/apps/gitsource_test.go cover provider inference, origin-URL normalization (https, .git suffix, scp-style ssh, credential stripping, unknown hosts), and detectGitScaffoldSource across repo-root/in-place, subdirectory, not-yet-created subdir, no-repo, no-origin, unknown-provider, and detached-HEAD cases.
  • Verified end-to-end that the paired AppKit template renders valid YAML in both the git-detected and no-git branches.

This pull request and its description were written by Isaac.

atreyadbrx and others added 2 commits August 27, 2026 20:24
`databricks apps init` now detects the Git repository that will contain the
new app and scaffolds a git-backed databricks.yml — git_repository (origin
url + inferred provider) and git_source (current branch + repo-relative
source_code_path) — instead of a plain source_code_path upload.

Detection is best-effort and conservative: it falls back to source_code_path
when there is no repo, no origin remote, an unrecognized provider host, a
detached HEAD, or a destination outside the repo, so it never emits an
unusable git block. Backward compatible with the AppKit template: older CLIs
render the git fields empty and the template keeps source_code_path.

Pairs with the AppKit template change in databricks/appkit#556.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: atreyadbrx <atreya.misra@databricks.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: atreyadbrx <atreya.misra@databricks.com>
atreyadbrx added a commit to atreyadbrx/appkit that referenced this pull request Aug 27, 2026
Replace the commented git_repository/git_source example with a conditional
that the Databricks CLI's `apps init` fills in when scaffolding inside a Git
repository (databricks/cli#6406). When the CLI detects an origin remote it
renders a real git_repository (url + provider) and git_source (branch +
repo-relative source_code_path); otherwise the template keeps
source_code_path, so older CLIs and non-repo scaffolds are unaffected.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: atreyadbrx <atreya.misra@databricks.com>
@github-actions

Copy link
Copy Markdown
Contributor

Approval status: pending

/cmd/apps/ - needs approval

Files: cmd/apps/gitsource.go, cmd/apps/gitsource_test.go, cmd/apps/init.go
Suggested: @pkosiec
Also eligible: @MarioCadenas, @calvarjorge, @keugenek, @atilafassina, @fjakobs, @Shridhad, @igrekun, @pffigueiredo, @ditadi

General files (require maintainer)

Files: .nextchanges/cli/apps-init-git-autodetect.md
Based on git history:

  • @pietern -- recent work in cmd/apps/, .nextchanges/cli/

Any maintainer (@andrewnester, @anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose, @lennartkats-db, @rugpanov, @rclarey) can approve all areas.
See OWNERS for ownership rules.

@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: c9afa34

Run: 33113685316

Env 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 1 1 274 1194 3:52
💚​ aws windows 1 1 276 1192 5:48
💚​ azure linux 1 1 273 1194 3:57
🔄​ azure windows 1 1 275 1192 6:28
💚​ gcp linux 1 1 274 1194 4:19
💚​ gcp windows 1 1 276 1192 3:30
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
🔄​ TestAccept 💚​R 💚​R 💚​R 🔄​f 💚​R 💚​R
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
Top 3 slowest tests (at least 2 minutes):
duration env testname
5:44 aws windows TestAccept
3:29 azure windows TestAccept
3:27 gcp windows TestAccept

@atreyadbrx

Copy link
Copy Markdown
Collaborator Author

Closing — changing direction per review feedback. Instead of detecting git at apps init/create time, git-backing will be an explicit opt-in migration for existing apps, driven by the databricks-apps skill (using the already-shipped apps update --git-* flags for the imperative path). The detection and gh-repo-create logic here will be salvaged into that migration skill.

@atreyadbrx atreyadbrx closed this Sep 1, 2026

@pkosiec pkosiec left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed with the paired appkit#556. The core looks solid: no network call on local init, a correct Create vs Deploy split (git_repository to Create, git_source to Deploy with the nested repo stripped), exactly one of source_code_path or git_source so validation passes, and a conservative fallback to local wherever detection declines. Findings are non-blocking; inline comments cover the code.

P1: the backward compat claim is wrong.

  • The description says an older CLI renders the git fields empty and keeps source_code_path.
  • The opposite happens, and it is easy to reproduce. With no git key, the new template's {{if .git.url}} fails hard ("nil pointer evaluating interface {}.url"). missingkey=zero only saves the first lookup, not the chained field.
  • Safety actually comes from cli-compat.json pinning each CLI version to a template tag, not from the template. apps init --version latest on any pre-#6406 CLI clones appkit main and hits this once #556 lands there.
  • Please fix the wording.

FYI (no action): not bumping cli-compat.json here is correct. The compat bump happens later, after evals pass, and that is also what gates when the new template reaches users. Good sequencing.

P2: the unpushed deploy path is opaque.

  • Git-backed deploy needs the branch committed and pushed first. Run apps init then apps deploy before pushing and you get a raw server error with no hint.
  • This is the new default inside a repo. Consider detecting the missing ref and printing "commit and push first". The deploy path is outside this diff.

P2: private repos need a linked git credential.

  • Deploy clones the repo on the workspace side. A private repo needs a linked git credential for the provider (git_repository has caller_credential_id). The scaffold sets only url and provider and cannot detect visibility, so a private repo with no linked credential fails at deploy, with no init-time hint.
  • Question for the author: how is auth handled for private repos on deploy? Does it use the caller's linked credential on its own, or must something be set? Should init warn when git-backed is chosen but no credential is linked? This is inferred from the schema (caller_credential_id), not from PR code, since the clone is server side.

P2: add an acceptance test.

  • Run apps init in a fake git repo and golden file both the rendered git block and the no-git fallback.
  • It locks the url/provider/branch/sourceCodePath contract that P1 depends on. acceptance/apps/ already exists.

Questions on the end to end flow (init is interactive and non-interactive):

  • No opt-out. Detection runs in both modes, with no flag (--source or --no-git) and no prompt.
  • CI and non-interactive: apps init --name x inside a repo silently produces git-backed config. Automation that expected a local upload changes behavior with no signal. Should there be an opt-out flag?
  • Interactive: the choice is never shown or confirmed, unlike other resources. Should it be surfaced?
  • Determinism: the same command yields a different databricks.yml depending on whether the destination is in a repo. Intended for scripts?
  • Detection keys off destDir, so --output-dir is honored, not CWD. Please confirm that is intended. An --output-dir into a different repo uses that repo's origin.

Minor:

  • FetchRepositoryInfoAPI can nil deref on a Databricks runtime with a dest under /Workspace/ and a nil client. Local runs are fine.
  • A line noting the deployed branch is pinned to the current branch at init time would help.

Comment thread cmd/apps/init.go
// the bundle deploys from the repo/ref instead of uploading local files.
// Falls back to a plain source_code_path when there is no repo to point at.
gitSource := detectGitScaffoldSource(ctx, destDir, cmdctx.WorkspaceClient(ctx))
if gitSource.active() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The mode switch is only logged at debug level, so it is invisible by default. Consider a visible success line ("Configured git-backed deploy from (), branch "), and a short reason when a repo is present but git-backing is declined (unknown provider, detached HEAD, no origin, dest outside repo). Today the user cannot tell which mode they got, or why.

Comment thread cmd/apps/gitsource.go
return "bitbucketCloud"
case "dev.azure.com":
return "azureDevOpsServices"
default:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Azure DevOps SSH remotes are dropped silently. ssh.dev.azure.com (e.g. git@ssh.dev.azure.com:v3/org/proj/repo) maps to no provider, so it falls back to local, and the test locks that in. SSH is a common Azure clone method. Consider mapping ssh.dev.azure.com to azureDevOpsServices and rewriting v3/org/proj/repo to the dev.azure.com/org/proj/_git/repo https form, with a test.

Comment thread cmd/apps/gitsource.go
if provider == "" {
return "", ""
}
path = strings.Trim(strings.TrimSuffix(path, ".git"), "/")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Trim order mishandles a trailing slash after .git. For "o/r.git/", TrimSuffix(".git") does nothing (the string ends in "/"), then Trim("/") gives "o/r.git", so .git stays in the URL. Trim "/" first, then TrimSuffix(".git"). Rare input, cheap to fix.

Comment thread cmd/apps/gitsource.go
if path == "" {
return "", ""
}
return "https://" + host + "/" + path, provider

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Host case leaks into the URL. "GitHub.com" matches the provider (the match is case insensitive), but the emitted URL keeps "GitHub.com". Git hosts are case insensitive, so this is likely harmless. Lowercasing the host avoids any host vs provider mismatch on the backend.

Comment thread cmd/apps/gitsource.go
}

sourcePath := "./"
if rel, relErr := filepath.Rel(info.WorktreeRoot, absDest); relErr == nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

On filepath.Rel error (e.g. cross volume on Windows) the code keeps sourcePath "./" and still returns an active source at the repo root. Unreachable today, since WorktreeRoot is always an ancestor of absDest, so it is benign. return gitScaffoldSource{} on relErr is safer if that ever changes.

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