╭ changed ────╮ │src/serve.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────────────────────────────────╮ │1 const std = @import("std"); │ │2 +const builtin = @import("builtin");│ │3 const xit = @import("xit"); │ │4 const rp = xit.repo; │ │5 const ui = @import("./ui.zig"); │ ╰──────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │14 ssh_listen: []const u8 = "127.0.0.1:8022", │ │15 wui_listen: []const u8 = "127.0.0.1:8000", │ │16 data_dir: []const u8 = ".", │ │16 - │ │17 - // the port from `wui_listen`, for the TUI footer's url. │ │18 - pub fn wuiPort(self: Options) !u16 { │ │19 - return (try parseListenAddress(self.wui_listen)).port;│ │20 - } │ │17 }; │ │18 │ │19 const ListenAddress = struct { │ ╰──────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │50 │ │51 const admin_repo_path = try std.fs.path.resolve(allocator, &.{ data_dir_path, "admin" });│ │52 defer allocator.free(admin_repo_path); │ │53 + │ │54 + // a debug build is a dev run, so a taken port falls back to a random one; │ │55 + // a release build fails, since whatever points at the configured port │ │56 + // would silently break. │ │57 + const fallback = builtin.mode == .Debug; │ │58 │ │59 // create http listener │ │60 │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │61 const http_listen_address = try parseListenAddress(options.http_listen); │ │61 - const http_address = try std.Io.net.IpAddress.parseIp4(http_listen_address.host, http_listen_address.port);│ │62 - var http_server = try http_address.listen(io, .{ .reuse_address = true }); │ │62 + var http_server = try listenWithFallback(io, http_listen_address, fallback); │ │63 defer http_server.deinit(io); │ │64 │ │65 // create ssh listener │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │66 │ │67 const ssh_listen_address = try parseListenAddress(options.ssh_listen); │ │68 - const ssh_address = try std.Io.net.IpAddress.parseIp4(ssh_listen_address.host, ssh_listen_address.port);│ │69 - var ssh_server = try ssh_address.listen(io, .{ .reuse_address = true }); │ │68 + var ssh_server = try listenWithFallback(io, ssh_listen_address, fallback); │ │69 defer ssh_server.deinit(io); │ │70 │ │71 // load or generate the SSH host key │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │78 // create wui listener │ │79 │ │80 const wui_listen_address = try parseListenAddress(options.wui_listen); │ │82 - const wui_address = try std.Io.net.IpAddress.parseIp4(wui_listen_address.host, wui_listen_address.port);│ │83 - var wui_server = try wui_address.listen(io, .{ .reuse_address = true }); │ │81 + var wui_server = try listenWithFallback(io, wui_listen_address, fallback); │ │82 defer wui_server.deinit(io); │ │83 │ │84 // start task group │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │86 var tasks: std.Io.Group = .init; │ │87 defer tasks.cancel(io); │ │88 │ │91 - // run listeners │ │89 + // run listeners, printing the ports actually bound │ │90 │ │93 - try err.print("serving HTTP on {s}, repo root {s}\n", .{ options.http_listen, repo_root_path }); │ │91 + try err.print("serving HTTP on {s}:{d}, repo root {s}\n", .{ http_listen_address.host, http_server.socket.address.getPort(), repo_root_path });│ │92 try err.flush(); │ │93 │ │94 serve_http.runListener(repo_kind, any_repo_opts, io, allocator, repo_root_path, admin_repo_path, &http_server, &tasks, err); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │95 │ │98 - try err.print("serving SSH on {s}\n", .{options.ssh_listen}); │ │96 + try err.print("serving SSH on {s}:{d}\n", .{ ssh_listen_address.host, ssh_server.socket.address.getPort() });│ │97 try err.flush(); │ │98 │ │99 const ssh_session_handler = serve_ssh.SessionHandler{ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │100 .admin_repo_path = admin_repo_path, │ │101 .repo_root_path = repo_root_path, │ │104 - .wui_port = wui_listen_address.port, │ │102 + .wui_port = wui_server.socket.address.getPort(), │ │103 .err = err, │ │104 }; │ │105 serve_ssh.runListener(io, allocator, &host_key, &ssh_session_handler, &ssh_server, &tasks, err);│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │106 │ │109 - try err.print("serving web UI on http://{s}/\n", .{options.wui_listen}); │ │107 + try err.print("serving web UI on http://{s}:{d}/\n", .{ wui_listen_address.host, wui_server.socket.address.getPort() });│ │108 try err.flush(); │ │109 │ │110 runWebListener(io, allocator, &wui_server, &tasks, .{ .remote = .{ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯