Skip to content
Merged
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
155 changes: 85 additions & 70 deletions cmd/bundle/utils/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,71 @@ func ProcessBundleRet(cmd *cobra.Command, opts ProcessOptions) (b *bundle.Bundle
return b, stateDesc, root.ErrAlreadyPrinted
}

}

// --plan applies a precomputed plan, so it skips Build and PreDeployChecks; a plain
// deploy builds and runs the predeploy checks. These only flip opts (no state access),
// so they run before phases.Build; the plan file itself is loaded during state
// resolution after the build, once the engine is known and the state is open.
if opts.ReadPlanPath != "" {
opts.Build = false
opts.PreDeployChecks = false
} else if opts.Deploy {
opts.Build = true
opts.PreDeployChecks = true
}

if opts.FastValidate {
t1 := time.Now()
bundle.ApplyContext(ctx, b, validate.FastValidate())
b.Metrics.ExecutionTimes = append(b.Metrics.ExecutionTimes, protos.IntMapEntry{
Key: "validate.FastValidate",
Value: time.Since(t1).Milliseconds(),
})

if logdiag.HasError(ctx) {
return b, stateDesc, root.ErrAlreadyPrinted
}

// Pipeline CLI only validation.
if opts.IsPipelinesCLI {
rejectDefinitions(ctx, b)
if logdiag.HasError(ctx) {
return b, stateDesc, root.ErrAlreadyPrinted
}
}
}

if opts.Validate {
validate.Validate(ctx, b)
if logdiag.HasError(ctx) {
return b, stateDesc, root.ErrAlreadyPrinted
}
}

var libs phases.LibLocationMap

if opts.Build {
t2 := time.Now()
libs = phases.Build(ctx, b)
b.Metrics.ExecutionTimes = append(b.Metrics.ExecutionTimes, protos.IntMapEntry{
Key: "phases.Build",
Value: time.Since(t2).Milliseconds(),
})

if logdiag.HasError(ctx) {
return b, stateDesc, root.ErrAlreadyPrinted
}
}

// Resolve the deployment state after phases.Build, in one place, so the order reads
// build → resolve-state → deploy. This is behavior-preserving today (nothing between
// FastValidate and Build consumes the opened state, and the auto-migration still runs
// post-deploy). It prepares for moving the terraform→direct migration before deploy,
// which must run after Build so the converted state records resolved library and
// ${artifacts.*} paths rather than unexpanded globs.
var plan *deployplan.Plan
if shouldReadState {
// Open direct engine state once for all subsequent operations (ExportState, CalculatePlan, Apply, etc.)
needDirectState := stateDesc.Engine.IsDirect() && (opts.InitIDs || opts.ErrorOnEmptyState || opts.Deploy || opts.ReadPlanPath != "" || opts.PreDeployChecks || opts.PostStateFunc != nil)
var localPath string
Expand Down Expand Up @@ -365,79 +430,29 @@ func ProcessBundleRet(cmd *cobra.Command, opts ProcessOptions) (b *bundle.Bundle
}
}

}

var plan *deployplan.Plan
if opts.ReadPlanPath != "" {
if !stateDesc.Engine.IsDirect() {
logdiag.LogError(ctx, errors.New("--plan is only supported with direct engine (set bundle.engine to \"direct\" or DATABRICKS_BUNDLE_ENGINE=direct)"))
return b, stateDesc, root.ErrAlreadyPrinted
}
// Artifact uploads are handled inside Deploy by extracting remote paths
// from the plan's new_state and finding the matching local files.
opts.Build = false
opts.PreDeployChecks = false

var err error
plan, err = deployplan.LoadPlanFromFile(opts.ReadPlanPath)
if err != nil {
logdiag.LogError(ctx, err)
return b, stateDesc, root.ErrAlreadyPrinted
}
currentVersion := build.GetInfo().Version
if plan.CLIVersion != currentVersion {
log.Warnf(ctx, "Plan was created with CLI version %s but current version is %s", plan.CLIVersion, currentVersion)
}

if err := direct.ValidatePlanAgainstState(&b.DeploymentBundle.StateDB, plan); err != nil {
logdiag.LogError(ctx, err)
return b, stateDesc, root.ErrAlreadyPrinted
}
} else if opts.Deploy {
opts.Build = true
opts.PreDeployChecks = true
}

if opts.FastValidate {
t1 := time.Now()
bundle.ApplyContext(ctx, b, validate.FastValidate())
b.Metrics.ExecutionTimes = append(b.Metrics.ExecutionTimes, protos.IntMapEntry{
Key: "validate.FastValidate",
Value: time.Since(t1).Milliseconds(),
})

if logdiag.HasError(ctx) {
return b, stateDesc, root.ErrAlreadyPrinted
}

// Pipeline CLI only validation.
if opts.IsPipelinesCLI {
rejectDefinitions(ctx, b)
if logdiag.HasError(ctx) {
// --plan: the engine is now known and the state is open, so validate and load the
// precomputed plan. Artifact uploads are handled inside Deploy by extracting remote
// paths from the plan's new_state and finding the matching local files.
if opts.ReadPlanPath != "" {
if !stateDesc.Engine.IsDirect() {
logdiag.LogError(ctx, errors.New("--plan is only supported with direct engine (set bundle.engine to \"direct\" or DATABRICKS_BUNDLE_ENGINE=direct)"))
return b, stateDesc, root.ErrAlreadyPrinted
}
}
}

if opts.Validate {
validate.Validate(ctx, b)
if logdiag.HasError(ctx) {
return b, stateDesc, root.ErrAlreadyPrinted
}
}

var libs phases.LibLocationMap

if opts.Build {
t2 := time.Now()
libs = phases.Build(ctx, b)
b.Metrics.ExecutionTimes = append(b.Metrics.ExecutionTimes, protos.IntMapEntry{
Key: "phases.Build",
Value: time.Since(t2).Milliseconds(),
})
var err error
plan, err = deployplan.LoadPlanFromFile(opts.ReadPlanPath)
if err != nil {
logdiag.LogError(ctx, err)
return b, stateDesc, root.ErrAlreadyPrinted
}
currentVersion := build.GetInfo().Version
if plan.CLIVersion != currentVersion {
log.Warnf(ctx, "Plan was created with CLI version %s but current version is %s", plan.CLIVersion, currentVersion)
}

if logdiag.HasError(ctx) {
return b, stateDesc, root.ErrAlreadyPrinted
if err := direct.ValidatePlanAgainstState(&b.DeploymentBundle.StateDB, plan); err != nil {
logdiag.LogError(ctx, err)
return b, stateDesc, root.ErrAlreadyPrinted
}
}
}

Expand Down
Loading