diff --git a/packages/databricks-vscode/package.json b/packages/databricks-vscode/package.json index 379a2950d..755cd6f96 100644 --- a/packages/databricks-vscode/package.json +++ b/packages/databricks-vscode/package.json @@ -698,7 +698,8 @@ "ansi-to-html": "^0.7.2", "bcryptjs": "^2.4.3", "triple-beam": "^1.4.1", - "winston": "^3.10.0" + "winston": "^3.10.0", + "yaml": "^2.3.2" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.2", diff --git a/packages/databricks-vscode/resources/whats-new/1.2.md b/packages/databricks-vscode/resources/whats-new/1.2.md new file mode 100644 index 000000000..a880ef09f --- /dev/null +++ b/packages/databricks-vscode/resources/whats-new/1.2.md @@ -0,0 +1,46 @@ +databricks-logo + +# Databricks Extension v1.2.0 + +## What's new? + +- [📗 Advanced notebook support with Databricks Connect](#dbconnect-notebook) + +## Advanced notebook support with Databricks Connect + +Before this update, you could already run notebooks locally with Databricks Connect. This update introduces advanced notebook support. This advanced support enables you to run notebooks locally with a similar experience as you have by running them on a Databricks cluster. The goal of this update is to bring much of the convenience of running notebooks on a Databricks cluster to your local development environment. + +This update includes: + +- [Magic commands such as `%run` and `%sql`](#magic-commands) +- [Preconfigured globals such as `spark` and `dbutils`](#preconf-globals) +- [Interactive widgets through `dbutils.widgets`](#widgets) +- [Support for Databricks notebooks](#dbnb) + +### Magic commands + +You can now use Databricks magic commands locally from your notebooks. + +magic_sql + +### Preconfigured globals + +Just like on a Databricks cluster, this update configures and provides you with some globals such as `spark` and `dbutils` when you run notebooks locally. + +preconf_globals + +### Interactive `dbutils.widgets` + +You can now use `dbutils.widgets` to create interactive widgets in your local notebooks. + +widgets + +### Support for Databricks notebooks + +You can use Databricks notebooks locally with [VS Code Interactive Windows](https://code.visualstudio.com/docs/python/jupyter-support-py). + +dbnb + +### Further details + +For a detailed documentation of this feature, see [Use notebooks with Databricks Connect](https://docs.databricks.com/en/dev-tools/vscode-ext/dev-tasks/notebooks.html). diff --git a/packages/databricks-vscode/resources/whats-new/1.2/dbnb.gif b/packages/databricks-vscode/resources/whats-new/1.2/dbnb.gif new file mode 100644 index 000000000..36db6d13e Binary files /dev/null and b/packages/databricks-vscode/resources/whats-new/1.2/dbnb.gif differ diff --git a/packages/databricks-vscode/resources/whats-new/1.2/magic_sql.gif b/packages/databricks-vscode/resources/whats-new/1.2/magic_sql.gif new file mode 100644 index 000000000..f585b824e Binary files /dev/null and b/packages/databricks-vscode/resources/whats-new/1.2/magic_sql.gif differ diff --git a/packages/databricks-vscode/resources/whats-new/1.2/preconf_globals.gif b/packages/databricks-vscode/resources/whats-new/1.2/preconf_globals.gif new file mode 100644 index 000000000..a46a48a47 Binary files /dev/null and b/packages/databricks-vscode/resources/whats-new/1.2/preconf_globals.gif differ diff --git a/packages/databricks-vscode/resources/whats-new/1.2/widgets.gif b/packages/databricks-vscode/resources/whats-new/1.2/widgets.gif new file mode 100644 index 000000000..63b53e0e9 Binary files /dev/null and b/packages/databricks-vscode/resources/whats-new/1.2/widgets.gif differ diff --git a/packages/databricks-vscode/src/extension.ts b/packages/databricks-vscode/src/extension.ts index 6ebb63f71..dd6c631ec 100644 --- a/packages/databricks-vscode/src/extension.ts +++ b/packages/databricks-vscode/src/extension.ts @@ -50,6 +50,7 @@ import {DbConnectStatusBarButton} from "./language/DbConnectStatusBarButton"; import {NotebookAccessVerifier} from "./language/notebooks/NotebookAccessVerifier"; import {NotebookInitScriptManager} from "./language/notebooks/NotebookInitScriptManager"; import {showRestartNotebookDialogue} from "./language/notebooks/restartNotebookDialogue"; +import {showWhatsNewPopup} from "./whatsNewPopup"; export async function activate( context: ExtensionContext @@ -559,6 +560,14 @@ export async function activate( ); }); + showWhatsNewPopup(context, stateStorage).catch((e) => { + logging.NamedLogger.getOrCreate(Loggers.Extension).error( + "Error while showing popup for what's new", + e + ); + }); + + stateStorage.lastInstalledExtensionVersion = packageMetadata.version; CustomWhenContext.setActivated(true); telemetry.recordEvent(Events.EXTENSION_ACTIVATED); diff --git a/packages/databricks-vscode/src/language/DbConnectInstallPrompt.ts b/packages/databricks-vscode/src/language/DbConnectInstallPrompt.ts index 16097e41a..5f4528186 100644 --- a/packages/databricks-vscode/src/language/DbConnectInstallPrompt.ts +++ b/packages/databricks-vscode/src/language/DbConnectInstallPrompt.ts @@ -47,11 +47,7 @@ export class DbConnectInstallPrompt implements Disposable { const pkgUpdateMessagePart = hasPyspark ? "(pyspark will be uninstalled)" : hasDbConnect - ? `(databricks-connect will be updated to the latest version: ${ - dbConnectDetails.version - } -> ${await this.pythonExtension.getLatestPackageVersion( - "databricks-connect" - )} )` + ? `(databricks-connect will be updated to the latest version: ${dbConnectDetails.version} -> ${DATABRICKS_CONNECT_VERSION} )` : ""; const message = `${mainMessagePart} ${envMessagePart}. ${pkgUpdateMessagePart}`; diff --git a/packages/databricks-vscode/src/vscode-objs/StateStorage.ts b/packages/databricks-vscode/src/vscode-objs/StateStorage.ts index 6941ebbe3..e77684648 100644 --- a/packages/databricks-vscode/src/vscode-objs/StateStorage.ts +++ b/packages/databricks-vscode/src/vscode-objs/StateStorage.ts @@ -86,6 +86,20 @@ export class StateStorage { ); } + get lastInstalledExtensionVersion() { + return this.context.workspaceState.get( + "databricks.lastInstalledExtensionVersion", + "0.0.0" + ); + } + + set lastInstalledExtensionVersion(value: string) { + this.context.workspaceState.update( + "databricks.lastInstalledExtensionVersion", + value + ); + } + get fixedUUID() { let uuid = this.context.workspaceState.get( "databricks.fixedUUID" diff --git a/packages/databricks-vscode/src/whatsNewPopup.test.ts b/packages/databricks-vscode/src/whatsNewPopup.test.ts new file mode 100644 index 000000000..dda141165 --- /dev/null +++ b/packages/databricks-vscode/src/whatsNewPopup.test.ts @@ -0,0 +1,63 @@ +import {expect} from "chai"; +import {findFileFowWhatsNew} from "./whatsNewPopup"; +import {SemVer} from "semver"; +import * as tmp from "tmp"; +import path from "path"; +import {mkdir, writeFile} from "fs/promises"; + +describe(__filename, () => { + let context: any; + let removeCallback: () => void; + let dirname: string; + + before(async () => { + ({name: dirname, removeCallback} = tmp.dirSync({unsafeCleanup: true})); + context = { + asAbsolutePath: (ip: string) => path.join(dirname, ip), + } as any; + + await mkdir(path.join(dirname, "resources", "whats-new"), { + recursive: true, + }); + await writeFile( + path.join(dirname, "resources", "whats-new", "1.2.md"), + "" + ); + }); + + after(() => { + removeCallback(); + }); + + it("should use CHANGELOG.md for patch version upgrade", async () => { + const previousVersion = new SemVer("1.2.1"); + const currentVersion = new SemVer("1.2.2"); + expect( + await findFileFowWhatsNew(context, previousVersion, currentVersion) + ).to.equal(path.join(dirname, "CHANGELOG.md")); + }); + + it("should use custom markdown file for minor version upgrade", async () => { + const previousVersion = new SemVer("1.1.2"); + const currentVersion = new SemVer("1.2.3"); + expect( + await findFileFowWhatsNew(context, previousVersion, currentVersion) + ).to.equal(path.join(dirname, "resources", "whats-new", "1.2.md")); + }); + + it("should use custom markdown file for major version upgrade", async () => { + const previousVersion = new SemVer("0.1.2"); + const currentVersion = new SemVer("1.2.3"); + expect( + await findFileFowWhatsNew(context, previousVersion, currentVersion) + ).to.equal(path.join(dirname, "resources", "whats-new", "1.2.md")); + }); + + it("should use CHANGELOG.md if custom markdown file does not exist", async () => { + const previousVersion = new SemVer("1.1.2"); + const currentVersion = new SemVer("1.3.3"); + expect( + await findFileFowWhatsNew(context, previousVersion, currentVersion) + ).to.equal(path.join(dirname, "CHANGELOG.md")); + }); +}); diff --git a/packages/databricks-vscode/src/whatsNewPopup.ts b/packages/databricks-vscode/src/whatsNewPopup.ts new file mode 100644 index 000000000..643bfd29e --- /dev/null +++ b/packages/databricks-vscode/src/whatsNewPopup.ts @@ -0,0 +1,76 @@ +import {ExtensionContext, Uri, commands, window} from "vscode"; +import {PackageJsonUtils} from "./utils"; +import {StateStorage} from "./vscode-objs/StateStorage"; +import path from "path"; +import {exists} from "fs-extra"; +import * as semver from "semver"; + +export async function findFileFowWhatsNew( + context: ExtensionContext, + previousVersion: semver.SemVer, + currentVersion: semver.SemVer +) { + const markdownFile = context.asAbsolutePath( + path.join( + "resources", + "whats-new", + `${currentVersion.major}.${currentVersion.minor}.md` + ) + ); + + // To maintain release discipline (prevent too big changes from being released in a patch version) + // we explicitly do not show the custom message popup for patch versions upgrades. + currentVersion.patch = 0; + previousVersion.patch = 0; + + if ( + semver.compare(currentVersion, previousVersion) > 0 && + (await exists(markdownFile)) + ) { + return markdownFile; + } + + return context.asAbsolutePath("CHANGELOG.md"); +} + +export async function showWhatsNewPopup( + context: ExtensionContext, + storage: StateStorage +) { + const packageJsonMetadata = await PackageJsonUtils.getMetadata(context); + const currentVersion = semver.parse(packageJsonMetadata.version); + if (currentVersion === null) { + return; + } + + const previousVersion = + semver.parse(storage.lastInstalledExtensionVersion) ?? + new semver.SemVer("0.0.0"); + + // if the extension is downgraded, we do not want to show the popup + if (semver.compare(currentVersion, previousVersion) <= 0) { + return; + } + + // We try to find a custom markdown file for the current version, + // if not found, we use the changelog.md in its entirety. + const markdownFile = await findFileFowWhatsNew( + context, + previousVersion, + currentVersion + ); + + if (window.state.focused) { + commands.executeCommand("markdown.showPreview", Uri.file(markdownFile)); + return; + } + + const listener = window.onDidChangeWindowState((e) => { + if (!e.focused) { + return; + } + commands.executeCommand("markdown.showPreview", Uri.file(markdownFile)); + listener.dispose(); + }); + context.subscriptions.push(listener); +} diff --git a/yarn.lock b/yarn.lock index 227f851c1..24c7ab1aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -376,7 +376,7 @@ __metadata: inversify: ^6.0.1 reflect-metadata: ^0.1.13 semver: ^7.5.4 - checksum: 2e28fb154da0743e5b26cd3bdb1dba6f286e1f34f41d64291fca872ae25678f44a7780bd965f8166632f5a2303a00ecc6def5a9570d09e8d37bcd6546d489cd3 + checksum: b33948b1c9bb7ff37fe5d34eefedf78ac1df594b311aa2f3cbc7b5cf888c64512d06acb646635653c63fa7dc7634c09cfe7d0ff3cf0bda616c3f0b3494f50dde languageName: node linkType: hard @@ -391,7 +391,7 @@ __metadata: inversify: ^6.0.1 reflect-metadata: ^0.1.13 semver: ^7.5.4 - checksum: 2e28fb154da0743e5b26cd3bdb1dba6f286e1f34f41d64291fca872ae25678f44a7780bd965f8166632f5a2303a00ecc6def5a9570d09e8d37bcd6546d489cd3 + checksum: b33948b1c9bb7ff37fe5d34eefedf78ac1df594b311aa2f3cbc7b5cf888c64512d06acb646635653c63fa7dc7634c09cfe7d0ff3cf0bda616c3f0b3494f50dde languageName: node linkType: hard @@ -3717,6 +3717,7 @@ __metadata: wdio-video-reporter: ^4.0.3 wdio-vscode-service: ^5.2.0 winston: ^3.10.0 + yaml: ^2.3.2 yargs: ^17.7.2 languageName: unknown linkType: soft @@ -11184,6 +11185,13 @@ __metadata: languageName: node linkType: hard +"yaml@npm:^2.3.2": + version: 2.3.2 + resolution: "yaml@npm:2.3.2" + checksum: acd80cc24df12c808c6dec8a0176d404ef9e6f08ad8786f746ecc9d8974968c53c6e8a67fdfabcc5f99f3dc59b6bb0994b95646ff03d18e9b1dcd59eccc02146 + languageName: node + linkType: hard + "yargs-parser@npm:20.2.4": version: 20.2.4 resolution: "yargs-parser@npm:20.2.4"