Skip to content

Commit

Permalink
feat: rename shell to run-on-golem and fix help
Browse files Browse the repository at this point in the history
JST-498
  • Loading branch information
pgrzy-golem committed Oct 17, 2023
1 parent a0ee301 commit 26e99b3
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 61 deletions.
4 changes: 2 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Command } from "commander";
import { version } from "./lib/version";
import { manifestCommand } from "./manifest/manifest.command";
import { newCommand } from "./new/new.command";
import { shellCommand } from "./shell/shell.command";
import { runOnGolemCommand } from "./run-on-golem/run-on-golem.command";

const program = new Command("golem-sdk");
program.version(version);
Expand All @@ -12,6 +12,6 @@ program.version(version);
// chalk.level = 0;
// });

program.addCommand(manifestCommand).addCommand(newCommand).addCommand(shellCommand);
program.addCommand(manifestCommand).addCommand(newCommand).addCommand(runOnGolemCommand);

program.parse();
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ShellOptions } from "./shell.options";
import { RunOnGolemOptions } from "./run-on-golem.options";
import { createInterface } from "readline/promises";
import { parse, ParseEntry } from "shell-quote";
import { CommanderError } from "commander";
import { ParseError, ShellError } from "./shell.errors";
import { ParseError, ShellError } from "./errors";
import { ExecutorOptions, TaskExecutor } from "@golem-sdk/golem-js";
import { assertFileExists } from "../lib/file";
import { assertFileExists, checkFileExists } from "../lib/file";
import { readFile } from "fs/promises";
import { TaskAPIContext, VarsType } from "./shell-context";
import * as fs from "fs";
Expand Down Expand Up @@ -157,7 +157,7 @@ function execConsole(context: TaskAPIContext): Promise<void> {
input: process.stdin,
output: process.stdout,
terminal: true, // process.stdout.isTTY,
prompt: "cmd> ",
prompt: "golem> ",
});
rl.prompt();
rl.on("line", (line) => {
Expand All @@ -175,7 +175,7 @@ function execConsole(context: TaskAPIContext): Promise<void> {
});
}

async function createExecutor(options: ShellOptions) {
async function createExecutor(options: RunOnGolemOptions) {
const timeout = options.timeout ? parseInt(options.timeout, 10) : 60 * 60;
const opts: ExecutorOptions = {
taskTimeout: 1000 * timeout,
Expand All @@ -186,7 +186,18 @@ async function createExecutor(options: ShellOptions) {
opts.package = options.image;
} else {
console.log(`Creating task executor using manifest file ${options.manifest}`);
await assertFileExists("Manifest file", options.manifest);
if (!(await checkFileExists("Manifest file", options.manifest))) {
console.log("You can create a manifest file by running:\n\n\tgolem-sdk manifest create\n");
console.log(
"You can specify a manifest file to use by using the --manifest option:" +
"\n\n\tgolem-sdk run-on-golem --manifest <manifest>\n",
);
console.log(
"If you want to use an image directly instead of a manifest file, use the --image option:" +
"\n\n\tgolem-sdk run-on-golem --image <image>\n",
);
process.exit(1);
}

// TODO: add option for signed manifests.
opts.manifest = (await readFile(options.manifest)).toString("base64");
Expand All @@ -195,7 +206,7 @@ async function createExecutor(options: ShellOptions) {
return TaskExecutor.create(opts);
}

export async function shellAction(files: string[], options: ShellOptions) {
export async function runOnGolemAction(files: string[], options: RunOnGolemOptions) {
// Prepare shell variables.
const vars: VarsType = {
...(options.env ? process.env : {}),
Expand All @@ -207,15 +218,15 @@ export async function shellAction(files: string[], options: ShellOptions) {

// Handle SIGINT and SIGTERM.
process.on("SIGINT", async () => {
console.log("SIGINT received, terminating...");
console.log("SIGINT received, terminating shell...");
if (!context.terminated) {
await context.terminate();
}
process.exit(0);
});

process.on("SIGTERM", async () => {
console.log("SIGTERM received, terminating...");
console.log("SIGTERM received, terminating shell...");
if (!context.terminated) {
await context.terminate();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Command } from "commander";
import { ShellOptions } from "./shell.options";
import { RunOnGolemOptions } from "./run-on-golem.options";

/*
Expand Down Expand Up @@ -40,9 +40,9 @@ More features ideas:
[ ] --silent so the shell doesn't use any stdout by itself
*/

export const shellCommand = new Command("shell");
export const runOnGolemCommand = new Command("run-on-golem");

shellCommand
runOnGolemCommand
.summary("Run task API shell")
.option(
"--image <image>",
Expand All @@ -60,7 +60,7 @@ shellCommand
"\n\nIf either --execute or batch files are used, upon completion of the commands, the shell will exit. This behaviour can be changed by using --interactive option.",
)
.allowExcessArguments(false)
.action(async (file: string[], options: ShellOptions) => {
const action = await import("./shell.action.js");
await action.default.shellAction(file, options);
.action(async (file: string[], options: RunOnGolemOptions) => {
const action = await import("./run-on-golem.action.js");
await action.default.runOnGolemAction(file, options);
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface ShellOptions {
export interface RunOnGolemOptions {
manifest: string; // At worse, it will contain a default value
image?: string;
execute?: string;
Expand Down
File renamed without changes.
120 changes: 77 additions & 43 deletions src/shell/shell-program.ts → src/run-on-golem/shell-program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,65 @@ import { writeFile } from "fs/promises";

export function shellProgram(pContext: ProgramContext): Command {
const program = new Command("shell");
program.exitOverride().addHelpCommand(false).helpOption(false).enablePositionalOptions(true);
program
.exitOverride()
.addHelpCommand(false)
.helpOption(false)
.enablePositionalOptions(true)
.configureHelp({
commandUsage: (cmd: Command) => {
return `${cmd.name()} ${cmd.usage()}`;
},
});

program
.command("help")
.allowExcessArguments(false)
.summary("Display available commands.")
.action(() => {
program.help();
.description("Display available commands.")
.argument("[command]", "The command to display help for.")
.action((command) => {
if (!command) {
const help = program.helpInformation();
// This will remove 'Usage: shell ...' from help output.
console.log(help.split("\n").slice(2).join("\n"));
} else {
const cmd = program.commands.find((c) => c.name() === command);
if (!cmd) {
console.error(`Error: Command ${command} not found.`);
return;
}

cmd.outputHelp();
}
});

program
.command("exit")
.summary("Terminate activity and exit shell.")
.description("Terminate activity and exit shell.")
.allowExcessArguments(false)
.showHelpAfterError("Type `exit --help` form more information.")
.helpOption(true)
.action(() => {
pContext.exited = true;
});

program
.command("echo")
.summary("Display a line of text.")
.description("Display a line of text.")
.argument("<text...>", "Text to display.")
.showHelpAfterError("Type `echo --help` form more information.")
.helpOption(true)
.action((lines: string[]) => {
console.log(lines.join(" "));
});

program
.command("set")
.summary("Display or modify shell variables.")
.description("Display or modify shell variables.")
.argument("[name]", "Variable name or variable assignment (ie. var=VALUE.")
.allowExcessArguments(false)
.showHelpAfterError("Type `set --help` form more information.")
.helpOption(true)
.action((name?: string) => {
const parts = name ? name?.split("=") : null;

Expand All @@ -53,16 +81,16 @@ export function shellProgram(pContext: ProgramContext): Command {
}
});

program.createCommand("exit").summary("Terminates the activity on the provider and exists the shell.");

program
.command("run")
.summary("Execute a command on the provider using shell.")
.description("Execute a command on the provider using shell.")
.option("-o, --stdout <file>", "Redirect stdout to a file.")
.option("-e, --stderr <file>", "Redirect stderr to a file. Use - to redirect stderr to stdout.")
.argument("<command>", "The command to run on the provider.")
.addHelpText("after", "\nExamples:\n" + '\t$ run "ls /"\n' + '\t$ run "cat /proc/cpuinfo"\n')
.allowExcessArguments(false)
.addHelpCommand(true)
.showHelpAfterError("Type `run --help` form more information.")
.helpOption(true)
.action(async (command: string, options) => {
const result = await pContext.workContext.run(command);
if (result.result !== "Ok") {
Expand All @@ -83,42 +111,44 @@ export function shellProgram(pContext: ProgramContext): Command {
}
});

program
.command("exec")
.summary("Execute a command on the provider.")
.option("-o, --stdout <file>", "Redirect stdout to a file.")
.option("-e, --stderr <file>", "Redirect stderr to a file. Use - to redirect stderr to stdout.")
.addHelpText("after", "Use -- after exec options to separate exec options for your application options")
.argument("<executable>", "Path to executable to run.")
.argument("[arguments...]", "Command arguments and options to pass")
.allowUnknownOption(true)
.passThroughOptions(true)
.action(async (command: string, args: string[], options) => {
const result = await pContext.workContext.run(command, args);
if (result.result !== "Ok") {
console.error(`Command error: ${result.message}`);
}
// TODO: Handle stdout redirect.
let stdout = result.stdout as string;
if (options.stderr === "-") stdout += result.stderr as string;

if (options.stdout) {
await writeFile(options.stdout, stdout, { encoding: "utf-8" });
} else {
process.stdout.write(result.stdout as string);
}

if (options.stderr && options.stderr !== "-") {
process.stderr.write(result.stderr as string);
}
});
// Needs figuring out how to bypass Commander.js, so it doesn't hijack options.
// program
// .command("exec")
// .summary("Execute a command on the provider.")
// .option("-o, --stdout <file>", "Redirect stdout to a file.")
// .option("-e, --stderr <file>", "Redirect stderr to a file. Use - to redirect stderr to stdout.")
// .addHelpText("after", "Use -- after exec options to separate exec options for your application options")
// .argument("<executable>", "Path to executable to run.")
// .argument("[arguments...]", "Command arguments and options to pass")
// .allowUnknownOption(true)
// .passThroughOptions(true)
// .action(async (command: string, args: string[], options) => {
// const result = await pContext.workContext.run(command, args);
// if (result.result !== "Ok") {
// console.error(`Command error: ${result.message}`);
// }
// let stdout = result.stdout as string;
// if (options.stderr === "-") stdout += result.stderr as string;
//
// if (options.stdout) {
// await writeFile(options.stdout, stdout, { encoding: "utf-8" });
// } else {
// process.stdout.write(result.stdout as string);
// }
//
// if (options.stderr && options.stderr !== "-") {
// process.stderr.write(result.stderr as string);
// }
// });

program
.command("upload")
.summary("Upload a file to the provider.")
.description("Upload a file to the provider.")
.argument("<source-file>", "Path to the file to upload.")
.argument("<destination-file>", "Remote path to upload the file to.")
.allowExcessArguments(false)
.showHelpAfterError("Type `upload --help` form more information.")
.helpOption(true)
.action(async (sourceFile: string, destinationFile: string) => {
const exists = await checkFileExists("Source file", sourceFile);
if (!exists) {
Expand All @@ -137,13 +167,15 @@ export function shellProgram(pContext: ProgramContext): Command {

program
.command("download")
.summary("Download a file from the provider.")
.description("Download a file from the provider.")
.argument("<sourceFile>", "Path to the file to download.")
.argument(
"[localPath]",
"Local path to download the file to. If not provided, file will be saved in current directory using the name from source file.",
)
.allowExcessArguments(false)
.showHelpAfterError("Type `download --help` form more information.")
.helpOption(true)
.action(async (sourceFile: string, destinationPath: string) => {
if (!destinationPath) destinationPath = path.basename(sourceFile);
const destDir = path.dirname(destinationPath ?? "./");
Expand All @@ -164,8 +196,10 @@ export function shellProgram(pContext: ProgramContext): Command {

program
.command("time")
.summary("Displays the amount of time current activity is running on the provider.")
.description("Displays the amount of time current activity is running on the provider.")
.allowExcessArguments(false)
.showHelpAfterError("Type `time --help` form more information.")
.helpOption(true)
.action(async () => {
const start = pContext.startDate;
const now = Date.now();
Expand Down

0 comments on commit 26e99b3

Please sign in to comment.