Skip to content

Commit

Permalink
feat: improve install location behavior (#86)
Browse files Browse the repository at this point in the history
* 3 cli   setup testing framework (#35)

* test: setup jest configuration with typescript

* refactor: removed path alias and changed simulator service toa class injected in the command declaration

* test: added init action adn command tests

* docs: added testing section on readme file

* fix: handle Fetch Error (Mac M3)

* refactor: improved function response

* fix: await for runSimulator async function

* fix: added init action test

* fix: solving conflicts

* test: added reset docker images and containers calls from init

* 33 cli add a warning at init to say that the config is going to be overwritten (#38)

* feat: added initial warning about reseting the setup

* test: added a test for new step introduced

* 37 cli run simulator on certain branch (#39)

* feat: added initial warning about reseting the setup

* test: added a test for new step introduced

* feat: added optional branch when fetching the simulator from github

* feat: added branch name on init and up commands

* test: added tests for branch option on init command

* 41 cli add beta releases for staging branch (#42)

* chore: added npm command for beta release

* ci: added github action for beta release

* Release v0.0.32-beta.0 [skip ci]

* 43 cli fix missing endpoints and simulator response (#44)

* fix: removed non existing simulator endpoint

* refactor: adapted ping simulator response for current and next version

* tests: removed non existing simulator endpoint test

* Release v0.0.32-beta.1 [skip ci]

* 43 cli fix missing endpoints and simulator response (#45)

* fix: removed non existing simulator endpoint

* refactor: adapted ping simulator response for current and next version

* tests: removed non existing simulator endpoint test

* Release v0.0.32-beta.2 [skip ci]

* 46 cli add node and docker version (#47)

* add node and docker version checks

* remove redundant check

* set correct version numbers, add todo

* use config constant for versions

* refactor methods and improve error handling

* implement PR comments: split install and version checks in two separate checks, move checkVersion method to simulator service

* add tests for version checks on init command

* flip version issue evalution

* separate checks in two steps

* remove console log

---------

Co-authored-by: Den <[email protected]>

* Release v0.0.32-beta.3 [skip ci]

* fix: removed unused import

* fix: removed unused import

* fix: updated license in package.json

* tests: added jest setup for globals

* feat: added location for init and up commands

* fix: fixed default location and related tests

* fix: removing genlayer-simulator folder

* fix: removing unused code

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@genlayer.com>
Co-authored-by: den <[email protected]>
Co-authored-by: Den <[email protected]>
Co-authored-by: Edinaldo Junior <[email protected]>
  • Loading branch information
5 people authored Nov 6, 2024
1 parent 8de4a76 commit 63cf9d3
Show file tree
Hide file tree
Showing 13 changed files with 79 additions and 43 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@





## 0.0.34 (2024-10-22)

## 0.0.33 (2024-10-22)
Expand All @@ -9,6 +12,8 @@

* response check ([#78](https://github.com/yeagerai/genlayer-cli/issues/78)) ([09c824f](https://github.com/yeagerai/genlayer-cli/commit/09c824f8b7ac1fff5355317d046752fbf58ab162))

## 0.0.32-beta.3 (2024-07-26)

## 0.0.32 (2024-07-15)

## 0.0.32-beta.2 (2024-07-03)
Expand Down
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: "ts-jest/presets/default-esm",
setupFiles: ["./jest.setup.js"],
};
1 change: 1 addition & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global.__dirname = "~/current/directory";
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"cli"
],
"author": "GenLayer",
"license": "ISC",
"license": "MIT",
"bugs": {
"url": "https://github.com/yeagerai/genlayer-cli/issues"
},
Expand Down
2 changes: 2 additions & 0 deletions src/commands/general/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export function initializeGeneralCommands(program: Command) {
.description("Initialize the GenLayer Environment")
.option("--numValidators <numValidators>", "Number of validators", "5")
.option("--branch <branch>", "Branch", "main")
.option("--location <folder>", "Location where it will be installed", process.cwd())
.action((options: InitActionOptions) => initAction(options, simulatorService));

program
Expand All @@ -19,6 +20,7 @@ export function initializeGeneralCommands(program: Command) {
.option("--reset-validators", "Remove all current validators and create new random ones", false)
.option("--numValidators <numValidators>", "Number of validators", "5")
.option("--branch <branch>", "Branch", "main")
.option("--location <folder>", "Location where it will be installed", process.cwd())
.action((options: StartActionOptions) => startAction(options, simulatorService));

return program;
Expand Down
6 changes: 6 additions & 0 deletions src/commands/general/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {AI_PROVIDERS_CONFIG, AiProviders} from "../../lib/config/simulator";
export interface InitActionOptions {
numValidators: number;
branch: string;
location: string;

}

function getRequirementsErrorMessage({git, docker}: Record<string, boolean>): string {
Expand Down Expand Up @@ -36,6 +38,10 @@ function getVersionErrorMessage({docker, node}: Record<string, string>): string
}

export async function initAction(options: InitActionOptions, simulatorService: ISimulatorService) {
// Update simulator location with user input
simulatorService.setSimulatorLocation(options.location);


// Check if requirements are installed
try {
const requirementsInstalled = await simulatorService.checkInstallRequirements();
Expand Down
9 changes: 7 additions & 2 deletions src/commands/general/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ export interface StartActionOptions {
resetValidators: string;
numValidators: number;
branch: string;
location: string;
}

export async function startAction(options: StartActionOptions, simulatorService: ISimulatorService) {
const {resetValidators, numValidators, branch} = options;
const {resetValidators, numValidators, branch, location} = options;
// Update simulator location with user input
simulatorService.setSimulatorLocation(location);


const restartValidatorsHintText = resetValidators
? `creating new ${numValidators} random validators`
Expand Down Expand Up @@ -96,7 +100,8 @@ export async function startAction(options: StartActionOptions, simulatorService:
`GenLayer simulator initialized successfully! Go to ${simulatorService.getFrontendUrl()} in your browser to access it.`,
);
try {
simulatorService.openFrontend();
await simulatorService.openFrontend();

} catch (error) {
console.error(error);
}
Expand Down
5 changes: 0 additions & 5 deletions src/lib/clients/system.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import util from "node:util";
import {ChildProcess, PromiseWithChild, exec} from "child_process";
import os from "os";
import open from "open";

import {RunningPlatform, AVAILABLE_PLATFORMS} from "../config/simulator";
Expand Down Expand Up @@ -38,10 +37,6 @@ export function executeCommand(
}
}

export function getHomeDirectory(): string {
return os.homedir();
}

function getPlatform(): RunningPlatform {
const currentPlatform = process.platform as RunningPlatform;
if (!AVAILABLE_PLATFORMS.includes(currentPlatform)) {
Expand Down
3 changes: 1 addition & 2 deletions src/lib/interfaces/ISimulatorService.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {AiProviders} from "../config/simulator";

export interface ISimulatorService {
setSimulatorLocation(location: string): void;
getSimulatorLocation(): string;
readEnvConfigValue(key: string): string;
addConfigToEnvFile(newConfig: Record<string, string>): void;
checkInstallRequirements(): Promise<Record<string, boolean>>;
checkVersionRequirements(): Promise<Record<string, string>>;
downloadSimulator(branch?: string): Promise<DownloadSimulatorResultType>;
Expand Down
57 changes: 29 additions & 28 deletions src/lib/services/simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
import {
checkCommand,
getVersion,
getHomeDirectory,
executeCommand,
openUrl,
listDockerContainers,
Expand All @@ -34,7 +33,6 @@ import {
ISimulatorService,
DownloadSimulatorResultType,
WaitForSimulatorToBeReadyResultType,
InitializeDatabaseResultType,
} from "../interfaces/ISimulatorService";
import {VersionRequiredError} from "../errors/versionRequired";

Expand All @@ -43,21 +41,29 @@ function sleep(millliseconds: number): Promise<void> {
}

export class SimulatorService implements ISimulatorService {
public simulatorLocation: string;

constructor() {
this.simulatorLocation = "";
}

public setSimulatorLocation(location: string): void {
this.simulatorLocation = location;
}

public getSimulatorLocation(): string {
return path.join(getHomeDirectory(), "genlayer-simulator");
return this.simulatorLocation;
}

public readEnvConfigValue(key: string): string {
const simulatorLocation = this.getSimulatorLocation();
const envFilePath = path.join(simulatorLocation, ".env");
private readEnvConfigValue(key: string): string {
const envFilePath = path.join(this.simulatorLocation, ".env");
// Transform the config string to object
const envConfig = dotenv.parse(fs.readFileSync(envFilePath, "utf8"));
return envConfig[key];
}

public addConfigToEnvFile(newConfig: Record<string, string>): void {
const simulatorLocation = this.getSimulatorLocation();
const envFilePath = path.join(simulatorLocation, ".env");
private addConfigToEnvFile(newConfig: Record<string, string>): void {
const envFilePath = path.join(this.simulatorLocation, ".env");

// Create a backup of the original .env file
fs.writeFileSync(`${envFilePath}.bak`, fs.readFileSync(envFilePath));
Expand Down Expand Up @@ -103,6 +109,7 @@ export class SimulatorService implements ISimulatorService {
}
}


if (requirementsInstalled.docker) {
try {
await checkCommand("docker ps", "docker");
Expand All @@ -116,8 +123,8 @@ export class SimulatorService implements ISimulatorService {

public async checkVersionRequirements(): Promise<Record<string, string>> {
const missingVersions = {
docker: '',
node: '',
docker: "",
node: "",
};

try {
Expand Down Expand Up @@ -150,14 +157,12 @@ export class SimulatorService implements ISimulatorService {
}

public async downloadSimulator(branch: string = "main"): Promise<DownloadSimulatorResultType> {
const simulatorLocation = this.getSimulatorLocation();

try {
const gitCommand = `git clone -b ${branch} ${DEFAULT_REPO_GH_URL} ${simulatorLocation}`;
const gitCommand = `git clone -b ${branch} ${DEFAULT_REPO_GH_URL} ${this.simulatorLocation}`;
const cmdsByPlatform = {darwin: gitCommand, win32: gitCommand, linux: gitCommand};
await executeCommand(cmdsByPlatform, "git");
} catch (error: any) {
const simulatorLocationExists = fs.existsSync(simulatorLocation);
const simulatorLocationExists = fs.existsSync(this.simulatorLocation);
if (simulatorLocationExists) {
return {wasInstalled: true};
}
Expand All @@ -167,48 +172,44 @@ export class SimulatorService implements ISimulatorService {
}

public async updateSimulator(branch: string = "main"): Promise<boolean> {
const simulatorLocation = this.getSimulatorLocation();
const gitCleanCommand = `git -C "${simulatorLocation}" clean -f`;
const gitCleanCommand = `git -C "${this.simulatorLocation}" clean -f`;
const cleanCmdsByPlatform = {darwin: gitCleanCommand, win32: gitCleanCommand, linux: gitCleanCommand};
await executeCommand(cleanCmdsByPlatform, "git");

const gitFetchCommand = `git -C "${simulatorLocation}" fetch`;
const gitFetchCommand = `git -C "${this.simulatorLocation}" fetch`;
const fetchCmdsByPlatform = {darwin: gitFetchCommand, win32: gitFetchCommand, linux: gitFetchCommand};
await executeCommand(fetchCmdsByPlatform, "git");

const gitCheckoutCommand = `git -C "${simulatorLocation}" checkout ${branch}`;
const gitCheckoutCommand = `git -C "${this.simulatorLocation}" checkout ${branch}`;
const checkoutCmdsByPlatform = {
darwin: gitCheckoutCommand,
win32: gitCheckoutCommand,
linux: gitCheckoutCommand,
};
await executeCommand(checkoutCmdsByPlatform, "git");

const gitPullCommand = `git -C "${simulatorLocation}" pull`;
const gitPullCommand = `git -C "${this.simulatorLocation}" pull`;
const pullCmdsByPlatform = {darwin: gitPullCommand, win32: gitPullCommand, linux: gitPullCommand};
await executeCommand(pullCmdsByPlatform, "git");
return true;
}

public async pullOllamaModel(): Promise<boolean> {
const simulatorLocation = this.getSimulatorLocation();
const cmdsByPlatform = DEFAULT_PULL_OLLAMA_COMMAND(simulatorLocation);
const cmdsByPlatform = DEFAULT_PULL_OLLAMA_COMMAND(this.simulatorLocation);
await executeCommand(cmdsByPlatform);
return true;
}

public async configSimulator(newConfig: Record<string, string>): Promise<boolean> {
const simulatorLocation = this.getSimulatorLocation();
const envExample = path.join(simulatorLocation, ".env.example");
const envFilePath = path.join(simulatorLocation, ".env");
const envExample = path.join(this.simulatorLocation, ".env.example");
const envFilePath = path.join(this.simulatorLocation, ".env");
fs.copyFileSync(envExample, envFilePath);
this.addConfigToEnvFile(newConfig);
return true;
}

public runSimulator(): Promise<{stdout: string; stderr: string}> {
const simulatorLocation = this.getSimulatorLocation();
const commandsByPlatform = DEFAULT_RUN_SIMULATOR_COMMAND(simulatorLocation);
const commandsByPlatform = DEFAULT_RUN_SIMULATOR_COMMAND(this.simulatorLocation);
return executeCommand(commandsByPlatform);
}

Expand All @@ -220,7 +221,7 @@ export class SimulatorService implements ISimulatorService {
const response = await rpcClient.request({method: "ping", params: []});

//Compatibility with current simulator version
if (response && (response.result === "OK" || response.result.status === "OK" || response.result.data.status === "OK")) {
if (response && (response.result.status === "OK" || response.result.data.status === "OK")) {
return {initialized: true};
}
if (retries > 0) {
Expand Down
15 changes: 12 additions & 3 deletions tests/actions/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import inquirer from "inquirer";
import simulatorService from "../../src/lib/services/simulator";
import {initAction} from "../../src/commands/general/init";

const __dirname = "/current-dir";
// Default options for the action
const defaultActionOptions = {numValidators: 5, branch: "main"};
const defaultActionOptions = {numValidators: 5, branch: "main", location: __dirname};


describe("init action", () => {
let error: jest.Mock<any>;
Expand Down Expand Up @@ -36,8 +38,15 @@ describe("init action", () => {
log = jest.spyOn(console, "log").mockImplementation(() => {}) as jest.Mock<any>;
inquirerPrompt = jest.spyOn(inquirer, "prompt") as jest.Mock<any>;

simServCheckInstallRequirements = jest.spyOn(simulatorService, "checkInstallRequirements") as jest.Mock<any>;
simServCheckVersionRequirements = jest.spyOn(simulatorService, "checkVersionRequirements") as jest.Mock<any>;
simServCheckInstallRequirements = jest.spyOn(
simulatorService,
"checkInstallRequirements",
) as jest.Mock<any>;
simServCheckVersionRequirements = jest.spyOn(
simulatorService,
"checkVersionRequirements",
) as jest.Mock<any>;

simServResetDockerContainers = jest.spyOn(simulatorService, "resetDockerContainers") as jest.Mock<any>;
simServResetDockerImages = jest.spyOn(simulatorService, "resetDockerImages") as jest.Mock<any>;
simServDownloadSimulator = jest.spyOn(simulatorService, "downloadSimulator") as jest.Mock<any>;
Expand Down
14 changes: 13 additions & 1 deletion tests/commands/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ describe("init command", () => {
expect(numValidatorsOption?.defaultValue).toBe("main");
});

test("option --location is accepted", async () => {
expect(() => program.parse(["node", "test", "init", "--location", "./current-dir"])).not.toThrow();
});

test("option --location default value is user's current directory", async () => {
// Given // When
const locationOption = getCommandOption(initCommand, "--location");
expect(locationOption?.defaultValue).toBe(process.cwd());
});


test("random option is not accepted", async () => {
initCommand?.exitOverride();
expect(() => program.parse(["node", "test", "init", "-random"])).toThrow(
Expand All @@ -67,6 +78,7 @@ describe("init command", () => {
program.parse(["node", "test", "init"]);
// Then
expect(action).toHaveBeenCalledTimes(1);
expect(action).toHaveBeenCalledWith({numValidators: "5", branch: "main"});
expect(action).toHaveBeenCalledWith({numValidators: "5", branch: "main", location: process.cwd()});

});
});

0 comments on commit 63cf9d3

Please sign in to comment.