- Add modern CLI interface built with Zig for performance - Include TUI (Terminal User Interface) with bubbletea-like features - Implement ML experiment commands (run, status, manage) - Add configuration management and validation - Include shell completion scripts for bash and zsh - Add comprehensive CLI testing framework - Support for multiple ML frameworks and project types CLI provides fast, efficient interface for ML experiment management with modern terminal UI and comprehensive feature set.
107 lines
3.8 KiB
Zig
107 lines
3.8 KiB
Zig
const std = @import("std");
|
|
|
|
/// Embedded rsync binary functionality
|
|
pub const EmbeddedRsync = struct {
|
|
allocator: std.mem.Allocator,
|
|
|
|
const Self = @This();
|
|
|
|
/// Extract embedded rsync binary to temporary location
|
|
pub fn extractRsyncBinary(self: Self) ![]const u8 {
|
|
const rsync_path = "/tmp/ml_rsync";
|
|
|
|
// Check if rsync binary already exists
|
|
if (std.fs.cwd().openFile(rsync_path, .{})) |file| {
|
|
file.close();
|
|
// Check if it's executable
|
|
const stat = try std.fs.cwd().statFile(rsync_path);
|
|
if (stat.mode & 0o111 != 0) {
|
|
return try self.allocator.dupe(u8, rsync_path);
|
|
}
|
|
} else |_| {
|
|
// Need to extract the binary
|
|
try self.extractAndSetExecutable(rsync_path);
|
|
}
|
|
|
|
return try self.allocator.dupe(u8, rsync_path);
|
|
}
|
|
|
|
/// Extract rsync binary from embedded data and set executable permissions
|
|
fn extractAndSetExecutable(self: Self, path: []const u8) !void {
|
|
// Import embedded binary data
|
|
const embedded_binary = @import("rsync_embedded_binary.zig");
|
|
|
|
// Get the embedded rsync binary data
|
|
const binary_data = embedded_binary.getRsyncBinary();
|
|
|
|
// Debug output to show we're using embedded binary
|
|
const debug_msg = try std.fmt.allocPrint(self.allocator, "Extracting embedded rsync binary to {s} (size: {d} bytes)", .{ path, binary_data.len });
|
|
defer self.allocator.free(debug_msg);
|
|
std.log.debug("{s}", .{debug_msg});
|
|
|
|
// Write embedded binary to file system
|
|
try std.fs.cwd().writeFile(.{ .sub_path = path, .data = binary_data });
|
|
|
|
// Set executable permissions using OS API
|
|
const mode = 0o755; // rwxr-xr-x
|
|
std.posix.fchmodat(std.fs.cwd().fd, path, mode, 0) catch |err| {
|
|
std.log.warn("Failed to set executable permissions on {s}: {}", .{ path, err });
|
|
// Continue anyway - the script might still work
|
|
};
|
|
}
|
|
|
|
/// Sync using embedded rsync
|
|
pub fn sync(self: Self, local_path: []const u8, remote_path: []const u8, ssh_port: u16) !void {
|
|
const rsync_path = try self.extractRsyncBinary();
|
|
defer self.allocator.free(rsync_path);
|
|
|
|
const port_str = try std.fmt.allocPrint(self.allocator, "{d}", .{ssh_port});
|
|
defer self.allocator.free(port_str);
|
|
|
|
const ssh_opt = try std.fmt.allocPrint(self.allocator, "ssh -p {s}", .{port_str});
|
|
defer self.allocator.free(ssh_opt);
|
|
|
|
// Build rsync command using embedded binary
|
|
var child = std.process.Child.init(
|
|
&[_][]const u8{
|
|
rsync_path,
|
|
"-avz",
|
|
"--delete",
|
|
"-e",
|
|
ssh_opt,
|
|
local_path,
|
|
remote_path,
|
|
},
|
|
self.allocator,
|
|
);
|
|
|
|
child.stdin_behavior = .Ignore;
|
|
child.stdout_behavior = .Inherit;
|
|
child.stderr_behavior = .Inherit;
|
|
|
|
const term = try child.spawnAndWait();
|
|
|
|
switch (term) {
|
|
.Exited => |code| {
|
|
if (code != 0) {
|
|
std.debug.print("rsync failed with exit code {d}\n", .{code});
|
|
return error.RsyncFailed;
|
|
}
|
|
},
|
|
.Signal => {
|
|
return error.RsyncKilled;
|
|
},
|
|
else => {
|
|
return error.RsyncUnknownError;
|
|
},
|
|
}
|
|
}
|
|
};
|
|
|
|
/// Public sync function that uses embedded rsync
|
|
pub fn sync(allocator: std.mem.Allocator, local_path: []const u8, remote_path: []const u8, ssh_port: u16) !void {
|
|
var embedded_rsync = EmbeddedRsync{ .allocator = allocator };
|
|
try embedded_rsync.sync(local_path, remote_path, ssh_port);
|
|
|
|
std.debug.print("Synced {s} to {s} using embedded rsync\n", .{ local_path, remote_path });
|
|
}
|