Skip to content
Draft
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 @@ -1012,17 +1012,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