╭ changed ──────╮ │src/command.zig│ ╰───────────────╯ ╭─────────────────────────────────╮ │4 const hash = xit.hash; │ │5 │ │6 pub const CommandKind = enum {│ │7 - upload_pack, │ │8 - receive_pack, │ │9 - http_backend, │ │7 serve, │ │8 ssh_helper, │ │9 }; │ ╰─────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────╮ │16 │ │17 fn commandHelp(command_kind: CommandKind) Help { │ │18 return switch (command_kind) { │ │22 - .upload_pack => .{ │ │23 - .name = "upload-pack", │ │24 - .descrip = │ │25 - \\send what is fetched from the repository. │ │26 - , │ │27 - .example = │ │28 - \\haxy upload-pack <directory> │ │29 - , │ │30 - }, │ │31 - .receive_pack => .{ │ │32 - .name = "receive-pack", │ │33 - .descrip = │ │34 - \\receive what is pushed into the repository. │ │35 - , │ │36 - .example = │ │37 - \\haxy receive-pack <directory> │ │38 - , │ │39 - }, │ │40 - .http_backend => .{ │ │41 - .name = "http-backend", │ │42 - .descrip = │ │43 - \\a CGI program forwarding receive-pack and upload-pack over HTTP.│ │44 - , │ │45 - .example = │ │46 - \\haxy http-backend │ │47 - , │ │48 - }, │ │19 .serve => .{ │ │20 .name = "serve", │ │21 .descrip = │ ╰──────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │190 /// if any additional allocation needs to be done, the arena inside the cmd args will be used.│ │191 pub fn Command(comptime repo_kind: rp.RepoKind, comptime hash_kind: hash.HashKind) type { │ │192 return union(CommandKind) { │ │223 - upload_pack: struct { │ │224 - dir: []const u8, │ │225 - options: xit.net_server_upload_pack.Options, │ │226 - }, │ │227 - receive_pack: struct { │ │228 - dir: []const u8, │ │229 - options: xit.net_server_receive_pack.Options, │ │230 - }, │ │231 - http_backend, │ │193 serve: struct { │ │194 http_listen: []const u8, │ │195 ssh_listen: ?[]const u8, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │204 pub fn initMaybe(cmd_args: *CommandArgs) !?Command(repo_kind, hash_kind) { │ │205 const command_kind = cmd_args.command_kind orelse return null; │ │206 switch (command_kind) { │ │246 - .upload_pack => { │ │247 - if (cmd_args.positional_args.len != 1) return null; │ │248 - │ │249 - return .{ .upload_pack = .{ │ │250 - .dir = cmd_args.positional_args[0], │ │251 - .options = .{ │ │252 - .advertise_refs = cmd_args.contains("--http-backend-info-refs"), │ │253 - .is_stateless = cmd_args.contains("--stateless-rpc"), │ │254 - }, │ │255 - } }; │ │256 - }, │ │257 - .receive_pack => { │ │258 - if (cmd_args.positional_args.len != 1) return null; │ │259 - │ │260 - return .{ .receive_pack = .{ │ │261 - .dir = cmd_args.positional_args[0], │ │262 - .options = .{ │ │263 - .skip_connectivity_check = cmd_args.contains("--skip-connectivity-check"),│ │264 - .advertise_refs = cmd_args.contains("--http-backend-info-refs"), │ │265 - .is_stateless = cmd_args.contains("--stateless-rpc"), │ │266 - }, │ │267 - } }; │ │268 - }, │ │269 - .http_backend => { │ │270 - if (cmd_args.positional_args.len != 0) return null; │ │271 - │ │272 - return .http_backend; │ │273 - }, │ │207 .serve => { │ │208 if (cmd_args.positional_args.len != 0) return null; │ │209 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───╮ │src/main.zig│ ╰────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────╮ │74 }, │ │75 }, │ │76 .help => |cmd_kind_maybe| try cmd.printHelp(cmd_kind_maybe, run_opts.out), │ │77 - .cli => |cli_cmd| { │ │78 - if (cli_cmd == .serve) { │ │77 + .cli => |cli_cmd| switch (cli_cmd) { │ │78 + .serve => { │ │79 try serve.run(repo_kind, any_repo_opts, io, allocator, cwd_path, .{│ │80 .http_listen = cli_cmd.serve.http_listen, │ │81 .ssh_listen = cli_cmd.serve.ssh_listen, │ ╰───────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────╮ │82 .project_root = cli_cmd.serve.project_root, │ │83 }, run_opts.err); │ │84 - return; │ │85 - } │ │86 - if (cli_cmd == .ssh_helper) { │ │84 + }, │ │85 + .ssh_helper => { │ │86 try ssh_helper.run(io, allocator, .{ │ │87 .ssh_connect = cli_cmd.ssh_helper.ssh_connect,│ │88 .service = cli_cmd.ssh_helper.service, │ ╰──────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 89 .dir = cli_cmd.ssh_helper.dir, │ │ 90 }, run_opts.environ_map); │ │ 92 - return; │ │ 93 - } │ │ 94 - │ │ 95 - // some commands allow the path to be specified. for all others, just use the cwd path. │ │ 96 - const work_path = switch (cli_cmd) { │ │ 97 - .upload_pack => |upload_pack| try std.fs.path.resolve(allocator, &.{ cwd_path, upload_pack.dir }), │ │ 98 - .receive_pack => |receive_pack| try std.fs.path.resolve(allocator, &.{ cwd_path, receive_pack.dir }), │ │ 99 - .http_backend => xit.net_server_http_backend.resolveDir(allocator, cwd_path, run_opts.environ_map) catch { │ │100 - var http_stdout_buf: [any_repo_opts.buffer_size]u8 = undefined; │ │101 - var http_stdout_writer = std.Io.File.stdout().writer(io, &http_stdout_buf); │ │102 - try xit.net_server_http_backend.sendNotFound(&http_stdout_writer.interface); │ │103 - return; │ │104 - }, │ │105 - .serve => unreachable, │ │106 - .ssh_helper => unreachable, │ │107 - }; │ │108 - defer allocator.free(work_path); │ │109 - │ │110 - if (any_repo_opts.hash) |hash_kind| { │ │111 - var repo = try rp.Repo(repo_kind, any_repo_opts.toRepoOptsWithHash(hash_kind)).open(io, allocator, .{ .path = work_path }); │ │112 - defer repo.deinit(io, allocator); │ │113 - try runCommand(repo_kind, any_repo_opts.toRepoOptsWithHash(hash_kind), &repo, io, allocator, cli_cmd, run_opts); │ │114 - } else { │ │115 - // if no hash was specified, use AnyRepo to detect the hash being used │ │116 - var any_repo = try rp.AnyRepo(repo_kind, any_repo_opts).open(io, allocator, .{ .path = work_path }); │ │117 - defer any_repo.deinit(io, allocator); │ │118 - switch (any_repo) { │ │119 - inline else => |*repo| { │ │120 - const cmd_maybe = try cmd.Command(repo.self_repo_kind, repo.self_repo_opts.hash).initMaybe(&cmd_args); │ │121 - try runCommand(repo.self_repo_kind, repo.self_repo_opts, repo, io, allocator, cmd_maybe orelse return error.InvalidCommand, run_opts);│ │122 - }, │ │123 - } │ │124 - } │ │125 - }, │ │126 - } │ │127 -} │ │128 - │ │129 -fn runCommand( │ │130 - comptime repo_kind: rp.RepoKind, │ │131 - comptime repo_opts: rp.RepoOpts(repo_kind), │ │132 - repo: *rp.Repo(repo_kind, repo_opts), │ │133 - io: std.Io, │ │134 - allocator: std.mem.Allocator, │ │135 - command: cmd.Command(repo_kind, repo_opts.hash), │ │136 - run_opts: RunOpts, │ │137 -) !void { │ │138 - switch (command) { │ │139 - .upload_pack => |upload_pack_cmd| { │ │140 - var options = upload_pack_cmd.options; │ │141 - options.protocol_version = xit.net_server_common.detectProtocolVersion(run_opts.environ_map); │ │142 - var stdin_buf: [repo_opts.net_buffer_size]u8 = undefined; │ │143 - var stdin_reader = std.Io.File.stdin().reader(io, &stdin_buf); │ │144 - var stdout_buf: [repo_opts.net_buffer_size]u8 = undefined; │ │145 - var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buf); │ │146 - try repo.uploadPack(io, allocator, &stdin_reader.interface, &stdout_writer.interface, options); │ │147 - }, │ │148 - .receive_pack => |receive_pack_cmd| { │ │149 - var options = receive_pack_cmd.options; │ │150 - options.protocol_version = xit.net_server_common.detectProtocolVersion(run_opts.environ_map); │ │151 - var stdin_buf: [repo_opts.net_buffer_size]u8 = undefined; │ │152 - var stdin_reader = std.Io.File.stdin().reader(io, &stdin_buf); │ │153 - var stdout_buf: [repo_opts.net_buffer_size]u8 = undefined; │ │154 - var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buf); │ │155 - try repo.receivePack(io, allocator, &stdin_reader.interface, &stdout_writer.interface, options); │ │ 91 + }, │ │ 92 }, │ │157 - .http_backend => { │ │158 - const environ_map = run_opts.environ_map; │ │159 - var path_buf: [std.Io.Dir.max_path_bytes]u8 = undefined; │ │160 - const path = try xit.net_server_http_backend.resolveRepoPath(environ_map, &path_buf); │ │161 - │ │162 - var stdout_buf: [repo_opts.net_buffer_size]u8 = undefined; │ │163 - var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buf); │ │164 - │ │165 - const handler, const suffix = for (&xit.net_server_http_backend.routes) |*svc| { │ │166 - if (std.mem.endsWith(u8, path, svc.suffix)) │ │167 - break .{ svc.handler, svc.suffix }; │ │168 - } else { │ │169 - try xit.net_server_http_backend.sendNotFound(&stdout_writer.interface); │ │170 - return; │ │171 - }; │ │172 - │ │173 - const request_method: std.http.Method = blk: { │ │174 - const method_str = environ_map.get("REQUEST_METHOD") orelse break :blk .GET; │ │175 - const method = std.meta.stringToEnum(std.http.Method, method_str) orelse break :blk .GET; │ │176 - break :blk if (method == .HEAD) .GET else method; │ │177 - }; │ │178 - │ │179 - var stdin_buf: [repo_opts.net_buffer_size]u8 = undefined; │ │180 - var stdin_reader = std.Io.File.stdin().reader(io, &stdin_buf); │ │181 - try repo.httpBackend(io, allocator, &stdin_reader.interface, &stdout_writer.interface, .cgi, .{ │ │182 - .request_method = request_method, │ │183 - .handler = handler, │ │184 - .suffix = suffix, │ │185 - .query_string = environ_map.get("QUERY_STRING") orelse "", │ │186 - .content_type = environ_map.get("CONTENT_TYPE") orelse "", │ │187 - .has_remote_user = environ_map.get("REMOTE_USER") != null, │ │188 - .protocol_version = xit.net_server_common.detectProtocolVersion(environ_map), │ │189 - }); │ │190 - }, │ │191 - .serve => unreachable, │ │192 - .ssh_helper => unreachable, │ │ 93 } │ │ 94 } │ │ 95 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───╮ │src/test.zig│ ╰────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │10 test "fetch small" { │ │11 const io = std.testing.io; │ │12 const allocator = std.testing.allocator; │ │13 - try testFetch(.xit, .xit, .{ .wire = .http }, 3004, false, io, allocator); │ │14 - try testFetch(.xit, .xit, .{ .wire = .http }, 3005, true, io, allocator); │ │13 + try testFetch(.xit, .{ .wire = .http }, 3000, io, allocator); │ │14 if (.windows != builtin.os.tag) { │ │16 - try testFetch(.xit, .xit, .{ .wire = .ssh }, 3006, false, io, allocator);│ │17 - try testFetch(.xit, .xit, .{ .wire = .ssh }, 3007, true, io, allocator); │ │15 + try testFetch(.xit, .{ .wire = .ssh }, 3001, io, allocator); │ │16 } │ │17 } │ │18 │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │19 test "push small" { │ │20 const io = std.testing.io; │ │21 const allocator = std.testing.allocator; │ │24 - try testPush(.xit, .xit, .{ .wire = .http }, 3010, false, io, allocator); │ │25 - try testPush(.xit, .xit, .{ .wire = .http }, 3011, true, io, allocator); │ │22 + try testPush(.xit, .{ .wire = .http }, 3010, io, allocator); │ │23 if (.windows != builtin.os.tag) { │ │27 - try testPush(.xit, .xit, .{ .wire = .ssh }, 3012, false, io, allocator);│ │28 - try testPush(.xit, .xit, .{ .wire = .ssh }, 3013, true, io, allocator); │ │24 + try testPush(.xit, .{ .wire = .ssh }, 3011, io, allocator); │ │25 } │ │26 } │ │27 │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │28 test "push creates missing repo under serve" { │ │29 const io = std.testing.io; │ │30 const allocator = std.testing.allocator; │ │35 - try testPushCreatesMissingRepo(.xit, .xit, .{ .wire = .http }, 3014, io, allocator); │ │31 + try testPushCreatesMissingRepo(.xit, .{ .wire = .http }, 3020, io, allocator); │ │32 if (.windows != builtin.os.tag) { │ │37 - try testPushCreatesMissingRepo(.xit, .xit, .{ .wire = .ssh }, 3015, io, allocator);│ │33 + try testPushCreatesMissingRepo(.xit, .{ .wire = .ssh }, 3021, io, allocator); │ │34 } │ │35 } │ │36 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯