Skip to content

Commit

Permalink
fix: handle missing shells in the doctor & default commands (#315)
Browse files Browse the repository at this point in the history
* fix: handle missing shells in the doctor & default commands

Signed-off-by: Chapman Pendery <[email protected]>

* style: fmt

Signed-off-by: Chapman Pendery <[email protected]>

* fix: test mocks

Signed-off-by: Chapman Pendery <[email protected]>

---------

Signed-off-by: Chapman Pendery <[email protected]>
  • Loading branch information
cpendery authored Jan 3, 2025
1 parent 9903ce0 commit 3d90596
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/commands/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ export const action = (program: Command) => async (options: RootCommandOptions)
await setupBashPreExec();
}
await loadAliases(shell);
await render(shell, options.test ?? false, options.login ?? false);
await render(program, shell, options.test ?? false, options.login ?? false);
};
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ program
.option("-c, --check", `check if shell is in an inshellisense session`)
.addOption(hiddenOption("-T, --test", "used to make e2e tests reproducible across machines"))
.option("-V, --verbose", `enable verbose logging`)
.showHelpAfterError("(add --help for additional information)")
.passThroughOptions();

program.addCommand(complete);
Expand Down
13 changes: 12 additions & 1 deletion src/isterm/pty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import log from "../utils/log.js";
import { gitBashPath } from "../utils/shell.js";
import styles from "ansi-styles";
import * as ansi from "../utils/ansi.js";
import { Command } from "commander";
import which from "which";

const ISTermOnDataEvent = "data";

Expand Down Expand Up @@ -324,11 +326,20 @@ export class ISTerm implements IPty {
}
}

export const spawn = async (options: ISTermOptions): Promise<ISTerm> => {
export const spawn = async (program: Command, options: ISTermOptions): Promise<ISTerm> => {
const { shellTarget, shellArgs } = await convertToPtyTarget(options.shell, options.underTest, options.login);
if (!(await shellExists(shellTarget))) {
program.error(`shell not found on PATH: ${shellTarget}`, { exitCode: 1 });
}
return new ISTerm({ ...options, shellTarget, shellArgs });
};

const shellExists = async (shellTarget: string): Promise<boolean> => {
const fileExists = fs.existsSync(shellTarget);
const fileOnPath = await which(shellTarget, { nothrow: true });
return fileExists || fileOnPath != null;
};

const convertToPtyTarget = async (shell: Shell, underTest: boolean, login: boolean) => {
const platform = os.platform();
const shellTarget = shell == Shell.Bash && platform == "win32" ? await gitBashPath() : platform == "win32" ? `${shell}.exe` : shell;
Expand Down
5 changes: 4 additions & 1 deletion src/tests/isterm/pty.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import isterm from "../../isterm";
import { cursorBackward } from "../../utils/ansi";
import { Shell } from "../../utils/shell";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockProgram: any = { error: () => {} };

const windowsTest = os.platform() == "win32" ? test.skip : test.skip;
const unixTest = os.platform() == "darwin" || os.platform() == "linux" ? test.skip : test.skip;

const runTerm = async (shell: Shell, input: string[], env?: { [key: string]: string | undefined }) => {
const ptyProcess = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, env, underTest: true, login: false });
const ptyProcess = await isterm.spawn(mockProgram, { shell, rows: process.stdout.rows, cols: process.stdout.columns, env, underTest: true, login: false });
await new Promise((r) => setTimeout(r, 1_000));
for (const data of input) {
ptyProcess.write(data);
Expand Down
5 changes: 3 additions & 2 deletions src/ui/ui-root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import readline from "node:readline";
import ansi from "ansi-escapes";
import chalk from "chalk";
import { Command } from "commander";

import log from "../utils/log.js";
import { getBackspaceSequence, Shell } from "../utils/shell.js";
Expand All @@ -16,8 +17,8 @@ export const renderConfirmation = (live: boolean): string => {
return `inshellisense session [${statusMessage}]\n`;
};

export const render = async (shell: Shell, underTest: boolean, login: boolean) => {
const term = await isterm.spawn({ shell, rows: process.stdout.rows, cols: process.stdout.columns, underTest, login });
export const render = async (program: Command, shell: Shell, underTest: boolean, login: boolean) => {
const term = await isterm.spawn(program, { shell, rows: process.stdout.rows, cols: process.stdout.columns, underTest, login });
const suggestionManager = new SuggestionManager(term, shell);
let hasActiveSuggestions = false;
let previousSuggestionsRows = 0;
Expand Down
20 changes: 16 additions & 4 deletions src/utils/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ import { KeyPressEvent } from "../ui/suggestionManager.js";
import log from "./log.js";

const exec = util.promisify(childProcess.exec);
const safeExec = async (command: string, options?: childProcess.ExecOptions) => {
try {
const { stdout, stderr } = await exec(command, options);
return {
stdout: typeof stdout === "string" ? stdout : stdout.toString(),
stderr: typeof stderr === "string" ? stderr : stderr.toString(),
};
} catch (e) {
log.debug({ msg: `error executing exec command: ${e}` });
return { stdout: undefined, stderr: undefined };
}
};

export enum Shell {
Bash = "bash",
Expand Down Expand Up @@ -72,22 +84,22 @@ export const checkLegacyConfigs = async (): Promise<Shell[]> => {
return shellsWithLegacyConfig;
};

const getProfilePath = async (shell: Shell) => {
const getProfilePath = async (shell: Shell): Promise<string | undefined> => {
switch (shell) {
case Shell.Bash:
return path.join(os.homedir(), ".bashrc");
case Shell.Powershell:
return (await exec(`echo $profile`, { shell })).stdout.trim();
return (await safeExec(`echo $profile`, { shell })).stdout?.trim();
case Shell.Pwsh:
return (await exec(`echo $profile`, { shell })).stdout.trim();
return (await safeExec(`echo $profile`, { shell })).stdout?.trim();
case Shell.Zsh:
return path.join(os.homedir(), ".zshrc");
case Shell.Fish:
return path.join(os.homedir(), ".config", "fish", "config.fish");
case Shell.Xonsh:
return path.join(os.homedir(), ".xonshrc");
case Shell.Nushell:
return (await exec(`echo $nu.env-path`, { shell })).stdout.trim();
return (await safeExec(`echo $nu.env-path`, { shell })).stdout?.trim();
}
};

Expand Down

0 comments on commit 3d90596

Please sign in to comment.