Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changes/unreleased/Added-20260801-152458.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Added
body: A configuration file can now be configured to store a number of common setting. Create the configuration file using the `--init` flag.
time: 2026-08-01T15:24:58.621532877+01:00
3 changes: 3 additions & 0 deletions .changes/unreleased/Added-20260801-161340.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Added
body: 'Logs now have some color. '
time: 2026-08-01T16:13:40.454204347+01:00
3 changes: 3 additions & 0 deletions .changes/unreleased/Changed-20260719-232001.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Changed
body: branch.autoSetupMerge change to "true" from "always"."
time: 2026-07-19T23:20:01.678634875+01:00
14 changes: 14 additions & 0 deletions src/default_config.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
//Default clone path
//Setting `GRAB_PATH` env var overrides
//.path = "path/to/source",

//Default clone mode (.worktree or .standard)
.action = .worktree,

//Create shallow clones by default
.shallow = false,

//Default log level (.debug, .info, .warn, .error)
.log_level = .info,
}
5 changes: 5 additions & 0 deletions src/defaults.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const builtin = @import("builtin");

pub const log_level = if (builtin.mode == .Debug) .debug else .info;
pub const shallow = false;
pub const action = .worktree;
46 changes: 46 additions & 0 deletions src/logging.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const std = @import("std");

const defaults = @import("defaults.zig");

pub const Level = enum { info, debug, @"error", warn };
pub var log_level: std.log.Level = defaults.log_level;

pub fn log(
comptime level: std.log.Level,
comptime scope: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
const prefix = comptime blk: {
if (scope == .default)
break :blk levelColor(level);
break :blk levelColor(level) ++ "[" ++ @tagName(scope) ++ "] ";
};

if (@intFromEnum(level) <= @intFromEnum(log_level)) {
std.debug.print(prefix ++ format ++ "\n", args);
}
}

pub fn set_log_level(level: Level) void {
switch (level) {
.debug => log_level = .debug,
.@"error" => log_level = .err,
.info => log_level = .info,
.warn => log_level = .warn,
}
}

fn levelColor(level: std.log.Level) []const u8 {
const csi = "\x1b[";
const end = csi ++ "0m";
const yellow = csi ++ "33m";
const red = csi ++ "31m";
const blue = csi ++ "34m";
return switch (level) {
.debug => blue ++ "[" ++ level.asText() ++ "]" ++ end ++ " ",
.warn => yellow ++ "[" ++ level.asText() ++ "]" ++ end ++ " ",
.err => red ++ "[" ++ level.asText() ++ "]" ++ end ++ " ",
.info => "[" ++ level.asText() ++ "] ",
};
}
96 changes: 37 additions & 59 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -1,34 +1,16 @@
const std = @import("std");
const clap = @import("clap");
const grab = @import("grab");
const build_options = @import("build_options");

const help = @import("help.zig");

pub const Level = enum { info, debug, @"error", warn };
pub const std_options: std.Options = .{
// Keep compile-time logging permissive; runtime filter in `log`.
.log_level = .debug,
.logFn = log,
.logFn = grab.logging.log,
};

pub var log_level: std.log.Level = .info;

pub fn log(
comptime level: std.log.Level,
comptime scope: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
const prefix = comptime blk: {
if (scope == .default)
break :blk "[" ++ level.asText() ++ "] ";
break :blk "[" ++ level.asText() ++ "][" ++ @tagName(scope) ++ "] ";
};

if (@intFromEnum(level) <= @intFromEnum(log_level)) {
std.debug.print(prefix ++ format ++ "\n", args);
}
}

pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;

Expand All @@ -42,12 +24,13 @@ pub fn main(init: std.process.Init) !void {
\\-r, --remote Add remote to existing repo.
\\--log-level <LEVEL> Set the log level. All logs are saved to file. Possible values are (debug, info, warn, error). Defualt level is info.
\\--version Show program's version number and exit
\\--init Create a configuration file
);

const parsers = comptime .{
.PATH = clap.parsers.string,
.REPO = clap.parsers.string,
.LEVEL = clap.parsers.enumeration(Level),
.LEVEL = clap.parsers.enumeration(grab.logging.Level),
};

var diag = clap.Diagnostic{};
Expand All @@ -61,29 +44,27 @@ pub fn main(init: std.process.Init) !void {
};
defer res.deinit();

if (res.args.help != 0)
return clap.helpToFile(init.io, .stderr(), clap.Help, &params, .{});
if (res.args.version != 0) {
const build_options = @import("build_options");
std.log.info("{s}: {s}", .{ build_options.name, build_options.version });
std.process.exit(0);
}

var config = grab.Configuration.init();
// Load configuration first to ensure the correct log level is set.
var config = try grab.Configuration.init(init.io, init.gpa, init.minimal.environ);
defer config.deinit(allocator);

// Set up logger
var level = Level.info;
if (res.args.@"log-level") |l| {
level = l;
}
if (res.args.@"log-level") |l| grab.logging.set_log_level(l);

if (res.args.help != 0) return clap.helpToFile(
init.io,
.stderr(),
clap.Help,
&params,
.{},
);

switch (level) {
.debug => log_level = std.log.Level.debug,
.@"error" => log_level = std.log.Level.err,
.info => log_level = std.log.Level.info,
.warn => log_level = std.log.Level.warn,
}
if (res.args.version != 0) return std.log.info(
"{s}: {s}",
.{ build_options.name, build_options.version },
);

if (res.args.init != 0) return try grab.init(init.io, init.gpa, init.minimal.environ);

if (res.args.temp != 0 and res.args.path != null) {
std.log.err("Cannot specify both --temp and --path", .{});
Expand All @@ -107,29 +88,22 @@ pub fn main(init: std.process.Init) !void {
config.path = .{ .provided = path };
std.log.info("using {s} as path", .{path});
} else {
const path = init.minimal.environ.getPosix("GRAB_PATH") orelse {
std.log.err("unable to get GRAB_PATH, please set or use --temp or --path", .{});
std.process.exit(1);
};
const path = init.minimal.environ.getPosix("GRAB_PATH") orelse "";

if (path.len == 0) {
std.log.err("unable to get GRAB_PATH, please set or use --temp or --path", .{});
std.process.exit(1);
if (path.len != 0) {
config.path = .{ .provided = path };
std.log.debug("Using path from env var", .{});
}
config.path = .{ .provided = path };
std.log.debug("try to get path from env", .{});
}
if (res.args.remote != 0) {
config.action = .remote;
}

if (res.args.standard != 0) {
config.action = .standard;
if (config.getPath() == null) {
std.log.err("No path source path found, please set path in config file, see --init, set GRAB_PATH env var or use --temp or --path", .{});
std.process.exit(1);
}

if (res.args.shallow != 0) {
config.shallow = true;
}
config.action = if (res.args.remote != 0) .remote else config.action;
config.action = if (res.args.standard != 0) .standard else config.action;
config.shallow = if (res.args.shallow != 0) true else config.shallow;

try grab.setLocation(init.io, config);

Expand Down Expand Up @@ -160,7 +134,11 @@ pub fn main(init: std.process.Init) !void {
}
}
std.log.info("Finished", .{});
if (run_failure) std.process.exit(1);
if (run_failure) {
std.log.warn("Finished with errors", .{});
std.process.exit(1);
}
std.log.info("Finished", .{});
}

const gitOpts = struct { shallow: bool = false };
Expand Down
Loading
Loading