fetch_ml/cli/src/commands/info.zig
Jeremie Fraeys fd317c9791
refactor(cli): Update network handlers and info command
Replace Unicode symbols with ASCII in handshake.zig

Add [OK]/[FAIL] status indicators in response_handlers.zig

Simplify info.zig output formatting
2026-02-23 14:12:22 -05:00

184 lines
6.6 KiB
Zig

const std = @import("std");
const Config = @import("../config.zig").Config;
const io = @import("../utils/io.zig");
const json = @import("../utils/json.zig");
const manifest = @import("../utils/manifest.zig");
const core = @import("../core.zig");
pub const Options = struct {
json: bool = false,
base: ?[]const u8 = null,
};
pub fn run(allocator: std.mem.Allocator, args: []const []const u8) !void {
var flags = core.flags.CommonFlags{};
var base: ?[]const u8 = null;
var target_path: ?[]const u8 = null;
var i: usize = 0;
while (i < args.len) : (i += 1) {
const arg = args[i];
if (std.mem.eql(u8, arg, "--json")) {
flags.json = true;
} else if (std.mem.eql(u8, arg, "--base") and i + 1 < args.len) {
base = args[i + 1];
i += 1;
} else if (std.mem.startsWith(u8, arg, "--help")) {
return printUsage();
} else if (std.mem.startsWith(u8, arg, "--")) {
core.output.err("Unknown option");
return error.InvalidArgs;
} else {
target_path = arg;
}
}
core.output.setMode(if (flags.json) .json else .text);
if (target_path == null) {
core.output.err("No target path specified");
return printUsage();
}
const manifest_path = manifest.resolvePathWithBase(allocator, target_path.?, base) catch |err| {
if (err == error.FileNotFound) {
core.output.err("Manifest not found");
}
return err;
};
const data = try manifest.readFileAlloc(allocator, manifest_path);
defer {
allocator.free(manifest_path);
allocator.free(data);
}
if (flags.json) {
var out = io.stdoutWriter();
try out.print("{s}\n", .{data});
return;
}
const parsed = try std.json.parseFromSlice(std.json.Value, allocator, data, .{});
defer parsed.deinit();
if (parsed.value != .object) {
core.output.err("run manifest is not a JSON object");
return error.InvalidManifest;
}
const root = parsed.value.object;
const run_id = json.getString(root, "run_id") orelse "";
const task_id = json.getString(root, "task_id") orelse "";
const job_name = json.getString(root, "job_name") orelse "";
const commit_id = json.getString(root, "commit_id") orelse "";
const worker_version = json.getString(root, "worker_version") orelse "";
const podman_image = json.getString(root, "podman_image") orelse "";
const snapshot_id = json.getString(root, "snapshot_id") orelse "";
const snapshot_sha = json.getString(root, "snapshot_sha256") orelse "";
const command = json.getString(root, "command") orelse "";
const cmd_args = json.getString(root, "args") orelse "";
const exit_code = json.getInt(root, "exit_code");
const err_msg = json.getString(root, "error") orelse "";
const created_at = json.getString(root, "created_at") orelse "";
const started_at = json.getString(root, "started_at") orelse "";
const ended_at = json.getString(root, "ended_at") orelse "";
const staging_ms = json.getInt(root, "staging_duration_ms") orelse 0;
const exec_ms = json.getInt(root, "execution_duration_ms") orelse 0;
const finalize_ms = json.getInt(root, "finalize_duration_ms") orelse 0;
const total_ms = json.getInt(root, "total_duration_ms") orelse 0;
std.debug.print("run_manifest\t{s}\n", .{manifest_path});
if (job_name.len > 0) std.debug.print("job_name\t{s}\n", .{job_name});
if (run_id.len > 0) std.debug.print("run_id\t{s}\n", .{run_id});
if (task_id.len > 0) std.debug.print("task_id\t{s}\n", .{task_id});
if (commit_id.len > 0) std.debug.print("commit_id\t{s}\n", .{commit_id});
if (worker_version.len > 0) std.debug.print("worker_version\t{s}\n", .{worker_version});
if (podman_image.len > 0) std.debug.print("podman_image\t{s}\n", .{podman_image});
if (snapshot_id.len > 0) std.debug.print("snapshot_id\t{s}\n", .{snapshot_id});
if (snapshot_sha.len > 0) std.debug.print("snapshot_sha256\t{s}\n", .{snapshot_sha});
if (command.len > 0) {
if (cmd_args.len > 0) {
std.debug.print("command\t{s} {s}\n", .{ command, cmd_args });
} else {
std.debug.print("command\t{s}\n", .{command});
}
}
if (created_at.len > 0) std.debug.print("created_at\t{s}\n", .{created_at});
if (started_at.len > 0) std.debug.print("started_at\t{s}\n", .{started_at});
if (ended_at.len > 0) std.debug.print("ended_at\t{s}\n", .{ended_at});
if (total_ms > 0 or staging_ms > 0 or exec_ms > 0 or finalize_ms > 0) {
std.debug.print("durations_ms\ttotal={d}\tstaging={d}\texecution={d}\tfinalize={d}\n", .{ total_ms, staging_ms, exec_ms, finalize_ms });
}
if (exit_code) |ec| {
if (ec == 0 and err_msg.len == 0) {
std.debug.print("exit_code\t0\n", .{});
} else {
std.debug.print("exit_code\t{d}\n", .{ec});
}
}
if (err_msg.len > 0) {
std.debug.print("error\t{s}\n", .{err_msg});
}
}
fn printUsage() !void {
std.debug.print("Usage:\n", .{});
std.debug.print("\tml info <run_dir_or_manifest_path_or_id> [--json] [--base <path>]\n", .{});
}
test "resolveManifestPath uses run_manifest.json for directories" {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
try tmp.dir.makeDir("run");
const run_abs = try tmp.dir.realpathAlloc(allocator, "run");
defer allocator.free(run_abs);
const got = try manifest.resolvePathWithBase(allocator, run_abs, null);
try std.testing.expect(std.mem.endsWith(u8, got, "run/run_manifest.json"));
}
test "resolveManifestPath resolves by task id when base is provided" {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
try tmp.dir.makePath("finished/run-a");
var file = try tmp.dir.createFile("finished/run-a/run_manifest.json", .{});
defer file.close();
try file.writeAll(
"{\n" ++
" \"run_id\": \"run-a\",\n" ++
" \"task_id\": \"task-123\",\n" ++
" \"job_name\": \"job\"\n" ++
"}\n",
);
const base_abs = try tmp.dir.realpathAlloc(allocator, ".");
defer allocator.free(base_abs);
const got = try manifest.resolvePathWithBase(allocator, "task-123", base_abs);
try std.testing.expect(std.mem.endsWith(u8, got, "finished/run-a/run_manifest.json"));
}