Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .agents/rules/auto-generated-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ globs:
- "internal/genkit/tagging.py"
- "internal/mocks/**/*.go"
- "bundle/direct/dresources/configs/*.generated.yml"
- "bundle/internal/validation/generated/*.go"
- "bundle/config/validate/*_walk.go"
- "bundle/schema/jsonschema.json"
- "python/databricks/bundles/version.py"
- "python/databricks/bundles/*/__init__.py"
Expand All @@ -33,7 +33,7 @@ paths:
- "internal/genkit/tagging.py"
- "internal/mocks/**/*.go"
- "bundle/direct/dresources/configs/*.generated.yml"
- "bundle/internal/validation/generated/*.go"
- "bundle/config/validate/*_walk.go"
- "bundle/schema/jsonschema.json"
- "python/databricks/bundles/version.py"
- "python/databricks/bundles/*/__init__.py"
Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/bump-sdk/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ allowed-tools: Read, Edit, Write, Bash, Glob, Grep, WebFetch, AskUserQuestion

The SDK version lives in `go.mod` (`github.com/databricks/databricks-sdk-go`) and the pinned spec SHA lives in `.codegen/_openapi_sha`.
These two move as a pair; everything else in this skill is regenerated from them or is fallout you fix by hand.
Do not hand-edit generated files (`.codegen/cli.json`, `cmd/workspace/*`, `cmd/account/*`, `bundle/schema/jsonschema.json`, `bundle/internal/validation/generated/*`, `bundle/direct/dresources/configs/*.generated.yml`, `python/databricks/bundles/**`); regenerate them.
Do not hand-edit generated files (`.codegen/cli.json`, `cmd/workspace/*`, `cmd/account/*`, `bundle/schema/jsonschema.json`, `bundle/config/validate/*_walk.go`, `bundle/direct/dresources/configs/*.generated.yml`, `python/databricks/bundles/**`); regenerate them.

The Python tasks (`pydabs-*`, and the `pydabs-codegen` step inside `generate-check`) all run through `uv`. If one fails because `uv` is missing or because the host's `python3` is too old (e.g. 3.9), install `uv` (`curl -LsSf https://astral.sh/uv/install.sh | sh`) rather than touching the system Python: `uv run` provisions the interpreter each package pins (`>=3.10`, and `==3.13.*` under `python/codegen/`) and downloads it if needed. Do not chase the system Python version.
The acceptance goldens are the exception: they need CI's `uv` index, see step 4.
Expand Down
6 changes: 3 additions & 3 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1008,17 +1008,17 @@ tasks:
- "go run ./bundle/internal/schema ./bundle/internal/schema ./bundle/schema/jsonschema.json .codegen/cli.json"

generate-validation:
desc: Generate enum and required field validation code
desc: Generate the walkers of the enum and required field validators
sources:
- "**/*.go"
- exclude: "**/*_test.go"
- go.mod
- go.sum
- "{{.EMBED_SOURCES}}"
generates:
- bundle/internal/validation/generated/**/*.go
- bundle/config/validate/*_walk.go
cmds:
- "sh -c 'go run ./bundle/internal/validation/. && gofmt -w -s ./bundle/internal/validation/generated'"
- "sh -c 'go run ./bundle/internal/validation/. && gofmt -w -s ./bundle/config/validate/required_walk.go ./bundle/config/validate/enum_walk.go'"

generate-direct:
desc: Generate direct engine config (apitypes + resources)
Expand Down
1 change: 1 addition & 0 deletions bundle/config/validate/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*_walk.go linguist-generated=true
72 changes: 10 additions & 62 deletions bundle/config/validate/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import (
"slices"

"github.com/databricks/cli/bundle"
"github.com/databricks/cli/bundle/internal/validation/generated"
"github.com/databricks/cli/libs/diag"
"github.com/databricks/cli/libs/dyn"
"github.com/databricks/cli/libs/dyn/dynvar"
)

type enum struct{}
Expand All @@ -23,66 +20,17 @@ func (f *enum) Name() string {
return "validate:enum"
}

// Apply reports enum fields with invalid values. The walker is generated (enum_walk.go)
// by bundle/internal/validation.
func (f *enum) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
diags := diag.Diagnostics{}

// Generate prefix tree for all enum fields.
trie := &dyn.TrieNode{}
for k := range generated.EnumFields {
pattern, err := dyn.NewPatternFromString(k)
if err != nil {
return diag.FromErr(fmt.Errorf("invalid pattern %q for enum field validation: %w", k, err))
}

err = trie.Insert(pattern)
if err != nil {
return diag.FromErr(fmt.Errorf("failed to insert pattern %q into trie: %w", k, err))
}
}

err := dyn.WalkReadOnly(b.Config.Value(), func(p dyn.Path, v dyn.Value) error {
// If the path is not found in the prefix tree, we do not need to validate any enum
// fields in it.
pattern, ok := trie.SearchPath(p)
if !ok {
return nil
}

// Get the string value for comparison
strValue, ok := v.AsString()
if !ok {
return nil
}

// Skip validation for values containing variable references (e.g.
// ${resources.jobs.my_job.id}) since they are not yet resolved.
if dynvar.ContainsVariableReference(strValue) {
return nil
}

// Get valid values for this pattern
validValues := generated.EnumFields[pattern.String()]

// Check if the value is in the list of valid enum values
validValue := slices.Contains(validValues, strValue)

if !validValue {
// p is a slice of path components. We need to clone it before using it in diagnostics
// since the WalkReadOnly function will mutate it while walking the config tree.

diags = diags.Append(diag.Diagnostic{
Severity: diag.Warning,
Summary: fmt.Sprintf("invalid value %q for enum field. Valid values are %v", strValue, validValues),
Locations: v.Locations(),
Paths: dyn.ToStructPaths(p),
})
}

return nil
})
if err != nil {
return diag.FromErr(err)
}
w := &walker{diags: diag.Diagnostics{}}
n := rootNode(b.Config.Value())
enumConfigRoot(w, &n, &b.Config)
diags := w.diags

// Start from the order of a walk of the configuration tree, so that the sort
// below breaks ties between diagnostics as it did with that walk.
sortByWalkOrder(b.Config.Value(), diags)

// Sort diagnostics to make them deterministic
slices.SortFunc(diags, func(a, b diag.Diagnostic) int {
Expand Down
Loading
Loading