Skip to content

Commit

Permalink
(#85) Publish VSCode extension 0.1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
mario4tier committed May 6, 2024
1 parent ad0da35 commit bf51d9e
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 26 deletions.
6 changes: 4 additions & 2 deletions typescript/vscode-extension/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ All notable changes to the "suibase" extension will be documented in this file.

Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.

## [Unreleased]

## 0.1.4
- Warn user if some Sui prerequisites are not installed (git and rustc)
- Start suibase-daemon if not running.

## 0.1.2
- Warn about requiring suibase to be installed.

2 changes: 1 addition & 1 deletion typescript/vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"license": "Apache-2.0",
"icon": "media/logo_128.png",
"version": "0.1.3",
"version": "0.1.4",
"repository": {
"type": "git",
"url": "https://github.com/ChainMovers/suibase.git"
Expand Down
48 changes: 42 additions & 6 deletions typescript/vscode-extension/src/BackendSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
UpdateWorkdirStatus,
} from "./common/ViewMessages";
import { SuibaseJson, SuibaseJsonVersions } from "./common/SuibaseJson";
import { SuibaseExec } from "./SuibaseExec";

// One instance per workdir, instantiated in same size and order as WORKDIRS_KEYS.
class BackendWorkdirTracking {
Expand Down Expand Up @@ -120,7 +121,11 @@ export class BackendSync {

private async asyncLoop(forceRefresh: boolean): Promise<void> {
await this.loopMutex.runExclusive(async () => {
await this.update(forceRefresh);
try {
await this.update(forceRefresh);
} catch (error) {
console.error(`Catch done in asyncLoop: ${JSON.stringify(error)}`);
}

if (forceRefresh === false) {
// Schedule another call in one second.
Expand Down Expand Up @@ -213,13 +218,37 @@ export class BackendSync {
}
}

private diagnoseBackendError(workdirIdx: number) {
private async diagnoseBackendError(workdirIdx: number) {
// This is a helper function to diagnose the backend error.
//
// It may attempts fixes, and it is assumed the caller will
// retry to contact the backend periodically until success.
//
// It will send a message to the views to display the error message.
// For now, always broadcast a message to all views that the backend is not responding.
// For now, always broadcast problems to all views.
this.mForceRefreshOnNextReconnect = true;

const msg = new UpdateVersions(WEBVIEW_BACKEND, workdirIdx, undefined);
msg.setSetupIssue("Suibase not responding. Is it installed?");

const sb = SuibaseExec.getInstance();
if (sb === undefined) {
msg.setSetupIssue("Internal error. Shell commands failed.");
} else if ((await sb.isSuibaseInstalled()) === false) {
msg.setSetupIssue("Suibase not installed?\nCheck https://suibase.io/how-to/install");
} else if ((await sb.isGitInstalled()) === false) {
msg.setSetupIssue(
"Git not installed?\nPlease install Sui prerequisites\nhttps://docs.sui.io/guides/developer/getting-started/sui-install"
);
} else if ((await sb.isSuibaseBackendRunning()) === false) {
if ((await sb.startDaemon()) === true) {
msg.setSetupIssue("Suibase initializing...");
} else {
msg.setSetupIssue("Suibase backend not starting");
}
} else {
msg.setSetupIssue("Suibase backend not responding");
}

BaseWebview.broadcastMessage(msg);
}

Expand All @@ -232,14 +261,17 @@ export class BackendSync {
for (let workdirIdx = 0; workdirIdx < WORKDIRS_KEYS.length; workdirIdx++) {
const workdir = WORKDIRS_KEYS[workdirIdx];
let data = undefined;

try {
data = await this.fetchGetVersions(workdir);
} catch (error) {
this.diagnoseBackendError(workdirIdx);
await this.diagnoseBackendError(workdirIdx);
return;
}

if (data) {
try {
//console.log("update versions: ", JSON.stringify(data));
// This is an example of data:
// {"jsonrpc":"2.0","result":{
// "header":{"method":"getVersions", "methodUuid":"...","dataUuid":"...","key":"localnet"},
Expand All @@ -258,6 +290,7 @@ export class BackendSync {
// The views will then decide if they need to synchronize further with the extension.
if (hasChanged || forceRefresh || this.mForceRefreshOnNextReconnect) {
this.mForceRefreshOnNextReconnect = false;

BaseWebview.broadcastMessage(new UpdateVersions(WEBVIEW_BACKEND, workdirIdx, data.result));
}
} catch (error) {
Expand Down Expand Up @@ -298,6 +331,7 @@ export class BackendSync {
// {"label":"Multi-link RPC","status":"OK","statusInfo":null,"helpInfo":null,"pid":null}]},"id":2}
//
// Update the SuibaseJson instance for the workdir.
//console.log("replyWorkdirStatus: ", JSON.stringify(data));
BaseWebview.postMessageTo(sender, new UpdateWorkdirStatus(WEBVIEW_BACKEND, workdirIdx, data));
} catch (error) {
const errorMsg = `Error in replyWorkdirStatus: ${JSON.stringify(error)}. Data: ${JSON.stringify(data)}`;
Expand Down Expand Up @@ -342,7 +376,9 @@ export class BackendSync {
// Update the SuibaseJson instance for the workdir.
BaseWebview.postMessageTo(sender, new UpdateWorkdirPackages(WEBVIEW_BACKEND, workdirIdx, data));
} catch (error) {
const errorMsg = `Error in replyWorkdirPackages: ${JSON.stringify(error)}. Data: ${JSON.stringify(data)}`;
const errorMsg = `Error in replyWorkdirPackages: ${JSON.stringify(error)}. Data: ${JSON.stringify(
data
)}`;
console.error(errorMsg);
//throw new Error(errorMsg);
}
Expand Down
108 changes: 91 additions & 17 deletions typescript/vscode-extension/src/SuibaseExec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ const execShell = (cmd: string) =>
});
});

const execShellBackground = (cmd: string) =>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
new Promise<string>((resolve, _reject) => {
cp.exec(cmd, (err, stdout, stderr) => {
if (err) {
console.warn(err);
}
resolve(stdout ? stdout : stderr);
});
});

export class SuibaseExec {
private static instance?: SuibaseExec;
private static context?: vscode.ExtensionContext;
Expand Down Expand Up @@ -93,20 +104,56 @@ export class SuibaseExec {
return SuibaseExec.instance;
}

public async version(): Promise<string> {
public async isRustInstalled(): Promise<boolean> {
// Returns true if the rust compiler can be call.
// Returns false on any error.
try {
const result = await execShell("localnet --version");
console.log(result);
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
const result = await execShell("rustc --version");
if (result.startsWith("rustc") && !result.includes("error")) {
return true;
}
} catch (error) {
console.error("rustc not installed");
}
return false;
}

private async startDaemon() {
// Check if suibase-daemon is running, if not, attempt
// to start it and return once confirmed ready to
// process requests.
public async isGitInstalled(): Promise<boolean> {
// Returns true if the git can be call.
// Returns false on any error.
try {
const result = await execShell("git --version");
if (result.startsWith("git") && !result.includes("error")) {
return true;
}
} catch (error) {
console.error("git not installed");
}
return false;
}

public async isSuibaseInstalled(): Promise<boolean> {
// Verify if Suibase itself is installed.
// Returns true if all the following files exists:
// ~/suibase/install
// ~/suibase/scripts/common/run-daemon.sh
try {
let result = await execShell("ls ~/suibase/install");
if (result.includes("suibase/install")) {
return true;
}
result = await execShell("ls ~/suibase/scripts/common/run-daemon.sh");
if (result.includes("suibase/scripts/common/run-daemon.sh")) {
return true;
}
} catch (error) {
console.error("suibase not installed");
}
return false;
}

public async isSuibaseBackendRunning(): Promise<boolean> {
// Returns true if suibase-daemon is running.
let suibaseRunning = false;
try {
const result = await execShell("lsof /tmp/.suibase/suibase-daemon.lock");
Expand All @@ -122,18 +169,45 @@ export class SuibaseExec {
} catch (err) {
/* Do nothing */
}
return suibaseRunning;
}

if (!suibaseRunning) {
// Start suibase daemon
await execShell("~/suibase/scripts/common/run-daemon.sh suibase &");
public async version(): Promise<string> {
try {
const result = await execShell("localnet --version");
console.log(result);
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
}
}

// TODO Implement retry and error handling of run-daemon.sh for faster startup.
public async startDaemon(): Promise<boolean> {
// Check if suibase-daemon is running, if not, attempt
// to start it and return once confirmed ready to
// process requests.
let suibaseRunning = await this.isSuibaseBackendRunning();

// Sleep 500 milliseconds to give it a chance to start.
await new Promise((r) => setTimeout(r, 500));
if (!suibaseRunning) {
// Start suibase daemon
void execShellBackground("~/suibase/scripts/common/run-daemon.sh suibase");

// Check for up to ~5 seconds that it is started.
let attempts = 10;
while (!suibaseRunning && attempts > 0) {
// Sleep 500 millisecs to give it a chance to start.
await new Promise((r) => setTimeout(r, 500));
suibaseRunning = await this.isSuibaseBackendRunning();
attempts--;
}
}

// TODO Confirm that suibase-daemon is responding to requests.
if (suibaseRunning) {
return true;
}

console.error("Failed to start suibase.daemon");
return false;
}

private makeJsonRpcCall() {
Expand Down

0 comments on commit bf51d9e

Please sign in to comment.