diff --git a/bundle/internal/schema/annotations.go b/bundle/internal/schema/annotations.go index 548cf5ae95f..a7773f9565b 100644 --- a/bundle/internal/schema/annotations.go +++ b/bundle/internal/schema/annotations.go @@ -6,6 +6,8 @@ import ( "regexp" "strings" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/internal/annotation" "github.com/databricks/cli/internal/clijson" "github.com/databricks/cli/libs/dyn" @@ -112,15 +114,41 @@ func (d *annotationHandler) addAnnotations(typ reflect.Type, s jsonschema.Schema for k, v := range s.Properties { item := ta.Fields[k] - if item.Description == "" { - item.Description = annotation.Placeholder - d.missingAnnotations.SetField(refPath, k, annotation.Descriptor{Description: annotation.Placeholder}) + if item.Description == "" || item.Description == annotation.Placeholder { + if text := boilerplateDescription(typ, k); text != "" { + item.Description = text + d.missingAnnotations.SetField(refPath, k, annotation.Descriptor{Description: text}) + } else if item.Description == "" { + item.Description = annotation.Placeholder + d.missingAnnotations.SetField(refPath, k, annotation.Descriptor{Description: annotation.Placeholder}) + } } assignAnnotation(v, item) } return s } +const resourceLifecycleDescription = "Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed." + +// boilerplateDescription returns the text that every resource type repeats for +// field k of typ, or "". Callers apply it only to undocumented or placeholder +// fields, so hand-written text is never replaced. +func boilerplateDescription(typ reflect.Type, k string) string { + switch { + case typ == reflect.TypeFor[config.Resources](): + desc, ok := config.SupportedResources()[k] + if !ok { + return "" + } + name := strings.ReplaceAll(desc.SingularName, "_", " ") + return fmt.Sprintf("The %s definitions for the bundle, where each key is the name of the %s.", name, name) + case k == "lifecycle" && typ.PkgPath() == reflect.TypeFor[resources.Job]().PkgPath(): + return resourceLifecycleDescription + default: + return "" + } +} + // Writes the annotations file back in canonical form, adding placeholder // descriptions for fields that have no documentation anywhere. Entries for // fields that no longer exist in the config are dropped with a warning. diff --git a/bundle/internal/schema/annotations.yml b/bundle/internal/schema/annotations.yml index f983a1a99b7..80d6f712b0e 100644 --- a/bundle/internal/schema/annotations.yml +++ b/bundle/internal/schema/annotations.yml @@ -258,7 +258,7 @@ resources: "$fields": "alerts": "description": |- - PLACEHOLDER + The alert definitions for the bundle, where each key is the name of the alert. "$fields": "evaluation": "description": |- @@ -525,7 +525,7 @@ resources: PLACEHOLDER "catalogs": "description": |- - PLACEHOLDER + The catalog definitions for the bundle, where each key is the name of the catalog. "$fields": "grants": "description": |- @@ -562,11 +562,11 @@ resources: PLACEHOLDER "cluster_policies": "description": |- - PLACEHOLDER + The cluster policy definitions for the bundle, where each key is the name of the cluster policy. "$fields": "lifecycle": "description": |- - PLACEHOLDER + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. "permissions": "description": |- The permissions to apply to this resource. @@ -810,7 +810,7 @@ resources: Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. "database_catalogs": "description": |- - PLACEHOLDER + The database catalog definitions for the bundle, where each key is the name of the database catalog. "$fields": "create_database_if_not_exists": "description": |- @@ -823,7 +823,7 @@ resources: PLACEHOLDER "database_instances": "description": |- - PLACEHOLDER + The database instance definitions for the bundle, where each key is the name of the database instance. "$fields": "lifecycle": "description": |- @@ -902,7 +902,7 @@ resources: The name of the user granted the permission level. "external_locations": "description": |- - PLACEHOLDER + The external location definitions for the bundle, where each key is the name of the external location. "$fields": "effective_file_event_queue": "$fields": @@ -987,7 +987,7 @@ resources: PLACEHOLDER "lifecycle": "description": |- - PLACEHOLDER + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. "source": "$fields": "delta_table_source": @@ -1011,7 +1011,7 @@ resources: PLACEHOLDER "genie_spaces": "description": |- - PLACEHOLDER + The genie space definitions for the bundle, where each key is the name of the genie space. "$fields": "description": "description": |- @@ -1311,7 +1311,7 @@ resources: Deprecated: setting this field is rejected. Use `cron_schedule_trigger` instead. "mcp_services": "description": |- - PLACEHOLDER + The mcp service definitions for the bundle, where each key is the name of the mcp service. "$fields": "comment": "description": |- @@ -1324,7 +1324,7 @@ resources: PLACEHOLDER "lifecycle": "description": |- - PLACEHOLDER + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. "mcp_service_id": "description": |- PLACEHOLDER @@ -1333,7 +1333,7 @@ resources: PLACEHOLDER "model_provider_services": "description": |- - PLACEHOLDER + The model provider service definitions for the bundle, where each key is the name of the model provider service. "$fields": "comment": "description": |- @@ -1346,7 +1346,7 @@ resources: PLACEHOLDER "lifecycle": "description": |- - PLACEHOLDER + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. "model_provider_service_id": "description": |- PLACEHOLDER @@ -1355,7 +1355,7 @@ resources: PLACEHOLDER "model_services": "description": |- - PLACEHOLDER + The model service definitions for the bundle, where each key is the name of the model service. "$fields": "comment": "description": |- @@ -1368,7 +1368,7 @@ resources: PLACEHOLDER "lifecycle": "description": |- - PLACEHOLDER + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. "model_service_id": "description": |- PLACEHOLDER @@ -2115,7 +2115,7 @@ resources: PLACEHOLDER "synced_database_tables": "description": |- - PLACEHOLDER + The synced database table definitions for the bundle, where each key is the name of the synced database table. "$fields": "data_synchronization_status": "$fields": @@ -2129,7 +2129,7 @@ resources: Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. "vector_search_endpoints": "description": |- - PLACEHOLDER + The vector search endpoint definitions for the bundle, where each key is the name of the vector search endpoint. "$fields": "lifecycle": "description": |- @@ -2166,7 +2166,7 @@ resources: PLACEHOLDER "vector_search_indexes": "description": |- - PLACEHOLDER + The vector search index definitions for the bundle, where each key is the name of the vector search index. "$fields": "grants": "description": |- diff --git a/bundle/internal/schema/annotations_test.go b/bundle/internal/schema/annotations_test.go index 3ad9bae6fd6..a67360ffd9d 100644 --- a/bundle/internal/schema/annotations_test.go +++ b/bundle/internal/schema/annotations_test.go @@ -1,8 +1,11 @@ package main import ( + "reflect" "testing" + "github.com/databricks/cli/bundle/config" + "github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/internal/annotation" "github.com/databricks/cli/internal/clijson" "github.com/databricks/cli/libs/jsonschema" @@ -270,3 +273,23 @@ func TestBuildEnumDescriptions(t *testing.T) { assert.Equal(t, []string{"[Public Preview]", "", "[Beta]"}, got) }) } + +// A new resource with no annotations gets the boilerplate descriptions for its +// resource-level entry and its lifecycle field instead of the placeholder. +func TestBoilerplateDescriptionsForNewResource(t *testing.T) { + h, err := newAnnotationHandler(annotation.File{}, annotation.File{}) + require.NoError(t, err) + + resourcesSchema := jsonschema.Schema{Properties: map[string]*jsonschema.Schema{"jobs": {}}} + h.addAnnotations(reflect.TypeFor[config.Resources](), resourcesSchema) + const jobsDescription = "The job definitions for the bundle, where each key is the name of the job." + assert.Equal(t, jobsDescription, resourcesSchema.Properties["jobs"].Description) + assert.Equal(t, jobsDescription, h.missingAnnotations[getPath(reflect.TypeFor[config.Resources]())].Fields["jobs"].Description) + + jobSchema := jsonschema.Schema{Properties: map[string]*jsonschema.Schema{"lifecycle": {}, "name": {}}} + h.addAnnotations(reflect.TypeFor[resources.Job](), jobSchema) + jobPath := getPath(reflect.TypeFor[resources.Job]()) + assert.Equal(t, resourceLifecycleDescription, h.missingAnnotations[jobPath].Fields["lifecycle"].Description) + // Fields without boilerplate keep the placeholder. + assert.Equal(t, annotation.Placeholder, h.missingAnnotations[jobPath].Fields["name"].Description) +} diff --git a/bundle/schema/jsonschema.json b/bundle/schema/jsonschema.json index 93c4d40e6a3..2b20c9b08a3 100644 --- a/bundle/schema/jsonschema.json +++ b/bundle/schema/jsonschema.json @@ -684,6 +684,7 @@ "x-databricks-launch-stage": "GA" }, "lifecycle": { + "description": "Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed.", "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "max_clusters_per_user": { @@ -1136,6 +1137,7 @@ "deprecated": true }, "lifecycle": { + "description": "Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed.", "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "lineage_context": { @@ -1818,6 +1820,7 @@ "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/catalog.PrivilegeAssignment" }, "lifecycle": { + "description": "Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed.", "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "mcp_service_id": { @@ -2010,6 +2013,7 @@ "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/catalog.PrivilegeAssignment" }, "lifecycle": { + "description": "Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed.", "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "model_provider_service_id": { @@ -2046,6 +2050,7 @@ "$ref": "#/$defs/slice/github.com/databricks/databricks-sdk-go/service/catalog.PrivilegeAssignment" }, "lifecycle": { + "description": "Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed.", "$ref": "#/$defs/github.com/databricks/cli/bundle/config/resources.Lifecycle" }, "model_service_id": { @@ -4232,6 +4237,7 @@ "type": "object", "properties": { "alerts": { + "description": "The alert definitions for the bundle, where each key is the name of the alert.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.Alert" }, "apps": { @@ -4240,9 +4246,11 @@ "markdownDescription": "The app resource defines a [Databricks app](https://docs.databricks.com/api/workspace/apps/create). For information about Databricks Apps, see [link](https://docs.databricks.com/dev-tools/databricks-apps/index.html)." }, "catalogs": { + "description": "The catalog definitions for the bundle, where each key is the name of the catalog.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.Catalog" }, "cluster_policies": { + "description": "The cluster policy definitions for the bundle, where each key is the name of the cluster policy.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.ClusterPolicy" }, "clusters": { @@ -4261,9 +4269,11 @@ "markdownDescription": "The data quality monitor definitions for the bundle, where each key is the name of the data quality monitor. See [data_quality_monitors](https://docs.databricks.com/dev-tools/bundles/resources.html#data_quality_monitors)." }, "database_catalogs": { + "description": "The database catalog definitions for the bundle, where each key is the name of the database catalog.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.DatabaseCatalog" }, "database_instances": { + "description": "The database instance definitions for the bundle, where each key is the name of the database instance.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.DatabaseInstance" }, "experiments": { @@ -4272,6 +4282,7 @@ "markdownDescription": "The experiment definitions for the bundle, where each key is the name of the experiment. See [experiments](https://docs.databricks.com/dev-tools/bundles/resources.html#experiments)." }, "external_locations": { + "description": "The external location definitions for the bundle, where each key is the name of the external location.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.ExternalLocation" }, "features": { @@ -4280,6 +4291,7 @@ "markdownDescription": "The feature definitions for the bundle, where each key is the name of the feature." }, "genie_spaces": { + "description": "The genie space definitions for the bundle, where each key is the name of the genie space.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.GenieSpace" }, "instance_pools": { @@ -4302,12 +4314,15 @@ "markdownDescription": "The materialization set definitions for the bundle, where each key is the name of the materialization set." }, "mcp_services": { + "description": "The mcp service definitions for the bundle, where each key is the name of the mcp service.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.McpService" }, "model_provider_services": { + "description": "The model provider service definitions for the bundle, where each key is the name of the model provider service.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.ModelProviderService" }, "model_services": { + "description": "The model service definitions for the bundle, where each key is the name of the model service.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.ModelService" }, "model_serving_endpoints": { @@ -4388,12 +4403,15 @@ "markdownDescription": "The SQL warehouse definitions for the bundle, where each key is the name of the warehouse. See [sql_warehouses](https://docs.databricks.com/dev-tools/bundles/resources.html#sql_warehouses)." }, "synced_database_tables": { + "description": "The synced database table definitions for the bundle, where each key is the name of the synced database table.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.SyncedDatabaseTable" }, "vector_search_endpoints": { + "description": "The vector search endpoint definitions for the bundle, where each key is the name of the vector search endpoint.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.VectorSearchEndpoint" }, "vector_search_indexes": { + "description": "The vector search index definitions for the bundle, where each key is the name of the vector search index.", "$ref": "#/$defs/map/github.com/databricks/cli/bundle/config/resources.VectorSearchIndex" }, "volumes": { diff --git a/python/databricks/bundles/cluster_policies/_models/cluster_policy.py b/python/databricks/bundles/cluster_policies/_models/cluster_policy.py index d8033d95728..43a13c5bd61 100644 --- a/python/databricks/bundles/cluster_policies/_models/cluster_policy.py +++ b/python/databricks/bundles/cluster_policies/_models/cluster_policy.py @@ -41,6 +41,9 @@ class ClusterPolicy(Resource): """ lifecycle: VariableOrOptional[Lifecycle] = None + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ max_clusters_per_user: VariableOrOptional[int] = None """ @@ -103,6 +106,9 @@ class ClusterPolicyDict(TypedDict, total=False): """ lifecycle: VariableOrOptional[LifecycleParam] + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ max_clusters_per_user: VariableOrOptional[int] """ diff --git a/python/databricks/bundles/features/_models/feature.py b/python/databricks/bundles/features/_models/feature.py index b489a756bcc..df0789ea107 100644 --- a/python/databricks/bundles/features/_models/feature.py +++ b/python/databricks/bundles/features/_models/feature.py @@ -68,6 +68,9 @@ class Feature(Resource): """ lifecycle: VariableOrOptional[Lifecycle] = None + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ lineage_context: VariableOrOptional[LineageContext] = None """ @@ -125,6 +128,9 @@ class FeatureDict(TypedDict, total=False): """ lifecycle: VariableOrOptional[LifecycleParam] + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ lineage_context: VariableOrOptional[LineageContextParam] """ diff --git a/python/databricks/bundles/mcp_services/_models/mcp_service.py b/python/databricks/bundles/mcp_services/_models/mcp_service.py index 129935ea097..56005ec33e5 100644 --- a/python/databricks/bundles/mcp_services/_models/mcp_service.py +++ b/python/databricks/bundles/mcp_services/_models/mcp_service.py @@ -40,6 +40,9 @@ class McpService(Resource): grants: VariableOrList[PrivilegeAssignment] = field(default_factory=list) lifecycle: VariableOrOptional[Lifecycle] = None + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ @classmethod def from_dict(cls, value: "McpServiceDict") -> "Self": @@ -63,6 +66,9 @@ class McpServiceDict(TypedDict, total=False): grants: VariableOrList[PrivilegeAssignmentParam] lifecycle: VariableOrOptional[LifecycleParam] + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ McpServiceParam = McpServiceDict | McpService diff --git a/python/databricks/bundles/model_provider_services/_models/model_provider_service.py b/python/databricks/bundles/model_provider_services/_models/model_provider_service.py index 41a21652ac8..3f3766bb1a8 100644 --- a/python/databricks/bundles/model_provider_services/_models/model_provider_service.py +++ b/python/databricks/bundles/model_provider_services/_models/model_provider_service.py @@ -43,6 +43,9 @@ class ModelProviderService(Resource): grants: VariableOrList[PrivilegeAssignment] = field(default_factory=list) lifecycle: VariableOrOptional[Lifecycle] = None + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ @classmethod def from_dict(cls, value: "ModelProviderServiceDict") -> "Self": @@ -66,6 +69,9 @@ class ModelProviderServiceDict(TypedDict, total=False): grants: VariableOrList[PrivilegeAssignmentParam] lifecycle: VariableOrOptional[LifecycleParam] + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ ModelProviderServiceParam = ModelProviderServiceDict | ModelProviderService diff --git a/python/databricks/bundles/model_services/_models/model_service.py b/python/databricks/bundles/model_services/_models/model_service.py index 88f9d8cdeb7..d390288e698 100644 --- a/python/databricks/bundles/model_services/_models/model_service.py +++ b/python/databricks/bundles/model_services/_models/model_service.py @@ -43,6 +43,9 @@ class ModelService(Resource): grants: VariableOrList[PrivilegeAssignment] = field(default_factory=list) lifecycle: VariableOrOptional[Lifecycle] = None + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ @classmethod def from_dict(cls, value: "ModelServiceDict") -> "Self": @@ -66,6 +69,9 @@ class ModelServiceDict(TypedDict, total=False): grants: VariableOrList[PrivilegeAssignmentParam] lifecycle: VariableOrOptional[LifecycleParam] + """ + Settings that control the deployment lifecycle of the resource, such as preventing it from being destroyed. + """ ModelServiceParam = ModelServiceDict | ModelService