Skip to content

Commit 633fe09

Browse files
authored
feat(vscode): add installation progress bar for sqlmesh enterprise (#4230)
1 parent 8db14dd commit 633fe09

1 file changed

Lines changed: 122 additions & 14 deletions

File tree

vscode/extension/src/utilities/sqlmesh/sqlmesh.ts

Lines changed: 122 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import path from "path"
2-
import { traceLog, traceVerbose } from "../common/log"
2+
import { traceInfo, traceLog, traceVerbose } from "../common/log"
33
import { getInterpreterDetails } from "../common/python"
44
import { Result, err, isErr, ok } from "../functional/result"
55
import { getProjectRoot } from "../common/utilities"
6-
import { execFile } from "child_process"
7-
import { promisify } from "util"
86
import { isPythonModuleInstalled } from "../python"
97
import fs from "fs"
108
import { ErrorType } from "../errors"
119
import { isSignedIntoTobikoCloud } from "../../auth/auth"
10+
import { execAsync } from "../exec"
11+
import z from "zod"
12+
import { ProgressLocation, window } from "vscode"
1213

1314
export type sqlmesh_exec = {
1415
workspacePath: string;
@@ -48,12 +49,114 @@ export const get_tcloud_bin = async (): Promise<Result<string, string>> => {
4849
return ok(binPath)
4950
}
5051

52+
const isSqlmeshInstalledSchema = z.object({
53+
is_installed: z.boolean(),
54+
})
55+
56+
/**
57+
* Returns true if the current project is a sqlmesh enterprise project is installed and updated.
58+
*
59+
* @returns A Result indicating whether sqlmesh enterprise is installed and updated.
60+
*/
61+
export const isSqlmeshEnterpriseInstalled = async (): Promise<
62+
Result<boolean, string>
63+
> => {
64+
traceInfo("Checking if sqlmesh enterprise is installed")
65+
const tcloudBin = await get_tcloud_bin()
66+
if (isErr(tcloudBin)) {
67+
return err(tcloudBin.error)
68+
}
69+
const called = await execAsync(tcloudBin.value, ["is_sqlmesh_installed"])
70+
if (called.exitCode !== 0) {
71+
return err(
72+
`Failed to check if sqlmesh enterprise is installed: ${called.stderr}`
73+
)
74+
}
75+
const parsed = isSqlmeshInstalledSchema.safeParse(JSON.parse(called.stdout))
76+
if (!parsed.success) {
77+
return err(
78+
`Failed to parse sqlmesh enterprise installation status: ${parsed.error}`
79+
)
80+
}
81+
return ok(parsed.data.is_installed)
82+
}
83+
84+
/**
85+
* Install sqlmesh enterprise.
86+
*
87+
* @returns A Result indicating whether sqlmesh enterprise was installed.
88+
*/
89+
export const installSqlmeshEnterprise = async (
90+
abortController: AbortController
91+
): Promise<Result<boolean, string>> => {
92+
const tcloudBin = await get_tcloud_bin()
93+
if (isErr(tcloudBin)) {
94+
return err(tcloudBin.error)
95+
}
96+
const called = await execAsync(tcloudBin.value, ["install_sqlmesh"], {
97+
signal: abortController.signal,
98+
})
99+
if (called.exitCode !== 0) {
100+
return err(`Failed to install sqlmesh enterprise: ${called.stderr}`)
101+
}
102+
return ok(true)
103+
}
104+
105+
/**
106+
* Checks if sqlmesh enterprise is installed and updated. If not, it will install it.
107+
* This will also create a progress message in vscode in order to inform the user that sqlmesh enterprise is being installed.
108+
*
109+
* @returns A Result indicating whether sqlmesh enterprise was installed in the call.
110+
*/
111+
export const ensureSqlmeshEnterpriseInstalled = async (): Promise<
112+
Result<boolean, string>
113+
> => {
114+
traceInfo("Ensuring sqlmesh enterprise is installed")
115+
const isInstalled = await isSqlmeshEnterpriseInstalled()
116+
if (isErr(isInstalled)) {
117+
return err(isInstalled.error)
118+
}
119+
if (isInstalled.value) {
120+
traceInfo("Sqlmesh enterprise is installed")
121+
return ok(false)
122+
}
123+
traceInfo("Sqlmesh enterprise is not installed, installing...")
124+
const abortController = new AbortController()
125+
const installResult = await window.withProgress(
126+
{
127+
location: ProgressLocation.Notification,
128+
title: "Installing sqlmesh enterprise...",
129+
cancellable: true,
130+
},
131+
async (progress, token) => {
132+
// Connect the cancellation token to our abort controller
133+
token.onCancellationRequested(() => {
134+
abortController.abort()
135+
traceInfo("Sqlmesh enterprise installation cancelled")
136+
window.showInformationMessage("Installation cancelled")
137+
})
138+
progress.report({ message: "Installing sqlmesh enterprise..." })
139+
const result = await installSqlmeshEnterprise(abortController)
140+
if (isErr(result)) {
141+
return result
142+
}
143+
return ok(true)
144+
}
145+
)
146+
if (isErr(installResult)) {
147+
return installResult
148+
}
149+
return ok(true)
150+
}
151+
51152
/**
52153
* Get the sqlmesh executable for the current workspace.
53154
*
54155
* @returns The sqlmesh executable for the current workspace.
55156
*/
56-
export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, ErrorType>> => {
157+
export const sqlmesh_exec = async (): Promise<
158+
Result<sqlmesh_exec, ErrorType>
159+
> => {
57160
const projectRoot = await getProjectRoot()
58161
const workspacePath = projectRoot.uri.fsPath
59162
const interpreterDetails = await getInterpreterDetails()
@@ -72,7 +175,7 @@ export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, ErrorType>> =
72175
return err({
73176
type: "generic",
74177
message: isTcloudInstalled.error,
75-
})
178+
})
76179
}
77180
if (isTcloudInstalled.value) {
78181
const tcloudBin = await get_tcloud_bin()
@@ -88,6 +191,13 @@ export const sqlmesh_exec = async (): Promise<Result<sqlmesh_exec, ErrorType>> =
88191
type: "not_signed_in",
89192
})
90193
}
194+
const ensured = await ensureSqlmeshEnterpriseInstalled()
195+
if (isErr(ensured)) {
196+
return err({
197+
type: "generic",
198+
message: ensured.error,
199+
})
200+
}
91201
return ok({
92202
bin: `${tcloudBin.value} sqlmesh`,
93203
workspacePath,
@@ -164,15 +274,13 @@ export const sqlmesh_lsp_exec = async (): Promise<
164274
type: "not_signed_in",
165275
})
166276
}
167-
const execFileAsync = promisify(execFile)
168-
await execFileAsync(tcloudBin.value, ["install_sqlmesh"], {
169-
cwd: workspacePath,
170-
env: {
171-
PYTHONPATH: interpreterDetails.path?.[0],
172-
VIRTUAL_ENV: path.dirname(interpreterDetails.binPath!),
173-
PATH: interpreterDetails.binPath!,
174-
},
175-
})
277+
const ensured = await ensureSqlmeshEnterpriseInstalled()
278+
if (isErr(ensured)) {
279+
return err({
280+
type: "generic",
281+
message: ensured.error,
282+
})
283+
}
176284
}
177285
const binPath = path.join(interpreterDetails.binPath!, "sqlmesh_lsp")
178286
traceLog(`Bin path: ${binPath}`)

0 commit comments

Comments
 (0)