diff --git a/.changes/unreleased/Changes-20260521-151222.yaml b/.changes/unreleased/Changes-20260521-151222.yaml new file mode 100644 index 00000000..c5f68fbe --- /dev/null +++ b/.changes/unreleased/Changes-20260521-151222.yaml @@ -0,0 +1,3 @@ +kind: Changes +body: Allow new Fusion release tracks (`fusion-stable`, `fusion-extended`, `fusion-nightly`, `fusion-fallback`) as `dbt_version` on `dbtcloud_environment` and `dbtcloud_job`, alongside `latest-fusion`. The `force_node_selection` validator now treats any Fusion release track the same as `latest-fusion` (#684). +time: 2026-05-21T15:12:22.000000+00:00 diff --git a/docs/resources/environment.md b/docs/resources/environment.md index b3979563..a3226d00 100644 --- a/docs/resources/environment.md +++ b/docs/resources/environment.md @@ -14,7 +14,7 @@ Resource to manage dbt Cloud environments for the different dbt Cloud projects. ```terraform resource "dbtcloud_environment" "ci_environment" { - // the dbt_version is major.minor.0-latest , major.minor.0-pre, compatible, extended, versionless, latest or latest-fusion (by default, it is set to latest if not configured) + // the dbt_version is major.minor.0-latest, major.minor.0-pre, compatible, extended, versionless, latest, or one of the Fusion release tracks: latest-fusion, fusion-stable, fusion-extended, fusion-nightly, fusion-fallback (by default, it is set to latest if not configured) dbt_version = "latest" name = "CI" project_id = dbtcloud_project.dbt_project.id @@ -72,7 +72,7 @@ resource "dbtcloud_environment" "profiled_environment" { - `connection_id` (Number) A connection ID (used with Global Connections) - `credential_id` (Number) The Credential ID for this environment. A credential is not actionable for development environments, as users have to set their own development credentials in dbt Cloud. - `custom_branch` (String) The custom branch name to use -- `dbt_version` (String) Version number of dbt to use in this environment. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest` or `latest-fusion`. While `versionless` is still supported, using `latest` is recommended. Defaults to `latest` if no version is provided +- `dbt_version` (String) Version number of dbt to use in this environment. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest`, or one of the Fusion release tracks (`latest-fusion`, `fusion-stable`, `fusion-extended`, `fusion-nightly`, `fusion-fallback`). While `versionless` is still supported, using `latest` is recommended. Defaults to `latest` if no version is provided - `deployment_type` (String) The type of environment. Only valid for environments of type 'deployment' and for now can only be 'production', 'staging' or left empty for generic environments - `enable_model_query_history` (Boolean) Whether to enable model query history in this environment. As of Oct 2024, works only for Snowflake and BigQuery. - `extended_attributes_id` (Number) The ID of the extended attributes applied diff --git a/docs/resources/job.md b/docs/resources/job.md index e7152375..8a91a4d5 100644 --- a/docs/resources/job.md +++ b/docs/resources/job.md @@ -218,13 +218,13 @@ An example can be found [in this GitHub issue](https://github.com/dbt-labs/terra ### Optional - `compare_changes_flags` (String) The model selector for checking changes in the compare changes Advanced CI feature -- `dbt_version` (String) Version number of dbt to use in this job. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest` or `latest-fusion`. While `versionless` is still supported, using `latest` is recommended. If not set, the `dbt_version` configured on the environment is used. +- `dbt_version` (String) Version number of dbt to use in this job. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest`, or one of the Fusion release tracks (`latest-fusion`, `fusion-stable`, `fusion-extended`, `fusion-nightly`, `fusion-fallback`). While `versionless` is still supported, using `latest` is recommended. If not set, the `dbt_version` configured on the environment is used. - `deferring_environment_id` (Number) Environment identifier that this job defers to (new deferring approach) - `deferring_job_id` (Number) Job identifier that this job defers to (legacy deferring approach) - `description` (String) Description for the job - `errors_on_lint_failure` (Boolean) Whether the CI job should fail when a lint error is found. Only used when `run_lint` is set to `true`. Defaults to `true`. - `execution` (Attributes) Execution settings for the job (see [below for nested schema](#nestedatt--execution)) -- `force_node_selection` (Boolean) Whether to force node selection (SAO - Select All Optimizations) for the job. If `dbt_version` is not set to `latest-fusion`, this must be set to `true` when specified. +- `force_node_selection` (Boolean) Whether to force node selection (SAO - Select All Optimizations) for the job. If `dbt_version` is not set to a Fusion release track (e.g. `latest-fusion`), this must be set to `true` when specified. - `generate_docs` (Boolean) Flag for whether the job should generate documentation - `is_active` (Boolean) Should always be set to true as setting it to false is the same as creating a job in a deleted state. To create/keep a job in a 'deactivated' state, check the `triggers` config. Setting it to false essentially deletes the job. On resource creation, this field is enforced to be true. - `job_completion_trigger_condition` (Block List) Which other job should trigger this job when it finishes, and on which conditions (sometimes referred as 'job chaining'). (see [below for nested schema](#nestedblock--job_completion_trigger_condition)) diff --git a/examples/resources/dbtcloud_environment/resource.tf b/examples/resources/dbtcloud_environment/resource.tf index 41d29cdd..ce474748 100644 --- a/examples/resources/dbtcloud_environment/resource.tf +++ b/examples/resources/dbtcloud_environment/resource.tf @@ -1,5 +1,5 @@ resource "dbtcloud_environment" "ci_environment" { - // the dbt_version is major.minor.0-latest , major.minor.0-pre, compatible, extended, versionless, latest or latest-fusion (by default, it is set to latest if not configured) + // the dbt_version is major.minor.0-latest, major.minor.0-pre, compatible, extended, versionless, latest, or one of the Fusion release tracks: latest-fusion, fusion-stable, fusion-extended, fusion-nightly, fusion-fallback (by default, it is set to latest if not configured) dbt_version = "latest" name = "CI" project_id = dbtcloud_project.dbt_project.id diff --git a/pkg/framework/objects/environment/schema.go b/pkg/framework/objects/environment/schema.go index 53cde5ea..3f9dd30b 100644 --- a/pkg/framework/objects/environment/schema.go +++ b/pkg/framework/objects/environment/schema.go @@ -216,7 +216,7 @@ func (r *environmentResource) Schema( Computed: true, Optional: true, Default: stringdefault.StaticString("latest"), - Description: "Version number of dbt to use in this environment. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest` or `latest-fusion`. While `versionless` is still supported, using `latest` is recommended. Defaults to `latest` if no version is provided", + Description: "Version number of dbt to use in this environment. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest`, or one of the Fusion release tracks (`latest-fusion`, `fusion-stable`, `fusion-extended`, `fusion-nightly`, `fusion-fallback`). While `versionless` is still supported, using `latest` is recommended. Defaults to `latest` if no version is provided", Validators: []validator.String{ helper.DbtVersionValidator{}, // Custom validator to check the dbt version format }, diff --git a/pkg/framework/objects/job/schema.go b/pkg/framework/objects/job/schema.go index 3b3f67f6..6f366f5f 100644 --- a/pkg/framework/objects/job/schema.go +++ b/pkg/framework/objects/job/schema.go @@ -466,12 +466,12 @@ func (j *jobResource) Schema( }, "dbt_version": resource_schema.StringAttribute{ Optional: true, - Description: "Version number of dbt to use in this job. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest` or `latest-fusion`. While `versionless` is still supported, using `latest` is recommended. If not set, the `dbt_version` configured on the environment is used.", + Description: "Version number of dbt to use in this job. It needs to be in the format `major.minor.0-latest` (e.g. `1.5.0-latest`), `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest`, or one of the Fusion release tracks (`latest-fusion`, `fusion-stable`, `fusion-extended`, `fusion-nightly`, `fusion-fallback`). While `versionless` is still supported, using `latest` is recommended. If not set, the `dbt_version` configured on the environment is used.", }, "force_node_selection": resource_schema.BoolAttribute{ Optional: true, Computed: true, - Description: "Whether to force node selection (SAO - Select All Optimizations) for the job. If `dbt_version` is not set to `latest-fusion`, this must be set to `true` when specified.", + Description: "Whether to force node selection (SAO - Select All Optimizations) for the job. If `dbt_version` is not set to a Fusion release track (e.g. `latest-fusion`), this must be set to `true` when specified.", Validators: []validator.Bool{ job_validators.ForceNodeSelectionValidator(), }, diff --git a/pkg/framework/objects/job/validators/force_node_selection_validator.go b/pkg/framework/objects/job/validators/force_node_selection_validator.go index 057b5a59..ae6d12dc 100644 --- a/pkg/framework/objects/job/validators/force_node_selection_validator.go +++ b/pkg/framework/objects/job/validators/force_node_selection_validator.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/dbt-labs/terraform-provider-dbtcloud/pkg/helper" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -15,12 +16,12 @@ type forceNodeSelectionValidator struct{} // Description returns a plain text description of the validator's behavior, suitable for a practitioner to understand its impact. func (v forceNodeSelectionValidator) Description(ctx context.Context) string { - return "When dbt_version is not 'latest-fusion', force_node_selection must be set to true" + return "When dbt_version is not a Fusion release track (e.g. 'latest-fusion'), force_node_selection must be set to true" } // MarkdownDescription returns a markdown formatted description of the validator's behavior, suitable for a practitioner to understand its impact. func (v forceNodeSelectionValidator) MarkdownDescription(ctx context.Context) string { - return "When `dbt_version` is not `latest-fusion`, `force_node_selection` must be set to `true`" + return "When `dbt_version` is not a Fusion release track (e.g. `latest-fusion`), `force_node_selection` must be set to `true`" } // ValidateBool performs the validation. @@ -46,14 +47,14 @@ func (v forceNodeSelectionValidator) ValidateBool(ctx context.Context, req valid return } - // Validation: if dbt_version is not "latest-fusion", force_node_selection must be true - if dbtVersion.ValueString() != "latest-fusion" && !forceNodeSelection { + // Validation: if dbt_version is not a Fusion release track, force_node_selection must be true + if !helper.IsFusionVersion(dbtVersion.ValueString()) && !forceNodeSelection { resp.Diagnostics.AddAttributeError( req.Path, "Invalid force_node_selection Configuration", fmt.Sprintf( - "When dbt_version is '%s' (not 'latest-fusion'), force_node_selection must be set to true. "+ - "Set force_node_selection = true or change dbt_version to 'latest-fusion'.", + "When dbt_version is '%s' (not a Fusion release track such as 'latest-fusion'), force_node_selection must be set to true. "+ + "Set force_node_selection = true or change dbt_version to a Fusion release track.", dbtVersion.ValueString(), ), ) @@ -61,7 +62,7 @@ func (v forceNodeSelectionValidator) ValidateBool(ctx context.Context, req valid } // ForceNodeSelectionValidator returns a validator that ensures force_node_selection is true -// when dbt_version is not "latest-fusion". +// when dbt_version is not a Fusion release track. func ForceNodeSelectionValidator() validator.Bool { return forceNodeSelectionValidator{} } diff --git a/pkg/helper/dbt_version_validator.go b/pkg/helper/dbt_version_validator.go index 1179d2e1..ea4bfc91 100644 --- a/pkg/helper/dbt_version_validator.go +++ b/pkg/helper/dbt_version_validator.go @@ -10,12 +10,14 @@ import ( type DbtVersionValidator struct{} +const validDbtVersionsDescription = "`major.minor.0-latest`, `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest`, `latest-fusion`, `fusion-stable`, `fusion-extended`, `fusion-nightly` or `fusion-fallback`" + func (v DbtVersionValidator) Description(ctx context.Context) string { - return "Validates that the dbt_version is in the format `major.minor.0-latest`, `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest`, or `latest-fusion`." + return "Validates that the dbt_version is in the format " + validDbtVersionsDescription + "." } func (v DbtVersionValidator) MarkdownDescription(ctx context.Context) string { - return "Validates that the `dbt_version` is in the format `major.minor.0-latest`, `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest` or `latest-fusion`." + return "Validates that the `dbt_version` is in the format " + validDbtVersionsDescription + "." } func (v DbtVersionValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { @@ -28,7 +30,7 @@ func (v DbtVersionValidator) ValidateString(ctx context.Context, req validator.S dbtVersion := req.ConfigValue.ValueString() // Define the regex pattern for valid dbt_version formats - validVersionPattern := `^(compatible|extended|latest|versionless|latest-fusion|[0-9]+\.[0-9]+\.0-(latest|pre))$` + validVersionPattern := `^(compatible|extended|latest|versionless|latest-fusion|fusion-stable|fusion-extended|fusion-nightly|fusion-fallback|[0-9]+\.[0-9]+\.0-(latest|pre))$` matched, err := regexp.MatchString(validVersionPattern, dbtVersion) if err != nil { resp.Diagnostics.AddError( @@ -42,7 +44,17 @@ func (v DbtVersionValidator) ValidateString(ctx context.Context, req validator.S if !matched { resp.Diagnostics.AddError( "Invalid dbt_version Format", - fmt.Sprintf("The `dbt_version` must be in the format `major.minor.0-latest`, `major.minor.0-pre`, `compatible`, `extended`, `versionless`, `latest` or `latest-fusion`. Got: %s", dbtVersion), + fmt.Sprintf("The `dbt_version` must be in the format "+validDbtVersionsDescription+". Got: %s", dbtVersion), ) } } + +// IsFusionVersion reports whether the supplied dbt_version string is one of +// the Fusion release tracks. +func IsFusionVersion(dbtVersion string) bool { + switch dbtVersion { + case "latest-fusion", "fusion-stable", "fusion-extended", "fusion-nightly", "fusion-fallback": + return true + } + return false +}