Skip to content

Commit acfa61a

Browse files
authored
Add bundle project manager (#1011)
"Bundle project manager" is being initialised before configModel or connectionManager. If necessary, it does automatic migration from the legacy projects to the bundles (will be implemented in the follow-up). If it detects multiple bundle projects and offers to choose which one to open. If it detects a bundle config in the root of the workspace it kicks off configModel and connectionManager. If it detects no bundle config in the root, it offers to create a project (spawning the Databricks CLI in the terminal) I've played around with displaying notifications for things like "hey, chose one project to open", or "there's no config in your workspace, would you like to init a new project?", but they don't flow well with existing quick-pick/input wizards and are pretty easy to miss. Currently I'm using welcome views to show such information: The default view is still the same: ![Screenshot 2024-01-22 at 10 11 20](https://github.com/databricks/databricks-vscode/assets/148094031/b185b327-5e91-4fed-8609-93724c9532c8) If we don't detect bundle config in the root, but there are subfolders with bundles, we show an additional text and a button: ![Screenshot 2024-01-22 at 10 11 58](https://github.com/databricks/databricks-vscode/assets/148094031/e90184ea-3e74-44b1-b0ba-f90720c7c72f) It's still possible to login in this state, after which we will also show "init" button: ![Screenshot 2024-01-22 at 10 13 27](https://github.com/databricks/databricks-vscode/assets/148094031/9b43a3b6-ac62-47cc-93db-1dd1285023c2) If there are no sub-projects, then we just show login button alone in the view: ![Screenshot 2024-01-22 at 10 14 54](https://github.com/databricks/databricks-vscode/assets/148094031/68f4a84f-4974-4ba3-9d96-45f7113668c2)
1 parent d8852bd commit acfa61a

File tree

11 files changed

+441
-31
lines changed

11 files changed

+441
-31
lines changed

packages/databricks-vscode/package.json

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@
5757
"enablement": "databricks.context.activated && databricks.context.loggedIn"
5858
},
5959
{
60-
"command": "databricks.connection.configureWorkspace",
60+
"command": "databricks.connection.configureLogin",
6161
"icon": "$(gear)",
62-
"title": "Configure workspace",
62+
"title": "Sign in to Databricks workspace",
6363
"enablement": "databricks.context.activated",
6464
"category": "Databricks"
6565
},
@@ -230,6 +230,12 @@
230230
"title": "Select a Databricks Asset Bundle target.",
231231
"enablement": "databricks.context.activated",
232232
"category": "Databricks"
233+
},
234+
{
235+
"command": "databricks.bundle.initNewProject",
236+
"title": "Initialize new project",
237+
"enablement": "databricks.context.activated",
238+
"category": "Databricks"
233239
}
234240
],
235241
"viewsContainers": {
@@ -268,7 +274,12 @@
268274
"viewsWelcome": [
269275
{
270276
"view": "configurationView",
271-
"contents": "In order to connect to a cluster you first have to configure your Databricks workspace:\n[Configure Databricks](command:databricks.connection.configureWorkspace)\n[Show Quickstart](command:databricks.quickstart.open)\nTo learn more about how to use Databricks with VS Code [read our docs](https://docs.databricks.com/dev-tools/vscode-ext.html).",
277+
"contents": "There are multiple Databricks projects in the workspace, please chose which one to open:\n[Open Existing Project](command:databricks.bundle.openSubProject)\n",
278+
"when": "workspaceFolderCount > 0 && databricks.context.subProjectsAvailable"
279+
},
280+
{
281+
"view": "configurationView",
282+
"contents": "Current workspace has no Databricks configuration at the root level, do you want to initialize a new Databricks project?\n[Initialize New Project](command:databricks.bundle.initNewProject)\n[Show Quickstart](command:databricks.quickstart.open)\nTo learn more about how to use Databricks with VS Code [read our docs](https://docs.databricks.com/dev-tools/vscode-ext.html).",
272283
"when": "workspaceFolderCount > 0"
273284
},
274285
{
@@ -280,14 +291,18 @@
280291
"menus": {
281292
"view/title": [
282293
{
283-
"command": "databricks.connection.configureWorkspace",
294+
"command": "databricks.connection.configureLogin",
284295
"when": "view == configurationView",
285296
"group": "navigation@3"
286297
},
287298
{
288299
"command": "databricks.quickstart.open",
289300
"when": "view == configurationView"
290301
},
302+
{
303+
"command": "databricks.bundle.initNewProject",
304+
"when": "view == configurationView"
305+
},
291306
{
292307
"when": "view == clusterView",
293308
"group": "navigation@1",
@@ -337,7 +352,7 @@
337352
"group": "inline@2"
338353
},
339354
{
340-
"command": "databricks.connection.configureWorkspace",
355+
"command": "databricks.connection.configureLogin",
341356
"when": "view == configurationView && viewItem =~ /^databricks.configuration.authType.*$/",
342357
"group": "inline@2"
343358
},

packages/databricks-vscode/src/bundle/BundleFileSet.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ function toGlobPath(path: string) {
2525
}
2626
export class BundleFileSet {
2727
private rootFilePattern: string = "{bundle,databricks}.{yaml,yml}";
28+
private subProjectFilePattern: string = `**/${this.rootFilePattern}`;
29+
2830
public readonly bundleDataCache: CachedValue<BundleSchema> =
2931
new CachedValue<BundleSchema>(async () => {
3032
let bundle = {};
@@ -36,11 +38,11 @@ export class BundleFileSet {
3638

3739
constructor(private readonly workspaceRoot: Uri) {}
3840

39-
getAbsolutePath(path: string | Uri) {
41+
getAbsolutePath(path: string | Uri, root?: Uri) {
4042
if (typeof path === "string") {
41-
return Uri.joinPath(this.workspaceRoot, path);
43+
return Uri.joinPath(root ?? this.workspaceRoot, path);
4244
}
43-
return Uri.joinPath(this.workspaceRoot, path.fsPath);
45+
return Uri.joinPath(root ?? this.workspaceRoot, path.fsPath);
4446
}
4547

4648
async getRootFile() {
@@ -54,6 +56,32 @@ export class BundleFileSet {
5456
return Uri.file(rootFile[0]);
5557
}
5658

59+
async getSubProjects(
60+
root?: Uri
61+
): Promise<{relative: Uri; absolute: Uri}[]> {
62+
const subProjectRoots = await glob.glob(
63+
toGlobPath(
64+
this.getAbsolutePath(this.subProjectFilePattern, root).fsPath
65+
),
66+
{nocase: process.platform === "win32"}
67+
);
68+
const normalizedRoot = path.normalize(
69+
root?.fsPath ?? this.workspaceRoot.fsPath
70+
);
71+
return subProjectRoots
72+
.map((rootFile) => {
73+
const dirname = path.dirname(path.normalize(rootFile));
74+
const absolute = Uri.file(dirname);
75+
const relative = Uri.file(
76+
absolute.fsPath.replace(normalizedRoot, "")
77+
);
78+
return {absolute, relative};
79+
})
80+
.filter(({absolute}) => {
81+
return absolute.fsPath !== normalizedRoot;
82+
});
83+
}
84+
5785
async getIncludedFilesGlob() {
5886
const rootFile = await this.getRootFile();
5987
if (rootFile === undefined) {

0 commit comments

Comments
 (0)