╭ changed ───╮ │src/test.zig│ ╰────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 1 -const std = @import("std"); │ │ 2 -const builtin = @import("builtin"); │ │ 3 -const xit = @import("xit"); │ │ 4 -const rp = xit.repo; │ │ 5 -const rf = xit.ref; │ │ 6 -const work = xit.workdir; │ │ 7 -const hash = xit.hash; │ │ 8 -const net = xit.net; │ │ 9 - │ │ 10 -test "fetch small" { │ │ 11 - const io = std.testing.io; │ │ 12 - const allocator = std.testing.allocator; │ │ 13 - try testFetch(.xit, .{ .wire = .http }, 3000, io, allocator); │ │ 14 - if (.windows != builtin.os.tag) { │ │ 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; │ │ 22 - try testPush(.xit, .{ .wire = .http }, 3010, io, allocator); │ │ 23 - if (.windows != builtin.os.tag) { │ │ 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; │ │ 31 - try testPushCreatesMissingRepo(.xit, .{ .wire = .http }, 3020, io, allocator); │ │ 32 - if (.windows != builtin.os.tag) { │ │ 33 - try testPushCreatesMissingRepo(.xit, .{ .wire = .ssh }, 3021, io, allocator); │ │ 34 - } │ │ 35 -} │ │ 36 - │ │ 37 -test "clone small" { │ │ 38 - const io = std.testing.io; │ │ 39 - const allocator = std.testing.allocator; │ │ 40 - try testClone(.xit, .{ .wire = .http }, 3030, false, io, allocator); │ │ 41 - if (.windows != builtin.os.tag) { │ │ 42 - try testClone(.xit, .{ .wire = .ssh }, 3031, false, io, allocator); │ │ 43 - } │ │ 44 -} │ │ 45 - │ │ 46 -test "clone small subprocess" { │ │ 47 - const io = std.testing.io; │ │ 48 - const allocator = std.testing.allocator; │ │ 49 - try testClone(.git, .{ .wire = .http }, 3040, true, io, allocator); │ │ 50 - if (.windows != builtin.os.tag) { │ │ 51 - try testClone(.git, .{ .wire = .ssh }, 3041, true, io, allocator); │ │ 52 - } │ │ 53 -} │ │ 54 -test "fetch large subprocess" { │ │ 55 - const io = std.testing.io; │ │ 56 - const allocator = std.testing.allocator; │ │ 57 - try testFetchLarge(.git, .{ .wire = .http }, 3050, true, io, allocator); │ │ 58 - if (.windows != builtin.os.tag) { │ │ 59 - try testFetchLarge(.git, .{ .wire = .ssh }, 3051, true, io, allocator); │ │ 60 - } │ │ 61 -} │ │ 62 -test "push large subprocess" { │ │ 63 - const io = std.testing.io; │ │ 64 - const allocator = std.testing.allocator; │ │ 65 - try testPushLarge(.git, .{ .wire = .http }, 3060, true, io, allocator); │ │ 66 - if (.windows != builtin.os.tag) { │ │ 67 - try testPushLarge(.git, .{ .wire = .ssh }, 3061, true, io, allocator); │ │ 68 - } │ │ 69 -} │ │ 70 - │ │ 71 -fn Server( │ │ 72 - comptime transport_def: net.TransportDefinition, │ │ 73 - comptime temp_dir_name: []const u8, │ │ 74 - comptime port: u16, │ │ 75 -) type { │ │ 76 - return struct { │ │ 77 - core: Core, │ │ 78 - │ │ 79 - const Core = switch (transport_def) { │ │ 80 - .file => void, │ │ 81 - .wire => |wire_kind| switch (wire_kind) { │ │ 82 - .http => struct { │ │ 83 - io: std.Io, │ │ 84 - allocator: std.mem.Allocator, │ │ 85 - process: ?std.process.Child, │ │ 86 - }, │ │ 87 - .raw => unreachable, │ │ 88 - .ssh => struct { │ │ 89 - io: std.Io, │ │ 90 - allocator: std.mem.Allocator, │ │ 91 - sshd_process: ?std.process.Child, │ │ 92 - serve_process: ?std.process.Child, │ │ 93 - }, │ │ 94 - }, │ │ 95 - }; │ │ 96 - │ │ 97 - fn init( │ │ 98 - io: std.Io, │ │ 99 - allocator: std.mem.Allocator, │ │ 100 - ) !Server(transport_def, temp_dir_name, port) { │ │ 101 - switch (transport_def) { │ │ 102 - .file => return .{ .core = {} }, │ │ 103 - .wire => |wire_kind| switch (wire_kind) { │ │ 104 - .http => return .{ │ │ 105 - .core = .{ │ │ 106 - .io = io, │ │ 107 - .allocator = allocator, │ │ 108 - .process = null, │ │ 109 - }, │ │ 110 - }, │ │ 111 - .raw => unreachable, │ │ 112 - .ssh => { │ │ 113 - // create priv host key │ │ 114 - const host_key_file = try std.Io.Dir.cwd().createFile(io, temp_dir_name ++ "/host_key", .{}); │ │ 115 - defer host_key_file.close(io); │ │ 116 - try host_key_file.writeStreamingAll(io, │ │ 117 - \\-----BEGIN OPENSSH PRIVATE KEY----- │ │ 118 - \\b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS │ │ 119 - \\1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQS1ppUfk8n7yvVKEgz3tXjt4q76VGuj │ │ 120 - \\LcQlRwmogzovV40LLcX0aTObZlQaLWfzJMNpCa/ztMpQlr86nsarE4lEAAAAqLe43zK3uN │ │ 121 - \\8yAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLWmlR+TyfvK9UoS │ │ 122 - \\DPe1eO3irvpUa6MtxCVHCaiDOi9XjQstxfRpM5tmVBotZ/Mkw2kJr/O0ylCWvzqexqsTiU │ │ 123 - \\QAAAAgQ+LCk30ZNJxb2Da5JL+QOFWCMf7bgXCWcEzhEGGvFWYAAAALcmFkYXJAcm9hcmsB │ │ 124 - \\AgMEBQ== │ │ 125 - \\-----END OPENSSH PRIVATE KEY----- │ │ 126 - \\ │ │ 127 - ); │ │ 128 - if (.windows != builtin.os.tag) { │ │ 129 - try host_key_file.setPermissions(io, @enumFromInt(0o600)); │ │ 130 - } │ │ 131 - │ │ 132 - // create priv client key │ │ 133 - const priv_key_file = try std.Io.Dir.cwd().createFile(io, temp_dir_name ++ "/key", .{}); │ │ 134 - defer priv_key_file.close(io); │ │ 135 - try priv_key_file.writeStreamingAll(io, │ │ 136 - \\-----BEGIN OPENSSH PRIVATE KEY----- │ │ 137 - \\b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW │ │ 138 - \\QyNTUxOQAAACCniLPJiaooAWecvOCeAjoJwCSeWxzysvpTNkpYjF22JgAAAJA+7hikPu4Y │ │ 139 - \\pAAAAAtzc2gtZWQyNTUxOQAAACCniLPJiaooAWecvOCeAjoJwCSeWxzysvpTNkpYjF22Jg │ │ 140 - \\AAAEDVlopOMnKt/7by/IA8VZvQXUS/O6VLkixOqnnahUdPCKeIs8mJqigBZ5y84J4COgnA │ │ 141 - \\JJ5bHPKy+lM2SliMXbYmAAAAC3JhZGFyQHJvYXJrAQI= │ │ 142 - \\-----END OPENSSH PRIVATE KEY----- │ │ 143 - \\ │ │ 144 - ); │ │ 145 - if (.windows != builtin.os.tag) { │ │ 146 - try priv_key_file.setPermissions(io, @enumFromInt(0o600)); │ │ 147 - } │ │ 148 - │ │ 149 - // create pub key │ │ 150 - const pub_key_file = try std.Io.Dir.cwd().createFile(io, temp_dir_name ++ "/key.pub", .{}); │ │ 151 - defer pub_key_file.close(io); │ │ 152 - try pub_key_file.writeStreamingAll(io, │ │ 153 - \\ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKeIs8mJqigBZ5y84J4COgnAJJ5bHPKy+lM2SliMXbYm radar@roark │ │ 154 - \\ │ │ 155 - ); │ │ 156 - if (.windows != builtin.os.tag) { │ │ 157 - try pub_key_file.setPermissions(io, @enumFromInt(0o600)); │ │ 158 - } │ │ 159 - │ │ 160 - // create authorized_keys file │ │ 161 - const auth_keys_file = try std.Io.Dir.cwd().createFile(io, temp_dir_name ++ "/authorized_keys", .{}); │ │ 162 - defer auth_keys_file.close(io); │ │ 163 - try auth_keys_file.writeStreamingAll(io, │ │ 164 - \\ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKeIs8mJqigBZ5y84J4COgnAJJ5bHPKy+lM2SliMXbYm radar@roark │ │ 165 - \\ │ │ 166 - ); │ │ 167 - if (.windows != builtin.os.tag) { │ │ 168 - try auth_keys_file.setPermissions(io, @enumFromInt(0o600)); │ │ 169 - } │ │ 170 - │ │ 171 - // create known_hosts file │ │ 172 - const known_hosts_file = try std.Io.Dir.cwd().createFile(io, temp_dir_name ++ "/known_hosts", .{}); │ │ 173 - defer known_hosts_file.close(io); │ │ 174 - const port_str = std.fmt.comptimePrint("{}", .{port}); │ │ 175 - try known_hosts_file.writeStreamingAll(io, "[localhost]:" ++ port_str ++ " ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLWmlR+TyfvK9UoSDPe1eO3irvpUa6MtxCVHCaiDOi9XjQstxfRpM5tmVBotZ/Mkw2kJr/O0ylCWvzqexqsTiUQ=");│ │ 176 - if (.windows != builtin.os.tag) { │ │ 177 - try known_hosts_file.setPermissions(io, @enumFromInt(0o600)); │ │ 178 - } │ │ 179 - │ │ 180 - // create sshd_config file │ │ 181 - const sshd_config_file = try std.Io.Dir.cwd().createFile(io, temp_dir_name ++ "/sshd_config", .{}); │ │ 182 - defer sshd_config_file.close(io); │ │ 183 - try sshd_config_file.writeStreamingAll(io, │ │ 184 - \\AuthenticationMethods publickey │ │ 185 - \\PubkeyAuthentication yes │ │ 186 - \\PasswordAuthentication no │ │ 187 - \\StrictModes no │ │ 188 - \\ │ │ 189 - ); │ │ 190 - if (.windows != builtin.os.tag) { │ │ 191 - try sshd_config_file.setPermissions(io, @enumFromInt(0o600)); │ │ 192 - } │ │ 193 - │ │ 194 - // create sshd.sh contents │ │ 195 - const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │ 196 - defer allocator.free(cwd_path); │ │ 197 - const host_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "host_key" }); │ │ 198 - defer allocator.free(host_key_path); │ │ 199 - const auth_keys_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "authorized_keys" }); │ │ 200 - defer allocator.free(auth_keys_path); │ │ 201 - const sshd_contents = try std.fmt.allocPrint( │ │ 202 - allocator, │ │ 203 - "#!/bin/sh\nexec $(which sshd) -p {} -f sshd_config -h \"{s}\" -D -e -o AuthorizedKeysFile=\"{s}\"", │ │ 204 - .{ port, host_key_path, auth_keys_path }, │ │ 205 - ); │ │ 206 - defer allocator.free(sshd_contents); │ │ 207 - │ │ 208 - // if path has a space char, it fucks up sshd │ │ 209 - try std.testing.expect(null == std.mem.indexOfScalar(u8, auth_keys_path, ' ')); │ │ 210 - │ │ 211 - // create sshd.sh │ │ 212 - { │ │ 213 - const sshd_file = try std.Io.Dir.cwd().createFile(io, temp_dir_name ++ "/sshd.sh", .{}); │ │ 214 - defer sshd_file.close(io); │ │ 215 - try sshd_file.writeStreamingAll(io, sshd_contents); │ │ 216 - if (.windows != builtin.os.tag) { │ │ 217 - try sshd_file.setPermissions(io, .executable_file); │ │ 218 - } │ │ 219 - } │ │ 220 - return .{ │ │ 221 - .core = .{ │ │ 222 - .io = io, │ │ 223 - .allocator = allocator, │ │ 224 - .sshd_process = null, │ │ 225 - .serve_process = null, │ │ 226 - }, │ │ 227 - }; │ │ 228 - }, │ │ 229 - }, │ │ 230 - } │ │ 231 - } │ │ 232 - │ │ 233 - fn start(self: *Server(transport_def, temp_dir_name, port)) !void { │ │ 234 - switch (transport_def) { │ │ 235 - .file => {}, │ │ 236 - .wire => |wire_kind| switch (wire_kind) { │ │ 237 - .http => { │ │ 238 - std.debug.assert(self.core.process == null); │ │ 239 - │ │ 240 - const cwd_path = try std.process.currentPathAlloc(self.core.io, self.core.allocator); │ │ 241 - defer self.core.allocator.free(cwd_path); │ │ 242 - const haxy_path = try std.fs.path.join(self.core.allocator, &.{ cwd_path, "zig-out/bin/haxy" }); │ │ 243 - defer self.core.allocator.free(haxy_path); │ │ 244 - const listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port}); │ │ 245 - │ │ 246 - self.core.process = try std.process.spawn(self.core.io, .{ │ │ 247 - .argv = &.{ haxy_path, "serve", "--http-listen", listen_arg, "--data-dir", temp_dir_name }, │ │ 248 - .stdin = .ignore, │ │ 249 - .stdout = .ignore, │ │ 250 - .stderr = .ignore, │ │ 251 - }); │ │ 252 - }, │ │ 253 - .raw => unreachable, │ │ 254 - .ssh => { │ │ 255 - std.debug.assert(self.core.sshd_process == null); │ │ 256 - std.debug.assert(self.core.serve_process == null); │ │ 257 - │ │ 258 - const cwd_path = try std.process.currentPathAlloc(self.core.io, self.core.allocator); │ │ 259 - defer self.core.allocator.free(cwd_path); │ │ 260 - const haxy_path = try std.fs.path.join(self.core.allocator, &.{ cwd_path, "zig-out/bin/haxy" }); │ │ 261 - defer self.core.allocator.free(haxy_path); │ │ 262 - const ssh_listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port + 1000}); │ │ 263 - const http_listen_arg = std.fmt.comptimePrint("127.0.0.1:{}", .{port + 2000}); │ │ 264 - │ │ 265 - self.core.serve_process = try std.process.spawn(self.core.io, .{ │ │ 266 - .argv = &.{ haxy_path, "serve", "--http-listen", http_listen_arg, "--ssh-listen", ssh_listen_arg, "--data-dir", temp_dir_name }, │ │ 267 - .stdin = .ignore, │ │ 268 - .stdout = .ignore, │ │ 269 - .stderr = .ignore, │ │ 270 - }); │ │ 271 - │ │ 272 - const serve_address = try std.Io.net.IpAddress.parseIp4("127.0.0.1", port + 1000); │ │ 273 - for (0..50) |_| { │ │ 274 - const stream = serve_address.connect(self.core.io, .{ .mode = .stream }) catch { │ │ 275 - try std.Io.sleep(self.core.io, .fromMilliseconds(100), .real); │ │ 276 - continue; │ │ 277 - }; │ │ 278 - var probe_buf = [_]u8{0} ** 16; │ │ 279 - var probe_writer = stream.writer(self.core.io, &probe_buf); │ │ 280 - try probe_writer.interface.writeAll("probe\n"); │ │ 281 - try probe_writer.interface.flush(); │ │ 282 - stream.close(self.core.io); │ │ 283 - break; │ │ 284 - } │ │ 285 - │ │ 286 - self.core.sshd_process = try std.process.spawn(self.core.io, .{ │ │ 287 - .argv = &.{"./sshd.sh"}, │ │ 288 - .cwd = .{ .path = temp_dir_name }, │ │ 289 - .stdin = .pipe, │ │ 290 - .stdout = .ignore, │ │ 291 - .stderr = .ignore, │ │ 292 - }); │ │ 293 - }, │ │ 294 - }, │ │ 295 - } │ │ 296 - │ │ 297 - if (transport_def == .wire) { │ │ 298 - const address = try std.Io.net.IpAddress.parseIp4("127.0.0.1", port); │ │ 299 - for (0..50) |_| { │ │ 300 - const stream = address.connect(self.core.io, .{ .mode = .stream }) catch { │ │ 301 - try std.Io.sleep(self.core.io, .fromMilliseconds(100), .real); │ │ 302 - continue; │ │ 303 - }; │ │ 304 - stream.close(self.core.io); │ │ 305 - break; │ │ 306 - } │ │ 307 - } │ │ 308 - } │ │ 309 - │ │ 310 - fn stop(self: *Server(transport_def, temp_dir_name, port)) void { │ │ 311 - switch (transport_def) { │ │ 312 - .file => {}, │ │ 313 - .wire => |wire_kind| switch (wire_kind) { │ │ 314 - .http => if (self.core.process) |*process| { │ │ 315 - _ = process.kill(self.core.io); │ │ 316 - self.core.process = null; │ │ 317 - }, │ │ 318 - .raw => unreachable, │ │ 319 - .ssh => { │ │ 320 - if (self.core.sshd_process) |*process| { │ │ 321 - _ = process.kill(self.core.io); │ │ 322 - self.core.sshd_process = null; │ │ 323 - } │ │ 324 - if (self.core.serve_process) |*process| { │ │ 325 - _ = process.kill(self.core.io); │ │ 326 - self.core.serve_process = null; │ │ 327 - } │ │ 328 - }, │ │ 329 - }, │ │ 330 - } │ │ 331 - } │ │ 332 - }; │ │ 333 -} │ │ 334 - │ │ 335 -fn uploadPackCommand( │ │ 336 - comptime is_ssh: bool, │ │ 337 - allocator: std.mem.Allocator, │ │ 338 - cwd_path: []const u8, │ │ 339 - comptime port: u16, │ │ 340 -) ![]const u8 { │ │ 341 - return if (is_ssh) │ │ 342 - std.fmt.allocPrint(allocator, "{s}/zig-out/bin/haxy ssh-helper --ssh-connect 127.0.0.1:{} --service upload-pack", .{ cwd_path, port + 1000 }) │ │ 343 - else │ │ 344 - allocator.dupe(u8, "git-upload-pack"); │ │ 345 -} │ │ 346 - │ │ 347 -fn receivePackCommand( │ │ 348 - comptime is_ssh: bool, │ │ 349 - allocator: std.mem.Allocator, │ │ 350 - cwd_path: []const u8, │ │ 351 - comptime port: u16, │ │ 352 -) ![]const u8 { │ │ 353 - return if (is_ssh) │ │ 354 - std.fmt.allocPrint(allocator, "{s}/zig-out/bin/haxy ssh-helper --ssh-connect 127.0.0.1:{} --service receive-pack", .{ cwd_path, port + 1000 }) │ │ 355 - else │ │ 356 - allocator.dupe(u8, "git-receive-pack"); │ │ 357 -} │ │ 358 - │ │ 359 -fn testFetch( │ │ 360 - comptime repo_kind: rp.RepoKind, │ │ 361 - comptime transport_def: net.TransportDefinition, │ │ 362 - comptime port: u16, │ │ 363 - io: std.Io, │ │ 364 - allocator: std.mem.Allocator, │ │ 365 -) !void { │ │ 366 - const temp_dir_name = "temp-testnet-fetch"; │ │ 367 - │ │ 368 - // create the temp dir │ │ 369 - const cwd = std.Io.Dir.cwd(); │ │ 370 - var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); │ │ 371 - if (temp_dir_or_err) |*temp_dir| { │ │ 372 - temp_dir.close(io); │ │ 373 - try cwd.deleteTree(io, temp_dir_name); │ │ 374 - } else |_| {} │ │ 375 - var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); │ │ 376 - defer cwd.deleteTree(io, temp_dir_name) catch {}; │ │ 377 - defer temp_dir.close(io); │ │ 378 - │ │ 379 - // init server │ │ 380 - var server = try Server(transport_def, temp_dir_name, port).init(io, allocator); │ │ 381 - try server.start(); │ │ 382 - defer server.stop(); │ │ 383 - │ │ 384 - const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │ 385 - defer allocator.free(cwd_path); │ │ 386 - │ │ 387 - const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "repos", "server" }); │ │ 388 - defer allocator.free(server_path); │ │ 389 - │ │ 390 - var server_repo = try rp.Repo(.xit, .{ .is_test = true }).init(io, allocator, .{ .path = server_path }); │ │ 391 - defer server_repo.deinit(io, allocator); │ │ 392 - │ │ 393 - // make a commit │ │ 394 - const commit1 = blk: { │ │ 395 - const hello_txt = try server_repo.core.work_dir.createFile(io, "hello.txt", .{ .truncate = true }); │ │ 396 - defer hello_txt.close(io); │ │ 397 - try hello_txt.writeStreamingAll(io, "hello, world!"); │ │ 398 - try server_repo.add(io, allocator, &.{"hello.txt"}); │ │ 399 - break :blk try server_repo.commit(io, allocator, .{ .message = "let there be light" }); │ │ 400 - }; │ │ 401 - │ │ 402 - // export server repo │ │ 403 - { │ │ 404 - const export_file = try server_repo.core.repo_dir.createFile(io, "git-daemon-export-ok", .{}); │ │ 405 - defer export_file.close(io); │ │ 406 - │ │ 407 - try server_repo.addConfig(io, allocator, .{ .name = "uploadpack.allowAnySHA1InWant", .value = "true" }); │ │ 408 - } │ │ 409 - │ │ 410 - // add a tag │ │ 411 - _ = try server_repo.addTag(io, allocator, .{ .name = "1.0.0", .message = "hi" }); │ │ 412 - │ │ 413 - const client_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "client" }); │ │ 414 - defer allocator.free(client_path); │ │ 415 - │ │ 416 - var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).init(io, allocator, .{ .path = client_path }); │ │ 417 - defer client_repo.deinit(io, allocator); │ │ 418 - │ │ 419 - // add remote │ │ 420 - { │ │ 421 - if (.windows == builtin.os.tag) { │ │ 422 - std.mem.replaceScalar(u8, server_path, '\\', '/'); │ │ 423 - } │ │ 424 - const separator = if (server_path[0] == '/') "" else "/"; │ │ 425 - │ │ 426 - const remote_url = switch (transport_def) { │ │ 427 - //.file => try std.fmt.allocPrint(allocator, "file://{s}{s}", .{ separator, server_path }), │ │ 428 - .file => try std.fmt.allocPrint(allocator, "../server", .{}), // relative file paths work too │ │ 429 - .wire => |wire_kind| switch (wire_kind) { │ │ 430 - .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{port}), │ │ 431 - .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/server", .{port}), │ │ 432 - .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ port, separator, server_path }), │ │ 433 - }, │ │ 434 - }; │ │ 435 - defer allocator.free(remote_url); │ │ 436 - │ │ 437 - try client_repo.addRemote(io, allocator, .{ .name = "origin", .value = remote_url }); │ │ 438 - try client_repo.addConfig(io, allocator, .{ .name = "branch.master.remote", .value = "origin" }); │ │ 439 - } │ │ 440 - │ │ 441 - // create refspec with oid as a test │ │ 442 - const oid_refspec = try std.fmt.allocPrint(allocator, "+{s}:refs/heads/foo", .{&commit1}); │ │ 443 - defer allocator.free(oid_refspec); │ │ 444 - │ │ 445 - const refspecs = &.{ │ │ 446 - "+refs/heads/master:refs/heads/master", │ │ 447 - oid_refspec, │ │ 448 - }; │ │ 449 - │ │ 450 - const is_ssh = switch (transport_def) { │ │ 451 - .file => false, │ │ 452 - .wire => |wire_kind| .ssh == wire_kind, │ │ 453 - }; │ │ 454 - const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { │ │ 455 - const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); │ │ 456 - defer allocator.free(known_hosts_path); │ │ 457 - │ │ 458 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │ 459 - defer allocator.free(priv_key_path); │ │ 460 - │ │ 461 - break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); │ │ 462 - } else null; │ │ 463 - defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); │ │ 464 - │ │ 465 - const upload_pack_command = try uploadPackCommand(is_ssh, allocator, cwd_path, port); │ │ 466 - defer allocator.free(upload_pack_command); │ │ 467 - │ │ 468 - try client_repo.fetch( │ │ 469 - io, │ │ 470 - allocator, │ │ 471 - "origin", │ │ 472 - .{ .refspecs = refspecs, .wire = .{ .ssh = .{ │ │ 473 - .command = ssh_cmd_maybe, │ │ 474 - .upload_pack_command = upload_pack_command, │ │ 475 - } } }, │ │ 476 - ); │ │ 477 - │ │ 478 - // update the working dir │ │ 479 - try client_repo.restore(io, allocator, "."); │ │ 480 - │ │ 481 - // make sure fetch was successful │ │ 482 - { │ │ 483 - const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); │ │ 484 - defer hello_txt.close(io); │ │ 485 - │ │ 486 - try std.testing.expect(null != try client_repo.readRef(io, .{ .kind = .tag, .name = "1.0.0" })); │ │ 487 - try std.testing.expect(null != try client_repo.readRef(io, .{ .kind = .head, .name = "foo" })); │ │ 488 - │ │ 489 - const oid_master = (try client_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │ 490 - try std.testing.expectEqualStrings(&commit1, &oid_master); │ │ 491 - } │ │ 492 - │ │ 493 - // make another commit │ │ 494 - const commit2 = blk: { │ │ 495 - const goodbye_txt = try server_repo.core.work_dir.createFile(io, "goodbye.txt", .{ .truncate = true }); │ │ 496 - defer goodbye_txt.close(io); │ │ 497 - try goodbye_txt.writeStreamingAll(io, "goodbye, world!"); │ │ 498 - try server_repo.add(io, allocator, &.{"goodbye.txt"}); │ │ 499 - break :blk try server_repo.commit(io, allocator, .{ .message = "goodbye" }); │ │ 500 - }; │ │ 501 - │ │ 502 - try client_repo.fetch( │ │ 503 - io, │ │ 504 - allocator, │ │ 505 - "origin", │ │ 506 - .{ .refspecs = refspecs, .wire = .{ .ssh = .{ │ │ 507 - .command = ssh_cmd_maybe, │ │ 508 - .upload_pack_command = upload_pack_command, │ │ 509 - } } }, │ │ 510 - ); │ │ 511 - │ │ 512 - // update the working dir │ │ 513 - try client_repo.restore(io, allocator, "."); │ │ 514 - │ │ 515 - // make sure fetch was successful │ │ 516 - { │ │ 517 - const goodbye_txt = try temp_dir.openFile(io, "client/goodbye.txt", .{}); │ │ 518 - defer goodbye_txt.close(io); │ │ 519 - │ │ 520 - const oid_master = (try client_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │ 521 - try std.testing.expectEqualStrings(&commit2, &oid_master); │ │ 522 - } │ │ 523 -} │ │ 524 - │ │ 525 -fn testPush( │ │ 526 - comptime repo_kind: rp.RepoKind, │ │ 527 - comptime transport_def: net.TransportDefinition, │ │ 528 - comptime port: u16, │ │ 529 - io: std.Io, │ │ 530 - allocator: std.mem.Allocator, │ │ 531 -) !void { │ │ 532 - const temp_dir_name = "temp-testnet-push"; │ │ 533 - │ │ 534 - // create the temp dir │ │ 535 - const cwd = std.Io.Dir.cwd(); │ │ 536 - var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); │ │ 537 - if (temp_dir_or_err) |*temp_dir| { │ │ 538 - temp_dir.close(io); │ │ 539 - try cwd.deleteTree(io, temp_dir_name); │ │ 540 - } else |_| {} │ │ 541 - var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); │ │ 542 - defer cwd.deleteTree(io, temp_dir_name) catch {}; │ │ 543 - defer temp_dir.close(io); │ │ 544 - │ │ 545 - // init server │ │ 546 - var server = try Server(transport_def, temp_dir_name, port).init(io, allocator); │ │ 547 - try server.start(); │ │ 548 - defer server.stop(); │ │ 549 - │ │ 550 - const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │ 551 - defer allocator.free(cwd_path); │ │ 552 - │ │ 553 - const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "repos", "server" }); │ │ 554 - defer allocator.free(server_path); │ │ 555 - │ │ 556 - var server_repo = try rp.Repo(.xit, .{ .is_test = true }).init(io, allocator, .{ .path = server_path }); │ │ 557 - defer server_repo.deinit(io, allocator); │ │ 558 - │ │ 559 - // add config │ │ 560 - switch (transport_def) { │ │ 561 - .file => try server_repo.addConfig(io, allocator, .{ .name = "core.bare", .value = "true" }), │ │ 562 - .wire => { │ │ 563 - try server_repo.addConfig(io, allocator, .{ .name = "core.bare", .value = "false" }); │ │ 564 - try server_repo.addConfig(io, allocator, .{ .name = "receive.denycurrentbranch", .value = "updateinstead" }); │ │ 565 - }, │ │ 566 - } │ │ 567 - try server_repo.addConfig(io, allocator, .{ .name = "http.receivepack", .value = "true" }); │ │ 568 - │ │ 569 - // export server repo │ │ 570 - { │ │ 571 - const export_file = try server_repo.core.repo_dir.createFile(io, "git-daemon-export-ok", .{}); │ │ 572 - defer export_file.close(io); │ │ 573 - } │ │ 574 - │ │ 575 - const client_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "client" }); │ │ 576 - defer allocator.free(client_path); │ │ 577 - │ │ 578 - var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).init(io, allocator, .{ .path = client_path }); │ │ 579 - defer client_repo.deinit(io, allocator); │ │ 580 - │ │ 581 - // make a commit │ │ 582 - const commit1 = blk: { │ │ 583 - const hello_txt = try client_repo.core.work_dir.createFile(io, "hello.txt", .{ .truncate = true }); │ │ 584 - defer hello_txt.close(io); │ │ 585 - try hello_txt.writeStreamingAll(io, "hello, world!"); │ │ 586 - try client_repo.add(io, allocator, &.{"hello.txt"}); │ │ 587 - break :blk try client_repo.commit(io, allocator, .{ .message = "let there be light" }); │ │ 588 - }; │ │ 589 - │ │ 590 - // add a tag │ │ 591 - _ = try client_repo.addTag(io, allocator, .{ .name = "1.0.0", .message = "hi" }); │ │ 592 - │ │ 593 - // add remote │ │ 594 - { │ │ 595 - if (.windows == builtin.os.tag) { │ │ 596 - std.mem.replaceScalar(u8, server_path, '\\', '/'); │ │ 597 - } │ │ 598 - const separator = if (server_path[0] == '/') "" else "/"; │ │ 599 - │ │ 600 - const remote_url = switch (transport_def) { │ │ 601 - //.file => try std.fmt.allocPrint(allocator, "file://{s}{s}", .{ separator, server_path }), │ │ 602 - .file => try std.fmt.allocPrint(allocator, "../server", .{}), // relative file paths work too │ │ 603 - .wire => |wire_kind| switch (wire_kind) { │ │ 604 - .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{port}), │ │ 605 - .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/server", .{port}), │ │ 606 - .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ port, separator, server_path }), │ │ 607 - }, │ │ 608 - }; │ │ 609 - defer allocator.free(remote_url); │ │ 610 - │ │ 611 - try client_repo.addRemote(io, allocator, .{ .name = "origin", .value = remote_url }); │ │ 612 - try client_repo.addConfig(io, allocator, .{ .name = "branch.master.remote", .value = "origin" }); │ │ 613 - } │ │ 614 - │ │ 615 - const refspecs = &.{ │ │ 616 - "refs/tags/1.0.0:refs/tags/1.0.0", │ │ 617 - }; │ │ 618 - │ │ 619 - const is_ssh = switch (transport_def) { │ │ 620 - .file => false, │ │ 621 - .wire => |wire_kind| .ssh == wire_kind, │ │ 622 - }; │ │ 623 - const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { │ │ 624 - const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); │ │ 625 - defer allocator.free(known_hosts_path); │ │ 626 - │ │ 627 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │ 628 - defer allocator.free(priv_key_path); │ │ 629 - │ │ 630 - break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); │ │ 631 - } else null; │ │ 632 - defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); │ │ 633 - │ │ 634 - const upload_pack_command = try uploadPackCommand(is_ssh, allocator, cwd_path, port); │ │ 635 - defer allocator.free(upload_pack_command); │ │ 636 - const receive_pack_command = try receivePackCommand(is_ssh, allocator, cwd_path, port); │ │ 637 - defer allocator.free(receive_pack_command); │ │ 638 - │ │ 639 - try client_repo.push( │ │ 640 - io, │ │ 641 - allocator, │ │ 642 - "origin", │ │ 643 - "master", │ │ 644 - false, │ │ 645 - .{ .refspecs = refspecs, .wire = .{ .ssh = .{ │ │ 646 - .command = ssh_cmd_maybe, │ │ 647 - .receive_pack_command = receive_pack_command, │ │ 648 - } } }, │ │ 649 - ); │ │ 650 - │ │ 651 - // make sure push was successful │ │ 652 - { │ │ 653 - try std.testing.expect(null != try server_repo.readRef(io, .{ .kind = .tag, .name = "1.0.0" })); │ │ 654 - │ │ 655 - const oid_master = (try server_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │ 656 - try std.testing.expectEqualStrings(&commit1, &oid_master); │ │ 657 - } │ │ 658 - │ │ 659 - // make a commit on the server │ │ 660 - { │ │ 661 - const hello_txt = try server_repo.core.work_dir.createFile(io, "hello.txt", .{ .truncate = true }); │ │ 662 - defer hello_txt.close(io); │ │ 663 - try hello_txt.writeStreamingAll(io, "hello, world from the server!"); │ │ 664 - try server_repo.add(io, allocator, &.{"hello.txt"}); │ │ 665 - _ = try server_repo.commit(io, allocator, .{ .message = "new commit from the server" }); │ │ 666 - } │ │ 667 - │ │ 668 - // make another commit │ │ 669 - const commit2 = blk: { │ │ 670 - const goodbye_txt = try client_repo.core.work_dir.createFile(io, "goodbye.txt", .{ .truncate = true }); │ │ 671 - defer goodbye_txt.close(io); │ │ 672 - try goodbye_txt.writeStreamingAll(io, "goodbye, world!"); │ │ 673 - try client_repo.add(io, allocator, &.{"goodbye.txt"}); │ │ 674 - break :blk try client_repo.commit(io, allocator, .{ .message = "goodbye" }); │ │ 675 - }; │ │ 676 - │ │ 677 - // can't push because server has commit not found locally │ │ 678 - try std.testing.expectError(error.RemoteRefContainsCommitsNotFoundLocally, client_repo.push( │ │ 679 - io, │ │ 680 - allocator, │ │ 681 - "origin", │ │ 682 - "master", │ │ 683 - false, │ │ 684 - .{ .wire = .{ .ssh = .{ │ │ 685 - .command = ssh_cmd_maybe, │ │ 686 - .receive_pack_command = receive_pack_command, │ │ 687 - } } }, │ │ 688 - )); │ │ 689 - │ │ 690 - // make a commit on the server with no parents, thus creating an incompatible git history │ │ 691 - { │ │ 692 - const hello_txt = try server_repo.core.work_dir.createFile(io, "hello.txt", .{ .truncate = true }); │ │ 693 - defer hello_txt.close(io); │ │ 694 - try hello_txt.writeStreamingAll(io, "hello, world from the server again!"); │ │ 695 - try server_repo.add(io, allocator, &.{"hello.txt"}); │ │ 696 - _ = try server_repo.commit(io, allocator, .{ .message = "new git history on the server", .parent_oids = &.{} }); │ │ 697 - } │ │ 698 - │ │ 699 - // can't push because commit doesn't exist locally │ │ 700 - try std.testing.expectError(error.RemoteRefContainsCommitsNotFoundLocally, client_repo.push( │ │ 701 - io, │ │ 702 - allocator, │ │ 703 - "origin", │ │ 704 - "master", │ │ 705 - false, │ │ 706 - .{ .wire = .{ .ssh = .{ │ │ 707 - .command = ssh_cmd_maybe, │ │ 708 - .receive_pack_command = receive_pack_command, │ │ 709 - } } }, │ │ 710 - )); │ │ 711 - │ │ 712 - // retrieve the commit object │ │ 713 - try client_repo.fetch( │ │ 714 - io, │ │ 715 - allocator, │ │ 716 - "origin", │ │ 717 - .{ .wire = .{ .ssh = .{ │ │ 718 - .command = ssh_cmd_maybe, │ │ 719 - .upload_pack_command = upload_pack_command, │ │ 720 - } } }, │ │ 721 - ); │ │ 722 - │ │ 723 - // can't push because server's history is incompatible │ │ 724 - try std.testing.expectError(error.RemoteRefContainsIncompatibleHistory, client_repo.push( │ │ 725 - io, │ │ 726 - allocator, │ │ 727 - "origin", │ │ 728 - "master", │ │ 729 - false, │ │ 730 - .{ .wire = .{ .ssh = .{ │ │ 731 - .command = ssh_cmd_maybe, │ │ 732 - .receive_pack_command = receive_pack_command, │ │ 733 - } } }, │ │ 734 - )); │ │ 735 - │ │ 736 - // test denyNonFastForwards (only for wire transports, file transport bypasses receive-pack) │ │ 737 - switch (transport_def) { │ │ 738 - .file => {}, │ │ 739 - .wire => { │ │ 740 - // set denyNonFastForwards on server │ │ 741 - try server_repo.addConfig(io, allocator, .{ .name = "receive.denynonfastforwards", .value = "true" }); │ │ 742 - │ │ 743 - // save the server's current master ref │ │ 744 - const oid_before_denied_push = (try server_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │ 745 - │ │ 746 - // force push should be rejected by server due to denyNonFastForwards │ │ 747 - try client_repo.push( │ │ 748 - io, │ │ 749 - allocator, │ │ 750 - "origin", │ │ 751 - "master", │ │ 752 - true, │ │ 753 - .{ .wire = .{ .ssh = .{ │ │ 754 - .command = ssh_cmd_maybe, │ │ 755 - .receive_pack_command = receive_pack_command, │ │ 756 - } } }, │ │ 757 - ); │ │ 758 - │ │ 759 - // verify the server ref was not updated (push was denied) │ │ 760 - { │ │ 761 - const oid_master = (try server_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │ 762 - try std.testing.expectEqualStrings(&oid_before_denied_push, &oid_master); │ │ 763 - } │ │ 764 - │ │ 765 - // remove denyNonFastForwards from server │ │ 766 - try server_repo.removeConfig(io, allocator, .{ .name = "receive.denynonfastforwards" }); │ │ 767 - }, │ │ 768 - } │ │ 769 - │ │ 770 - // force push │ │ 771 - try client_repo.push( │ │ 772 - io, │ │ 773 - allocator, │ │ 774 - "origin", │ │ 775 - "master", │ │ 776 - true, │ │ 777 - .{ .wire = .{ .ssh = .{ │ │ 778 - .command = ssh_cmd_maybe, │ │ 779 - .receive_pack_command = receive_pack_command, │ │ 780 - } } }, │ │ 781 - ); │ │ 782 - │ │ 783 - // make sure push was successful │ │ 784 - { │ │ 785 - const oid_master = (try server_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │ 786 - try std.testing.expectEqualStrings(&commit2, &oid_master); │ │ 787 - } │ │ 788 - │ │ 789 - // remove the remote tag │ │ 790 - try client_repo.push( │ │ 791 - io, │ │ 792 - allocator, │ │ 793 - "origin", │ │ 794 - ":refs/tags/1.0.0", │ │ 795 - false, │ │ 796 - .{ .wire = .{ .ssh = .{ │ │ 797 - .command = ssh_cmd_maybe, │ │ 798 - .receive_pack_command = receive_pack_command, │ │ 799 - } } }, │ │ 800 - ); │ │ 801 - │ │ 802 - // make sure push was successful │ │ 803 - try std.testing.expect(null == try server_repo.readRef(io, .{ .kind = .tag, .name = "1.0.0" })); │ │ 804 -} │ │ 805 - │ │ 806 -fn testPushCreatesMissingRepo( │ │ 807 - comptime repo_kind: rp.RepoKind, │ │ 808 - comptime transport_def: net.TransportDefinition, │ │ 809 - comptime port: u16, │ │ 810 - io: std.Io, │ │ 811 - allocator: std.mem.Allocator, │ │ 812 -) !void { │ │ 813 - const temp_dir_name = "temp-testnet-push-create"; │ │ 814 - │ │ 815 - const cwd = std.Io.Dir.cwd(); │ │ 816 - var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); │ │ 817 - if (temp_dir_or_err) |*temp_dir| { │ │ 818 - temp_dir.close(io); │ │ 819 - try cwd.deleteTree(io, temp_dir_name); │ │ 820 - } else |_| {} │ │ 821 - var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); │ │ 822 - defer cwd.deleteTree(io, temp_dir_name) catch {}; │ │ 823 - defer temp_dir.close(io); │ │ 824 - │ │ 825 - var server = try Server(transport_def, temp_dir_name, port).init(io, allocator); │ │ 826 - try server.start(); │ │ 827 - defer server.stop(); │ │ 828 - │ │ 829 - const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │ 830 - defer allocator.free(cwd_path); │ │ 831 - │ │ 832 - const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "repos", "server" }); │ │ 833 - defer allocator.free(server_path); │ │ 834 - │ │ 835 - const client_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "client" }); │ │ 836 - defer allocator.free(client_path); │ │ 837 - │ │ 838 - var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).init(io, allocator, .{ .path = client_path }); │ │ 839 - defer client_repo.deinit(io, allocator); │ │ 840 - │ │ 841 - const commit1 = blk: { │ │ 842 - const hello_txt = try client_repo.core.work_dir.createFile(io, "hello.txt", .{ .truncate = true }); │ │ 843 - defer hello_txt.close(io); │ │ 844 - try hello_txt.writeStreamingAll(io, "hello, world!"); │ │ 845 - try client_repo.add(io, allocator, &.{"hello.txt"}); │ │ 846 - break :blk try client_repo.commit(io, allocator, .{ .message = "let there be light" }); │ │ 847 - }; │ │ 848 - │ │ 849 - _ = try client_repo.addTag(io, allocator, .{ .name = "1.0.0", .message = "hi" }); │ │ 850 - │ │ 851 - { │ │ 852 - if (.windows == builtin.os.tag) { │ │ 853 - std.mem.replaceScalar(u8, server_path, '\\', '/'); │ │ 854 - } │ │ 855 - const separator = if (server_path[0] == '/') "" else "/"; │ │ 856 - │ │ 857 - const remote_url = switch (transport_def) { │ │ 858 - .file => unreachable, │ │ 859 - .wire => |wire_kind| switch (wire_kind) { │ │ 860 - .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{port}), │ │ 861 - .raw => unreachable, │ │ 862 - .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ port, separator, server_path }), │ │ 863 - }, │ │ 864 - }; │ │ 865 - defer allocator.free(remote_url); │ │ 866 - │ │ 867 - try client_repo.addRemote(io, allocator, .{ .name = "origin", .value = remote_url }); │ │ 868 - try client_repo.addConfig(io, allocator, .{ .name = "branch.master.remote", .value = "origin" }); │ │ 869 - } │ │ 870 - │ │ 871 - const is_ssh = switch (transport_def) { │ │ 872 - .file => false, │ │ 873 - .wire => |wire_kind| .ssh == wire_kind, │ │ 874 - }; │ │ 875 - const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { │ │ 876 - const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); │ │ 877 - defer allocator.free(known_hosts_path); │ │ 878 - │ │ 879 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │ 880 - defer allocator.free(priv_key_path); │ │ 881 - │ │ 882 - break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); │ │ 883 - } else null; │ │ 884 - defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); │ │ 885 - │ │ 886 - const receive_pack_command = try receivePackCommand(is_ssh, allocator, cwd_path, port); │ │ 887 - defer allocator.free(receive_pack_command); │ │ 888 - │ │ 889 - try client_repo.push( │ │ 890 - io, │ │ 891 - allocator, │ │ 892 - "origin", │ │ 893 - "master", │ │ 894 - false, │ │ 895 - .{ .refspecs = &.{"refs/tags/1.0.0:refs/tags/1.0.0"}, .wire = .{ .ssh = .{ │ │ 896 - .command = ssh_cmd_maybe, │ │ 897 - .receive_pack_command = receive_pack_command, │ │ 898 - } } }, │ │ 899 - ); │ │ 900 - │ │ 901 - var server_repo = try rp.Repo(.xit, .{ .is_test = true }).open(io, allocator, .{ .path = server_path }); │ │ 902 - defer server_repo.deinit(io, allocator); │ │ 903 - │ │ 904 - try std.testing.expect(null != try server_repo.readRef(io, .{ .kind = .tag, .name = "1.0.0" })); │ │ 905 - │ │ 906 - const oid_master = (try server_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │ 907 - try std.testing.expectEqualStrings(&commit1, &oid_master); │ │ 908 -} │ │ 909 - │ │ 910 -fn testClone( │ │ 911 - comptime repo_kind: rp.RepoKind, │ │ 912 - comptime transport_def: net.TransportDefinition, │ │ 913 - comptime port: u16, │ │ 914 - comptime shell_out_to_git: bool, │ │ 915 - io: std.Io, │ │ 916 - allocator: std.mem.Allocator, │ │ 917 -) !void { │ │ 918 - const temp_dir_name = "temp-testnet-clone"; │ │ 919 - │ │ 920 - // create the temp dir │ │ 921 - const cwd = std.Io.Dir.cwd(); │ │ 922 - var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); │ │ 923 - if (temp_dir_or_err) |*temp_dir| { │ │ 924 - temp_dir.close(io); │ │ 925 - try cwd.deleteTree(io, temp_dir_name); │ │ 926 - } else |_| {} │ │ 927 - var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); │ │ 928 - defer cwd.deleteTree(io, temp_dir_name) catch {}; │ │ 929 - defer temp_dir.close(io); │ │ 930 - │ │ 931 - // init server │ │ 932 - var server = try Server(transport_def, temp_dir_name, port).init(io, allocator); │ │ 933 - try server.start(); │ │ 934 - defer server.stop(); │ │ 935 - │ │ 936 - const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │ 937 - defer allocator.free(cwd_path); │ │ 938 - │ │ 939 - const temp_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name }); │ │ 940 - defer allocator.free(temp_path); │ │ 941 - │ │ 942 - const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "repos", "server" }); │ │ 943 - defer allocator.free(server_path); │ │ 944 - │ │ 945 - // init server repo with default branch name as main │ │ 946 - // is_test must be false when shell_out_to_git so commits get real timestamps (needed for --shallow-since) │ │ 947 - var server_repo = try rp.Repo(.xit, .{ .is_test = !shell_out_to_git }).init(io, allocator, .{ .path = server_path, .create_default_branch = "main" }); │ │ 948 - defer server_repo.deinit(io, allocator); │ │ 949 - │ │ 950 - if (shell_out_to_git) { │ │ 951 - try server_repo.addConfig(io, allocator, .{ .name = "user.name", .value = "test" }); │ │ 952 - try server_repo.addConfig(io, allocator, .{ .name = "user.email", .value = "test@test" }); │ │ 953 - try server_repo.addConfig(io, allocator, .{ .name = "uploadpack.allowfilter", .value = "true" }); │ │ 954 - } │ │ 955 - │ │ 956 - // make a commit │ │ 957 - { │ │ 958 - const hello_txt = try server_repo.core.work_dir.createFile(io, "hello.txt", .{ .truncate = true }); │ │ 959 - defer hello_txt.close(io); │ │ 960 - try hello_txt.writeStreamingAll(io, "hello, world!"); │ │ 961 - try server_repo.add(io, allocator, &.{"hello.txt"}); │ │ 962 - _ = try server_repo.commit(io, allocator, .{ .message = "let there be light" }); │ │ 963 - } │ │ 964 - │ │ 965 - // tag first commit │ │ 966 - _ = try server_repo.addTag(io, allocator, .{ .name = "v1", .message = "first" }); │ │ 967 - │ │ 968 - // make a commit │ │ 969 - { │ │ 970 - const goodbye_txt = try server_repo.core.work_dir.createFile(io, "goodbye.txt", .{ .truncate = true }); │ │ 971 - defer goodbye_txt.close(io); │ │ 972 - try goodbye_txt.writeStreamingAll(io, "goodbye, world!"); │ │ 973 - try server_repo.add(io, allocator, &.{"goodbye.txt"}); │ │ 974 - _ = try server_repo.commit(io, allocator, .{ .message = "add goodbye file" }); │ │ 975 - } │ │ 976 - │ │ 977 - // export server repo │ │ 978 - { │ │ 979 - const export_file = try server_repo.core.repo_dir.createFile(io, "git-daemon-export-ok", .{}); │ │ 980 - defer export_file.close(io); │ │ 981 - } │ │ 982 - │ │ 983 - const client_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "client" }); │ │ 984 - defer allocator.free(client_path); │ │ 985 - │ │ 986 - // get remote url │ │ 987 - const remote_url = blk: { │ │ 988 - if (.windows == builtin.os.tag) { │ │ 989 - std.mem.replaceScalar(u8, server_path, '\\', '/'); │ │ 990 - } │ │ 991 - const separator = if (server_path[0] == '/') "" else "/"; │ │ 992 - │ │ 993 - break :blk switch (transport_def) { │ │ 994 - //.file => try std.fmt.allocPrint(allocator, "file://{s}{s}", .{ separator, server_path }), │ │ 995 - .file => try std.fmt.allocPrint(allocator, "server", .{}), // relative file paths work too │ │ 996 - .wire => |wire_kind| switch (wire_kind) { │ │ 997 - .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{port}), │ │ 998 - .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/server", .{port}), │ │ 999 - .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ port, separator, server_path }), │ │1000 - }, │ │1001 - }; │ │1002 - }; │ │1003 - defer allocator.free(remote_url); │ │1004 - │ │1005 - const is_ssh = switch (transport_def) { │ │1006 - .file => false, │ │1007 - .wire => |wire_kind| .ssh == wire_kind, │ │1008 - }; │ │1009 - │ │1010 - const upload_pack_command = try uploadPackCommand(is_ssh, allocator, cwd_path, port); │ │1011 - defer allocator.free(upload_pack_command); │ │1012 - │ │1013 - if (shell_out_to_git) { │ │1014 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1015 - defer allocator.free(priv_key_path); │ │1016 - const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand=ssh -o StrictHostKeyChecking=no -o IdentityFile={s}", .{priv_key_path}); │ │1017 - defer allocator.free(ssh_config_arg); │ │1018 - │ │1019 - { │ │1020 - var process = try std.process.spawn(io, .{ │ │1021 - .argv = if (is_ssh) │ │1022 - &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--depth", "1", remote_url, "client" } │ │1023 - else │ │1024 - &.{ "git", "clone", "--depth", "1", remote_url, "client" }, │ │1025 - .cwd = .{ .path = temp_path }, │ │1026 - .stdin = .ignore, │ │1027 - .stdout = .ignore, │ │1028 - .stderr = .ignore, │ │1029 - }); │ │1030 - const term = try process.wait(io); │ │1031 - if (term != .exited) { │ │1032 - return error.GitCommandFailed; │ │1033 - } │ │1034 - } │ │1035 - │ │1036 - // make sure shallow clone was successful │ │1037 - { │ │1038 - const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); │ │1039 - hello_txt.close(io); │ │1040 - } │ │1041 - │ │1042 - // make a third commit on the server │ │1043 - { │ │1044 - const extra_txt = try server_repo.core.work_dir.createFile(io, "extra.txt", .{ .truncate = true }); │ │1045 - defer extra_txt.close(io); │ │1046 - try extra_txt.writeStreamingAll(io, "extra content"); │ │1047 - try server_repo.add(io, allocator, &.{"extra.txt"}); │ │1048 - _ = try server_repo.commit(io, allocator, .{ .message = "add extra file" }); │ │1049 - } │ │1050 - │ │1051 - // pull --unshallow to deepen the clone and get the new commit │ │1052 - { │ │1053 - var process = try std.process.spawn(io, .{ │ │1054 - .argv = if (is_ssh) │ │1055 - &.{ "git", "-c", ssh_config_arg, "pull", "--upload-pack", upload_pack_command, "--unshallow" } │ │1056 - else │ │1057 - &.{ "git", "pull", "--unshallow" }, │ │1058 - .cwd = .{ .path = client_path }, │ │1059 - .stdin = .ignore, │ │1060 - .stdout = .ignore, │ │1061 - .stderr = .ignore, │ │1062 - }); │ │1063 - const term = try process.wait(io); │ │1064 - if (term != .exited) { │ │1065 - return error.GitCommandFailed; │ │1066 - } │ │1067 - } │ │1068 - │ │1069 - // make sure unshallow pull was successful │ │1070 - { │ │1071 - const extra_txt = try temp_dir.openFile(io, "client/extra.txt", .{}); │ │1072 - extra_txt.close(io); │ │1073 - } │ │1074 - │ │1075 - // delete client and clone again with --shallow-since │ │1076 - try cwd.deleteTree(io, temp_dir_name ++ "/client"); │ │1077 - │ │1078 - { │ │1079 - var process = try std.process.spawn(io, .{ │ │1080 - .argv = if (is_ssh) │ │1081 - &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--shallow-since=2000-01-01", remote_url, "client" } │ │1082 - else │ │1083 - &.{ "git", "clone", "--shallow-since=2000-01-01", remote_url, "client" }, │ │1084 - .cwd = .{ .path = temp_path }, │ │1085 - .stdin = .ignore, │ │1086 - .stdout = .ignore, │ │1087 - .stderr = .ignore, │ │1088 - }); │ │1089 - const term = try process.wait(io); │ │1090 - if (term != .exited) { │ │1091 - return error.GitCommandFailed; │ │1092 - } │ │1093 - } │ │1094 - │ │1095 - // make sure shallow clone was successful │ │1096 - { │ │1097 - const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); │ │1098 - hello_txt.close(io); │ │1099 - } │ │1100 - │ │1101 - // delete client and clone again with --shallow-exclude │ │1102 - try cwd.deleteTree(io, temp_dir_name ++ "/client"); │ │1103 - │ │1104 - { │ │1105 - var process = try std.process.spawn(io, .{ │ │1106 - .argv = if (is_ssh) │ │1107 - &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--shallow-exclude=v1", remote_url, "client" } │ │1108 - else │ │1109 - &.{ "git", "clone", "--shallow-exclude=v1", remote_url, "client" }, │ │1110 - .cwd = .{ .path = temp_path }, │ │1111 - .stdin = .ignore, │ │1112 - .stdout = .ignore, │ │1113 - .stderr = .ignore, │ │1114 - }); │ │1115 - const term = try process.wait(io); │ │1116 - if (term != .exited) { │ │1117 - return error.GitCommandFailed; │ │1118 - } │ │1119 - } │ │1120 - │ │1121 - // make sure shallow clone was successful │ │1122 - { │ │1123 - const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); │ │1124 - hello_txt.close(io); │ │1125 - } │ │1126 - │ │1127 - // delete client and clone again with --filter=blob:none │ │1128 - try cwd.deleteTree(io, temp_dir_name ++ "/client"); │ │1129 - │ │1130 - { │ │1131 - var process = try std.process.spawn(io, .{ │ │1132 - .argv = if (is_ssh) │ │1133 - &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--filter=blob:none", remote_url, "client" } │ │1134 - else │ │1135 - &.{ "git", "clone", "--filter=blob:none", remote_url, "client" }, │ │1136 - .cwd = .{ .path = temp_path }, │ │1137 - .stdin = .ignore, │ │1138 - .stdout = .ignore, │ │1139 - .stderr = .ignore, │ │1140 - }); │ │1141 - const term = try process.wait(io); │ │1142 - if (term != .exited) { │ │1143 - return error.GitCommandFailed; │ │1144 - } │ │1145 - } │ │1146 - │ │1147 - // make sure partial clone was successful │ │1148 - { │ │1149 - const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); │ │1150 - hello_txt.close(io); │ │1151 - } │ │1152 - │ │1153 - // delete client and clone again with --filter=tree:0 │ │1154 - try cwd.deleteTree(io, temp_dir_name ++ "/client"); │ │1155 - │ │1156 - { │ │1157 - var process = try std.process.spawn(io, .{ │ │1158 - .argv = if (is_ssh) │ │1159 - &.{ "git", "-c", ssh_config_arg, "clone", "--upload-pack", upload_pack_command, "--filter=tree:0", remote_url, "client" } │ │1160 - else │ │1161 - &.{ "git", "clone", "--filter=tree:0", remote_url, "client" }, │ │1162 - .cwd = .{ .path = temp_path }, │ │1163 - .stdin = .ignore, │ │1164 - .stdout = .ignore, │ │1165 - .stderr = .ignore, │ │1166 - }); │ │1167 - const term = try process.wait(io); │ │1168 - if (term != .exited) { │ │1169 - return error.GitCommandFailed; │ │1170 - } │ │1171 - } │ │1172 - │ │1173 - // make sure treeless clone was successful │ │1174 - { │ │1175 - const goodbye_txt = try temp_dir.openFile(io, "client/goodbye.txt", .{}); │ │1176 - goodbye_txt.close(io); │ │1177 - } │ │1178 - } else { │ │1179 - const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { │ │1180 - const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); │ │1181 - defer allocator.free(known_hosts_path); │ │1182 - │ │1183 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1184 - defer allocator.free(priv_key_path); │ │1185 - │ │1186 - break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); │ │1187 - } else null; │ │1188 - defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); │ │1189 - │ │1190 - // clone repo │ │1191 - var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).clone( │ │1192 - io, │ │1193 - allocator, │ │1194 - remote_url, │ │1195 - temp_path, │ │1196 - client_path, │ │1197 - .{ .wire = .{ .ssh = .{ │ │1198 - .command = ssh_cmd_maybe, │ │1199 - .upload_pack_command = upload_pack_command, │ │1200 - } } }, │ │1201 - ); │ │1202 - defer client_repo.deinit(io, allocator); │ │1203 - │ │1204 - // make sure HEAD points to the right default branch │ │1205 - var current_branch_buffer = [_]u8{0} ** rf.MAX_REF_CONTENT_SIZE; │ │1206 - const head = try client_repo.head(io, &current_branch_buffer); │ │1207 - try std.testing.expectEqualStrings("main", head.ref.name); │ │1208 - │ │1209 - // make sure clone was successful │ │1210 - const hello_txt = try temp_dir.openFile(io, "client/hello.txt", .{}); │ │1211 - defer hello_txt.close(io); │ │1212 - } │ │1213 -} │ │1214 - │ │1215 -fn testFetchLarge( │ │1216 - comptime repo_kind: rp.RepoKind, │ │1217 - comptime transport_def: net.TransportDefinition, │ │1218 - comptime port: u16, │ │1219 - comptime shell_out_to_git: bool, │ │1220 - io: std.Io, │ │1221 - allocator: std.mem.Allocator, │ │1222 -) !void { │ │1223 - const temp_dir_name = "temp-testnet-fetch-large"; │ │1224 - │ │1225 - // create the temp dir │ │1226 - const cwd = std.Io.Dir.cwd(); │ │1227 - var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); │ │1228 - if (temp_dir_or_err) |*temp_dir| { │ │1229 - temp_dir.close(io); │ │1230 - try cwd.deleteTree(io, temp_dir_name); │ │1231 - } else |_| {} │ │1232 - var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); │ │1233 - defer cwd.deleteTree(io, temp_dir_name) catch {}; │ │1234 - defer temp_dir.close(io); │ │1235 - │ │1236 - // init server │ │1237 - var server = try Server(transport_def, temp_dir_name, port).init(io, allocator); │ │1238 - try server.start(); │ │1239 - defer server.stop(); │ │1240 - │ │1241 - const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │1242 - defer allocator.free(cwd_path); │ │1243 - │ │1244 - const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "repos", "server" }); │ │1245 - defer allocator.free(server_path); │ │1246 - │ │1247 - var server_repo = try rp.Repo(.xit, .{ .is_test = true }).init(io, allocator, .{ .path = server_path }); │ │1248 - defer server_repo.deinit(io, allocator); │ │1249 - │ │1250 - var server_dir = try cwd.openDir(io, server_path, .{}); │ │1251 - defer server_dir.close(io); │ │1252 - │ │1253 - // copy files from current repo into server dir │ │1254 - for (&[_][]const u8{"src"}) |dir_name| { │ │1255 - var src_repo_dir = try cwd.openDir(io, dir_name, .{ .iterate = true }); │ │1256 - defer src_repo_dir.close(io); │ │1257 - │ │1258 - var dest_repo_dir = try server_dir.createDirPathOpen(io, dir_name, .{}); │ │1259 - defer dest_repo_dir.close(io); │ │1260 - │ │1261 - try copyDir(io, src_repo_dir, dest_repo_dir); │ │1262 - │ │1263 - try server_repo.add(io, allocator, &.{dir_name}); │ │1264 - } │ │1265 - │ │1266 - // make a commit │ │1267 - const commit1 = try server_repo.commit(io, allocator, .{ .message = "let there be light" }); │ │1268 - │ │1269 - // export server repo │ │1270 - { │ │1271 - const export_file = try server_repo.core.repo_dir.createFile(io, "git-daemon-export-ok", .{}); │ │1272 - defer export_file.close(io); │ │1273 - } │ │1274 - │ │1275 - if (shell_out_to_git) { │ │1276 - try server_repo.addConfig(io, allocator, .{ .name = "uploadpack.allowrefinwant", .value = "true" }); │ │1277 - } │ │1278 - │ │1279 - const client_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "client" }); │ │1280 - defer allocator.free(client_path); │ │1281 - │ │1282 - var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).init(io, allocator, .{ .path = client_path }); │ │1283 - defer client_repo.deinit(io, allocator); │ │1284 - │ │1285 - // add remote │ │1286 - { │ │1287 - if (.windows == builtin.os.tag) { │ │1288 - std.mem.replaceScalar(u8, server_path, '\\', '/'); │ │1289 - } │ │1290 - const separator = if (server_path[0] == '/') "" else "/"; │ │1291 - │ │1292 - const remote_url = switch (transport_def) { │ │1293 - //.file => try std.fmt.allocPrint(allocator, "file://{s}{s}", .{ separator, server_path }), │ │1294 - .file => try std.fmt.allocPrint(allocator, "../server", .{}), // relative file paths work too │ │1295 - .wire => |wire_kind| switch (wire_kind) { │ │1296 - .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{port}), │ │1297 - .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/server", .{port}), │ │1298 - .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ port, separator, server_path }), │ │1299 - }, │ │1300 - }; │ │1301 - defer allocator.free(remote_url); │ │1302 - │ │1303 - try client_repo.addRemote(io, allocator, .{ .name = "origin", .value = remote_url }); │ │1304 - try client_repo.addConfig(io, allocator, .{ .name = "branch.master.remote", .value = "origin" }); │ │1305 - } │ │1306 - │ │1307 - const is_ssh = switch (transport_def) { │ │1308 - .file => false, │ │1309 - .wire => |wire_kind| .ssh == wire_kind, │ │1310 - }; │ │1311 - │ │1312 - const upload_pack_command = try uploadPackCommand(is_ssh, allocator, cwd_path, port); │ │1313 - defer allocator.free(upload_pack_command); │ │1314 - │ │1315 - if (shell_out_to_git) { │ │1316 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1317 - defer allocator.free(priv_key_path); │ │1318 - const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand=ssh -o StrictHostKeyChecking=no -o IdentityFile={s}", .{priv_key_path}); │ │1319 - defer allocator.free(ssh_config_arg); │ │1320 - │ │1321 - { │ │1322 - var process = try std.process.spawn(io, .{ │ │1323 - .argv = if (is_ssh) │ │1324 - &.{ "git", "-c", ssh_config_arg, "pull", "--upload-pack", upload_pack_command, "origin", "master" } │ │1325 - else │ │1326 - &.{ "git", "pull", "origin", "master" }, │ │1327 - .cwd = .{ .path = client_path }, │ │1328 - .stdin = .ignore, │ │1329 - .stdout = .ignore, │ │1330 - .stderr = .ignore, │ │1331 - }); │ │1332 - const term = try process.wait(io); │ │1333 - if (term != .exited) { │ │1334 - return error.GitCommandFailed; │ │1335 - } │ │1336 - } │ │1337 - │ │1338 - // make sure pull was successful │ │1339 - { │ │1340 - const oid_master = (try client_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │1341 - try std.testing.expectEqualStrings(&commit1, &oid_master); │ │1342 - } │ │1343 - │ │1344 - // make another commit on the server │ │1345 - const commit2 = blk: { │ │1346 - const extra_txt = try server_repo.core.work_dir.createFile(io, "extra.txt", .{ .truncate = true }); │ │1347 - defer extra_txt.close(io); │ │1348 - try extra_txt.writeStreamingAll(io, "extra content"); │ │1349 - try server_repo.add(io, allocator, &.{"extra.txt"}); │ │1350 - break :blk try server_repo.commit(io, allocator, .{ .message = "add extra file" }); │ │1351 - }; │ │1352 - │ │1353 - // fetch with ref-in-want (git uses want-ref in protocol v2 when fetching named refs) │ │1354 - { │ │1355 - var process = try std.process.spawn(io, .{ │ │1356 - .argv = if (is_ssh) │ │1357 - &.{ "git", "-c", ssh_config_arg, "fetch", "--upload-pack", upload_pack_command, "origin", "master" } │ │1358 - else │ │1359 - &.{ "git", "fetch", "origin", "master" }, │ │1360 - .cwd = .{ .path = client_path }, │ │1361 - .stdin = .ignore, │ │1362 - .stdout = .ignore, │ │1363 - .stderr = .ignore, │ │1364 - }); │ │1365 - const term = try process.wait(io); │ │1366 - if (term != .exited) { │ │1367 - return error.GitCommandFailed; │ │1368 - } │ │1369 - } │ │1370 - │ │1371 - // make sure fetch with want-ref was successful │ │1372 - { │ │1373 - const oid_remote_master = (try client_repo.readRef(io, .{ .kind = .{ .remote = "origin" }, .name = "master" })).?; │ │1374 - try std.testing.expectEqualStrings(&commit2, &oid_remote_master); │ │1375 - } │ │1376 - } else { │ │1377 - const refspecs = &.{ │ │1378 - "+refs/heads/master:refs/heads/master", │ │1379 - }; │ │1380 - │ │1381 - const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { │ │1382 - const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); │ │1383 - defer allocator.free(known_hosts_path); │ │1384 - │ │1385 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1386 - defer allocator.free(priv_key_path); │ │1387 - │ │1388 - break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); │ │1389 - } else null; │ │1390 - defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); │ │1391 - │ │1392 - try client_repo.fetch( │ │1393 - io, │ │1394 - allocator, │ │1395 - "origin", │ │1396 - .{ .refspecs = refspecs, .wire = .{ .ssh = .{ │ │1397 - .command = ssh_cmd_maybe, │ │1398 - .upload_pack_command = upload_pack_command, │ │1399 - } } }, │ │1400 - ); │ │1401 - │ │1402 - // update the working dir │ │1403 - try client_repo.restore(io, allocator, "."); │ │1404 - } │ │1405 - │ │1406 - // make sure fetch was successful │ │1407 - { │ │1408 - const oid_master = (try client_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │1409 - try std.testing.expectEqualStrings(&commit1, &oid_master); │ │1410 - } │ │ 1 +comptime { │ │ 2 + _ = @import("test/net.zig"); │ │ 3 } │ │ 4 │ │1413 -fn testPushLarge( │ │1414 - comptime repo_kind: rp.RepoKind, │ │1415 - comptime transport_def: net.TransportDefinition, │ │1416 - comptime port: u16, │ │1417 - comptime shell_out_to_git: bool, │ │1418 - io: std.Io, │ │1419 - allocator: std.mem.Allocator, │ │1420 -) !void { │ │1421 - const temp_dir_name = "temp-testnet-push-large"; │ │1422 - │ │1423 - // create the temp dir │ │1424 - const cwd = std.Io.Dir.cwd(); │ │1425 - var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); │ │1426 - if (temp_dir_or_err) |*temp_dir| { │ │1427 - temp_dir.close(io); │ │1428 - try cwd.deleteTree(io, temp_dir_name); │ │1429 - } else |_| {} │ │1430 - var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); │ │1431 - defer cwd.deleteTree(io, temp_dir_name) catch {}; │ │1432 - defer temp_dir.close(io); │ │1433 - │ │1434 - // init server │ │1435 - var server = try Server(transport_def, temp_dir_name, port).init(io, allocator); │ │1436 - try server.start(); │ │1437 - defer server.stop(); │ │1438 - │ │1439 - const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │1440 - defer allocator.free(cwd_path); │ │1441 - │ │1442 - const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "repos", "server" }); │ │1443 - defer allocator.free(server_path); │ │1444 - │ │1445 - var server_repo = try rp.Repo(.xit, .{ .is_test = true }).init(io, allocator, .{ .path = server_path }); │ │1446 - defer server_repo.deinit(io, allocator); │ │1447 - │ │1448 - // add config │ │1449 - switch (transport_def) { │ │1450 - .file => try server_repo.addConfig(io, allocator, .{ .name = "core.bare", .value = "true" }), │ │1451 - .wire => { │ │1452 - try server_repo.addConfig(io, allocator, .{ .name = "core.bare", .value = "false" }); │ │1453 - try server_repo.addConfig(io, allocator, .{ .name = "receive.denycurrentbranch", .value = "updateinstead" }); │ │1454 - }, │ │1455 - } │ │1456 - try server_repo.addConfig(io, allocator, .{ .name = "http.receivepack", .value = "true" }); │ │1457 - │ │1458 - // export server repo │ │1459 - { │ │1460 - const export_file = try server_repo.core.repo_dir.createFile(io, "git-daemon-export-ok", .{}); │ │1461 - defer export_file.close(io); │ │1462 - } │ │1463 - │ │1464 - const client_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "client" }); │ │1465 - defer allocator.free(client_path); │ │1466 - │ │1467 - var client_repo = try rp.Repo(repo_kind, .{ .is_test = true }).init(io, allocator, .{ .path = client_path }); │ │1468 - defer client_repo.deinit(io, allocator); │ │1469 - │ │1470 - var client_dir = try cwd.openDir(io, client_path, .{}); │ │1471 - defer client_dir.close(io); │ │1472 - │ │1473 - { │ │1474 - const hello_txt = try client_repo.core.work_dir.createFile(io, "hello.txt", .{ .truncate = true }); │ │1475 - defer hello_txt.close(io); │ │1476 - try hello_txt.writeStreamingAll(io, "hello, world!"); │ │1477 - try client_repo.add(io, allocator, &.{"hello.txt"}); │ │1478 - } │ │1479 - │ │1480 - // copy files from current repo into client dir │ │1481 - for (&[_][]const u8{"src"}) |dir_name| { │ │1482 - var src_repo_dir = try cwd.openDir(io, dir_name, .{ .iterate = true }); │ │1483 - defer src_repo_dir.close(io); │ │1484 - │ │1485 - var dest_repo_dir = try client_dir.createDirPathOpen(io, dir_name, .{}); │ │1486 - defer dest_repo_dir.close(io); │ │1487 - │ │1488 - try copyDir(io, src_repo_dir, dest_repo_dir); │ │1489 - │ │1490 - try client_repo.add(io, allocator, &.{dir_name}); │ │1491 - } │ │1492 - │ │1493 - _ = try client_repo.commit(io, allocator, .{ .message = "let there be light" }); │ │1494 - │ │1495 - // change the files so git will send them as delta objects │ │1496 - for (&[_][]const u8{"src"}) |dir_name| { │ │1497 - var dest_repo_dir = try client_dir.createDirPathOpen(io, dir_name, .{ .open_options = .{ .iterate = true } }); │ │1498 - defer dest_repo_dir.close(io); │ │1499 - │ │1500 - { │ │1501 - var iter = dest_repo_dir.iterate(); │ │1502 - while (try iter.next(io)) |entry| { │ │1503 - switch (entry.kind) { │ │1504 - .file => { │ │1505 - const file = try dest_repo_dir.openFile(io, entry.name, .{ .mode = .read_write }); │ │1506 - defer file.close(io); │ │1507 - var writer = file.writer(io, &.{}); │ │1508 - try writer.interface.writeAll("EDIT"); │ │1509 - }, │ │1510 - else => {}, │ │1511 - } │ │1512 - } │ │1513 - } │ │1514 - │ │1515 - try client_repo.add(io, allocator, &.{dir_name}); │ │1516 - } │ │1517 - │ │1518 - const commit2 = try client_repo.commit(io, allocator, .{ .message = "more stuff" }); │ │1519 - │ │1520 - // add remote │ │1521 - { │ │1522 - if (.windows == builtin.os.tag) { │ │1523 - std.mem.replaceScalar(u8, server_path, '\\', '/'); │ │1524 - } │ │1525 - const separator = if (server_path[0] == '/') "" else "/"; │ │1526 - │ │1527 - const remote_url = switch (transport_def) { │ │1528 - //.file => try std.fmt.allocPrint(allocator, "file://{s}{s}", .{ separator, server_path }), │ │1529 - .file => try std.fmt.allocPrint(allocator, "../server", .{}), // relative file paths work too │ │1530 - .wire => |wire_kind| switch (wire_kind) { │ │1531 - .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{port}), │ │1532 - .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/server", .{port}), │ │1533 - .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ port, separator, server_path }), │ │1534 - }, │ │1535 - }; │ │1536 - defer allocator.free(remote_url); │ │1537 - │ │1538 - try client_repo.addRemote(io, allocator, .{ .name = "origin", .value = remote_url }); │ │1539 - try client_repo.addConfig(io, allocator, .{ .name = "branch.master.remote", .value = "origin" }); │ │1540 - } │ │1541 - │ │1542 - const is_ssh = switch (transport_def) { │ │1543 - .file => false, │ │1544 - .wire => |wire_kind| .ssh == wire_kind, │ │1545 - }; │ │1546 - │ │1547 - const receive_pack_command = try receivePackCommand(is_ssh, allocator, cwd_path, port); │ │1548 - defer allocator.free(receive_pack_command); │ │1549 - │ │1550 - if (shell_out_to_git) { │ │1551 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1552 - defer allocator.free(priv_key_path); │ │1553 - const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand=ssh -o StrictHostKeyChecking=no -o IdentityFile={s}", .{priv_key_path}); │ │1554 - defer allocator.free(ssh_config_arg); │ │1555 - │ │1556 - // shell out to git so it will send delta objects │ │1557 - var process = try std.process.spawn(io, .{ │ │1558 - .argv = if (is_ssh) │ │1559 - &.{ "git", "-c", ssh_config_arg, "push", "--receive-pack", receive_pack_command, "origin", "master" } │ │1560 - else │ │1561 - &.{ "git", "push", "origin", "master" }, │ │1562 - .cwd = .{ .path = client_path }, │ │1563 - .stdin = .ignore, │ │1564 - .stdout = .ignore, │ │1565 - .stderr = .ignore, │ │1566 - }); │ │1567 - const term = try process.wait(io); │ │1568 - if (term != .exited) { │ │1569 - return error.GitCommandFailed; │ │1570 - } │ │1571 - } else { │ │1572 - const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { │ │1573 - const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); │ │1574 - defer allocator.free(known_hosts_path); │ │1575 - │ │1576 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1577 - defer allocator.free(priv_key_path); │ │1578 - │ │1579 - break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path }); │ │1580 - } else null; │ │1581 - defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); │ │1582 - │ │1583 - try client_repo.push( │ │1584 - io, │ │1585 - allocator, │ │1586 - "origin", │ │1587 - "master", │ │1588 - false, │ │1589 - .{ .wire = .{ .ssh = .{ │ │1590 - .command = ssh_cmd_maybe, │ │1591 - .receive_pack_command = receive_pack_command, │ │1592 - } } }, │ │1593 - ); │ │1594 - │ │1595 - if (transport_def == .file) { │ │1596 - // update the working dir │ │1597 - try server_repo.restore(io, allocator, "."); │ │1598 - } │ │1599 - } │ │1600 - │ │1601 - // make sure push was successful │ │1602 - { │ │1603 - const oid_master = (try server_repo.readRef(io, .{ .kind = .head, .name = "master" })).?; │ │1604 - try std.testing.expectEqualStrings(&commit2, &oid_master); │ │1605 - │ │1606 - const hello_txt = try temp_dir.openFile(io, "repos/server/hello.txt", .{}); │ │1607 - defer hello_txt.close(io); │ │1608 - } │ │1609 -} │ │1610 - │ │1611 -fn copyDir(io: std.Io, src_dir: std.Io.Dir, dest_dir: std.Io.Dir) !void { │ │1612 - var iter = src_dir.iterate(); │ │1613 - while (try iter.next(io)) |entry| { │ │1614 - switch (entry.kind) { │ │1615 - .file => try src_dir.copyFile(entry.name, dest_dir, entry.name, io, .{}), │ │1616 - .directory => { │ │1617 - try dest_dir.createDirPath(io, entry.name); │ │1618 - var dest_entry_dir = try dest_dir.openDir(io, entry.name, .{ .access_sub_paths = true, .iterate = true, .follow_symlinks = false }); │ │1619 - defer dest_entry_dir.close(io); │ │1620 - var src_entry_dir = try src_dir.openDir(io, entry.name, .{ .access_sub_paths = true, .iterate = true, .follow_symlinks = false }); │ │1621 - defer src_entry_dir.close(io); │ │1622 - try copyDir(io, src_entry_dir, dest_entry_dir); │ │1623 - }, │ │1624 - else => {}, │ │1625 - } │ │1626 - } │ │1627 -} │ │1628 - │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ added ─────────╮ │src/test/net.zig│ ╰────────────────╯