Skip to content

Commit

Permalink
Look for Shadowenv in specific Homebrew path and check for untrusted …
Browse files Browse the repository at this point in the history
…workspace explicitly (#2791)

* Search for Shadowenv executable in Homebrew path first

* Explicitly check for untrusted workspace error
  • Loading branch information
vinistock authored Oct 28, 2024
1 parent 2c8ed52 commit b7dc6d2
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 34 deletions.
66 changes: 44 additions & 22 deletions vscode/src/ruby/shadowenv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ export class Shadowenv extends VersionManager {
);
}

const shadowenvExec = await this.findShadowenvExec();

try {
const parsedResult = await this.runEnvActivationScript(
"shadowenv exec -- ruby",
`${shadowenvExec} exec -- ruby`,
);

return {
Expand All @@ -34,39 +36,59 @@ export class Shadowenv extends VersionManager {
gemPath: parsedResult.gemPath,
};
} catch (error: any) {
const { stdout } = await this.runScript("command -v shadowenv");

if (stdout.trim().length === 0) {
// If the workspace is untrusted, offer to trust it for the user
if (error.message.includes("untrusted shadowenv program")) {
const answer = await vscode.window.showErrorMessage(
`Couldn't find shadowenv executable. Double-check that it's installed and that it's in your PATH.`,
"Reload window",
"Cancel",
);

if (answer === "Reload window") {
return vscode.commands.executeCommand(
"workbench.action.reloadWindow",
);
}
} else {
// If running `shadowev exec` fails, it's typically because the workspace has not been trusted yet. Here we
// offer to trust it and fail it the user decides to not the trust the workspace (since in that case, we are
// unable to activate the Ruby environment).
const answer = await vscode.window.showErrorMessage(
`Failed to run shadowenv. Is ${this.bundleUri.fsPath} trusted? Run 'shadowenv trust --help' to know more`,
`Tried to activate Shadowenv, but the workspace is untrusted.
Workspaces must be trusted to before allowing Shadowenv to load the environment for security reasons.`,
"Trust workspace",
"Cancel",
"Shutdown Ruby LSP",
);

if (answer === "Trust workspace") {
await asyncExec("shadowenv trust", { cwd: this.bundleUri.fsPath });
return this.activate();
}

throw new Error(
"Cannot activate Ruby environment in an untrusted workspace",
);
}

try {
await asyncExec("shadowenv --version");
} catch (_error: any) {
throw new Error(
`Shadowenv executable not found. Ensure it is installed and available in the PATH.
This error may happen if your shell configuration is failing to be sourced from the editor or if
another extension is mutating the process PATH.`,
);
}

// If it failed for some other reason, present the error to the user
throw new Error(
"Cannot activate Ruby environment in an untrusted workspace",
`Failed to activate Ruby environment with Shadowenv: ${error.message}`,
);
}
}

// Tries to find the Shadowenv executable either directly in known paths or in the PATH
async findShadowenvExec() {
// If we can find the Shadowenv executable in the Homebrew installation path, then prefer it over relying on the
// executable being available in the PATH. Sometimes, users might have shell scripts or other extensions that can
// mess up the PATH and then we can't find the Shadowenv executable
const possibleUris = [vscode.Uri.file("/opt/homebrew/bin/shadowenv")];

for (const uri of possibleUris) {
try {
await vscode.workspace.fs.stat(uri);
this.outputChannel.info(`Found Shadowenv executable at ${uri.fsPath}`);
return uri.fsPath;
} catch (error: any) {
// continue searching
}
}

return "shadowenv";
}
}
13 changes: 1 addition & 12 deletions vscode/src/test/suite/ruby/shadowenv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,23 +212,12 @@ suite("Shadowenv", () => {
.onFirstCall()
.rejects(new Error("shadowenv: command not found"))
.onSecondCall()
.resolves({ stdout: "", stderr: "" });

const windowStub = sinon
.stub(vscode.window, "showErrorMessage")
.resolves("Cancel" as any);
.rejects(new Error("shadowenv: command not found"));

await assert.rejects(async () => {
await shadowenv.activate();
});

assert.ok(
windowStub.calledOnceWith(
"Couldn't find shadowenv executable. Double-check that it's installed and that it's in your PATH.",
),
);

execStub.restore();
windowStub.restore();
});
});

0 comments on commit b7dc6d2

Please sign in to comment.