-
Notifications
You must be signed in to change notification settings - Fork 207
localenv: populate setup-local warnings and durationMs #6176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,5 +49,5 @@ | |
| ], | ||
| "warnings": [], | ||
| "error": null, | ||
| "durationMs": 0 | ||
| "durationMs": [DURATION_MS] | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
|
|
||
| >>> [CLI] environments setup-local --serverless-version 4 --dry-run --output json | ||
| { | ||
| "schemaVersion": 1, | ||
| "command": "environments setup-local", | ||
| "ok": true, | ||
| "mode": "default", | ||
| "dryRun": true, | ||
| "compute": { | ||
| "source": "serverless", | ||
| "serverlessVersion": "v4", | ||
| "envKey": "serverless/serverless-v4" | ||
| }, | ||
| "resolved": { | ||
| "pythonVersion": "3.12", | ||
| "dbconnectVersion": "17.2.0", | ||
| "artifactSource": "network" | ||
| }, | ||
| "greenfield": false, | ||
| "plan": { | ||
| "wouldWrite": "[TEST_TMP_DIR]/pyproject.toml", | ||
| "wouldBackup": "[TEST_TMP_DIR]/pyproject.toml.bak", | ||
| "wouldInstallPython": "3.12", | ||
| "diff": "--- pyproject.toml\n+++ pyproject.toml.new\n@@ -1,7 +1,15 @@\n [project]\n name = \"existing\"\n-requires-python = \"\u003e=3.10\"\n+requires-python = \"==3.12.*\"\n dependencies = [\"pyarrow==17.0.0\"]\n \n [dependency-groups]\n-dev = [\"databricks-connect~=16.1.0\"]\n+dev = [\"databricks-connect~=17.2.0\"]\n+\n+# managed by databricks environments setup-local — do not edit\n+[tool.uv]\n+constraint-dependencies = [\n+ \"pyarrow~=21.0.0\",\n+ \"pandas\u003c3\",\n+]\n+# end managed by databricks environments setup-local\n" | ||
| }, | ||
| "phases": [ | ||
| { | ||
| "phase": "preflight", | ||
| "status": "ok" | ||
| }, | ||
| { | ||
| "phase": "resolve", | ||
| "status": "ok" | ||
| }, | ||
| { | ||
| "phase": "fetch", | ||
| "status": "ok" | ||
| }, | ||
| { | ||
| "phase": "merge", | ||
| "status": "ok" | ||
| }, | ||
| { | ||
| "phase": "provision", | ||
| "status": "ok" | ||
| }, | ||
| { | ||
| "phase": "validate", | ||
| "status": "ok" | ||
| } | ||
| ], | ||
| "warnings": [ | ||
| { | ||
| "code": "W_REQUIRES_PYTHON_OVERRIDDEN", | ||
| "message": "requires-python \"\u003e=3.10\" was replaced by the environment's \"==3.12.*\"" | ||
| }, | ||
| { | ||
| "code": "W_DBCONNECT_PIN_OVERRIDDEN", | ||
| "message": "databricks-connect \"databricks-connect~=16.1.0\" was replaced by the environment's \"databricks-connect~=17.2.0\"" | ||
| }, | ||
| { | ||
| "code": "W_USER_CONSTRAINT_CONFLICT", | ||
| "message": "dependency \"pyarrow==17.0.0\" conflicts with the environment constraint \"pyarrow~=21.0.0\"" | ||
| } | ||
| ], | ||
| "error": null, | ||
| "durationMs": [DURATION_MS] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # A pre-existing project whose pins conflict with the env constraints: an older | ||
| # requires-python and databricks-connect, plus a dependency pinned outside the | ||
| # env's constraint range. The dry-run merge should surface all three warnings. | ||
| cat > pyproject.toml <<'PYPROJECT' | ||
| [project] | ||
| name = "existing" | ||
| requires-python = ">=3.10" | ||
| dependencies = ["pyarrow==17.0.0"] | ||
|
|
||
| [dependency-groups] | ||
| dev = ["databricks-connect~=16.1.0"] | ||
| PYPROJECT | ||
|
|
||
| trace $CLI environments setup-local --serverless-version 4 --dry-run --output json |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| EnvMatrix.DATABRICKS_BUNDLE_ENGINE = ["direct"] | ||
|
|
||
| # The script writes a pre-existing pyproject.toml to trigger the merge path; it is | ||
| # an input the test creates, not a golden to compare. | ||
| Ignore = ["pyproject.toml"] | ||
|
|
||
| [Env] | ||
| DATABRICKS_LOCALENV_CONSTRAINT_SOURCE_URL_TEST_OVERRIDE = "$DATABRICKS_HOST" | ||
|
|
||
| [[Server]] | ||
| Pattern = "GET /serverless/serverless-v4/pyproject.toml" | ||
| Response.Body = ''' | ||
| [project] | ||
| requires-python = "==3.12.*" | ||
|
|
||
| [dependency-groups] | ||
| dev = ["databricks-connect~=17.2.0"] | ||
|
|
||
| [tool.uv] | ||
| constraint-dependencies = ["pyarrow~=21.0.0", "pandas<3"] | ||
| ''' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,5 +50,5 @@ | |
| ], | ||
| "warnings": [], | ||
| "error": null, | ||
| "durationMs": 0 | ||
| "durationMs": [DURATION_MS] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ import ( | |
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/databricks/cli/libs/log" | ||
| "github.com/hexops/gotextdiff" | ||
|
|
@@ -53,10 +54,23 @@ type Pipeline struct { | |
| Compute ComputeClient | ||
| PM PackageManager | ||
|
|
||
| // Now returns the current time; it exists so tests can inject a deterministic | ||
| // clock (acceptance goldens would otherwise carry a real, changing durationMs). | ||
| // nil means time.Now — see now(). | ||
| Now func() time.Time | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking (#6) — the stated rationale isn't the real one. The comment says acceptance goldens "would otherwise carry a real, changing durationMs," but the So an injectable field on the exported The timing logic itself is right — measuring in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed. Took the drop-it option in #6190, given the repo stance on speculative flexibility: an exported field on |
||
|
|
||
| // res accumulates phase statuses and result fields as the run progresses. | ||
| res *Result | ||
| } | ||
|
|
||
| // now returns the current time via the injected clock, defaulting to time.Now. | ||
| func (p *Pipeline) now() time.Time { | ||
| if p.Now != nil { | ||
| return p.Now() | ||
| } | ||
| return time.Now() | ||
| } | ||
|
|
||
| // Run executes all pipeline phases in order and returns a fully populated Result. | ||
| // On a phase error, Result.Error is set and the same error is also returned. The | ||
| // Result always carries the full canonical phase list: phases completed before a | ||
|
|
@@ -81,6 +95,12 @@ func (p *Pipeline) Run(ctx context.Context) (*Result, error) { | |
| // Phases start as pending and flip to ok/error as the run progresses. | ||
| p.res.Phases = initialPhases() | ||
|
|
||
| // Measure wall time across every exit path (success and failure alike) so the | ||
| // --json durationMs reflects the whole pipeline. Injected clock keeps it | ||
| // deterministic under test. | ||
| start := p.now() | ||
| defer func() { p.res.DurationMs = p.now().Sub(start).Milliseconds() }() | ||
|
|
||
| if err := p.run(ctx); err != nil { | ||
| // A cancelled context means the user or parent interrupted us (SIGINT/ | ||
| // SIGTERM). The phase that was running reports its own failure (e.g. uv | ||
|
|
@@ -307,6 +327,11 @@ func (p *Pipeline) mergePlan(_ context.Context, pyMinor string, c *Constraints, | |
| if err != nil { | ||
| return nil, greenfield, p.fail(PhaseMerge, false, NewError(ErrMerge, err, "merge managed regions failed")) | ||
| } | ||
| // Surface merge-quality warnings (overridden pins, conflicting user | ||
| // constraints) from the pre-merge file. Greenfield has nothing of the | ||
| // user's to override, so it is skipped. This runs for both dry-run and real | ||
| // runs so the --json consumer sees the same warnings either way. | ||
| p.res.Warnings = append(p.res.Warnings, detectMergeWarnings(baseBytes, effective)...) | ||
| } | ||
|
|
||
| // Under --dry-run, build the plan (with a diff) for reporting. A real run does | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking (#7) —
localenvis the only thing emitting this key, so this fits better inacceptance/localenv/test.toml. Global scope means a future command that emitsdurationMsgets it silently normalized in its goldens without anyone opting in.(The
Order = 8choice is correct — it has to beat theOrder = 10numeric repls. Worth keeping that note wherever it lands.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed and moved in #6190 — it now lives in a new
acceptance/localenv/test.toml, and theOrder = 8note moved with it, including why it has to beat the rootOrder = 10numeric repls.