Skip to content

Commit 4b3e2bb

Browse files
authored
chore(vscode): handle tcloud not being present better (#4424)
1 parent d0129bb commit 4b3e2bb

5 files changed

Lines changed: 77 additions & 36 deletions

File tree

vscode/extension/src/auth/auth.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { execAsync } from '../utilities/exec'
1414
import { getProjectRoot } from '../utilities/common/utilities'
1515
import z from 'zod'
1616
import { traceError } from '../utilities/common/log'
17+
import { ErrorType } from '../utilities/errors'
1718

1819
export const AUTH_TYPE = 'tobikodata'
1920
export const AUTH_NAME = 'Tobiko'
@@ -70,11 +71,11 @@ export class AuthenticationProviderTobikoCloud
7071
* Get the status of the authentication provider from the cli
7172
* @returns true if the user is logged in with the id token, false otherwise
7273
*/
73-
private async get_status(): Promise<Result<StatusResponse, string>> {
74+
private async get_status(): Promise<Result<StatusResponse, ErrorType>> {
7475
const workspacePath = await getProjectRoot()
7576
const tcloudBin = await getTcloudBin()
7677
if (isErr(tcloudBin)) {
77-
return err(tcloudBin.error)
78+
return tcloudBin
7879
}
7980
const tcloudBinPath = tcloudBin.value
8081
const result = await execAsync(
@@ -85,7 +86,10 @@ export class AuthenticationProviderTobikoCloud
8586
},
8687
)
8788
if (result.exitCode !== 0) {
88-
return err('Failed to get tcloud auth status')
89+
return err({
90+
type: 'generic',
91+
message: 'Failed to get tcloud auth status',
92+
})
8993
}
9094
const status = result.stdout
9195
const statusToJson: any = JSON.parse(status)

vscode/extension/src/commands/format.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
handleNotSginedInError,
88
handleSqlmeshLspNotFoundError,
99
handleSqlmeshLspDependenciesMissingError,
10+
handleTcloudBinNotFoundError,
1011
} from '../utilities/errors'
1112
import { AuthenticationProviderTobikoCloud } from '../auth/auth'
1213
import { execAsync } from '../utilities/exec'
@@ -27,6 +28,9 @@ export const format =
2728
case 'sqlmesh_lsp_dependencies_missing':
2829
await handleSqlmeshLspDependenciesMissingError(out.error)
2930
return
31+
case 'tcloud_bin_not_found':
32+
await handleTcloudBinNotFoundError()
33+
return
3034
case 'generic':
3135
await vscode.window.showErrorMessage(
3236
`Project format failed: ${out.error.message}`,

vscode/extension/src/extension.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
handleNotSginedInError,
1818
handleSqlmeshLspNotFoundError,
1919
handleSqlmeshLspDependenciesMissingError,
20+
handleTcloudBinNotFoundError,
2021
} from './utilities/errors'
2122
import { selector, completionProvider } from './completion/completion'
2223
import { LineagePanel } from './webviews/lineagePanel'
@@ -94,6 +95,9 @@ export async function activate(context: vscode.ExtensionContext) {
9495
case 'sqlmesh_lsp_dependencies_missing':
9596
await handleSqlmeshLspDependenciesMissingError(restartResult.error)
9697
return
98+
case 'tcloud_bin_not_found':
99+
await handleTcloudBinNotFoundError()
100+
return
97101
case 'generic':
98102
await vscode.window.showErrorMessage(
99103
`Failed to restart LSP: ${restartResult.error.message}`,
@@ -129,6 +133,9 @@ export async function activate(context: vscode.ExtensionContext) {
129133
case 'sqlmesh_lsp_dependencies_missing':
130134
await handleSqlmeshLspDependenciesMissingError(result.error)
131135
break
136+
case 'tcloud_bin_not_found':
137+
await handleTcloudBinNotFoundError()
138+
break
132139
case 'generic':
133140
await vscode.window.showErrorMessage(
134141
`Failed to start LSP: ${result.error.message}`,

vscode/extension/src/utilities/errors.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ export type ErrorType =
1010
| { type: 'generic'; message: string }
1111
| { type: 'not_signed_in' }
1212
| { type: 'sqlmesh_lsp_not_found' }
13+
// tcloud_bin_not_found is used when the tcloud executable is not found. This is likely to happen if the user
14+
// opens a project that has a `tcloud.yaml` file but doesn't have tcloud installed.
15+
| { type: 'tcloud_bin_not_found' }
1316
// sqlmesh_lsp_dependencies_missing is used when the sqlmesh_lsp is found but the lsp extras are missing.
1417
| SqlmeshLspDependenciesMissingError
1518

@@ -73,3 +76,21 @@ export const handleSqlmeshLspDependenciesMissingError = async (
7376
}
7477
}
7578
}
79+
80+
/**
81+
* Handles the case where the tcloud executable is not found.
82+
*/
83+
export const handleTcloudBinNotFoundError = async (): Promise<void> => {
84+
const result = await window.showErrorMessage(
85+
'tcloud executable not found, please check installation',
86+
'Install',
87+
)
88+
if (result === 'Install') {
89+
const terminal = window.createTerminal({
90+
name: 'Tcloud Install',
91+
hideFromUser: false,
92+
})
93+
terminal.show()
94+
terminal.sendText("pip install tcloud", false)
95+
}
96+
}

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

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,36 @@ export interface SqlmeshExecInfo {
2828
export const isTcloudProject = async (): Promise<Result<boolean, string>> => {
2929
const projectRoot = await getProjectRoot()
3030
const tcloudYamlPath = path.join(projectRoot.uri.fsPath, 'tcloud.yaml')
31-
if (fs.existsSync(tcloudYamlPath)) {
31+
const isTcloudYamlFilePresent = fs.existsSync(tcloudYamlPath)
32+
if (isTcloudYamlFilePresent) {
33+
traceVerbose(`tcloud yaml file present at : ${tcloudYamlPath}`)
3234
return ok(true)
3335
}
34-
return isPythonModuleInstalled('tcloud')
36+
const isTcloudInstalled = await isPythonModuleInstalled('tcloud')
37+
if (isErr(isTcloudInstalled)) {
38+
return isTcloudInstalled
39+
}
40+
traceVerbose(`tcloud is installed: ${isTcloudInstalled.value}`)
41+
return ok(isTcloudInstalled.value)
3542
}
3643

3744
/**
3845
* Get the tcloud executable for the current Python environment.
3946
*
4047
* @returns The tcloud executable for the current Python environment.
4148
*/
42-
export const getTcloudBin = async (): Promise<Result<string, string>> => {
49+
export const getTcloudBin = async (): Promise<Result<string, ErrorType>> => {
4350
const interpreterDetails = await getInterpreterDetails()
4451
if (!interpreterDetails.path) {
45-
return err('No Python interpreter found')
52+
return err({
53+
type: 'tcloud_bin_not_found',
54+
})
4655
}
4756
const pythonPath = interpreterDetails.path[0]
4857
const binPath = path.join(path.dirname(pythonPath), 'tcloud')
58+
if (!fs.existsSync(binPath)) {
59+
return err({type: 'tcloud_bin_not_found'})
60+
}
4961
return ok(binPath)
5062
}
5163

@@ -59,24 +71,26 @@ const isSqlmeshInstalledSchema = z.object({
5971
* @returns A Result indicating whether sqlmesh enterprise is installed and updated.
6072
*/
6173
export const isSqlmeshEnterpriseInstalled = async (): Promise<
62-
Result<boolean, string>
74+
Result<boolean, ErrorType>
6375
> => {
6476
traceInfo('Checking if sqlmesh enterprise is installed')
6577
const tcloudBin = await getTcloudBin()
6678
if (isErr(tcloudBin)) {
67-
return err(tcloudBin.error)
79+
return tcloudBin
6880
}
6981
const called = await execAsync(tcloudBin.value, ['is_sqlmesh_installed'])
7082
if (called.exitCode !== 0) {
71-
return err(
72-
`Failed to check if sqlmesh enterprise is installed: ${called.stderr}`,
73-
)
83+
return err({
84+
type: 'generic',
85+
message: `Failed to check if sqlmesh enterprise is installed: ${called.stderr}`,
86+
})
7487
}
7588
const parsed = isSqlmeshInstalledSchema.safeParse(JSON.parse(called.stdout))
7689
if (!parsed.success) {
77-
return err(
78-
`Failed to parse sqlmesh enterprise installation status: ${parsed.error.message}`,
79-
)
90+
return err({
91+
type: 'generic',
92+
message: `Failed to parse sqlmesh enterprise installation status: ${parsed.error.message}`,
93+
})
8094
}
8195
return ok(parsed.data.is_installed)
8296
}
@@ -88,16 +102,19 @@ export const isSqlmeshEnterpriseInstalled = async (): Promise<
88102
*/
89103
export const installSqlmeshEnterprise = async (
90104
abortController: AbortController,
91-
): Promise<Result<boolean, string>> => {
105+
): Promise<Result<boolean, ErrorType>> => {
92106
const tcloudBin = await getTcloudBin()
93107
if (isErr(tcloudBin)) {
94-
return err(tcloudBin.error)
108+
return tcloudBin
95109
}
96110
const called = await execAsync(tcloudBin.value, ['install_sqlmesh'], {
97111
signal: abortController.signal,
98112
})
99113
if (called.exitCode !== 0) {
100-
return err(`Failed to install sqlmesh enterprise: ${called.stderr}`)
114+
return err({
115+
type: 'generic',
116+
message: `Failed to install sqlmesh enterprise: ${called.stderr}`,
117+
})
101118
}
102119
return ok(true)
103120
}
@@ -109,12 +126,12 @@ export const installSqlmeshEnterprise = async (
109126
* @returns A Result indicating whether sqlmesh enterprise was installed in the call.
110127
*/
111128
export const ensureSqlmeshEnterpriseInstalled = async (): Promise<
112-
Result<boolean, string>
129+
Result<boolean, ErrorType>
113130
> => {
114131
traceInfo('Ensuring sqlmesh enterprise is installed')
115132
const isInstalled = await isSqlmeshEnterpriseInstalled()
116133
if (isErr(isInstalled)) {
117-
return err(isInstalled.error)
134+
return isInstalled
118135
}
119136
if (isInstalled.value) {
120137
traceInfo('Sqlmesh enterprise is installed')
@@ -180,10 +197,7 @@ export const sqlmeshExec = async (): Promise<
180197
if (isTcloudInstalled.value) {
181198
const tcloudBin = await getTcloudBin()
182199
if (isErr(tcloudBin)) {
183-
return err({
184-
type: 'generic',
185-
message: tcloudBin.error,
186-
})
200+
return tcloudBin
187201
}
188202
const isSignedIn = await isSignedIntoTobikoCloud()
189203
if (!isSignedIn) {
@@ -193,10 +207,7 @@ export const sqlmeshExec = async (): Promise<
193207
}
194208
const ensured = await ensureSqlmeshEnterpriseInstalled()
195209
if (isErr(ensured)) {
196-
return err({
197-
type: 'generic',
198-
message: ensured.error,
199-
})
210+
return ensured
200211
}
201212
return ok({
202213
bin: `${tcloudBin.value} sqlmesh`,
@@ -303,10 +314,7 @@ export const sqlmeshLspExec = async (): Promise<
303314
traceLog('Tcloud installed, installing sqlmesh')
304315
const tcloudBin = await getTcloudBin()
305316
if (isErr(tcloudBin)) {
306-
return err({
307-
type: 'generic',
308-
message: tcloudBin.error,
309-
})
317+
return tcloudBin
310318
}
311319
const isSignedIn = await isSignedIntoTobikoCloud()
312320
if (!isSignedIn) {
@@ -316,10 +324,7 @@ export const sqlmeshLspExec = async (): Promise<
316324
}
317325
const ensured = await ensureSqlmeshEnterpriseInstalled()
318326
if (isErr(ensured)) {
319-
return err({
320-
type: 'generic',
321-
message: ensured.error,
322-
})
327+
return ensured
323328
}
324329
}
325330
const ensuredDependencies = await ensureSqlmeshLspDependenciesInstalled()

0 commit comments

Comments
 (0)