Skip to content

Conversation

@fjakobs
Copy link
Contributor

@fjakobs fjakobs commented Jan 29, 2026

Summary

  • Add support for initializing apps from legacy templates (direct git clone)
  • Refactor apps code to eliminate duplication and improve organization
  • Add automatic .env and .gitignore generation for legacy templates
Apps.init.mp4

🤖 Generated with Claude Code

@fjakobs fjakobs requested a review from a team as a code owner January 29, 2026 09:20
@fjakobs fjakobs temporarily deployed to test-trigger-is January 29, 2026 09:20 — with GitHub Actions Inactive
@fjakobs fjakobs temporarily deployed to test-trigger-is January 29, 2026 09:32 — with GitHub Actions Inactive
@eng-dev-ecosystem-bot
Copy link
Collaborator

eng-dev-ecosystem-bot commented Jan 29, 2026

Commit: 522b879

Run: 21482904487

Env 🟨​KNOWN 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🟨​ aws linux 7 1 7 442 708 24:43
🟨​ aws windows 7 1 7 414 716 19:40
🟨​ aws-ucws linux 5 8 5 641 578 93:40
🟨​ aws-ucws windows 5 8 5 610 587 77:44
🔄​ azure linux 3 1 9 440 707 27:21
💚​ azure windows 2 9 414 715 22:56
🟨​ azure-ucws linux 3 4 7 637 577 88:46
🟨​ azure-ucws windows 3 4 7 606 586 80:11
🔄​ gcp linux 3 1 9 429 713 22:23
💚​ gcp windows 2 9 403 721 20:37
22 interesting tests: 9 KNOWN, 5 SKIP, 4 flaky, 4 RECOVERED
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 🟨​K 🟨​K 🔄​f 💚​R 🟨​K 🟨​K 🔄​f 💚​R
🙈​ TestAccept/bundle/deployment/bind/alert 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/generate/alert 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🟨​K 🟨​K 🙈​S 🙈​S 🟨​K 🟨​K 🙈​S 🙈​S
🟨​ TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=alert.yml.tmpl 🟨​K 🟨​K 🟨​K 🟨​K
🙈​ TestAccept/bundle/resources/alerts/basic 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/alerts/with_file 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestAccept/bundle/resources/dashboards/dataset-catalog-schema ✅​p ✅​p ✅​p ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/dashboards/dataset-catalog-schema/DATABRICKS_BUNDLE_ENGINE=direct ✅​p ✅​p ✅​p ✅​p 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p
🔄​ TestAccept/bundle/resources/dashboards/detect-change ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p 🔄​f ✅​p
🔄​ TestAccept/bundle/resources/dashboards/detect-change/DATABRICKS_BUNDLE_ENGINE=direct ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p ✅​p 🔄​f ✅​p
🙈​ 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 🟨​K 🟨​K 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🟨​ TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct 🟨​K 🟨​K 🟨​K 🟨​K
🟨​ 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/synced_database_tables/basic 🙈​S 🙈​S 💚​R 💚​R 🙈​S 🙈​S 💚​R 💚​R 🙈​S 🙈​S
💚​ TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform 💚​R 💚​R 💚​R 💚​R
💚​ TestAccept/ssh/connection 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
Top 50 slowest tests (at least 2 minutes):
duration env testname
7:04 aws-ucws linux TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct
6:45 azure-ucws windows TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct
6:39 aws-ucws windows TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct
6:34 aws-ucws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_catalog.yml.tmpl
6:21 aws-ucws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_instance.yml.tmpl
6:16 aws-ucws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=synced_database_table.yml.tmpl
6:11 aws-ucws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_catalog.yml.tmpl
6:10 aws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
6:08 aws-ucws linux TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform
6:00 aws-ucws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_instance.yml.tmpl
5:58 aws-ucws windows TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform
5:43 aws-ucws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=synced_database_table.yml.tmpl
5:43 azure windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:34 aws-ucws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:26 gcp windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:25 gcp linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
5:23 gcp windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
5:12 azure-ucws windows TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform
5:08 azure linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
4:57 gcp linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
4:38 azure windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
4:24 azure linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
4:02 azure-ucws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=synced_database_table.yml.tmpl
3:57 azure-ucws linux TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=direct
3:55 azure-ucws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_instance.yml.tmpl
3:43 azure-ucws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_instance.yml.tmpl
3:42 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:41 gcp windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:16 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:13 azure-ucws linux TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_catalog.yml.tmpl
3:10 azure-ucws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=synced_database_table.yml.tmpl
3:08 gcp linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:04 azure-ucws linux TestAccept/bundle/resources/synced_database_tables/basic/DATABRICKS_BUNDLE_ENGINE=terraform
3:03 azure-ucws windows TestAccept/bundle/invariant/no_drift/DATABRICKS_BUNDLE_ENGINE=direct/INPUT_CONFIG=database_catalog.yml.tmpl
3:00 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:56 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:55 azure-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:50 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:45 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:45 aws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:41 aws-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:39 aws-ucws windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:31 aws-ucws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
2:29 aws-ucws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
2:25 aws linux TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=direct
2:22 aws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:22 aws windows TestAccept/bundle/resources/clusters/deploy/update-after-create/DATABRICKS_BUNDLE_ENGINE=terraform
2:21 aws-ucws linux TestAccept/bundle/resources/registered_models/basic/DATABRICKS_BUNDLE_ENGINE=terraform
2:21 azure-ucws linux TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:18 azure windows TestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform

@fjakobs fjakobs temporarily deployed to test-trigger-is January 29, 2026 13:43 — with GitHub Actions Inactive
@fjakobs fjakobs changed the title Refactor apps: Add legacy template support and reduce code duplication Refactor apps: Add legacy template support to databricks apps init Jan 29, 2026
@fjakobs fjakobs temporarily deployed to test-trigger-is January 29, 2026 14:34 — with GitHub Actions Inactive
fjakobs and others added 23 commits January 29, 2026 15:52
When run from a Databricks Apps project directory without an app name,
`databricks apps start` and `databricks apps stop` now automatically
detect the app name from the databricks.yml configuration (similar to
how `databricks apps dev-remote` works).

When an app name is provided, they fall back to the original API behavior.

This provides a consistent UX for project-based workflows where users
don't need to manually specify the app name each time.

Changes:
- Add BundleStartOverrideWithWrapper in cmd/apps/start_stop_bundle.go
- Add BundleStopOverrideWithWrapper in cmd/apps/start_stop_bundle.go
- Register start and stop overrides in cmd/workspace/apps/overrides.go
- Remove old startOverride function (replaced by bundle version)
- Add unit tests for start/stop override functionality

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhanced the apps start, stop, deploy, and delete commands with better
target support and user experience:

Target Flag Support:
- Added examples showing --target flag usage in all project mode commands
- The --target flag was already working (via TryConfigureBundle) but now
  properly documented in help text

Human-Readable Output:
- start/stop commands in project mode now show friendly success messages
  instead of raw JSON output
- Provides clear feedback: "✔ App 'name' started/stopped successfully"

Changes:
- Add --target examples to all command help texts
- Improve start/stop output formatting in project mode
- Maintain JSON output for API mode (backward compatible)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed start and stop commands to handle cases where the app is already
in the desired state gracefully instead of returning an error.

Behavior:
- `databricks apps start` on an already-running app now shows:
  "✔ App 'name' is already started" (instead of erroring)

- `databricks apps stop` on an already-stopped app now shows:
  "✔ App 'name' is already stopped" (instead of erroring)

This makes the commands idempotent and provides a better user experience,
especially in automation scenarios where the exact state of the app may
not be known before running the command.

Works in both project mode and API mode.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Modified start and stop commands to respect the output format flag:

Text Mode (default):
- In project mode, calls the API directly and shows only human-readable
  messages without JSON output
- Shows spinner with progress updates during wait
- Clean output: "✔ App 'name' started/stopped successfully"
- Idempotent behavior maintained

JSON Mode (-o json):
- Uses the original command to render JSON output
- No additional human-readable messages added
- Useful for scripting and automation

This ensures users see clean, human-friendly output by default while
still supporting JSON output when explicitly requested.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Modified the start command to display the app URL after successfully
starting (or when the app is already started).

Behavior:
- After `databricks apps start` completes, shows: "App URL: <url>"
- Works in both project mode and API mode
- Works for both newly started apps and already-running apps (idempotent case)
- In project mode: Uses the app info from the wait response when available,
  otherwise makes a separate API call to get the URL
- In API mode with explicit app name: Gets URL when app is already started

This makes it easier for users to immediately access their app after starting it.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhanced the visual presentation of the app URL after starting:

Before:
  ✔ App 'app7' is already started
  App URL: https://app7-1966697730403610.10.azure.databricksapps.com

After:
  ✔ App 'app7' is already running

  🔗 https://app7-1966697730403610.10.azure.databricksapps.com

Changes:
- Added link emoji (🔗) before URL for better visual distinction
- Added blank lines before and after URL to improve readability
- Changed "already started" to "already running" for consistency
- Makes the URL stand out more clearly in the terminal output

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit adds bundle mode support to the `apps logs` command and
reduces code duplication across apps commands (logs, start, stop).

Changes:
- Add bundle_helpers.go with shared helper functions:
  - makeArgsOptionalWithBundle: Handles optional NAME argument
  - getAppNameFromArgs: Detects app name from args or bundle config
  - updateCommandHelp: Generates consistent help text
  - BundleLogsOverride: Applies bundle mode to logs command

- Update apps logs command to auto-detect app name from databricks.yml
  - Now supports `databricks apps logs` without NAME argument
  - Maintains backward compatibility with explicit NAME

- Refactor start/stop commands to reduce duplication:
  - Extract formatAppStatusMessage helper for status message generation
  - Reduces ~90 lines of duplicated code
  - Remove obvious comments and improve code clarity

- Fix apps delete double initialization panic:
  - Update CommandBundleDestroy to skip context initialization when
    already initialized by parent command (apps delete override)
  - Fixes: "must not call InitContext() twice" panic

All commands now support consistent dual mode behavior:
- Auto-detect from databricks.yml when no NAME provided
- Fall back to API mode with explicit NAME argument

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…rage

Extracted 3 helper functions (isIdempotencyError, displayAppURL,
handleAlreadyInStateError) to eliminate repeated patterns across
start/stop/delete commands. Replaced inline argument validation with
shared makeArgsOptionalWithBundle helper. Removed 15+ redundant comments
per CLAUDE.md guidelines.

Added comprehensive unit tests for all helper functions and command
override behavior, increasing test coverage from 10-15% to 70-80%.

Net impact: -92 lines of production code, +165 lines including tests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Move formatAppStatusMessage and helper functions to bundle_helpers.go
- Refactor start/stop overrides to use shared helper functions
- Remove verbose test assertion messages

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Inline BundleLogsOverride directly into logs.go since it's a custom command
- Split start_stop_bundle.go into separate start_bundle.go and stop_bundle.go
- Split corresponding test files for better organization
- Update acceptance test output for "Deploying project..." message

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extends `databricks apps init` to support both AppKit (TypeScript) and legacy
templates (Python/Dash/Streamlit/Gradio/Flask/Shiny) with automatic resource
detection and provisioning.

Key changes:
- Added interactive template type selection (AppKit vs Legacy)
- Embedded app-template-app-manifests.json with 25+ legacy templates
- Added resource requirement detection from template manifests
- Added prompts for required resources: SQL warehouse, serving endpoint,
  MLflow experiment, Lakebase database, and Unity Catalog volume
- Added resource flags: --warehouse-id, --serving-endpoint, --experiment-id,
  --database-name, --instance-name, --uc-volume
- Support legacy template identifiers via --template flag
- Fail with clear error messages in non-interactive mode when required
  resources are not provided
- Updated CODEOWNERS for /cmd/apps/

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
After materializing a legacy template, create a databricks.yml file to enable
DAB bundle workflows for legacy apps. The file includes the app resource
definition with source_code_path pointing to the template directory.

Changes:
- Added appName parameter to runLegacyTemplateInit
- Prompt for app name before template initialization
- Generate databricks.yml after template materialization
- Validate app name in both interactive and non-interactive modes

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Simplified legacy template initialization by directly cloning the git repository
and copying files, instead of using the bundle template machinery. This avoids
the bundle init dependency and makes the process more straightforward.

Changes:
- Added copyDir and copyFile helper functions for recursive directory copying
- Clone repository to temp directory using git.Clone with shallow clone
- Copy template subdirectory to destination
- Remove .git directory after copying
- Create .env.example with resource configurations instead of config file
- Removed templatelib dependency

The resource parameters (warehouse, serving endpoint, etc.) are now written to
.env.example as reference for the user to configure their application.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This refactoring improves the maintainability and readability of the apps init command by:

- Eliminating 150+ lines of duplicated legacy template resource gathering code through extraction of legacyResourceCollector and handleLegacyTemplateInit helper
- Replacing 5 nearly identical resource requirement checkers with a generic hasResourceSpec function using the checker pattern
- Extracting envBuilder for consistent .env file generation across templates
- Extracting resourceBindingsBuilder for databricks.yml resource bindings generation
- Consolidating deploy/run flag handling logic into deployRunConfig
- Adding comprehensive unit tests for all new builders and collectors (19 new test cases)

The refactoring maintains all existing behavior while significantly improving code organization and testability.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…e templates

This commit improves the `databricks apps init` command with several enhancements:

**Add workspace host to legacy templates:**
- Write DATABRICKS_HOST to .env file
- Include workspace section in databricks.yml
- Matches behavior of `databricks apps import`

**Migrate Python projects to uv:**
- Replace python venv with `uv venv`
- Replace pip install with `uv pip install -r requirements.txt`
- Use `uv run --env-file .env` to run apps (automatically loads .env)
- Update NextSteps message to show uv commands
- Remove platform-specific logic (uv handles cross-platform)

**Fix prompt text:**
- Change "Yes, run locally (npm run dev)" to generic "Yes, run locally"
- Prevents confusion when initializing Python apps

**Fix resource bindings in databricks.yml:**
- Generate valid YAML instead of commented-out blocks
- Resources are now immediately usable without manual uncommenting
- Update format:
  - Before: `# sql_warehouse:` (commented)
  - After: `sql_warehouse:` (valid YAML)

**Organize legacy template files:**
- Create cmd/apps/legacy-template/ directory
- Move app-template-app-manifests.json to legacy-template/
- Move databricks-yml.tmpl to legacy-template/
- Create gitignore.tmpl with common patterns
- Update go:embed directives to use new paths
- Simplify .gitignore creation logic to use template

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…anup

- Fix camelCase field names in app.yml inlining (e.g., valueFrom -> value_from)
- Add camelToSnake conversion function with comprehensive test coverage
- Remove unused envBuilder and .env generation code
- Use template path as default app name instead of "my-app"
- Remove unused gitignore template file

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The Python uv initializer now includes --env-file .env flag in both:
- NextSteps() message shown to users
- RunDev() command execution

This ensures environment variables from .env are loaded when running
Python apps with uv.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Creates a reusable builder that:
- Parses app.yml to extract environment variables
- Supports direct values (value) and resource references (value_from)
- Automatically adds DATABRICKS_HOST if not already present
- Validates that all value_from references exist in resources map
- Generates .env file content with proper formatting

The builder can be used both after cloning legacy templates and after
importing apps to ensure consistent .env file generation.

Includes comprehensive unit tests covering:
- Valid and invalid YAML parsing
- Direct values and value_from references
- Missing resource references (error cases)
- Empty env sections and empty values
- DATABRICKS_HOST handling
- File writing and overwriting
- End-to-end integration test

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The resources map now correctly uses resource names from databricks.yml
(e.g., "sql-warehouse", "experiment", "serving-endpoint") instead of
arbitrary keys. This matches how value_from references work in app.yml.

Example:
- databricks.yml: resource.name = "sql-warehouse"
- app.yml: value_from = "sql-warehouse"
- resources map: {"sql-warehouse": "abc123"}

Updated:
- Documentation to clarify resource names come from databricks.yml
- Error message to mention resource.name field
- All test cases to use realistic kebab-case resource names

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
After importing an app or cloning a legacy template, automatically
generate a .env file from app.yml if it exists.

Changes:
1. Added buildResourcesMap() to extract resource names and IDs from
   app.Resources into a map for EnvFileBuilder

2. Added generateEnvFile() for apps import flow
   - Called after generating databricks.yml
   - Extracts resources from imported app
   - Creates .env from app.yml with proper resource references

3. Added generateEnvFileForLegacyTemplate() for legacy template init
   - Called after inlining app.yml into databricks.yml
   - Builds resources map from collected parameters
   - Uses standard resource names (sql-warehouse, experiment, etc.)

4. Added comprehensive tests for buildResourcesMap covering all
   resource types (SQL warehouse, serving endpoint, experiment,
   database, Genie space, job, UC securable)

The .env generation is non-fatal - warnings are logged if it fails
but the overall operation continues successfully.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The .env file was not being generated because inlineAppYmlIntoBundle
deletes the app.yml file after inlining it into databricks.yml.

Fixed by swapping the order:
1. Generate .env from app.yml (while it still exists)
2. Inline app.yml into databricks.yml (which deletes app.yml)

Now legacy template initialization correctly creates .env files with
environment variables from app.yml.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove auto-generated header comments from .env files for cleaner output
- Fix comment formatting in NewEnvFileBuilder to follow Go doc conventions
- Fix whitespace alignment in test cases

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Legacy templates use camelCase `valueFrom` in app.yml, but EnvFileBuilder
was expecting snake_case `value_from`. This caused environment variables
with resource references to have empty values in the generated .env file.

Changes:
- Add convertKeysToSnakeCase() to recursively convert YAML keys
- Update NewEnvFileBuilder() to convert camelCase to snake_case before parsing
- Add test case for camelCase valueFrom (legacy template format)
- Add end-to-end test with exact legacy template YAML structure

This ensures .env files are generated correctly regardless of whether
app.yml uses camelCase (legacy templates) or snake_case (new templates).

Fixes issue where MLFLOW_EXPERIMENT_ID was empty in .env despite being
correct in databricks.yml when initializing from legacy templates.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
fjakobs and others added 27 commits January 29, 2026 15:52
…nfig

Refactors the EnvFileBuilder to use a Go template for cleaner .env file
generation and adds profile-aware MLFLOW_TRACKING_URI configuration for
local development.

Key changes:
- Use Go template (cmd/apps/legacy-template/env.tmpl) for .env generation
- Add profile parameter to configure MLFLOW_TRACKING_URI:
  * "databricks" for default/empty profile
  * "databricks://<profile-name>" for named profiles
- Add DATABRICKS_APP_NAME to .env file
- Include default port configurations for common frameworks (Flask, Gradio,
  Streamlit, Uvicorn)
- Template structure: default values at top, app.yml variables at bottom
- Always override MLFLOW_TRACKING_URI from app.yml with profile-based value

This ensures MLflow can authenticate properly when running apps locally by
using the correct Databricks CLI profile.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Allows templates to specify a custom command for running the app via
the start_command field in their manifest. When set, this overrides the
default command shown in the "Next steps" output after app creation.

This is useful for templates that use custom tooling or commands (e.g.,
"uv run start-app" instead of the standard Python commands).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updates the NextSteps() command for Python UV projects to use
"uv run start-app" instead of "uv run --env-file .env python app.py".

This aligns with the convention used in agent templates where start-app
is defined as a script entry point in pyproject.toml.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Creates a default .gitignore file when initializing legacy templates or
importing apps if one doesn't already exist. The template includes common
ignore patterns for Python, Node.js, IDEs, and importantly .env files.

Key changes:
- Added gitignore.tmpl with comprehensive ignore patterns
- Created writeGitignoreIfMissing() to check and write .gitignore
- Integrated into runLegacyTemplateInit() for new legacy templates
- Integrated into generateEnvFile() for imported apps

The .gitignore includes:
- Python artifacts (__pycache__, *.pyc, venv, etc.)
- Environment files (.env, .env.local)
- IDE files (.vscode, .idea, .DS_Store)
- Node modules and dependency directories
- Testing and logging artifacts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updates both Python initializers to explicitly use Python 3.11 when
creating virtual environments:
- python_uv: Adds --python 3.11 to uv sync command
- python_pip: Adds --python 3.11 to uv venv command

Introduces a package-level pythonVersion constant (3.11) to ensure
consistency across both initializers and make future version updates
easier.

This ensures apps are created with a consistent Python version that
matches the requirements of agent templates and other Python apps.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed an issue where user_api_scopes defined in app template manifests
were not being included in the generated databricks.yml file when
initializing apps from legacy templates.

Changes:
- Added UserAPIScopes field to templateVars struct
- Updated databricks-yml.tmpl to render user_api_scopes section
- Pass UserAPIScopes from manifest to template in runLegacyTemplateInit
- Added tests to verify user_api_scopes are correctly rendered
- Fixed yaml import to use go.yaml.in/yaml/v3 for consistency

Templates like dash-data-app-obo-user, gradio-data-app-obo-user, and
streamlit-data-app-obo-user now correctly generate databricks.yml with
the user_api_scopes field.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added "destroy" as an alias for the "delete" command in the apps
command group. Users can now run either:
  - databricks apps delete [NAME]
  - databricks apps destroy [NAME]

Both commands behave identically and support the same flags and
bundle integration features.

Changes:
- Added deleteOverride function to set command aliases
- Registered the override in the init function
- Added test to verify the alias works correctly

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit improves code organization and reduces duplication in the apps
commands by extracting shared YAML utility functions.

Changes:
- Create yaml_utils.go with shared utility functions:
  - camelToSnake: Convert camelCase strings to snake_case
  - yamlNodeToDynValue: Convert YAML nodes to dyn.Value
  - addBlankLinesBetweenTopLevelKeys: Format YAML files
- Move TestCamelToSnake to yaml_utils_test.go
- Remove duplicated functions from import.go
- Add .gitignore creation for AppKit templates in init.go
- Simplify .env template format (remove quotes around values)
- Remove unused bufio import from import.go
- Remove redundant comment about field ordering

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create resources.go with constants for resource names
- Move inlineAppConfigFile from import.go to yaml_utils.go
- Replace string literals with resource name constants
- Update doc comments for consistency and conciseness
- Use lowercase in warning messages for consistency

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Move all legacy template functionality from cmd/apps/init.go into a new
cmd/apps/legacytemplates package with clear separation of concerns:

- legacy.go: Manifest types and template loading
- prompts.go: User prompts for template selection and resources
- builders.go: Resource bindings and variables builders
- resources.go: Resource collection logic
- init.go: Template initialization workflow
- env_builder.go: Environment file generation (moved from cmd/apps)

Create cmd/apps/internal/yamlutil package to provide shared YAML utilities
and avoid import cycles between apps and legacytemplates packages.

Update cmd/apps/init.go and cmd/apps/import.go to use the new package structure.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This change addresses critical maintainability issues by consolidating
duplicate code and adding test coverage for legacy template functionality.

Changes:
- Extract duplicate parseDeployAndRunFlags function to cmd/apps/internal
- Consolidate resource name constants in cmd/apps/internal, re-export from apps
- Add comprehensive unit tests for builders (18 test cases)
- Add unit tests for file operations (copyFile, copyDir)
- Flatten internal package structure (no subdirectories)
- All tests pass with proper edge case coverage

Benefits:
- Single source of truth for resource constants and flag parsing
- Eliminates need to maintain identical code in multiple locations
- Prevents circular dependencies through proper package structure
- Increases test coverage for critical initialization logic
- Cleaner internal package structure without nested subdirectories

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Export CamelToSnake from internal package and remove duplicate implementation
from env_builder to eliminate code duplication.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace hardcoded resource type handling with an extensible registry
pattern that consolidates resource metadata and eliminates repetitive
code. This change makes adding new resource types significantly easier
(~25 lines vs ~50+ lines) and centralizes YAML generation logic in the
template.

Key changes:
- Introduce ResourceRegistry with init() registration for all resource types
- Create ResourceHandler interface for type-specific behavior
- Replace string-based YAML builders with template functions
- Remove 5 Requires*() and 5 Get*ForTemplate() helper functions
- Enhance databricks-yml.tmpl to iterate over resources directly
- Delete repetitive builders.go (180 lines) and resources.go (84 lines)

The registry pattern provides a single source of truth for resource
metadata (variable names, binding lines, descriptions) and makes the
template self-contained. All acceptance tests pass with byte-for-byte
identical YAML output.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fix issue where legacy templates were not initializing Python virtual
environments or Node.js dependencies after project creation. The fix
ensures that both 'databricks apps init' and 'databricks apps import'
now properly initialize project dependencies by calling runPostCreationSteps.

Changes:
- Modified RunLegacyTemplateInit to return absOutputDir and startCommand
- Updated HandleLegacyTemplateInit to return values needed for post-creation
- Added runPostCreationSteps calls after legacy template initialization
- Added initializeProjectDependencies to import command
- Removed redundant log messages for inlined app config files

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Eliminates direct YAML parsing and writing after template rendering by
parsing app.yml before template execution and passing its content to the
Go template. The template now generates the final databricks.yml with
inlined app.yml content in a single pass.

Changes:
- Parse app.yml from source directory before copying template files
- Add AppConfig struct to hold parsed app.yml content (command, env, resources)
- Enhance databricks-yml.tmpl to conditionally include config section
- Add 7 new template functions for accessing app.yml data
- Remove inlineAppYmlIntoBundle() function and all post-processing
- Export ConvertKeysToSnakeCase for reuse in init.go

Benefits:
- Simpler: Single-pass rendering with no post-processing
- Faster: Eliminates YAML parse/serialize cycles
- More maintainable: All YAML generation logic in templates

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes the "Deploy after creation?" prompt from huh.NewConfirm() to
huh.NewSelect[string]() with explicit "No" and "Yes" options.

This provides a more consistent UI pattern and better aligns with other
prompts in the apps init flow.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
UC volumes were being collected but not bound in the generated databricks.yml, preventing apps from accessing them at runtime.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit enhances the legacy template selection experience by:
- Adding a framework type selection step before template selection
- Filtering templates by framework type (Dash, Flask, Gradio, Node.js, Shiny, Streamlit)
- Prefixing template labels with framework name for clarity
- Changing project name prompt to prefill the default value instead of showing as placeholder

The user flow is now:
1. Select template type (AppKit or Legacy)
2. Select framework type (Dash, Flask, Gradio, etc.)
3. Select template from filtered list (e.g., "Flask - Hello world - ...")
4. Enter app name (now prefilled with default)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed the template selection UI to display all framework types
(Dash, Flask, Gradio, Node.js, Shiny, Streamlit) alongside AppKit
instead of nesting them under a "Legacy template" option.

The flow is now:
1. Select framework type directly from list with AppKit at top
2. Select template from filtered list based on chosen framework
3. Continue with app name and resource configuration

Removed intermediate "PromptForFrameworkType" function as framework
is now selected in the first step.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Only display the "Files: N" line in the success message when
fileCount > 0. This prevents showing "Files: 0" for legacy
templates where file counting is not applicable.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added instructions on how to run the imported app locally, similar to
the init command. The message now shows the project-specific run command
(e.g., "npm run dev", "uv run start-app") after the deploy instructions.

Example output:
  You can now deploy changes with: databricks bundle deploy
  To run locally: cd my-app && npm run dev

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Refactored to get and use the project initializer once instead of
calling GetProjectInitializer twice. The initializer is now returned
from runImport() and initializeProjectDependencies(), avoiding
redundant initialization and making the code cleaner.

Changes:
- Modified initializeProjectDependencies to return the initializer
- Modified runImport to return the initializer
- Reuse the returned initializer for getting NextSteps()

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated user-facing messages in the import command to refer to
"app project" or "project" instead of "bundle" for consistency
with apps terminology. Internal implementation details (variables,
function names) remain unchanged.

Changes:
- Command description: "Import as a project"
- Log messages: "Creating project configuration", "Binding project", etc.
- Flag descriptions: "Directory to output the project to"
- Comments: "generates app project files"

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Unified success message formatting by using prompt.PrintSuccess in the
import command. Also removed the fileCount parameter from PrintSuccess
and all related tracking code since it's not needed:

- Updated import.go to use prompt.PrintSuccess for consistent styling
- Removed fileCount parameter from PrintSuccess function
- Removed fileCount tracking from copyTemplate function
- Removed fileCount from runPostCreationSteps signature
- Removed strconv import that was only used for fileCount

The success message now shows consistent formatting between init and
import commands, with the location and next steps (when applicable).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Contributor

@MarioCadenas MarioCadenas left a comment

Choose a reason for hiding this comment

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

I see that with these new changes, init is now broken for agents

Image

How does that get overriden? also In theory appkit should be the default template when nothing is provided I think right?

)

// pythonVersion is the Python version to use for virtual environments.
const pythonVersion = "3.11"
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to pin this here or maybe try to discover which version the user has installed?


var name string
// Prefill with suggested name or default
name := "my-app"
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like this much, it makes you delete the whole name to type the one you want, it adds friction 😢. I think we should keep the placeholder

Comment on lines +230 to +233
Options(
huh.NewOption("No", "no"),
huh.NewOption("Yes", "yes"),
).
Copy link
Contributor

Choose a reason for hiding this comment

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

what's wrong with this one ?

Image

instead of

Image

)

// InitializerPythonPip implements initialization for Python projects using pip and venv.
// InitializerPythonPip implements initialization for Python projects using uv.
Copy link
Contributor

Choose a reason for hiding this comment

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

The idea of this file was to allow using python without uv for the templates that don't have that. Are all the templates updated to have uv no? in that case then we wouldn't even need this file no?


pythonPip := &InitializerPythonPip{}
assert.Contains(t, pythonPip.NextSteps(), ".venv")
assert.Contains(t, pythonPip.NextSteps(), "uv run")
Copy link
Contributor

Choose a reason for hiding this comment

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

}

// copyFile copies a single file from src to dst.
func copyFile(src, dst string) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe all these utils could be part of libs/[some file] if they don't exist there already?

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