From 7d74de948b4dd445cbc0a6ddfecb496c4fa126b7 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Tue, 31 Mar 2026 16:10:49 +0200 Subject: [PATCH 01/10] Add agent rule for auto-generated files --- .agent/rules/auto-generated-files.md | 77 ++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .agent/rules/auto-generated-files.md diff --git a/.agent/rules/auto-generated-files.md b/.agent/rules/auto-generated-files.md new file mode 100644 index 0000000000..ce8a27a54f --- /dev/null +++ b/.agent/rules/auto-generated-files.md @@ -0,0 +1,77 @@ +--- +name: auto-generated-files +description: Rules for how to deal with auto-generated files +globs: + - ".codegen/openapi.json" + - ".gitattributes" + - "acceptance/**/out*" + - "acceptance/**/output.txt" + - "acceptance/**/output.*.txt" + - "acceptance/**/output/**" + - "cmd/account/*.go" + - "cmd/account/**/*.go" + - "cmd/workspace/*.go" + - "cmd/workspace/**/*.go" + - "internal/genkit/tagging.py" + - "internal/mocks/**/*.go" + - "bundle/direct/dresources/*.generated.yml" + - "bundle/docsgen/output/**/*.md" + - "bundle/internal/schema/annotations_openapi.yml" + - "bundle/internal/validation/generated/*.go" + - "bundle/schema/jsonschema.json" + - "bundle/schema/jsonschema_for_docs.json" + - "python/databricks/bundles/version.py" + - "python/databricks/bundles/*/__init__.py" + - "python/databricks/bundles/*/_models/*.py" +--- + +# Auto-generated files + +## Identification + +The files matching this rule glob pattern are most likely generated artifacts. Auto-generated files generally have a comment (if the file type allows for comments) at or near the top of the file indicating that they are generated, or their file name/path may indicate they are generated. You may also consult Makefile as starting point to determine if a file is auto-generated. + +## Rules + +DO NOT "MANUALLY" EDIT THESE FILES! + +If a change is needed in any matched file: +1. Find the source logic/template/annotation that drives the file. +2. Run the appropriate generator/update command. +3. Commit both the source change (if any) and regenerated outputs. + +### Core generation commands + +- OpenAPI SDK/CLI command stubs and related generated artifacts: + - `make generate` + - Includes generated `cmd/account/**`, `cmd/workspace/**`, `.gitattributes`, `internal/genkit/tagging.py`, and direct engine refresh. +- Direct engine generated YAML: + - `make generate-direct` (or `make generate-direct-apitypes`, `make generate-direct-resources`) +- Bundle schemas: + - `make schema` + - `make schema-for-docs` + - This can also refresh `bundle/internal/schema/annotations_openapi.yml` when OpenAPI annotation extraction is enabled. +- Bundle docs: + - `make docs` +- Validation generated code: + - `make generate-validation` +- Mock files: + - `go run github.com/vektra/mockery/v2@b9df18e0f7b94f0bc11af3f379c8a9aea1e1e8da` +- Python bundle codegen: + - `make -C python codegen` + +### Acceptance and test generated outputs + +Acceptance outputs are generated and should not be hand-edited (except rare, intentional mass replacement when explicitly justified by repo guidance). + +- Preferred regeneration: + - `make test-update` + - `make test-update-templates` (templates only) + - `make generate-out-test-toml` (only `out.test.toml`) +- Typical generated files include: + - `acceptance/**/out*` + - `acceptance/**/output.txt` + - `acceptance/**/output.*.txt` + - `acceptance/**/output/**` (materialized template output trees) + +When touching acceptance sources (`databricks.yml`, scripts, templates, or test config), regenerate outputs instead of editing generated files directly. From f07fee169c33c0d0bcaf084c4b1e105436a3ed8a Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 1 Apr 2026 13:40:28 +0200 Subject: [PATCH 02/10] Cut-paste & format testing rules --- .agent/rules/testing.md | 78 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .agent/rules/testing.md diff --git a/.agent/rules/testing.md b/.agent/rules/testing.md new file mode 100644 index 0000000000..1fefb2e502 --- /dev/null +++ b/.agent/rules/testing.md @@ -0,0 +1,78 @@ +--- +name: testing +description: Rules for the testing strategy of this repo +--- + +## Test Types + +- **Unit tests**: Standard Go tests alongside source files +- **Integration tests**: `integration/` directory, requires live Databricks workspace +- **Acceptance tests**: `acceptance/` directory, uses mock HTTP server + +Each file like process_target_mode_test.go should have a corresponding test file +like process_target_mode_test.go. If you add new functionality to a file, +the test file should be extended to cover the new functionality. + +Tests should look like the following: +```go +package mutator_test + +func TestApplySomeChangeReturnsDiagnostics(t *testing.T) { + ... +} + +func TestApplySomeChangeFixesThings(t *testing.T) { + ctx := t.Context() + b, err := ...some operation... + require.NoError(t, err) + ... + assert.Equal(t, ...) +} +``` + +Notice that: +- Tests are often in the same package but suffixed wit _test. +- The test names are prefixed with Test and are named after the function or module they are testing. +- 'require' and 'require.NoError' are used to check for things that would cause the rest of the test case to fail. +- 'assert' is used to check for expected values where the rest of the test is not expected to fail. + +When writing tests, please don't include an explanation in each +test case in your responses. I am just interested in the tests. + +Use table-driven tests when testing multiple similar cases (e.g., different inputs producing different outputs). Reviewers prefer this pattern over repeating near-identical test functions. + +## Acceptance Tests + +- Located in `acceptance/` with nested directory structure. +- Each test directory contains `databricks.yml`, `script`, and `output.txt`. +- Source files: `test.toml`, `script`, `script.prepare`, `databricks.yml`, etc. +- Tests are configured via `test.toml`. Config schema and explanation is in `acceptance/internal/config.go`. Config is inherited from parent directories. Certain options are also dumped to `out.test.toml` so that inherited values are visible on PRs. +- Generated output files start with `out`: `output.txt`, `out.test.toml`, `out.requests.txt`. Never edit these directly — use `-update` to regenerate. Exception: mass string replacement when the change is predictable and much cheaper than re-running the test suite. +- Run a single test: `go test ./acceptance -run TestAccept/bundle///` +- Run a specific variant by appending EnvMatrix values to the test name: `go test ./acceptance -run 'TestAccept/.../DATABRICKS_BUNDLE_ENGINE=direct'`. When there are multiple EnvMatrix variables, they appear in alphabetical order. +- Useful flags: `-v` for verbose output, `-tail` to follow test output (requires `-v`), `-logrequests` to log all HTTP requests/responses (requires `-v`). +- Run tests on cloud: `deco env run -i -n aws-prod-ucws -- ` (requires `deco` tool and access to test env). +- Use `-update` flag to regenerate expected output files. When a test fails because of stale output, re-run with `-update` instead of editing output files. +- All EnvMatrix variants share the same output files — they MUST produce identical ot. Exception: filenames containing `$DATABRICKS_BUNDLE_ENGINE` (e.g. `output.direct.txt`) are recorded per-engine. +- `-update` with divergent variant outputs is destructive: overwrites with last variant, breaking others. To debug: run a single variant you consider correct with `-update`, then debug the other variant to find why it diverges. +- `test.toml` is inherited — put common options into a parent directory. +- Add test artifacts (e.g. `.databricks`) to `Ignore` in `test.toml`. +- `script.prepare` files from parent directories are concatenated into the test script — use them for shared bash helpers. + +**Helper scripts** in `acceptance/bin/` are available on `PATH` during test execution: +- `contains.py SUBSTR [!SUBSTR_NOT]` — passthrough filter (stdin→stdout) that checks substrings are present (or absent with `!` prefix). Errors are reported on stderr. +- `print_requests.py //path [^//exclude] [--get] [--sort] [--keep]` — print recorded HTTP requests matching path filters. Requires `RecordRequests=test.toml`. Clears `out.requests.txt` afterwards unless `--keep`. Use `--get` to include GET requests (excluded by default). Use `^` prefix to exclude paths. +- `replace_ids.py [-t TARGET]` — read deployment state and add `[NAME_ID]` replacements for all resource IDs. +- `read_id.py [-t TARGET] NAME` — read ID of a single resource from state, print it, and add a `[NAME_ID]` replacement. +- `add_repl.py VALUE REPLACEMENT` — add a custom replacement (VALUE will be replaced with `[REPLACEMENT]` in output). +- `update_file.py FILENAME OLD NEW` — replace all occurrences of OLD with NEW in FILENAME. Errors if OLD is not found. Cannot be used on `output.txt`. +- `find.py REGEX [--expect N]` — find files matching regex in current directory. `--expect N` to assert exact count. +- `diff.py DIR1 DIR2` or `diff.py FILE1 FILE2` — recursive diff with test replacements applied. +- `print_state.py [-t TARGET] [--backup]` — print deployment state (terraform or direct). +- `edit_resource.py TYPE ID < script.py` — fetD, execute Python on it (resource in `r`), then update it. TYPE is `jobs` or `pipelines`. +- `gron.py` — flatten JSON into greppable discrete assignments (simpler than `jq` for searching JSON). +- `jq` is also available for JSON processing. + +**Update workflow**: Run `make test-update` to regenerate outputs. Then run `make fmt` and `make lint` — if these modify files in `acceptance/`, there's an issue in source files. Fix the source, regenerate, and verify lint/fmt pass cleanly. + +**Template tests**: Tests in `acceptance/bundle/templates` include materialized templates in output directories. These directories follow the same `out` convention — everything starting with `out` is generated output. Sources are in `libs/template/templates/`. Use `make test-update-templates` to regenerate. If linters or formatters find issues in materialized templates, do not fix the output files — fix the source in `libs/template/templates/`, then regenerate. From 6b732892b314bc5ce95da22836939cb0fec608a2 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 1 Apr 2026 13:40:50 +0200 Subject: [PATCH 03/10] fixup! Add agent rule for auto-generated files --- .agent/rules/auto-generated-files.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.agent/rules/auto-generated-files.md b/.agent/rules/auto-generated-files.md index ce8a27a54f..9b7f423467 100644 --- a/.agent/rules/auto-generated-files.md +++ b/.agent/rules/auto-generated-files.md @@ -25,8 +25,6 @@ globs: - "python/databricks/bundles/*/_models/*.py" --- -# Auto-generated files - ## Identification The files matching this rule glob pattern are most likely generated artifacts. Auto-generated files generally have a comment (if the file type allows for comments) at or near the top of the file indicating that they are generated, or their file name/path may indicate they are generated. You may also consult Makefile as starting point to determine if a file is auto-generated. From 953abd2d512cd6cca7aaa5ffe79a0cdc09e4c3e2 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 1 Apr 2026 13:41:26 +0200 Subject: [PATCH 04/10] Cut-paste & format go style guide rules --- .agent/rules/style-guide-go.md | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .agent/rules/style-guide-go.md diff --git a/.agent/rules/style-guide-go.md b/.agent/rules/style-guide-go.md new file mode 100644 index 0000000000..ca2913eb4a --- /dev/null +++ b/.agent/rules/style-guide-go.md @@ -0,0 +1,70 @@ +--- +name: style-guide-go +description: Style Guide for Go +globs: "**/*.go" +--- + +## General guidance + +Please make sure code that you author is consistent with the codebase and concise. + +The code should be self-documenting based on the code and function names. + +Functions should be documented with a doc comment as follows: + +```go +// SomeFunc does something. +func SomeFunc() { + ... +} +``` + +Note how the comment starts with the name of the function and is followed by a period. + +Avoid redundant and verbose comments. Use terse comments and only add comments if it complements, not repeats the code. + +Focus on making implementation as small and elegant as possible. Avoid unnecessary loops and allocations. If you see an opportunity of making things simpler by dropping or relaxing some requirements, ask user about the trade-off. + +Use modern idiomatic Golang features (version 1.24+). Specifically: + - Use for-range for integer iteration where possible. Instead of for i:=0; i < X; i++ {} you must write for i := range X{}. + - Use builtin min() and max() where possible (works on any type and any number of values). + - Do not capture the for-range variable, since go 1.22 a new copy of the variable is created for each loop iteration. + - Use empty struct types for context keys: `type myKeyType struct{}` (not `int`). + - Define magic strings as named constants at the top of the file. + - When integrating external tools or detecting environment variables, include source reference URLs as comments so they can be traced later. + +### Configuration Patterns + +- Bundle config uses `dyn.Value` for dynamic typing +- Config loading supports includes, variable interpolation, and target overrides +- Schema generation is automated from Go struct tags + +## Context + +Always pass `context.Context` as a function argument; never store it in a struct. Storing context in a struct obscures the lifecycle and prevents callers from setting per-call deadlines, cancellation, and metadata (see https://go.dev/blog/context-and-structs). Do not use `context.Background()` outside of `main.go` files. In tests, use `t.Context()` (or `b.Context()` for benchmarks). + +## Logging + +Use the following for logging: + +```go +import "github.com/databricks/cli/libs/log" + +log.Infof(ctx, "...") +log.Debugf(ctx, "...") +log.Warnf(ctx, "...") +log.Errorf(ctx, "...") +``` + +Note that the 'ctx' variable here is something that should be passed in as +an argument by the caller. + +Use cmdio.LogString to print to stdout: + +```go +import "github.com/databricks/cli/libs/cmdio" + +cmdio.LogString(ctx, "...") +``` + +Always output file path with forward slashes, even on Windows, so that acceptance test output is stable between OSes. Use filepath.ToSlash for this. From 705040be5eb6a6db50f5d567b1b21b181965674f Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 1 Apr 2026 13:42:14 +0200 Subject: [PATCH 05/10] Cut-paste & format py style guide rules --- .agent/rules/style-guide-py.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .agent/rules/style-guide-py.md diff --git a/.agent/rules/style-guide-py.md b/.agent/rules/style-guide-py.md new file mode 100644 index 0000000000..1297509250 --- /dev/null +++ b/.agent/rules/style-guide-py.md @@ -0,0 +1,16 @@ +--- +name: style-guide-py +description: Style Guide for Python +globs: "**/*.py" +--- + +## General guidance + +When writing Python scripts, we bias for conciseness. We think of Python in this code base as scripts. +- use Python 3.11 +- Do not catch exceptions to make nicer messages, only catch if you can add critical information +- use pathlib.Path in almost all cases over os.path unless it makes code longer +- Do not add redundant comments. +- Try to keep your code small and the number of abstractions low. +- After done, format you code with `ruff format -n ` +- Use `#!/usr/bin/env python3` shebang. From cd3cd7b25af01056bdac45d75e6a94ff6f3c8439 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 1 Apr 2026 13:44:27 +0200 Subject: [PATCH 06/10] Add databricks-template-schema-json rules --- .../rules/databricks-template-schema-json.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .agent/rules/databricks-template-schema-json.md diff --git a/.agent/rules/databricks-template-schema-json.md b/.agent/rules/databricks-template-schema-json.md new file mode 100644 index 0000000000..21083ce18e --- /dev/null +++ b/.agent/rules/databricks-template-schema-json.md @@ -0,0 +1,78 @@ +--- +name: databricks-template-schema-json +description: Databricks template schema file reference template and guidance +globs: "**/databricks_template_schema.json" +--- + +A `databricks_template_schema.json` file is used to configure bundle templates. + +## Reference template + +Below is a good reference template: + +```json +{ + "welcome_message": "\nWelcome to the dbt template for Declarative Automation Bundles!\n\nA workspace was selected based on your current profile. For information about how to change this, see https://docs.databricks.com/dev-tools/cli/profiles.html.\nworkspace_host: {{workspace_host}}", + "properties": { + "project_name": { + "type": "string", + "pattern": "^[A-Za-z_][A-Za-z0-9-_]+$", + "pattern_match_failure_message": "Name must consist of letters, numbers, dashes, and underscores.", + "default": "dbt_project", + "description": "\nPlease provide a unique name for this project.\nproject_name", + "order": 1 + }, + "http_path": { + "type": "string", + "pattern": "^/sql/.\\../warehouses/[a-z0-9]+$", + "pattern_match_failure_message": "Path must be of the form /sql/1.0/warehouses/", + "description": "\nPlease provide the HTTP Path of the SQL warehouse you would like to use with dbt during development.\nYou can find this path by clicking on \"Connection details\" for your SQL warehouse.\nhttp_path [example: /sql/1.0/warehouses/abcdef1234567890]", + "order": 2 + }, + "default_catalog": { + "type": "string", + "default": "{{default_catalog}}", + "pattern": "^\\w*$", + "pattern_match_failure_message": "Invalid catalog name.", + "description": "\nPlease provide an initial catalog{{if eq (default_catalog) \"\"}} (leave blank when not using Unity Catalog){{end}}.\ndefault_catalog", + "order": 3 + }, + "personal_schemas": { + "type": "string", + "description": "\nWould you like to use a personal schema for each user working on this project? (e.g., 'catalog.{{short_name}}')\npersonal_schemas", + "enum": [ + "yes, use a schema based on the current user name during development", + "no, use a shared schema during development" + ], + "order": 4 + }, + "shared_schema": { + "skip_prompt_if": { + "properties": { + "personal_schemas": { + "const": "yes, use a schema based on the current user name during development" + } + } + }, + "type": "string", + "default": "default", + "pattern": "^\\w+$", + "pattern_match_failure_message": "Invalid schema name.", + "description": "\nPlease provide an initial schema during development.\ndefault_schema", + "order": 5 + } + }, + "success_message": "\n📊 Your new project has been created in the '{{.project_name}}' directory!\nIf you already have dbt installed, just type 'cd {{.project_name}}; dbt init' to get started.\nRefer to the README.md file for full \"getting started\" guide and pduction setup instructions.\n" +} +``` + +### Explanations + +Notice that: +- The welcome message has the template name. +- By convention, property messages include the property name after a newline, e.g. `default_catalog` above has a description that says `"\nPlease provide an initial catalog [...].\ndefault_catalog"`, +- Each property defines a variable that is used for the template. +- Each property has a unique 'order' value that increments by 1 with each property. +- Enums use `"type": "string"` and have an `enum` field with a list of possible values. +- Helpers such as `{{default_catalog}}` and `{{short_name}}` can be used within property descriptors. +- Properties can be referenced in messages and descriptions using `{{.property_name}}`. `{{.project_name}}` is an example. From 4fe3fc9c8f09aa7c1a88c1516446572d98b7da76 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 1 Apr 2026 13:45:00 +0200 Subject: [PATCH 07/10] Update AGENTS.md --- AGENTS.md | 257 ++---------------------------------------------------- 1 file changed, 8 insertions(+), 249 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6680a83e20..7386b4ec50 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,7 @@ When moving code from one place to another, please don't unnecessarily change th # Development Commands ### Building and Testing + - `make build` - Build the CLI binary - `make test` - Run unit tests for all packages - `go test ./acceptance -run TestAccept/bundle/// -tail -test.v` - run a single acceptance test @@ -18,6 +19,7 @@ When moving code from one place to another, please don't unnecessarily change th - `make cover` - Generate test coverage reports ### Code Quality + - `make lint` - Run linter on changed files only (uses lintdiff.py) - `make lintfull` - Run full linter with fixes (golangci-lint) - `make ws` - Run whitespace linter @@ -25,16 +27,20 @@ When moving code from one place to another, please don't unnecessarily change th - `make checks` - Run quick checks (tidy, whitespace, links) ### Specialized Commands + - `make schema` - Generate bundle JSON schema - `make docs` - Generate bundle documentation - `make generate` - Generate CLI code from OpenAPI spec (requires universe repo) ### Git Commands -Use "git rm" to remove and "git mv" to rename files instead of directly modifying files on FS. -If asked to rebase, always prefix each git command with appropriate settings so that it never launches interactive editor. +Use `git rm` to remove and `git mv` to rename files instead of directly modifying files on FS. + +If asked to rebase, always prefix each git command with appropriate settings so that it never launches interactive editor: +```sh GIT_EDITOR=true GIT_SEQUENCE_EDITOR=true VISUAL=true GIT_PAGER=cat git fetch origin main && GIT_EDITOR=true GIT_SEQUENCE_EDITOR=true VISUAL=true GIT_PAGER=cat git rebase origin/main +``` # Architecture @@ -69,259 +75,12 @@ GIT_EDITOR=true GIT_SEQUENCE_EDITOR=true VISUAL=true GIT_PAGER=cat git rebase or - `terraform` (default) - Uses Terraform for resource management - `direct` - Direct API calls without Terraform -# Code Style and Patterns - -## Go - -Please make sure code that you author is consistent with the codebase and concise. - -The code should be self-documenting based on the code and function names. - -Functions should be documented with a doc comment as follows: - -// SomeFunc does something. -func SomeFunc() { - ... -} - -Note how the comment starts with the name of the function and is followed by a period. - -Avoid redundant and verbose comments. Use terse comments and only add comments if it complements, not repeats the code. - -Focus on making implementation as small and elegant as possible. Avoid unnecessary loops and allocations. If you see an opportunity of making things simpler by dropping or relaxing some requirements, ask user about the trade-off. - -Use modern idiomatic Golang features (version 1.24+). Specifically: - - Use for-range for integer iteration where possible. Instead of for i:=0; i < X; i++ {} you must write for i := range X{}. - - Use builtin min() and max() where possible (works on any type and any number of values). - - Do not capture the for-range variable, since go 1.22 a new copy of the variable is created for each loop iteration. - - Use empty struct types for context keys: `type myKeyType struct{}` (not `int`). - - Define magic strings as named constants at the top of the file. - - When integrating external tools or detecting environment variables, include source reference URLs as comments so they can be traced later. - -### Configuration Patterns -- Bundle config uses `dyn.Value` for dynamic typing -- Config loading supports includes, variable interpolation, and target overrides -- Schema generation is automated from Go struct tags - -## Python - -When writing Python scripts, we bias for conciseness. We think of Python in this code base as scripts. - - use Python 3.11 - - Do not catch exceptions to make nicer messages, only catch if you can add critical information - - use pathlib.Path in almost all cases over os.path unless it makes code longer - - Do not add redundant comments. - - Try to keep your code small and the number of abstractions low. - - After done, format you code with "ruff format -n " - - Use "#!/usr/bin/env python3" shebang. - -# Testing - -### Test Types -- **Unit tests**: Standard Go tests alongside source files -- **Integration tests**: `integration/` directory, requires live Databricks workspace -- **Acceptance tests**: `acceptance/` directory, uses mock HTTP server - -Each file like process_target_mode_test.go should have a corresponding test file -like process_target_mode_test.go. If you add new functionality to a file, -the test file should be extended to cover the new functionality. - -Tests should look like the following: - -package mutator_test - -func TestApplySomeChangeReturnsDiagnostics(t *testing.T) { - ... -} - -func TestApplySomeChangeFixesThings(t *testing.T) { - ctx := t.Context() - b, err := ...some operation... - require.NoError(t, err) - ... - assert.Equal(t, ...) -} - -Notice that: -- Tests are often in the same package but suffixed wit _test. -- The test names are prefixed with Test and are named after the function or module they are testing. -- 'require' and 'require.NoError' are used to check for things that would cause the rest of the test case to fail. -- 'assert' is used to check for expected values where the rest of the test is not expected to fail. - -When writing tests, please don't include an explanation in each -test case in your responses. I am just interested in the tests. - -Use table-driven tests when testing multiple similar cases (e.g., different inputs producing different outputs). Reviewers prefer this pattern over repeating near-identical test functions. - -### Acceptance Tests - -- Located in `acceptance/` with nested directory structure. -- Each test directory contains `databricks.yml`, `script`, and `output.txt`. -- Source files: `test.toml`, `script`, `script.prepare`, `databricks.yml`, etc. -- Tests are configured via `test.toml`. Config schema and explanation is in `acceptance/internal/config.go`. Config is inherited from parent directories. Certain options are also dumped to `out.test.toml` so that inherited values are visible on PRs. -- Generated output files start with `out`: `output.txt`, `out.test.toml`, `out.requests.txt`. Never edit these directly — use `-update` to regenerate. Exception: mass string replacement when the change is predictable and much cheaper than re-running the test suite. -- Run a single test: `go test ./acceptance -run TestAccept/bundle///` -- Run a specific variant by appending EnvMatrix values to the test name: `go test ./acceptance -run 'TestAccept/.../DATABRICKS_BUNDLE_ENGINE=direct'`. When there are multiple EnvMatrix variables, they appear in alphabetical order. -- Useful flags: `-v` for verbose output, `-tail` to follow test output (requires `-v`), `-logrequests` to log all HTTP requests/responses (requires `-v`). -- Run tests on cloud: `deco env run -i -n aws-prod-ucws -- ` (requires `deco` tool and access to test env). -- Use `-update` flag to regenerate expected output files. When a test fails because of stale output, re-run with `-update` instead of editing output files. -- All EnvMatrix variants share the same output files — they MUST produce identical output. Exception: filenames containing `$DATABRICKS_BUNDLE_ENGINE` (e.g. `output.direct.txt`) are recorded per-engine. -- `-update` with divergent variant outputs is destructive: overwrites with last variant, breaking others. To debug: run a single variant you consider correct with `-update`, then debug the other variant to find why it diverges. -- `test.toml` is inherited — put common options into a parent directory. -- Add test artifacts (e.g. `.databricks`) to `Ignore` in `test.toml`. -- `script.prepare` files from parent directories are concatenated into the test script — use them for shared bash helpers. - -**Helper scripts** in `acceptance/bin/` are available on `PATH` during test execution: -- `contains.py SUBSTR [!SUBSTR_NOT]` — passthrough filter (stdin→stdout) that checks substrings are present (or absent with `!` prefix). Errors are reported on stderr. -- `print_requests.py //path [^//exclude] [--get] [--sort] [--keep]` — print recorded HTTP requests matching path filters. Requires `RecordRequests=true` in `test.toml`. Clears `out.requests.txt` afterwards unless `--keep`. Use `--get` to include GET requests (excluded by default). Use `^` prefix to exclude paths. -- `replace_ids.py [-t TARGET]` — read deployment state and add `[NAME_ID]` replacements for all resource IDs. -- `read_id.py [-t TARGET] NAME` — read ID of a single resource from state, print it, and add a `[NAME_ID]` replacement. -- `add_repl.py VALUE REPLACEMENT` — add a custom replacement (VALUE will be replaced with `[REPLACEMENT]` in output). -- `update_file.py FILENAME OLD NEW` — replace all occurrences of OLD with NEW in FILENAME. Errors if OLD is not found. Cannot be used on `output.txt`. -- `find.py REGEX [--expect N]` — find files matching regex in current directory. `--expect N` to assert exact count. -- `diff.py DIR1 DIR2` or `diff.py FILE1 FILE2` — recursive diff with test replacements applied. -- `print_state.py [-t TARGET] [--backup]` — print deployment state (terraform or direct). -- `edit_resource.py TYPE ID < script.py` — fetch resource by ID, execute Python on it (resource in `r`), then update it. TYPE is `jobs` or `pipelines`. -- `gron.py` — flatten JSON into greppable discrete assignments (simpler than `jq` for searching JSON). -- `jq` is also available for JSON processing. - -**Update workflow**: Run `make test-update` to regenerate outputs. Then run `make fmt` and `make lint` — if these modify files in `acceptance/`, there's an issue in source files. Fix the source, regenerate, and verify lint/fmt pass cleanly. - -**Template tests**: Tests in `acceptance/bundle/templates` include materialized templates in output directories. These directories follow the same `out` convention — everything starting with `out` is generated output. Sources are in `libs/template/templates/`. Use `make test-update-templates` to regenerate. If linters or formatters find issues in materialized templates, do not fix the output files — fix the source in `libs/template/templates/`, then regenerate. - -# Context - -Always pass `context.Context` as a function argument; never store it in a struct. Storing context in a struct obscures the lifecycle and prevents callers from setting per-call deadlines, cancellation, and metadata (see https://go.dev/blog/context-and-structs). Do not use `context.Background()` outside of `main.go` files. In tests, use `t.Context()` (or `b.Context()` for benchmarks). - -# Logging - -Use the following for logging: - -``` -import "github.com/databricks/cli/libs/log" - -log.Infof(ctx, "...") -log.Debugf(ctx, "...") -log.Warnf(ctx, "...") -log.Errorf(ctx, "...") -``` - -Note that the 'ctx' variable here is something that should be passed in as -an argument by the caller. - -Use cmdio.LogString to print to stdout: - -``` -import "github.com/databricks/cli/libs/cmdio" - -cmdio.LogString(ctx, "...") -``` - -Always output file path with forward slashes, even on Windows, so that acceptance test output is stable between OSes. Use filepath.ToSlash for this. - -# Specific File Guides - -## databricks_template_schema.json - -A databricks_template_schema.json file is used to configure bundle templates. - -Below is a good reference template: - -{ - "welcome_message": "\nWelcome to the dbt template for Declarative Automation Bundles!\n\nA workspace was selected based on your current profile. For information about how to change this, see https://docs.databricks.com/dev-tools/cli/profiles.html.\nworkspace_host: {{workspace_host}}", - "properties": { - "project_name": { - "type": "string", - "pattern": "^[A-Za-z_][A-Za-z0-9-_]+$", - "pattern_match_failure_message": "Name must consist of letters, numbers, dashes, and underscores.", - "default": "dbt_project", - "description": "\nPlease provide a unique name for this project.\nproject_name", - "order": 1 - }, - "http_path": { - "type": "string", - "pattern": "^/sql/.\\../warehouses/[a-z0-9]+$", - "pattern_match_failure_message": "Path must be of the form /sql/1.0/warehouses/", - "description": "\nPlease provide the HTTP Path of the SQL warehouse you would like to use with dbt during development.\nYou can find this path by clicking on \"Connection details\" for your SQL warehouse.\nhttp_path [example: /sql/1.0/warehouses/abcdef1234567890]", - "order": 2 - }, - "default_catalog": { - "type": "string", - "default": "{{default_catalog}}", - "pattern": "^\\w*$", - "pattern_match_failure_message": "Invalid catalog name.", - "description": "\nPlease provide an initial catalog{{if eq (default_catalog) \"\"}} (leave blank when not using Unity Catalog){{end}}.\ndefault_catalog", - "order": 3 - }, - "personal_schemas": { - "type": "string", - "description": "\nWould you like to use a personal schema for each user working on this project? (e.g., 'catalog.{{short_name}}')\npersonal_schemas", - "enum": [ - "yes, use a schema based on the current user name during development", - "no, use a shared schema during development" - ], - "order": 4 - }, - "shared_schema": { - "skip_prompt_if": { - "properties": { - "personal_schemas": { - "const": "yes, use a schema based on the current user name during development" - } - } - }, - "type": "string", - "default": "default", - "pattern": "^\\w+$", - "pattern_match_failure_message": "Invalid schema name.", - "description": "\nPlease provide an initial schema during development.\ndefault_schema", - "order": 5 - } - }, - "success_message": "\n📊 Your new project has been created in the '{{.project_name}}' directory!\nIf you already have dbt installed, just type 'cd {{.project_name}}; dbt init' to get started.\nRefer to the README.md file for full \"getting started\" guide and production setup instructions.\n" -} - -Notice that: -- The welcome message has the template name. -- By convention, property messages include the property name after a newline, e.g. default_catalog above has a description that says "\nPlease provide an initial catalog [...].\ndefault_catalog", -- Each property defines a variable that is used for the template. -- Each property has a unique 'order' value that increments by 1 with each property. -- Enums use 'type: "string' and have an 'enum' field with a list of possible values. -- Helpers such as {{default_catalog}} and {{short_name}} can be used within property descriptors. -- Properties can be referenced in messages and descriptions using {{.property_name}}. {{.project_name}} is an example. - # Development Tips - Use `make test-update` to regenerate acceptance test outputs after changes - The CLI binary supports both `databricks` and `pipelines` command modes based on executable name - Comments should explain "why", not "what" — reviewers consistently reject comments that merely restate the code -# Pre-PR Checklist - -Before submitting a PR, run these commands to match what CI checks. CI uses the **full** variants (not the diff-only wrappers), so `make lint` alone is insufficient. - -```bash -# 1. Formatting and checks (CI runs fmtfull, not fmt) -make fmtfull -make checks - -# 2. Linting (CI runs full golangci-lint, not the diff-only wrapper) -make lintfull - -# 3. Tests (CI runs with both deployment engines) -make test - -# 4. If you changed bundle config structs or schema-related code: -make schema - -# 5. If you changed files in python/: -cd python && make codegen && make test && make lint && make docs - -# 6. If you changed experimental/aitools or experimental/ssh: -make test-exp-aitools # only if aitools code changed -make test-exp-ssh # only if ssh code changed -``` - - # Common Mistakes - Do NOT add dependencies without checking license compatibility. From 6004f0752432f24ea53af3682adc7693bf253211 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 1 Apr 2026 13:48:43 +0200 Subject: [PATCH 08/10] Add pointer to .agent dir --- AGENTS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 7386b4ec50..014e1f6271 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,9 @@ This file provides guidance to AI assistants when working with code in this repository. +Also consider the `.agents` directory. +- `rules/*.md` can be intelligently loaded through its YAML frontmatter +- `skills/*/SKILL.md` contains specific instructions that can be pulled in when needed for specific tasks (consult SKILL.md frontmatter) + # Project Overview This is the Databricks CLI, a command-line interface for interacting with Databricks workspaces and managing Declarative Automation Bundles (DABs), formerly known as Databricks Asset Bundles. The project is written in Go and follows a modular architecture. From 010c2f5ed4c56ee57da868a81c6efda00fada4fe Mon Sep 17 00:00:00 2001 From: Jan N Rose Date: Wed, 1 Apr 2026 13:52:03 +0200 Subject: [PATCH 09/10] Fix typo --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 014e1f6271..4f0adbcebb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ This file provides guidance to AI assistants when working with code in this repository. -Also consider the `.agents` directory. +Also consider the `.agent/` directory. - `rules/*.md` can be intelligently loaded through its YAML frontmatter - `skills/*/SKILL.md` contains specific instructions that can be pulled in when needed for specific tasks (consult SKILL.md frontmatter) From b58824a2ecdf44820af3ad5e9d8e1b17be5a3344 Mon Sep 17 00:00:00 2001 From: Jan Rose Date: Wed, 1 Apr 2026 13:53:22 +0200 Subject: [PATCH 10/10] Add pr-checklist --- .agent/skills/pr-checklist/SKILL.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .agent/skills/pr-checklist/SKILL.md diff --git a/.agent/skills/pr-checklist/SKILL.md b/.agent/skills/pr-checklist/SKILL.md new file mode 100644 index 0000000000..ae21c13ca0 --- /dev/null +++ b/.agent/skills/pr-checklist/SKILL.md @@ -0,0 +1,28 @@ +--- +name: pr-checklist +description: Checklist to run before submitting a PR +--- + +Before submitting a PR, run these commands to match what CI checks. CI uses the **full** variants (not the diff-only wrappers), so `make lint` alone is insufficient. + +```bash +# 1. Formatting and checks (CI runs fmtfull, not fmt) +make fmtfull +make checks + +# 2. Linting (CI runs full golangci-lint, not the diff-only wrapper) +make lintfull + +# 3. Tests (CI runs with both deployment engines) +make test + +# 4. If you changed bundle config structs or schema-related code: +make schema + +# 5. If you changed files in python/: +cd python && make codegen && make test && make lint && make docs + +# 6. If you changed experimental/aitools or experimental/ssh: +make test-exp-aitools # only if aitools code changed +make test-exp-ssh # only if ssh code changed +```