Add first-class job rail profiles

This commit is contained in:
slhx agent
2026-06-21 14:09:53 +02:00
parent 5224fe4c6a
commit e4d6837f78
4 changed files with 415 additions and 5 deletions
+136
View File
@@ -13,8 +13,144 @@ pub const Error = error{
InvalidCommand,
InvalidJobRow,
InvalidPath,
MissingCurrentFile,
};
pub const Profile = enum {
lint_file,
lint_project,
build,
tests,
check,
pub fn name(self: Profile) []const u8 {
return switch (self) {
.lint_file => "lint_file",
.lint_project => "lint_project",
.build => "build",
.tests => "test",
.check => "check",
};
}
pub fn provider(self: Profile) []const u8 {
return switch (self) {
.lint_file, .lint_project => "zig-fmt",
.build, .tests, .check => "zig-build",
};
}
pub fn scope(self: Profile) []const u8 {
return switch (self) {
.lint_file => "file",
.lint_project, .build, .tests, .check => "project",
};
}
};
pub fn argvForProfileAlloc(allocator: std.mem.Allocator, profile: Profile, current_file: ?[]const u8) ![][]const u8 {
var argv = std.ArrayList([]const u8).empty;
errdefer {
for (argv.items) |arg| allocator.free(arg);
argv.deinit(allocator);
}
switch (profile) {
.lint_file => {
const path = current_file orelse return Error.MissingCurrentFile;
try validatePath(path);
try argv.append(allocator, try allocator.dupe(u8, "zig"));
try argv.append(allocator, try allocator.dupe(u8, "fmt"));
try argv.append(allocator, try allocator.dupe(u8, "--check"));
try argv.append(allocator, try allocator.dupe(u8, path));
},
.lint_project => {
try argv.append(allocator, try allocator.dupe(u8, "zig"));
try argv.append(allocator, try allocator.dupe(u8, "fmt"));
try argv.append(allocator, try allocator.dupe(u8, "--check"));
try argv.append(allocator, try allocator.dupe(u8, "src"));
},
.build => {
try argv.append(allocator, try allocator.dupe(u8, "zig"));
try argv.append(allocator, try allocator.dupe(u8, "build"));
},
.tests => {
try argv.append(allocator, try allocator.dupe(u8, "zig"));
try argv.append(allocator, try allocator.dupe(u8, "build"));
try argv.append(allocator, try allocator.dupe(u8, "test"));
},
.check => {
try argv.append(allocator, try allocator.dupe(u8, "zig"));
try argv.append(allocator, try allocator.dupe(u8, "build"));
try argv.append(allocator, try allocator.dupe(u8, "v1-smoke"));
},
}
return argv.toOwnedSlice(allocator);
}
pub fn freeArgv(allocator: std.mem.Allocator, argv: []const []const u8) void {
for (argv) |arg| allocator.free(arg);
allocator.free(argv);
}
pub fn profileRowsAlloc(allocator: std.mem.Allocator, io: std.Io, cwd: []const u8, profile: Profile, current_file: ?[]const u8) ![][]const u8 {
const argv = argvForProfileAlloc(allocator, profile, current_file) catch |err| {
if (err == Error.MissingCurrentFile) return missingCurrentFileRowsAlloc(allocator, profile);
return err;
};
defer freeArgv(allocator, argv);
return runRowsWithPreviewAlloc(allocator, io, cwd, argv, "job", profilePreview(profile));
}
pub fn missingToolRowsAlloc(allocator: std.mem.Allocator, profile: Profile, tool: []const u8) ![][]const u8 {
var rows = std.ArrayList([]const u8).empty;
errdefer freeOwnedRows(allocator, rows.items);
try rows.append(allocator, try std.fmt.allocPrint(allocator, "job:profile:{s}:scope:{s}:provider:{s}", .{ profile.name(), profile.scope(), profile.provider() }));
try rows.append(allocator, try std.fmt.allocPrint(allocator, "job:status:missing_tool:{s}:no_install_attempted", .{sanitizeToken(tool)}));
return rows.toOwnedSlice(allocator);
}
pub fn cancelRowsAlloc(allocator: std.mem.Allocator, profile: Profile) ![][]const u8 {
var rows = std.ArrayList([]const u8).empty;
errdefer freeOwnedRows(allocator, rows.items);
try rows.append(allocator, try std.fmt.allocPrint(allocator, "job:profile:{s}:scope:{s}:provider:{s}", .{ profile.name(), profile.scope(), profile.provider() }));
try rows.append(allocator, try allocator.dupe(u8, "job:status:cancelled:user"));
return rows.toOwnedSlice(allocator);
}
pub fn timeoutRowsAlloc(allocator: std.mem.Allocator, profile: Profile) ![][]const u8 {
var rows = std.ArrayList([]const u8).empty;
errdefer freeOwnedRows(allocator, rows.items);
try rows.append(allocator, try std.fmt.allocPrint(allocator, "job:profile:{s}:scope:{s}:provider:{s}", .{ profile.name(), profile.scope(), profile.provider() }));
try rows.append(allocator, try allocator.dupe(u8, "job:status:timeout:recoverable"));
return rows.toOwnedSlice(allocator);
}
fn missingCurrentFileRowsAlloc(allocator: std.mem.Allocator, profile: Profile) ![][]const u8 {
var rows = std.ArrayList([]const u8).empty;
errdefer freeOwnedRows(allocator, rows.items);
try rows.append(allocator, try std.fmt.allocPrint(allocator, "job:profile:{s}:scope:{s}:provider:{s}", .{ profile.name(), profile.scope(), profile.provider() }));
try rows.append(allocator, try allocator.dupe(u8, "job:status:missing_current_file"));
return rows.toOwnedSlice(allocator);
}
fn profilePreview(profile: Profile) []const u8 {
return switch (profile) {
.lint_file => "profile:lint_file:scope:file:provider:zig-fmt",
.lint_project => "profile:lint_project:scope:project:provider:zig-fmt",
.build => "profile:build:scope:project:provider:zig-build",
.tests => "profile:test:scope:project:provider:zig-build",
.check => "profile:check:scope:project:provider:zig-build",
};
}
fn sanitizeToken(text: []const u8) []const u8 {
if (text.len == 0) return "unknown";
for (text) |byte| {
if (byte <= 0x20 or byte == ':' or byte == '|') return "invalid";
}
return text;
}
pub const Location = struct {
path: []const u8,
line: usize,