11import path from "path"
2- import { traceLog , traceVerbose } from "../common/log"
2+ import { traceInfo , traceLog , traceVerbose } from "../common/log"
33import { getInterpreterDetails } from "../common/python"
44import { Result , err , isErr , ok } from "../functional/result"
55import { getProjectRoot } from "../common/utilities"
6- import { execFile } from "child_process"
7- import { promisify } from "util"
86import { isPythonModuleInstalled } from "../python"
97import fs from "fs"
108import { ErrorType } from "../errors"
119import { isSignedIntoTobikoCloud } from "../../auth/auth"
10+ import { execAsync } from "../exec"
11+ import z from "zod"
12+ import { ProgressLocation , window } from "vscode"
1213
1314export 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