Skip to content

Commit ace5f45

Browse files
authored
Consolidate bundle schema annotations into a single tree-structured file (#5574)
## Changes - Replace the three type-name-keyed annotation files (`annotations_openapi.yml`, `annotations_openapi_overrides.yml`, `annotations.yml`) with one `annotations.yml` structured like the bundle config tree. - Derive upstream docs (descriptions, enums, launch stages, field behaviors) from `.codegen/cli.json` in memory at generation time, instead of checking in ~7k lines of generated YAML. The file now holds only CLI-owned docs and overrides. - Structural keys are `$type`/`$fields`; absent entries inherit from cli.json, and `description: PLACEHOLDER` flags undocumented fields. - `task generate-schema` keeps the file in lockstep with the config structure: it adds placeholders for new fields and drops entries for fields the schema no longer emits. ## Why The old files duplicated cli.json data that went stale between regenerations and were keyed by Go type name, which hid where a field actually lives. One structured file gives a single place for CLI-owned docs, no duplicated upstream content, and a layout that reads like the config it documents. ## Tests - `bundle/schema/jsonschema.json` and `jsonschema_for_docs.json` are byte-for-byte identical to `main` — this is purely an internal representation change. - New unit tests for the type graph and the file codec; existing annotation tests carried over. _This PR was written by Isaac._
1 parent 0437746 commit ace5f45

17 files changed

Lines changed: 3380 additions & 10855 deletions

.agent/rules/auto-generated-files.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ globs:
1414
- "internal/genkit/tagging.py"
1515
- "internal/mocks/**/*.go"
1616
- "bundle/direct/dresources/*.generated.yml"
17-
- "bundle/internal/schema/annotations_openapi.yml"
1817
- "bundle/internal/validation/generated/*.go"
1918
- "bundle/schema/jsonschema.json"
2019
- "bundle/schema/jsonschema_for_docs.json"
@@ -35,7 +34,6 @@ paths:
3534
- "internal/genkit/tagging.py"
3635
- "internal/mocks/**/*.go"
3736
- "bundle/direct/dresources/*.generated.yml"
38-
- "bundle/internal/schema/annotations_openapi.yml"
3937
- "bundle/internal/validation/generated/*.go"
4038
- "bundle/schema/jsonschema.json"
4139
- "bundle/schema/jsonschema_for_docs.json"
@@ -72,7 +70,7 @@ Files matching this rule's glob pattern are most likely generated artifacts. Aut
7270
- Bundle schemas:
7371
- `./task generate-schema`
7472
- `./task generate-schema-docs`
75-
- This can also refresh `bundle/internal/schema/annotations_openapi.yml` when OpenAPI annotation extraction is enabled.
73+
- Both rewrite `bundle/internal/schema/annotations.yml` in place: upstream docs are sourced from `.codegen/cli.json` at generation time, and the file is synced with the config structure (placeholders added, stale entries dropped).
7674
- Validation generated code:
7775
- `./task generate-validation`
7876
- Mock files:

Taskfile.yml

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -856,35 +856,17 @@ tasks:
856856
cmds:
857857
- go test ./acceptance -run TestAccept/bundle/refschema -update
858858

859-
# Regenerates the OpenAPI annotation files (annotations_openapi*.yml) from the
860-
# checked-in .codegen/cli.json. This is the step that propagates a cli.json
861-
# change into the schema/docs artifacts: generate-schema and generate-schema-docs
862-
# both depend on it, so editing cli.json and running `task generate`
863-
# percolates into jsonschema.json, jsonschema_for_docs.json and pydabs.
864-
generate-annotations:
865-
desc: Regenerate annotation files from .codegen/cli.json
866-
# Dep of generate-schema and generate-schema-docs; `run: once`
867-
# plus the fingerprint keep `task generate` from re-running it per parent.
868-
run: once
869-
sources:
870-
- .codegen/cli.json
871-
- bundle/internal/schema/**/*.go
872-
- bundle/internal/schema/annotations*.yml
873-
- go.mod
874-
- go.sum
875-
generates:
876-
- bundle/internal/schema/annotations_openapi.yml
877-
- bundle/internal/schema/annotations.yml
878-
- bundle/schema/jsonschema.json
879-
cmds:
880-
- "DATABRICKS_CLI_JSON=.codegen/cli.json go run ./bundle/internal/schema ./bundle/internal/schema ./bundle/schema/jsonschema.json"
881-
859+
# Upstream field documentation comes from the checked-in .codegen/cli.json;
860+
# bundle/internal/schema/annotations.yml carries the CLI-owned docs and
861+
# overrides and is rewritten in place (synced with the config structure).
862+
# Editing either input and running `task generate` percolates into
863+
# jsonschema.json, jsonschema_for_docs.json and pydabs.
882864
generate-schema:
883865
desc: Generate bundle JSON schema
884-
deps: ['generate-annotations']
885866
sources: &SCHEMA_SOURCES
886867
- "**/*.go"
887-
- bundle/internal/schema/annotations*.yml
868+
- .codegen/cli.json
869+
- bundle/internal/schema/annotations.yml
888870
- exclude: "**/*_test.go"
889871
- go.mod
890872
- go.sum
@@ -893,11 +875,10 @@ tasks:
893875
- bundle/schema/jsonschema.json
894876
- bundle/internal/schema/annotations.yml
895877
cmds:
896-
- "go run ./bundle/internal/schema ./bundle/internal/schema ./bundle/schema/jsonschema.json"
878+
- "go run ./bundle/internal/schema ./bundle/internal/schema ./bundle/schema/jsonschema.json .codegen/cli.json"
897879

898880
generate-schema-docs:
899881
desc: Generate bundle JSON schema for documentation
900-
deps: ['generate-annotations']
901882
sources: *SCHEMA_SOURCES
902883
generates:
903884
- bundle/schema/jsonschema_for_docs.json
@@ -908,7 +889,7 @@ tasks:
908889
# silently dropped from the output. Restore the fetch that lived in the
909890
# old tools/post-generate.sh.
910891
- git fetch origin 'refs/tags/v*:refs/tags/v*'
911-
- "go run ./bundle/internal/schema ./bundle/internal/schema ./bundle/schema/jsonschema_for_docs.json --docs"
892+
- "go run ./bundle/internal/schema ./bundle/internal/schema ./bundle/schema/jsonschema_for_docs.json .codegen/cli.json --docs"
912893

913894
generate-validation:
914895
desc: Generate enum and required field validation code

bundle/internal/annotation/file.go

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,32 @@
11
package annotation
22

3-
import (
4-
"bytes"
5-
"os"
6-
7-
"github.com/databricks/cli/libs/dyn"
8-
"github.com/databricks/cli/libs/dyn/convert"
9-
"github.com/databricks/cli/libs/dyn/merge"
10-
"github.com/databricks/cli/libs/dyn/yamlloader"
11-
)
3+
// TypeAnnotation holds the documentation for one Go type. Self documents the
4+
// type itself — it is applied to the type's JSON-schema $defs entry and is
5+
// where enum values live — and Fields documents each of the type's fields by
6+
// JSON name.
7+
type TypeAnnotation struct {
8+
Self Descriptor `json:"type,omitempty"`
9+
Fields map[string]Descriptor `json:"fields,omitempty"`
10+
}
1211

13-
// Parsed file with annotations, expected format:
14-
// github.com/databricks/cli/bundle/config.Bundle:
15-
//
16-
// cluster_id:
17-
// description: "Description"
18-
type File map[string]map[string]Descriptor
12+
// File is the in-memory annotations, keyed by Go type path, e.g.
13+
// "github.com/databricks/cli/bundle/config.Bundle".
14+
type File map[string]TypeAnnotation
1915

20-
func LoadAndMerge(sources []string) (File, error) {
21-
prev := dyn.NilValue
22-
for _, path := range sources {
23-
b, err := os.ReadFile(path)
24-
if err != nil {
25-
return nil, err
26-
}
27-
generated, err := yamlloader.LoadYAML(path, bytes.NewBuffer(b))
28-
if err != nil {
29-
return nil, err
30-
}
31-
prev, err = merge.Merge(prev, generated)
32-
if err != nil {
33-
return nil, err
34-
}
16+
// SetField stores a descriptor for a field of typeKey, allocating the entry
17+
// and its field map as needed.
18+
func (f File) SetField(typeKey, name string, d Descriptor) {
19+
ta := f[typeKey]
20+
if ta.Fields == nil {
21+
ta.Fields = map[string]Descriptor{}
3522
}
23+
ta.Fields[name] = d
24+
f[typeKey] = ta
25+
}
3626

37-
var data File
38-
39-
err := convert.ToTyped(&data, prev)
40-
if err != nil {
41-
return nil, err
42-
}
43-
return data, nil
27+
// SetSelf stores the descriptor for the type itself.
28+
func (f File) SetSelf(typeKey string, d Descriptor) {
29+
ta := f[typeKey]
30+
ta.Self = d
31+
f[typeKey] = ta
4432
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package annotation
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestFileSetField(t *testing.T) {
10+
f := File{}
11+
12+
// SetField allocates the type entry and its field map on first use.
13+
f.SetField("pkg.Type", "a", Descriptor{Description: "A"})
14+
f.SetField("pkg.Type", "b", Descriptor{Description: "B"})
15+
16+
assert.Equal(t, "A", f["pkg.Type"].Fields["a"].Description)
17+
assert.Equal(t, "B", f["pkg.Type"].Fields["b"].Description)
18+
19+
// A later SetField overwrites the field, leaving siblings intact.
20+
f.SetField("pkg.Type", "a", Descriptor{Description: "A2"})
21+
assert.Equal(t, "A2", f["pkg.Type"].Fields["a"].Description)
22+
assert.Equal(t, "B", f["pkg.Type"].Fields["b"].Description)
23+
}
24+
25+
func TestFileSetSelf(t *testing.T) {
26+
f := File{}
27+
28+
f.SetSelf("pkg.Type", Descriptor{Description: "the type"})
29+
assert.Equal(t, "the type", f["pkg.Type"].Self.Description)
30+
31+
// SetSelf and SetField populate the same entry without clobbering each
32+
// other.
33+
f.SetField("pkg.Type", "a", Descriptor{Description: "A"})
34+
assert.Equal(t, "the type", f["pkg.Type"].Self.Description)
35+
assert.Equal(t, "A", f["pkg.Type"].Fields["a"].Description)
36+
}

bundle/internal/schema/.gitattributes

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)