From 0a7896f2b311a82e9b5a357ad69489ea8cfc3d74 Mon Sep 17 00:00:00 2001 From: Rada Kamysheva Date: Tue, 11 Aug 2026 11:10:08 +0000 Subject: [PATCH 1/3] job_runs: include resource key in deploy progress lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make concurrent job_runs deploys easier to tell apart by attaching the bundle resource key in the direct engine and formatting cmdio lines as "Output from job_runs.foo: id=…: …". --- .../bundles/job-runs-cmdio-resource-key.md | 1 + .../bundle/resources/job_runs/basic/output.txt | 4 ++-- .../resources/job_runs/failed_run/output.txt | 4 ++-- .../job_runs/interrupted_run/output.txt | 4 ++-- .../job_runs/job_parameters/output.txt | 4 ++-- .../resources/job_runs/redeploy/output.txt | 8 ++++---- .../bundle/resources/job_runs/wait/output.txt | 4 ++-- bundle/direct/apply.go | 8 ++++++++ bundle/direct/dresources/job_run.go | 8 +++++--- bundle/direct/dresources/job_run_test.go | 10 ++++++++++ bundle/direct/dresources/resource_key.go | 17 +++++++++++++++++ bundle/direct/dresources/resource_key_test.go | 13 +++++++++++++ 12 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 .nextchanges/bundles/job-runs-cmdio-resource-key.md create mode 100644 bundle/direct/dresources/resource_key.go create mode 100644 bundle/direct/dresources/resource_key_test.go diff --git a/.nextchanges/bundles/job-runs-cmdio-resource-key.md b/.nextchanges/bundles/job-runs-cmdio-resource-key.md new file mode 100644 index 00000000000..4267d570cc1 --- /dev/null +++ b/.nextchanges/bundles/job-runs-cmdio-resource-key.md @@ -0,0 +1 @@ +direct: job_runs deploy progress lines now include the resource key (e.g. `Output from job_runs.foo: id=123: ...`) so concurrent runs are easier to tell apart. diff --git a/acceptance/bundle/resources/job_runs/basic/output.txt b/acceptance/bundle/resources/job_runs/basic/output.txt index d34d8d0f3c7..3a125c23d3a 100644 --- a/acceptance/bundle/resources/job_runs/basic/output.txt +++ b/acceptance/bundle/resources/job_runs/basic/output.txt @@ -33,8 +33,8 @@ Resources: >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-basic/default/files... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Created job_runs.my_run Created jobs.my_job Files: 6 uploaded, 0 deleted diff --git a/acceptance/bundle/resources/job_runs/failed_run/output.txt b/acceptance/bundle/resources/job_runs/failed_run/output.txt index 8535abcc6e9..d2a9470ff1c 100644 --- a/acceptance/bundle/resources/job_runs/failed_run/output.txt +++ b/acceptance/bundle/resources/job_runs/failed_run/output.txt @@ -2,7 +2,7 @@ === a run that finishes FAILED fails the deploy >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -job run [MY_RUN_ID]: Run URL: [RUN_URL] +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [RUN_URL] Error: cannot create resources.job_runs.my_run: waiting after creating id=[MY_RUN_ID]: run did not succeed: FAILED: Task main failed with message: Workload failed, see run output for details. task "main": RuntimeError: intentional failure run page: [RUN_URL] @@ -56,7 +56,7 @@ Files: 8 uploaded, 0 deleted === so a redeploy runs the job again, and fails again >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -job run [MY_RUN_ID_2]: Run URL: [RUN_URL] +Output from job_runs.my_run: id=[MY_RUN_ID_2]: Run URL: [RUN_URL] Error: cannot recreate resources.job_runs.my_run: waiting after creating id=[MY_RUN_ID_2]: run did not succeed: FAILED: Task main failed with message: Workload failed, see run output for details. task "main": RuntimeError: intentional failure run page: [RUN_URL] diff --git a/acceptance/bundle/resources/job_runs/interrupted_run/output.txt b/acceptance/bundle/resources/job_runs/interrupted_run/output.txt index b77471cb56f..0a2ed13e5f9 100644 --- a/acceptance/bundle/resources/job_runs/interrupted_run/output.txt +++ b/acceptance/bundle/resources/job_runs/interrupted_run/output.txt @@ -68,8 +68,8 @@ Exit code: 1 === the deploy adopts that run and waits for it to finish >>> [CLI] bundle deploy --plan tmp.plan.json Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-interrupted-run/default/files... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Updated job_runs.my_run Files: 4 uploaded, 0 deleted Resources: 0 created, 1 changed, 0 deleted, 1 unchanged diff --git a/acceptance/bundle/resources/job_runs/job_parameters/output.txt b/acceptance/bundle/resources/job_runs/job_parameters/output.txt index 2520c8c4f11..b9c8ce59f60 100644 --- a/acceptance/bundle/resources/job_runs/job_parameters/output.txt +++ b/acceptance/bundle/resources/job_runs/job_parameters/output.txt @@ -2,8 +2,8 @@ === deploy triggers the run with only the overridden parameter >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-job-parameters/default/files... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Created job_runs.my_run Created jobs.my_job Files: 6 uploaded, 0 deleted diff --git a/acceptance/bundle/resources/job_runs/redeploy/output.txt b/acceptance/bundle/resources/job_runs/redeploy/output.txt index ae0babb43cf..75da974870d 100644 --- a/acceptance/bundle/resources/job_runs/redeploy/output.txt +++ b/acceptance/bundle/resources/job_runs/redeploy/output.txt @@ -2,8 +2,8 @@ === initial deploy triggers the first run >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-redeploy/default/files... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Created job_runs.my_run Created jobs.my_job Files: 6 uploaded, 0 deleted @@ -96,8 +96,8 @@ Resources: >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-redeploy/default/files... -job run [MY_RUN_ID_2]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID_2]?o=[NUMID] -job run [MY_RUN_ID_2]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID_2]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID_2]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID_2]: SUCCESS Recreated job_runs.my_run Files: 4 uploaded, 0 deleted Resources: 1 created, 0 changed, 1 deleted, 1 unchanged diff --git a/acceptance/bundle/resources/job_runs/wait/output.txt b/acceptance/bundle/resources/job_runs/wait/output.txt index ba32e44a2e0..906c6a7cf3c 100644 --- a/acceptance/bundle/resources/job_runs/wait/output.txt +++ b/acceptance/bundle/resources/job_runs/wait/output.txt @@ -2,8 +2,8 @@ === the deploy waits for the run to finish >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -job run [MY_RUN_ID]: Run URL: [RUN_URL] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [RUN_URL] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Created job_runs.my_run Created jobs.downstream_job Created jobs.my_job diff --git a/bundle/direct/apply.go b/bundle/direct/apply.go index 3ed9d233f60..4dd77df8a48 100644 --- a/bundle/direct/apply.go +++ b/bundle/direct/apply.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "reflect" + "strings" "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/cli/bundle/direct/dresources" @@ -14,8 +15,14 @@ import ( "github.com/databricks/databricks-sdk-go/apierr" ) +func (d *DeploymentUnit) withResourceKey(ctx context.Context) context.Context { + // Match plan output (e.g. "job_runs.foo"), not the internal "resources." form. + return dresources.WithResourceKey(ctx, strings.TrimPrefix(d.ResourceKey, "resources.")) +} + func (d *DeploymentUnit) Destroy(ctx context.Context, db *dstate.DeploymentState) error { ctx = log.WithPrefix(ctx, "destroying "+d.ResourceKey) + ctx = d.withResourceKey(ctx) id := db.GetResourceID(d.ResourceKey) if id == "" { log.Infof(ctx, "Cannot delete %s: missing from state", d.ResourceKey) @@ -27,6 +34,7 @@ func (d *DeploymentUnit) Destroy(ctx context.Context, db *dstate.DeploymentState func (d *DeploymentUnit) Deploy(ctx context.Context, db *dstate.DeploymentState, newState any, actionType deployplan.ActionType, planEntry *deployplan.PlanEntry) error { ctx = log.WithPrefix(ctx, "deploying "+d.ResourceKey) + ctx = d.withResourceKey(ctx) if actionType == deployplan.Create { return d.Create(ctx, db, newState) } diff --git a/bundle/direct/dresources/job_run.go b/bundle/direct/dresources/job_run.go index ac549b876cd..b91a110511a 100644 --- a/bundle/direct/dresources/job_run.go +++ b/bundle/direct/dresources/job_run.go @@ -329,11 +329,13 @@ func runIsTerminal(state jobs.RunLifeCycleState) bool { state == jobs.RunLifeCycleStateInternalError } -// reportRunLine names the run, since resources deploy concurrently onto one stream. +// reportRunLine names the resource and run id so concurrent deploys stay readable. +// Deploy attaches the key via [WithResourceKey]. func reportRunLine(ctx context.Context, runID int64, msg string) { - if cmdio.HasIO(ctx) { - cmdio.LogString(ctx, fmt.Sprintf("job run %d: %s", runID, msg)) + if !cmdio.HasIO(ctx) { + return } + cmdio.LogString(ctx, fmt.Sprintf("Output from %s: id=%d: %s", ResourceKey(ctx), runID, msg)) } // DoUpdate finishes the wait an interrupted deploy abandoned. diff --git a/bundle/direct/dresources/job_run_test.go b/bundle/direct/dresources/job_run_test.go index 62a22a9745f..456b20f1903 100644 --- a/bundle/direct/dresources/job_run_test.go +++ b/bundle/direct/dresources/job_run_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/structs/structpath" "github.com/databricks/cli/libs/testserver" "github.com/databricks/databricks-sdk-go" @@ -79,6 +80,15 @@ func TestJobRunWaitSucceeds(t *testing.T) { assert.Equal(t, jobs.RunResultStateSuccess, remote.State.ResultState) } +func TestReportRunLineIncludesResourceKey(t *testing.T) { + ctx, stderr := cmdio.NewTestContextWithStderr(t.Context()) + ctx = WithResourceKey(ctx, "job_runs.my_run") + + reportRunLine(ctx, 123, "SUCCESS") + + assert.Equal(t, "Output from job_runs.my_run: id=123: SUCCESS\n", stderr.String()) +} + func TestJobRunWaitFailsOnFailedResult(t *testing.T) { client := jobRunClient(t, &jobs.RunState{ LifeCycleState: jobs.RunLifeCycleStateTerminated, diff --git a/bundle/direct/dresources/resource_key.go b/bundle/direct/dresources/resource_key.go new file mode 100644 index 00000000000..640c02243d4 --- /dev/null +++ b/bundle/direct/dresources/resource_key.go @@ -0,0 +1,17 @@ +package dresources + +import "context" + +type resourceKeyType struct{} + +// WithResourceKey attaches the bundle resource key used in cmdio progress lines. +// key is the plan form without the "resources." prefix (e.g. "job_runs.foo"). +func WithResourceKey(ctx context.Context, key string) context.Context { + return context.WithValue(ctx, resourceKeyType{}, key) +} + +// ResourceKey returns the key attached by [WithResourceKey], or "" if none. +func ResourceKey(ctx context.Context) string { + key, _ := ctx.Value(resourceKeyType{}).(string) + return key +} diff --git a/bundle/direct/dresources/resource_key_test.go b/bundle/direct/dresources/resource_key_test.go new file mode 100644 index 00000000000..8d5bdfe54bb --- /dev/null +++ b/bundle/direct/dresources/resource_key_test.go @@ -0,0 +1,13 @@ +package dresources + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResourceKeyRoundTrip(t *testing.T) { + ctx := WithResourceKey(t.Context(), "job_runs.foo") + assert.Equal(t, "job_runs.foo", ResourceKey(ctx)) + assert.Equal(t, "", ResourceKey(t.Context())) +} From c5e6aa4759f291b3da7bed24cb5b54fb65777276 Mon Sep 17 00:00:00 2001 From: Rada Kamysheva Date: Tue, 11 Aug 2026 11:28:37 +0000 Subject: [PATCH 2/3] fix: use assert.Empty for missing resource key Satisfy testifylint so CI lint passes on the resource key round-trip test. --- bundle/direct/dresources/resource_key_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle/direct/dresources/resource_key_test.go b/bundle/direct/dresources/resource_key_test.go index 8d5bdfe54bb..b6e9c72b6eb 100644 --- a/bundle/direct/dresources/resource_key_test.go +++ b/bundle/direct/dresources/resource_key_test.go @@ -9,5 +9,5 @@ import ( func TestResourceKeyRoundTrip(t *testing.T) { ctx := WithResourceKey(t.Context(), "job_runs.foo") assert.Equal(t, "job_runs.foo", ResourceKey(ctx)) - assert.Equal(t, "", ResourceKey(t.Context())) + assert.Empty(t, ResourceKey(t.Context())) } From e09ca9226fd7bf8a55d5ebd4e33c720bc765d038 Mon Sep 17 00:00:00 2001 From: Rada Kamysheva Date: Tue, 11 Aug 2026 11:43:10 +0000 Subject: [PATCH 3/3] test: update retried_run_now golden for resource-key cmdio --- .../bundle/resources/job_runs/retried_run_now/output.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acceptance/bundle/resources/job_runs/retried_run_now/output.txt b/acceptance/bundle/resources/job_runs/retried_run_now/output.txt index b529da143c5..31deefb2bba 100644 --- a/acceptance/bundle/resources/job_runs/retried_run_now/output.txt +++ b/acceptance/bundle/resources/job_runs/retried_run_now/output.txt @@ -2,8 +2,8 @@ === the deploy succeeds, though its run-now was answered with a 503 >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/job-runs-retried-run-now/default/files... -job run [MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] -job run [MY_RUN_ID]: SUCCESS +Output from job_runs.my_run: id=[MY_RUN_ID]: Run URL: [DATABRICKS_URL]/jobs/[MY_JOB_ID]/runs/[MY_RUN_ID]?o=[NUMID] +Output from job_runs.my_run: id=[MY_RUN_ID]: SUCCESS Created job_runs.my_run Created jobs.my_job Files: 6 uploaded, 0 deleted