diff --git a/vscode/extension/package.json b/vscode/extension/package.json index aefc1d736c..997c8d50c2 100644 --- a/vscode/extension/package.json +++ b/vscode/extension/package.json @@ -73,6 +73,11 @@ "title": "SQLMesh: Restart Servers", "description": "SQLMesh" }, + { + "command": "sqlmesh.printEnvironment", + "title": "SQLMesh: Print Environment Variables", + "description": "SQLMesh" + }, { "command": "sqlmesh.signin", "title": "SQLMesh: Sign in to Tobiko Cloud", diff --git a/vscode/extension/src/commands/printEnvironment.ts b/vscode/extension/src/commands/printEnvironment.ts new file mode 100644 index 0000000000..41e74e40d3 --- /dev/null +++ b/vscode/extension/src/commands/printEnvironment.ts @@ -0,0 +1,40 @@ +import * as vscode from 'vscode' +import { getSqlmeshEnvironment } from '../utilities/sqlmesh/sqlmesh' +import { isErr } from '@bus/result' +import { IS_WINDOWS } from '../utilities/isWindows' + +export function printEnvironment() { + return async () => { + const envResult = await getSqlmeshEnvironment() + + if (isErr(envResult)) { + await vscode.window.showErrorMessage(envResult.error) + return + } + + const env = envResult.value + + // Create a new terminal with the SQLMesh environment + const terminal = vscode.window.createTerminal({ + name: 'SQLMesh Environment', + env: env, + }) + + // Show the terminal + terminal.show() + + // Run the appropriate command to display environment variables + if (IS_WINDOWS) { + // On Windows, use 'set' command + terminal.sendText('set') + } else { + // On Unix-like systems, use 'env' command + terminal.sendText('env | sort') + } + + // Show a notification + vscode.window.showInformationMessage( + 'SQLMesh environment variables displayed in terminal', + ) + } +} diff --git a/vscode/extension/src/extension.ts b/vscode/extension/src/extension.ts index 0525397cff..10e5b9953c 100644 --- a/vscode/extension/src/extension.ts +++ b/vscode/extension/src/extension.ts @@ -14,6 +14,7 @@ import { signIn } from './commands/signin' import { signInSpecifyFlow } from './commands/signinSpecifyFlow' import { renderModel, reRenderModelForSourceFile } from './commands/renderModel' import { stop } from './commands/stop' +import { printEnvironment } from './commands/printEnvironment' import { isErr } from '@bus/result' import { handleError } from './utilities/errors' import { selector, completionProvider } from './completion/completion' @@ -161,6 +162,7 @@ export async function activate(context: vscode.ExtensionContext) { await restart() }), registerCommand(`sqlmesh.stop`, stop(lspClient)), + registerCommand(`sqlmesh.printEnvironment`, printEnvironment()), ) const result = await lspClient.start() diff --git a/vscode/extension/src/lsp/lsp.ts b/vscode/extension/src/lsp/lsp.ts index 769a0b3753..ccf3981eb2 100644 --- a/vscode/extension/src/lsp/lsp.ts +++ b/vscode/extension/src/lsp/lsp.ts @@ -81,14 +81,7 @@ export class LSPClient implements Disposable { transport: TransportKind.stdio, options: { cwd: workspacePath, - // TODO: This is a temporary fix to avoid the issue with the LSP server - // crashing when the number of workers is too high. This is a workaround - // to avoid the issue. Once fixed, we should remove the whole env block. - env: { - MAX_FORK_WORKERS: '1', - ...process.env, - ...sqlmesh.value.env, - }, + env: sqlmesh.value.env, }, args: sqlmesh.value.args, }, @@ -97,14 +90,7 @@ export class LSPClient implements Disposable { transport: TransportKind.stdio, options: { cwd: workspacePath, - env: { - // TODO: This is a temporary fix to avoid the issue with the LSP server - // crashing when the number of workers is too high. This is a workaround - // to avoid the issue. Once fixed, we should remove the whole env block. - MAX_FORK_WORKERS: '1', - ...process.env, - ...sqlmesh.value.env, - }, + env: sqlmesh.value.env, }, args: sqlmesh.value.args, }, diff --git a/vscode/extension/src/utilities/sqlmesh/sqlmesh.ts b/vscode/extension/src/utilities/sqlmesh/sqlmesh.ts index 06f05f7792..d95017a2ca 100644 --- a/vscode/extension/src/utilities/sqlmesh/sqlmesh.ts +++ b/vscode/extension/src/utilities/sqlmesh/sqlmesh.ts @@ -21,6 +21,41 @@ export interface SqlmeshExecInfo { args: string[] } +/** + * Gets the current SQLMesh environment variables that would be used for execution. + * This is useful for debugging and understanding the environment configuration. + * + * @returns A Result containing the environment variables or an error + */ +export async function getSqlmeshEnvironment(): Promise, string>> { + const interpreterDetails = await getInterpreterDetails() + const envVariables = await getPythonEnvVariables() + if (isErr(envVariables)) { + return err(envVariables.error) + } + + const binPath = interpreterDetails.binPath + const virtualEnvPath = binPath && interpreterDetails.isVirtualEnvironment + ? path.dirname(path.dirname(binPath)) // binPath points to bin dir in venv + : binPath ? path.dirname(binPath) : undefined + + const env: Record = { + ...process.env, + ...envVariables.value, + PYTHONPATH: interpreterDetails.path?.[0] ?? '', + } + + if (virtualEnvPath) { + env['VIRTUAL_ENV'] = virtualEnvPath + } + + if (binPath) { + env['PATH'] = `${binPath}${path.delimiter}${process.env.PATH || ''}` + } + + return ok(env) +} + /** * Returns true if the current project is a Tcloud project. To detect this we, * 1. Check if the project has a tcloud.yaml file in the project root. If it does, we assume it's a Tcloud project. @@ -68,23 +103,17 @@ export const getTcloudBin = async (): Promise if (!fs.existsSync(binPath)) { return err({type: 'tcloud_bin_not_found'}) } - const envVariables = await getPythonEnvVariables() - if (isErr(envVariables)) { + const env = await getSqlmeshEnvironment() + if (isErr(env)) { return err({ type: 'generic', - message: envVariables.error, + message: env.error, }) } return ok({ bin: binPath, workspacePath: interpreterDetails.resource?.fsPath ?? '', - env: { - ...process.env, - ...envVariables.value, - PYTHONPATH: interpreterDetails.path[0], - VIRTUAL_ENV: path.dirname(interpreterDetails.binPath!), - PATH: `${interpreterDetails.binPath!}${path.delimiter}${process.env.PATH || ''}`, - }, + env: env.value, args: [], }) } @@ -300,16 +329,17 @@ export const sqlmeshExec = async (): Promise< } const binPath = path.join(interpreterDetails.binPath!, sqlmesh) traceLog(`Bin path: ${binPath}`) + const env = await getSqlmeshEnvironment() + if (isErr(env)) { + return err({ + type: 'generic', + message: env.error, + }) + } return ok({ bin: binPath, workspacePath, - env: { - ...process.env, - ...envVariables.value, - PYTHONPATH: interpreterDetails.path?.[0], - VIRTUAL_ENV: path.dirname(path.dirname(interpreterDetails.binPath!)), // binPath now points to bin dir - PATH: `${interpreterDetails.binPath!}${path.delimiter}${process.env.PATH || ''}`, - }, + env: env.value, args: [], }) } else { @@ -319,13 +349,17 @@ export const sqlmeshExec = async (): Promise< type: 'sqlmesh_not_found', }) } + const env = await getSqlmeshEnvironment() + if (isErr(env)) { + return err({ + type: 'generic', + message: env.error, + }) + } return ok({ bin: sqlmesh, workspacePath, - env: { - ...process.env, - ...envVariables.value, - }, + env: env.value, args: [], }) } @@ -455,19 +489,27 @@ export const sqlmeshLspExec = async (): Promise< if (isErr(ensuredDependencies)) { return ensuredDependencies } + const env = await getSqlmeshEnvironment() + if (isErr(env)) { + return err({ + type: 'generic', + message: env.error, + }) + } return ok({ bin: binPath, workspacePath, - env: { - ...process.env, - ...envVariables.value, - PYTHONPATH: interpreterDetails.path?.[0], - VIRTUAL_ENV: path.dirname(path.dirname(interpreterDetails.binPath!)), // binPath now points to bin dir - PATH: `${interpreterDetails.binPath!}${path.delimiter}${process.env.PATH || ''}`, // binPath already points to the bin directory - }, + env: env.value, args: [], }) } else { + const env = await getSqlmeshEnvironment() + if (isErr(env)) { + return err({ + type: 'generic', + message: env.error, + }) + } const exists = await doesExecutableExist(sqlmeshLSP) if (!exists) { return err({ @@ -477,10 +519,7 @@ export const sqlmeshLspExec = async (): Promise< return ok({ bin: sqlmeshLSP, workspacePath, - env: { - ...process.env, - ...envVariables.value, - }, + env: env.value, args: [], }) }