╭ changed ──────╮ │src/command.zig│ ╰───────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │22 \\haxy serve --web-listen 127.0.0.1:8080 --ssh-listen 127.0.0.1:8022 --data-dir /srv/git │ │23 \\use "none" for --ssh-listen to disable ssh. git clones over HTTP share the web UI's listener. │ │24 \\--web-url and --ssh-url set the public urls shown to users, like https://example.com and ssh://example.com.│ │25 + \\--max-repo-size caps each repo's on-disk size, like 1G, rejecting pushes past it. no limit by default. │ │26 , │ │27 }, │ │28 }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────╮ │89 .{"--ssh-listen"}, │ │90 .{"--web-url"}, │ │91 .{"--ssh-url"}, │ │92 + .{"--max-repo-size"},│ │93 .{"--data-dir"}, │ │94 }); │ │95 │ ╰─────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │213 // urls are joined with "/" + a path │ │214 if (cmd_args.get("--web-url")) |value_maybe| options.web_url = std.mem.trimEnd(u8, value_maybe orelse return error.WebUrlNeedsValue, "/"); │ │215 if (cmd_args.get("--ssh-url")) |value_maybe| options.ssh_url = std.mem.trimEnd(u8, value_maybe orelse return error.SshUrlNeedsValue, "/"); │ │216 + if (cmd_args.get("--max-repo-size")) |value_maybe| options.max_repo_size = try parseSize(value_maybe orelse return error.MaxRepoSizeNeedsValue);│ │217 if (cmd_args.get("--data-dir")) |value_maybe| options.data_dir = value_maybe orelse return error.DataDirNeedsValue; │ │218 return options; │ │219 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────╮ │220 │ │221 +// a byte count with an optional K, M or G suffix │ │222 +fn parseSize(value: []const u8) !u64 { │ │223 + if (value.len == 0) return error.InvalidSize; │ │224 + const shift: u6 = switch (value[value.len - 1]) { │ │225 + 'K', 'k' => 10, │ │226 + 'M', 'm' => 20, │ │227 + 'G', 'g' => 30, │ │228 + else => 0, │ │229 + }; │ │230 + const digits = if (shift == 0) value else value[0 .. value.len - 1]; │ │231 + const count = std.fmt.parseInt(u64, digits, 10) catch return error.InvalidSize; │ │232 + return std.math.shlExact(u64, count, shift) catch error.InvalidSize; │ │233 +} │ │234 + │ │235 /// parses the given args into a command if valid, and determines how it should be run│ │236 /// (via the TUI or CLI). │ │237 pub const CommandDispatch = union(enum) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯