This commit is contained in:
thatben 2025-04-09 09:31:22 +02:00
parent 7fa7820a53
commit 7730176ecc
No known key found for this signature in database
GPG Key ID: 62C543548433D43E
7 changed files with 115 additions and 33 deletions

View File

@ -74,7 +74,7 @@ export class Installer {
};
installCodexUnix = async (processCallbacks) => {
await this.ensureUnixDependencies();
if (!await this.ensureUnixDependencies(processCallbacks)) return;
await this.shell.run(
"curl -# --connect-timeout 10 --max-time 60 -L https://get.codex.storage/install.sh -o install.sh && chmod +x install.sh",
);

View File

@ -239,43 +239,59 @@ describe("Installer", () => {
installer.runInstallerLinux = vi.fn();
});
it("ensures Unix dependencies", async () => {
it("ensures unix dependencies", async () => {
await installer.installCodexUnix(processCallbacks);
expect(installer.ensureUnixDependencies).toHaveBeenCalled();
expect(installer.ensureUnixDependencies).toHaveBeenCalled(processCallbacks);
});
it("runs the curl command to download the installer", async () => {
it("returns early if unix dependencies are not met", async () => {
installer.ensureUnixDependencies.mockResolvedValue(false);
await installer.installCodexUnix(processCallbacks);
expect(mockShellService.run).toHaveBeenCalledWith(
"curl -# --connect-timeout 10 --max-time 60 -L https://get.codex.storage/install.sh -o install.sh && chmod +x install.sh",
);
expect(processCallbacks.downloadSuccessful).not.toHaveBeenCalled();
expect(installer.runInstallerDarwin).not.toHaveBeenCalled();
expect(installer.runInstallerLinux).not.toHaveBeenCalled();
});
it("calls downloadSuccessful", async () => {
await installer.installCodexUnix(processCallbacks);
expect(processCallbacks.downloadSuccessful).toHaveBeenCalled();
});
describe("when dependencies are met", () => {
beforeEach(() =>{
installer.ensureUnixDependencies.mockResolvedValue(true);
})
it("runs installer for darwin ", async () => {
mockOsService.isDarwin.mockReturnValue(true);
await installer.installCodexUnix(processCallbacks);
expect(installer.runInstallerDarwin).toHaveBeenCalled();
});
it("runs the curl command to download the installer", async () => {
await installer.installCodexUnix(processCallbacks);
expect(mockShellService.run).toHaveBeenCalledWith(
"curl -# --connect-timeout 10 --max-time 60 -L https://get.codex.storage/install.sh -o install.sh && chmod +x install.sh",
);
});
it("runs installer for linux", async () => {
mockOsService.isDarwin.mockReturnValue(false);
await installer.installCodexUnix(processCallbacks);
expect(installer.runInstallerLinux).toHaveBeenCalled();
});
it("calls downloadSuccessful", async () => {
await installer.installCodexUnix(processCallbacks);
expect(processCallbacks.downloadSuccessful).toHaveBeenCalled();
});
it("saves the codex install path", async () => {
await installer.installCodexUnix(processCallbacks);
expect(installer.saveCodexInstallPath).toHaveBeenCalledWith("codex");
});
it("runs installer for darwin ", async () => {
mockOsService.isDarwin.mockReturnValue(true);
await installer.installCodexUnix(processCallbacks);
expect(installer.runInstallerDarwin).toHaveBeenCalled();
});
it("deletes the installer script", async () => {
await installer.installCodexUnix(processCallbacks);
expect(mockShellService.run).toHaveBeenCalledWith("rm -f install.sh");
it("runs installer for linux", async () => {
mockOsService.isDarwin.mockReturnValue(false);
await installer.installCodexUnix(processCallbacks);
expect(installer.runInstallerLinux).toHaveBeenCalled();
});
it("saves the codex install path", async () => {
await installer.installCodexUnix(processCallbacks);
expect(installer.saveCodexInstallPath).toHaveBeenCalledWith("codex");
});
it("deletes the installer script", async () => {
await installer.installCodexUnix(processCallbacks);
expect(mockShellService.run).toHaveBeenCalledWith("rm -f install.sh");
});
});
});

View File

@ -31,6 +31,9 @@ import { PathSelector } from "./utils/pathSelector.js";
import { NumberSelector } from "./utils/numberSelector.js";
import { MenuLoop } from "./utils/menuLoop.js";
import { DataDirMover } from "./utils/dataDirMover.js";
import { Installer } from "./handlers/installer.js";
import { ShellService } from "./services/shellService.js";
import { OsService } from "./services/osService.js";
async function showNavigationMenu() {
console.log("\n");
@ -100,7 +103,10 @@ export async function main() {
const fsService = new FsService();
const pathSelector = new PathSelector(uiService, new MenuLoop(), fsService);
const numberSelector = new NumberSelector(uiService);
const installMenu = new InstallMenu(uiService, configService, pathSelector);
const shellService = new ShellService();
const osService = new OsService();
const installer = new Installer(configService, shellService, osService, fsService);
const installMenu = new InstallMenu(uiService, configService, pathSelector, installer);
const configMenu = new ConfigMenu(
uiService,
new MenuLoop(),

View File

@ -1,6 +1,7 @@
import boxen from "boxen";
import chalk from "chalk";
import inquirer from "inquirer";
import { createSpinner } from "nanospinner";
import { ASCII_ART } from "../constants/ascii.js";
@ -87,4 +88,18 @@ export class UiService {
]);
return response.valueStr;
};
createAndStartSpinner = (message) => {
return createSpinner(message).start();
}
stopSpinnerSuccess = (spinner) => {
if (spinner == undefined) return;
spinner.stop();
};
stopSpinnerError = (spinner) => {
if (spinner == undefined) return;
spinner.error();
};
}

View File

@ -1,12 +1,34 @@
export class InstallMenu {
constructor(uiService, configService, pathSelector) {
constructor(uiService, configService, pathSelector, installer) {
this.ui = uiService;
this.configService = configService;
this.config = configService.get();
this.pathSelector = pathSelector;
this.installer = installer;
}
show = async () => {
if (await this.installer.isCodexInstalled()) {
await this.showUninstallMenu();
} else {
await this.showInstallMenu();
}
}
showUninstallMenu = async () => {
await this.ui.askMultipleChoice("Codex is installed", [
{
label: "Uninstall",
action: this.performUninstall,
},
{
label: "Cancel",
action: this.doNothing,
},
]);
}
showInstallMenu = async () => {
await this.ui.askMultipleChoice("Configure your Codex installation", [
{
label: "Install path: " + this.config.codexInstallPath,
@ -40,7 +62,30 @@ export class InstallMenu {
await this.show();
};
performInstall = async () => {};
performInstall = async () => {
await this.installer.installCodex(this);
};
performUninstall = async () => {};
doNothing = async () => {};
// Progress callbacks from installer module:
installStarts = () => {
this.installSpinner = this.ui.createAndStartSpinner("Installing...");
};
downloadSuccessful = () => {
this.ui.showInfoMessage("Download successful...");
}
installSuccessful = () => {
this.ui.showInfoMessage("Installation successful!");
this.ui.stopSpinnerSuccess(this.installSpinner);
}
warn = (message) => {
this.ui.showErrorMessage(message);
this.ui.stopSpinnerError(this.installSpinner);
};
}

View File

@ -19,7 +19,7 @@ export class MainMenu {
promptMainMenu = async () => {
await this.ui.askMultipleChoice("Select an option", [
{
label: "Install Codex",
label: "Install/uninstall Codex",
action: this.installMenu.show,
},
{

View File

@ -47,7 +47,7 @@ describe("mainmenu", () => {
expect(mockUiService.askMultipleChoice).toHaveBeenCalledWith(
"Select an option",
[
{ label: "Install Codex", action: mockInstallMenu.show },
{ label: "Install/uninstall Codex", action: mockInstallMenu.show },
{ label: "Configure Codex", action: mockConfigMenu.show },
{ label: "Exit", action: mockMenuLoop.stopLoop },
],