fix(cli): CLI structure, manifest, and asset fixes

- Fix commands.zig imports (logs.zig → log.zig, remove missing modules)
- Fix manifest.writeManifest to accept allocator param
- Add db.Stmt type alias for sqlite3_stmt
- Fix rsync placeholder to be valid shell script (#!/bin/sh)
This commit is contained in:
Jeremie Fraeys 2026-02-21 17:59:20 -05:00
parent 382c67edfc
commit b1c9bc97fc
No known key found for this signature in database
4 changed files with 68 additions and 40 deletions

View file

@ -1 +1,2 @@
dummy
#!/bin/sh
exec /usr/bin/rsync "$@"

View file

@ -8,13 +8,10 @@ pub const find = @import("commands/find.zig");
pub const info = @import("commands/info.zig");
pub const init = @import("commands/init.zig");
pub const jupyter = @import("commands/jupyter.zig");
pub const logs = @import("commands/logs.zig");
pub const monitor = @import("commands/monitor.zig");
pub const narrative = @import("commands/narrative.zig");
pub const outcome = @import("commands/outcome.zig");
pub const logs = @import("commands/log.zig");
pub const prune = @import("commands/prune.zig");
pub const queue = @import("commands/queue.zig");
pub const requeue = @import("commands/requeue.zig");
pub const run = @import("commands/run.zig");
pub const status = @import("commands/status.zig");
pub const sync = @import("commands/sync.zig");
pub const validate = @import("commands/validate.zig");

View file

@ -1,10 +1,13 @@
const std = @import("std");
// SQLite C bindings
const c = @cImport({
pub const c = @cImport({
@cInclude("sqlite3.h");
});
// Public type alias for prepared statement
pub const Stmt = ?*c.sqlite3_stmt;
// SQLITE_TRANSIENT constant - use C wrapper to avoid Zig 0.15 C translation issue
extern fn fetchml_sqlite_transient() c.sqlite3_destructor_type;
fn sqliteTransient() c.sqlite3_destructor_type {

View file

@ -59,82 +59,109 @@ pub const RunManifest = struct {
};
/// Write manifest to JSON file
pub fn writeManifest(manifest: RunManifest, path: []const u8) !void {
pub fn writeManifest(manifest: RunManifest, path: []const u8, allocator: std.mem.Allocator) !void {
var file = try std.fs.cwd().createFile(path, .{});
defer file.close();
const writer = file.writer();
// Write JSON manually to avoid std.json complexity with hash maps
try writer.writeAll("{\n");
try file.writeAll("{\n");
try writer.print(" \"run_id\": \"{s}\",\n", .{manifest.run_id});
try writer.print(" \"experiment\": \"{s}\",\n", .{manifest.experiment});
try writer.print(" \"command\": \"{s}\",\n", .{manifest.command});
const line1 = try std.fmt.allocPrint(allocator, " \"run_id\": \"{s}\",\n", .{manifest.run_id});
defer allocator.free(line1);
try file.writeAll(line1);
const line2 = try std.fmt.allocPrint(allocator, " \"experiment\": \"{s}\",\n", .{manifest.experiment});
defer allocator.free(line2);
try file.writeAll(line2);
const line3 = try std.fmt.allocPrint(allocator, " \"command\": \"{s}\",\n", .{manifest.command});
defer allocator.free(line3);
try file.writeAll(line3);
// Args array
try writer.writeAll(" \"args\": [");
try file.writeAll(" \"args\": [");
for (manifest.args, 0..) |arg, i| {
if (i > 0) try writer.writeAll(", ");
try writer.print("\"{s}\"", .{arg});
if (i > 0) try file.writeAll(", ");
const arg_str = try std.fmt.allocPrint(allocator, "\"{s}\"", .{arg});
defer allocator.free(arg_str);
try file.writeAll(arg_str);
}
try writer.writeAll("],\n");
try file.writeAll("],\n");
// Commit ID (optional)
if (manifest.commit_id) |cid| {
try writer.print(" \"commit_id\": \"{s}\",\n", .{cid});
const cid_str = try std.fmt.allocPrint(allocator, " \"commit_id\": \"{s}\",\n", .{cid});
defer allocator.free(cid_str);
try file.writeAll(cid_str);
} else {
try writer.writeAll(" \"commit_id\": null,\n");
try file.writeAll(" \"commit_id\": null,\n");
}
try writer.print(" \"started_at\": \"{s}\",\n", .{manifest.started_at});
const started_str = try std.fmt.allocPrint(allocator, " \"started_at\": \"{s}\",\n", .{manifest.started_at});
defer allocator.free(started_str);
try file.writeAll(started_str);
// Ended at (optional)
if (manifest.ended_at) |ended| {
try writer.print(" \"ended_at\": \"{s}\",\n", .{ended});
const ended_str = try std.fmt.allocPrint(allocator, " \"ended_at\": \"{s}\",\n", .{ended});
defer allocator.free(ended_str);
try file.writeAll(ended_str);
} else {
try writer.writeAll(" \"ended_at\": null,\n");
try file.writeAll(" \"ended_at\": null,\n");
}
try writer.print(" \"status\": \"{s}\",\n", .{manifest.status});
const status_str = try std.fmt.allocPrint(allocator, " \"status\": \"{s}\",\n", .{manifest.status});
defer allocator.free(status_str);
try file.writeAll(status_str);
// Exit code (optional)
if (manifest.exit_code) |code| {
try writer.print(" \"exit_code\": {d},\n", .{code});
const exit_str = try std.fmt.allocPrint(allocator, " \"exit_code\": {d},\n", .{code});
defer allocator.free(exit_str);
try file.writeAll(exit_str);
} else {
try writer.writeAll(" \"exit_code\": null,\n");
try file.writeAll(" \"exit_code\": null,\n");
}
// Params object
try writer.writeAll(" \"params\": {");
try file.writeAll(" \"params\": {");
var params_first = true;
var params_iter = manifest.params.iterator();
while (params_iter.next()) |entry| {
if (!params_first) try writer.writeAll(", ");
if (!params_first) try file.writeAll(", ");
params_first = false;
try writer.print("\"{s}\": \"{s}\"", .{ entry.key_ptr.*, entry.value_ptr.* });
const param_str = try std.fmt.allocPrint(allocator, "\"{s}\": \"{s}\"", .{ entry.key_ptr.*, entry.value_ptr.* });
defer allocator.free(param_str);
try file.writeAll(param_str);
}
try writer.writeAll("},\n");
try file.writeAll("},\n");
// Metrics summary (optional)
if (manifest.metrics_summary) |summary| {
try writer.writeAll(" \"metrics_summary\": {");
try file.writeAll(" \"metrics_summary\": {");
var summary_first = true;
var summary_iter = summary.iterator();
while (summary_iter.next()) |entry| {
if (!summary_first) try writer.writeAll(", ");
if (!summary_first) try file.writeAll(", ");
summary_first = false;
try writer.print("\"{s}\": {d:.4}", .{ entry.key_ptr.*, entry.value_ptr.* });
const metric_str = try std.fmt.allocPrint(allocator, "\"{s}\": {d:.4}", .{ entry.key_ptr.*, entry.value_ptr.* });
defer allocator.free(metric_str);
try file.writeAll(metric_str);
}
try writer.writeAll("},\n");
try file.writeAll("},\n");
} else {
try writer.writeAll(" \"metrics_summary\": null,\n");
try file.writeAll(" \"metrics_summary\": null,\n");
}
try writer.print(" \"artifact_path\": \"{s}\",\n", .{manifest.artifact_path});
try writer.print(" \"synced\": {}", .{manifest.synced});
const artifact_str = try std.fmt.allocPrint(allocator, " \"artifact_path\": \"{s}\",\n", .{manifest.artifact_path});
defer allocator.free(artifact_str);
try file.writeAll(artifact_str);
try writer.writeAll("\n}\n");
const synced_str = try std.fmt.allocPrint(allocator, " \"synced\": {}", .{manifest.synced});
defer allocator.free(synced_str);
try file.writeAll(synced_str);
try file.writeAll("\n}\n");
}
/// Read manifest from JSON file
@ -265,7 +292,7 @@ pub fn updateManifestStatus(path: []const u8, status: []const u8, exit_code: ?i3
manifest.ended_at = try allocator.dupe(u8, timestamp);
try writeManifest(manifest, path);
try writeManifest(manifest, path, allocator);
}
/// Mark manifest as synced
@ -274,7 +301,7 @@ pub fn markManifestSynced(path: []const u8, allocator: std.mem.Allocator) !void
defer manifest.deinit(allocator);
manifest.synced = true;
try writeManifest(manifest, path);
try writeManifest(manifest, path, allocator);
}
/// Build manifest path from experiment and run_id