╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ────╮ │src/serve.zig│ ╰─────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │119 .host_key = try serve_ssh_protocol.HostKey.loadOrGenerate(io, allocator, data_dir_path),│ │120 .session_handler = .{ │ │121 .admin_repo_path = admin_repo_path, │ │117 - .users_dir = users_dir, │ │122 + .server = .{ .users_dir = users_dir, .dirty_repos = &dirty_repos }, │ │123 .wui_port = wui_server.socket.address.getPort(), │ │124 .git_http_port = git_http_port, │ │125 .git_ssh_port = ssh_port, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │134 │ │135 var tasks: std.Io.Group = .init; │ │136 defer tasks.cancel(io); │ │137 + │ │138 + tasks.concurrent(io, notify.run, .{ &dirty_repos, io, allocator, users_dir, admin_repo_path }) catch |spawn_err| {│ │139 + serve_common.logError(io, err, "notify worker spawn failed: {s}\n", .{@errorName(spawn_err)}); │ │140 + }; │ │141 │ │142 // run listeners, printing the ports actually bound │ │143 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │161 runWebListener(io, allocator, &wui_server, &tasks, .{ .server = .{│ │162 .admin_repo_path = admin_repo_path, │ │163 .users_dir = users_dir, │ │164 + .dirty_repos = &dirty_repos, │ │165 .session_store = session_store, │ │166 .git_http_port = git_http_port, │ │167 .git_ssh_port = git_ssh_port, │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────╮ │src/serve_ssh.zig│ ╰─────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────╮ │28 │ │29 pub const SessionHandler = struct { │ │30 admin_repo_path: []const u8, │ │31 - users_dir: []const u8, │ │31 + // where event writes go │ │32 + server: evt.Host.Server, │ │33 wui_port: u16, // port the web UI is served on, shown in the TUI footer's url│ │34 git_http_port: ?u16, │ │35 git_ssh_port: ?u16, │ ╰─────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────╮ │237 ui_session.data.git_ssh_prefix = handler.git_ssh_prefix;│ │238 // let page builders open on-disk repos │ │239 ui_session.io = io; │ │239 - ui_session.users_dir = handler.users_dir; │ │240 + ui_session.users_dir = handler.server.users_dir; │ │241 + ui_session.dirty_repos = handler.server.dirty_repos; │ │242 │ │243 var nav = try ui.Nav.init(allocator, &ui_session); │ │244 defer nav.deinit(allocator); │ ╰─────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │386 const owner_repo = evt.parseOwnerRepoPath(repo_identity) orelse return writeError(sess, "repo path must be <owner>:<repo>"); │ │387 var author_arena = std.heap.ArenaAllocator.init(allocator); │ │388 defer author_arena.deinit(); │ │387 - const author = switch (try authorizeRepoKey(io, &author_arena, handler.admin_repo_path, handler.users_dir, owner_repo.owner, owner_repo.name, parsed.service, &sess.fingerprint)) { │ │389 + const author = switch (try authorizeRepoKey(io, &author_arena, handler.admin_repo_path, handler.server.users_dir, owner_repo.owner, owner_repo.name, parsed.service, &sess.fingerprint)) {│ │390 .allowed => |user| user, │ │391 .create => |creator| return runCreatingPush(handler, sess, &creator, owner_repo.name, protocol_version, &reader.interface, &writer.interface), │ │392 .denied => return switch (parsed.service) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │396 .not_found => return writeError(sess, "repo not found"), │ │397 }; │ │398 │ │397 - const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, handler.users_dir, handler.admin_repo_path, repo_identity)) { │ │399 + const repo_path = switch (try serve_common.resolveRepoPath(io, allocator, handler.server.users_dir, handler.admin_repo_path, repo_identity)) {│ │400 .ok => |p| p, │ │401 .invalid => unreachable, // parsed above │ │402 .not_found => return writeError(sess, "repo not found"), │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │412 var sideband = progress.Sideband{ .writer = &writer.interface, .sess = sess }; │ │413 try repo.uploadPack(io, allocator, &reader.interface, &writer.interface, .{ .protocol_version = protocol_version }, &sideband); │ │414 }, │ │413 - .receive_pack => try push.receivePackAndConsume(repo.self_repo_opts, io, allocator, repo, &reader.interface, &writer.interface, .{ .protocol_version = protocol_version }, author, handler.users_dir, handler.err, sess),│ │415 + .receive_pack => try push.receivePackAndConsume(repo.self_repo_opts, io, allocator, repo, &reader.interface, &writer.interface, .{ .protocol_version = protocol_version }, author, handler.server, handler.err, sess), │ │416 }, │ │417 } │ │418 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │438 // a client with nothing to push creates nothing │ │439 const hash_kind = (try xit.net_server_receive_pack.peekObjectFormat(reader)) orelse return; │ │440 │ │439 - const location = evt.createRepo(io, allocator, handler.users_dir, &creator.owner_id, creator.owner, name, "", .private, hash_kind) catch |err| switch (err) { │ │441 + const location = evt.createRepo(io, allocator, handler.server.users_dir, &creator.owner_id, creator.owner, name, "", .private, hash_kind) catch |err| switch (err) {│ │442 error.NameTaken => return writeError(sess, "repo was just created by another push, try again"), │ │443 else => |e| return e, │ │444 }; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │443 - const repo_path = try evt.repoPath(allocator, handler.users_dir, &location.owner_id, &location.repo_id); │ │445 + const repo_path = try evt.repoPath(allocator, handler.server.users_dir, &location.owner_id, &location.repo_id);│ │446 defer allocator.free(repo_path); │ │447 │ │448 switch (hash_kind) { │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯