Skip to content
Open
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions .agents/skills/bump-sdk/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The SDK bumper (eng-dev-ecosystem automation) does steps 1-4 for every new stabl
It opens a PR from the `automation/go-sdk-vX.Y.Z` branch of `databricks/cli`, titled `Update Go SDK to vX.Y.Z`, containing the `go get` + `go mod tidy` result, `.codegen/_openapi_sha`, `.codegen/cli.json` (genkit, followed by `./task generate-clijson-post`), and a changelog fragment without a PR link.
`generate-clijson-post` ends with `bump-sdk-post` (step 4), whose error it ignores so that the bumper, which aborts on any error there, still opens a PR. The PR therefore carries whatever got regenerated, and stays red while the bump needs hand fixes (step 5).
Nothing logs the task's output in the bumper, so judge from the PR what got regenerated.
The bumper's environment has `uv` and Python but no `ruff`, which the acceptance tests require, so `test-update` stops at `TestAccept/prerequisites` there and bumper PRs carry no golden updates.
The bumper's image has `uv` and Python 3.12 but no `ruff`; the acceptance tests run ruff through `uv tool run` (prefetched by the harness) and `bump-sdk-post` installs the managed Pythons, so `test-update` can run there. Bumper PRs up to #7277 predate that and carry no golden updates.
Bumper PRs opened before `generate-clijson-post` ran `bump-sdk-post` (e.g. #7138) have nothing downstream of `cli.json`: their stale command stubs usually fail to compile, which fails lint and every test job.
The bumper does not merge CLI PRs, and it reuses an open PR for the same release on later runs, keeping commits pushed to its branch.

Expand Down Expand Up @@ -58,7 +58,7 @@ Run directly, it fails on errors: `generate-refschema` and `test-update` build t

Run it in CI's environment, or the goldens pick up local drift:
- `uv` index: the acceptance tests build wheels offline from the vendored packages (`UV_OFFLINE`, `UV_FIND_LINKS`), but `uv` still reads its default index's pages from the shared cache. If a `uv.toml` (e.g. `/etc/uv/uv.toml`, which applies even under the tests' temporary `HOME`) makes an internal PyPI proxy the default index, `uv` picks versions from cached proxy pages that it cannot download offline and fails with `No solution found when resolving: hatchling`; `-update` then bakes that error into dozens of template goldens and deletes their `output/` trees. The same failure on `main` tells it apart from bump fallout. Set `UV_DEFAULT_INDEX=https://pypi.org/simple` for the acceptance runs only, so `uv` stops reading cached proxy pages (CI's test jobs point `UV_INDEX_URL` at a JFrog mirror instead): the generators fetch tools such as `ruff` and need the proxy where `pypi.org` is unreachable. On such a machine, run the task's two halves separately: `./task generate-check`, then `UV_DEFAULT_INDEX=https://pypi.org/simple ./task test-update` (and the same prefix for `./task test` later). If `generate-clijson` already ran `test-update` without it, first revert the `acceptance/` churn (`git checkout -- acceptance`, plus any stray untracked files it left).
- Python: CI installs the managed interpreters with `./task install-pythons`; do the same.
- Python: the managed interpreters from `./task install-pythons`, which `bump-sdk-post` runs first (CI runs it in setup-build-environment).
- Go: the `toolchain` in `go.mod`. A newer local `go` also changes goldens and generated `gofmt` output; use `GOTOOLCHAIN=<pinned>` if they differ.

Before going on, account for every changed file outside the generated set, including the bumper's (its environment is not CI's either): a bump without fallout changes `cmd/**`, `bundle/schema/jsonschema.json`, `bundle/internal/validation/generated/*`, `python/databricks/bundles/**`, `acceptance/bundle/refschema/output` and help-text goldens (`acceptance/help/output.txt`, `acceptance/cmd/account/account-help/output.txt`).
Expand Down
6 changes: 6 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,12 @@ tasks:
bump-sdk-post:
desc: Regenerate everything downstream of an SDK bump and refresh acceptance goldens
cmds:
# The acceptance tests run uv offline, so the managed interpreters they
# pin (3.10 for bundle tests, 3.9-3.13 in one template matrix) must exist
# up front. CI installs them in setup-build-environment; the SDK bumper's
# image ships only Python 3.12, and a missing version would otherwise be
# baked into the goldens as a uv error.
- task: install-pythons
- task: generate-check
- task: test-update

Expand Down
33 changes: 18 additions & 15 deletions acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,6 @@ func requirePrerequisites(t *testing.T) bool {
// uv builds the databricks-bundles wheel and provides the test interpreter
// via `uv python find`, which landed in the 0.3 line.
internal.RequireUV(t, "0.4")
// ruff 0.9.1 is pinned across the repo (python/pyproject.toml, Taskfile.yml);
// the check-formatting test's golden output assumes its formatter behavior.
internal.RequireRuff(t, "0.9.1")
// Acceptance scripts import the stdlib tomllib module, added in Python 3.11.
internal.RequirePython(t, "3.11")
})
Expand Down Expand Up @@ -295,6 +292,15 @@ func testAccept(t *testing.T, inprocessMode bool, selectedTests []string, skipTo
// change survives for the rest of the run.
internal.ConfigurePython(t, "3.11")

// The check-formatting script runs `uv tool run ruff@0.9.1` (its golden
// assumes that release's formatter; keep the two pins in sync). Fetch it
// into the uv cache before UV_OFFLINE below cuts the sandbox off. Not inside
// requirePrerequisites: a -run filter for a test directory skips that
// subtest. DBR has no network, and no test that runs there uses ruff.
if !WorkspaceTmpDir {
internal.PrefetchRuff(t, "0.9.1")
}

buildDir := getBuildDir(t, cwd, runtime.GOOS, runtime.GOARCH)

// Download old CLI versions in parallel, overlapping with the builds below.
Expand Down Expand Up @@ -427,7 +433,7 @@ func testAccept(t *testing.T, inprocessMode bool, selectedTests []string, skipTo
t.Setenv("PATH", strings.Join(paths, string(os.PathListSeparator)))

// Make use of uv cache; since we set HomeEnvVar to temporary directory, it is not picked up automatically
uvCache := getUVDefaultCacheDir(t)
uvCache := getUVCacheDir(t)
t.Setenv("UV_CACHE_DIR", uvCache)

// UV_CACHE_DIR only applies to packages but not Python installations.
Expand Down Expand Up @@ -1805,17 +1811,14 @@ func ListDir(t *testing.T, src string) []string {
return files
}

func getUVDefaultCacheDir(t *testing.T) string {
// According to uv docs https://docs.astral.sh/uv/concepts/cache/#caching-in-continuous-integration
// the default cache directory is
// "A system-appropriate cache directory, e.g., $XDG_CACHE_HOME/uv or $HOME/.cache/uv on Unix and %LOCALAPPDATA%\uv\cache on Windows"
cacheDir, err := os.UserCacheDir()
require.NoError(t, err)
if runtime.GOOS == "windows" {
return cacheDir + "\\uv\\cache"
} else {
return cacheDir + "/uv"
}
// getUVCacheDir returns uv's effective cache directory. Must be called before
// HOME is overridden in tests. Asking uv, rather than deriving the default from
// HOME, honors UV_CACHE_DIR (CI's setup-uv points it at a per-job directory),
// so test scripts read the cache that PrefetchRuff filled.
func getUVCacheDir(t *testing.T) string {
out, err := exec.Command("uv", "cache", "dir").Output()
require.NoError(t, err, "uv cache dir")
return strings.TrimSpace(string(out))
}

// getUVPythonInstallDir returns the directory where uv stores managed Python installations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ Note that [DATABRICKS_URL] is used for initialization.
To get started, refer to the project README.md file and the documentation at https://docs.databricks.com/dev-tools/bundles/python/index.html.
Checking code formatting with ruff format --line-length 88

>>> ruff format --isolated --line-length 88 --quiet --diff --check my_pydabs
>>> uv tool run -q ruff@0.9.1 format --isolated --line-length 88 --quiet --diff --check my_pydabs
Checking code formatting with ruff format --line-length 120

>>> ruff format --isolated --line-length 120 --quiet --diff --check my_pydabs
>>> uv tool run -q ruff@0.9.1 format --isolated --line-length 120 --quiet --diff --check my_pydabs

>>> yamlcheck.py
9 changes: 7 additions & 2 deletions acceptance/bundle/templates/pydabs/check-formatting/script
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ trace $CLI bundle init pydabs --config-file ./input.json
#
# You can use trailing comma to force line breaks to make formatting consistent
# with different line-length settings.
#
# ruff comes from the uv cache: the harness prefetches this exact release
# (PrefetchRuff in acceptance_test.go) because the sandbox is offline. uv's -q
# hides its "Installed 1 package" line, printed only by the first variant that
# builds the tool environment for the test interpreter.

echo "Checking code formatting with ruff format --line-length 88"
trace ruff format --isolated --line-length 88 --quiet --diff --check my_pydabs
trace uv tool run -q ruff@0.9.1 format --isolated --line-length 88 --quiet --diff --check my_pydabs

echo "Checking code formatting with ruff format --line-length 120"
trace ruff format --isolated --line-length 120 --quiet --diff --check my_pydabs
trace uv tool run -q ruff@0.9.1 format --isolated --line-length 120 --quiet --diff --check my_pydabs

trace yamlcheck.py

Expand Down
29 changes: 8 additions & 21 deletions acceptance/internal/ruff.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,17 @@ package internal

import (
"os/exec"
"strings"
"testing"

"golang.org/x/mod/semver"
)

// RequireRuff fails the run if ruff is missing from PATH or older than
// minVersion (e.g. "0.9.1"). See the call site for why the minimum is what it is.
func RequireRuff(t *testing.T, minVersion string) {
out, err := exec.Command("ruff", "--version").Output()
// PrefetchRuff runs `uv tool run ruff@<version> --version` so that release
// lands in the uv cache before the harness sets UV_OFFLINE. Test scripts run
// ruff through the same command, offline, from that cache: not every
// environment the suite runs in has a ruff binary on PATH (the SDK bumper's
// image does not), while uv is a prerequisite anyway.
func PrefetchRuff(t *testing.T, version string) {
out, err := exec.Command("uv", "tool", "run", "ruff@"+version, "--version").CombinedOutput()
if err != nil {
t.Fatalf("ruff not found on PATH (acceptance tests require ruff >= %s): %v", minVersion, err)
}
version := strings.TrimSpace(string(out))
if !ruffVersionOK(version, minVersion) {
t.Fatalf("acceptance tests require ruff >= %s (found %q); install a newer ruff", minVersion, version)
}
}

// ruffVersionOK reports whether `ruff --version` output (e.g. "ruff 0.9.1") is >= minVersion.
func ruffVersionOK(versionOutput, minVersion string) bool {
fields := strings.Fields(versionOutput)
if len(fields) < 2 {
return false
t.Fatalf("uv could not fetch ruff %s for the acceptance tests: %v\n%s", version, err, out)
}
return semver.Compare("v"+fields[1], "v"+minVersion) >= 0
}
15 changes: 0 additions & 15 deletions acceptance/internal/ruff_test.go

This file was deleted.

Loading