diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 6a15893628e..4beaf2228ed 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -9,6 +9,7 @@ * Increase the SSH server startup timeout from 10 to 45 minutes when a GPU accelerator is requested via `databricks ssh connect --accelerator` ([#5569](https://github.com/databricks/cli/pull/5569)). * Fix authentication falling back to the default profile in `.databrickscfg` when a host is already configured via the environment (e.g. `DATABRICKS_HOST` with `DATABRICKS_TOKEN`) ([#5616](https://github.com/databricks/cli/pull/5616)). * ssh: fix opening remote environment in Cursor, which previously hung on default-extension install and never opened the editor ([#5619](https://github.com/databricks/cli/pull/5619)). +* Improve the error shown when `databricks labs install` cannot find a project's `labs.yml`: the message now explains that either the requested version does not exist or the project is not installable with the CLI, and links to the repository ([#5559](https://github.com/databricks/cli/pull/5559)). ### Bundles diff --git a/cmd/labs/project/fetcher.go b/cmd/labs/project/fetcher.go index c6969ae1ba7..395bd10ca54 100644 --- a/cmd/labs/project/fetcher.go +++ b/cmd/labs/project/fetcher.go @@ -151,6 +151,16 @@ func (f *fetcher) loadRemoteProjectDefinition(cmd *cobra.Command, version string var err error if !offlineInstall { raw, err = github.ReadFileFromRef(ctx, "databrickslabs", f.name, version, "labs.yml") + // A 404 on labs.yml has two causes we can't tell apart here: the requested + // version doesn't exist, or the repository simply doesn't ship a labs.yml + // (most databrickslabs repos don't, e.g. libraries published to package + // indexes) and so isn't installable through the CLI. Either way it's not a + // download failure, so surface both possibilities instead of the raw error. + if errors.Is(err, github.ErrNotFound) { + return nil, fmt.Errorf("no labs.yml at databrickslabs/%s@%s (%w); "+ + "either this version does not exist or this project cannot be installed with the Databricks CLI, "+ + "see https://github.com/databrickslabs/%s for instructions", f.name, version, err, f.name) + } if err != nil { return nil, fmt.Errorf("read labs.yml from GitHub: %w", err) } diff --git a/cmd/labs/project/fetcher_test.go b/cmd/labs/project/fetcher_test.go new file mode 100644 index 00000000000..2e93c1f79d9 --- /dev/null +++ b/cmd/labs/project/fetcher_test.go @@ -0,0 +1,46 @@ +package project_test + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/databricks/cli/cmd/labs/github" + "github.com/databricks/cli/internal/testcli" + "github.com/stretchr/testify/assert" +) + +func TestInstallerFailsWhenLabsYmlMissing(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasSuffix(r.URL.Path, "/labs.yml") { + w.WriteHeader(http.StatusNotFound) + return + } + t.Errorf("unexpected request: %s", r.URL.Path) + http.Error(w, "unexpected request", http.StatusInternalServerError) + })) + defer server.Close() + ctx := installerContext(t, server) + + // A 404 on labs.yml has two causes: the repository ships no labs.yml (reached + // here via the only released version, v0.3.15), or the requested version does + // not exist (v9.9.9). Both must produce the same actionable error. + tests := []struct { + name string + ref string + version string + }{ + {name: "released version without labs.yml", ref: "blueprint", version: "v0.3.15"}, + {name: "nonexistent version", ref: "blueprint@v9.9.9", version: "v9.9.9"}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + r := testcli.NewRunner(t, ctx, "labs", "install", tc.ref) + _, _, err := r.Run() + assert.ErrorIs(t, err, github.ErrNotFound) + assert.ErrorContains(t, err, "no labs.yml at databrickslabs/blueprint@"+tc.version) + assert.ErrorContains(t, err, "either this version does not exist or this project cannot be installed with the Databricks CLI") + }) + } +}