╭ changed ───────╮ │src/test/net.zig│ ╰────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1137 │ │1138 // add remote │ │1139 { │ │1246 - if (.windows == builtin.os.tag) { │ │1247 - std.mem.replaceScalar(u8, server_path, '\\', '/'); │ │1248 - } │ │1249 - const separator = if (server_path[0] == '/') "" else "/"; │ │1250 - │ │1251 - const remote_url = switch (transport_def) { │ │1252 - //.file => try std.fmt.allocPrint(allocator, "file://{s}{s}", .{ separator, server_path }), │ │1253 - .file => try std.fmt.allocPrint(allocator, "../server", .{}), // relative file paths work too │ │1254 - .wire => |wire_kind| switch (wire_kind) { │ │1255 - .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/server", .{http_port}), │ │1256 - .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/server", .{http_port}), │ │1257 - .ssh => try std.fmt.allocPrint(allocator, "ssh://localhost:{}{s}{s}", .{ ssh_port, separator, server_path }),│ │1258 - }, │ │1259 - }; │ │1140 + const remote_url = try remoteUrl(transport_def, allocator, "testrepo"); │ │1141 defer allocator.free(remote_url); │ │1142 │ │1143 try client_repo.addRemote(io, allocator, .{ .name = "origin", .value = remote_url }); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1152 if (shell_out_to_git) { │ │1153 const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1154 defer allocator.free(priv_key_path); │ │1274 - const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand=ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityFile={s}", .{priv_key_path}); │ │1155 + const ssh_config_arg = try std.fmt.allocPrint(allocator, "core.sshCommand=ssh -p {} -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityFile={s}", .{ ssh_port, priv_key_path });│ │1156 defer allocator.free(ssh_config_arg); │ │1157 │ │1158 // shell out to git so it will send delta objects │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1171 return error.GitCommandFailed; │ │1172 } │ │1173 } else { │ │1293 - const ssh_cmd_maybe: ?[]const u8 = if (is_ssh) blk: { │ │1294 - const known_hosts_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "known_hosts" }); │ │1295 - defer allocator.free(known_hosts_path); │ │1296 - │ │1297 - const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1298 - defer allocator.free(priv_key_path); │ │1299 - │ │1300 - break :blk try std.fmt.allocPrint(allocator, "ssh -o UserKnownHostsFile=\"{s}\" -o IdentityFile=\"{s}\"", .{ known_hosts_path, priv_key_path });│ │1301 - } else null; │ │1174 + const ssh_cmd_maybe = try sshCommand(is_ssh, allocator, cwd_path, temp_dir_name); │ │1175 defer if (ssh_cmd_maybe) |ssh_cmd| allocator.free(ssh_cmd); │ │1176 │ │1177 try client_repo.push( │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │1184 .command = ssh_cmd_maybe, │ │1185 } } }, │ │1186 ); │ │1314 - │ │1315 - if (transport_def == .file) { │ │1316 - // update the working dir │ │1317 - try server_repo.restore(io, allocator, ".");│ │1318 - } │ │1187 } │ │1188 │ │1189 // make sure push was successful │ ╰──────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1191 const oid_master = (try server_repo.readRef(io, .{ .kind = .head, .name = "master" })).?;│ │1192 try std.testing.expectEqualStrings(&commit2, &oid_master); │ │1193 │ │1326 - const hello_txt = try temp_dir.openFile(io, "repos/server/hello.txt", .{}); │ │1194 + const hello_txt = try server_repo.core.work_dir.openFile(io, "hello.txt", .{}); │ │1195 defer hello_txt.close(io); │ │1196 } │ │1197 } │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1212 else => {}, │ │1213 } │ │1214 } │ │1215 +} │ │1216 + │ │1217 +// the dev SSH public key matching the private key written by runServer │ │1218 +const admin_ssh_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKeIs8mJqigBZ5y84J4COgnAJJ5bHPKy+lM2SliMXbYm radar@roark"; │ │1219 + │ │1220 +// create the admin event repo with a user holding the dev SSH public key │ │1221 +fn setupAdmin(io: std.Io, allocator: std.mem.Allocator, data_dir_name: []const u8) !void { │ │1222 + const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │1223 + defer allocator.free(cwd_path); │ │1224 + │ │1225 + const admin_repo_path = try std.fs.path.join(allocator, &.{ cwd_path, data_dir_name, "admin" }); │ │1226 + defer allocator.free(admin_repo_path); │ │1227 + │ │1228 + var repo = try rp.Repo(.xit, evt.admin_repo_opts).init(io, allocator, .{ .path = admin_repo_path }); │ │1229 + defer repo.deinit(io, allocator); │ │1230 + │ │1231 + try repo.addConfig(io, allocator, .{ .name = "user.name", .value = "haxy" }); │ │1232 + try repo.addConfig(io, allocator, .{ .name = "user.email", .value = "admin@haxy" }); │ │1233 + │ │1234 + var prng = std.Random.DefaultPrng.init(std.testing.random_seed); │ │1235 + const user_id = evt.EventWithId.randomId(prng.random()); │ │1236 + │ │1237 + var password_hash_buf: [evt.User.password_hash_max_len]u8 = undefined; │ │1238 + const password_hash = try evt.User.hashPassword("password", &password_hash_buf, io); │ │1239 + │ │1240 + try evt.commitAndConsume(evt.admin_repo_opts, io, allocator, &repo, evt.events_ref, &[_]evt.EventWithId{.{ │ │1241 + .id = std.fmt.bytesToHex(user_id, .lower), │ │1242 + .event = .{ .user = .{ │ │1243 + .name = "admin", │ │1244 + .display_name = "Admin", │ │1245 + .email = "admin@example.test", │ │1246 + .password_hash = password_hash, │ │1247 + .ssh_keys = admin_ssh_key, │ │1248 + } }, │ │1249 + }}); │ │1250 +} │ │1251 + │ │1252 +// resolve admin/<repo_name> to its on-disk directory under <data_dir>/repos via │ │1253 +// the event store │ │1254 +fn repoOnDiskPath( │ │1255 + io: std.Io, │ │1256 + allocator: std.mem.Allocator, │ │1257 + data_dir_name: []const u8, │ │1258 + repo_name: []const u8, │ │1259 + create: bool, │ │1260 +) !?[]u8 { │ │1261 + const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │1262 + defer allocator.free(cwd_path); │ │1263 + │ │1264 + const admin_repo_path = try std.fs.path.join(allocator, &.{ cwd_path, data_dir_name, "admin" }); │ │1265 + defer allocator.free(admin_repo_path); │ │1266 + │ │1267 + const event_id_hex = (try evt.resolveOrCreateRepo(io, allocator, admin_repo_path, "admin", repo_name, create)) orelse return null; │ │1268 + return try std.fs.path.join(allocator, &.{ cwd_path, data_dir_name, "repos", &event_id_hex }); │ │1269 } │ │1270 │ │1271 +// build the remote URL addressing admin/<repo_name> over the given transport │ │1272 +fn remoteUrl( │ │1273 + comptime transport_def: net.TransportDefinition, │ │1274 + allocator: std.mem.Allocator, │ │1275 + repo_name: []const u8, │ │1276 +) ![]u8 { │ │1277 + return switch (transport_def) { │ │1278 + .file => unreachable, │ │1279 + .wire => |wire_kind| switch (wire_kind) { │ │1280 + .http => try std.fmt.allocPrint(allocator, "http://localhost:{}/admin/{s}", .{ http_port, repo_name }), │ │1281 + .raw => try std.fmt.allocPrint(allocator, "git://localhost:{}/admin/{s}", .{ http_port, repo_name }), │ │1282 + .ssh => try std.fmt.allocPrint(allocator, "git@localhost:admin/{s}", .{repo_name}), │ │1283 + }, │ │1284 + }; │ │1285 +} │ │1286 + │ │1287 +// the ssh command the haxy git client should invoke, or null when not using │ │1288 +// ssh. haxy generates a fresh host key on every run, so accept the unknown host │ │1289 +// and skip writing to known_hosts. │ │1290 +fn sshCommand( │ │1291 + is_ssh: bool, │ │1292 + allocator: std.mem.Allocator, │ │1293 + cwd_path: []const u8, │ │1294 + temp_dir_name: []const u8, │ │1295 +) !?[]u8 { │ │1296 + if (!is_ssh) return null; │ │1297 + │ │1298 + const priv_key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │1299 + defer allocator.free(priv_key_path); │ │1300 + │ │1301 + return try std.fmt.allocPrint(allocator, "ssh -p {} -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o IdentityFile=\"{s}\"", .{ ssh_port, priv_key_path });│ │1302 +} │ │1303 + │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯