╭──────────╮ │← previous│ ╰──────────╯ ╭ changed ───╮ │src/fork.zig│ ╰────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │157 var config = try xit.config.Config(.xit, repo_opts).init(state.readOnly(), ctx.io, ctx.allocator);│ │158 defer config.deinit(); │ │159 try config.add(state, ctx.io, .{ .name = "receive.denydeletes", .value = "true" }); │ │160 + try config.add(state, ctx.io, .{ .name = "haxy.forker", .value = &ctx.forker_id }); │ │161 │ │162 // the copied db carries the target's file index, so the patch │ │163 // branch's entry starts from one of those rather than walking │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │174 const history = try DB.ArrayList(.read_write).init(fork_repo.core.db.rootCursor()); │ │175 try history.appendContext( │ │176 .{ .slot = try history.getSlot(-1) }, │ │176 - Ctx{ .core = &fork_repo.core, .io = io, .allocator = allocator }, │ │177 + Ctx{ .core = &fork_repo.core, .io = io, .allocator = allocator, .forker_id = std.fmt.bytesToHex(input.user_id, .lower) },│ │178 ); │ │179 } │ │180 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │192 }}); │ │193 │ │194 // create the fork event │ │194 - try evt.consume(.server, .admin, .xit, evt.admin_repo_opts, io, allocator, admin_repo, evt.events_ref, &.{.{│ │195 + try evt.consume(.server, .user, .xit, evt.user_repo_opts, io, allocator, user_repo, evt.events_ref, &.{.{ │ │196 .id = input.id, │ │197 .timestamp = input.timestamp, │ │198 .author = input.author, │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────╮ │199 .event = .{ .fork = .{ │ │199 - .user_id = &input.user_id, │ │200 .repo_id = &input.repo_id, │ │201 + .repo_user_id = &input.repo_user_id,│ │202 } }, │ │203 }}); │ │204 │ ╰─────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────╮ │210 io: std.Io, │ │211 allocator: std.mem.Allocator, │ │212 repo_root_path: []const u8, │ │212 - admin_repo: *rp.Repo(.xit, evt.admin_repo_opts),│ │213 + user_repo: *rp.Repo(.xit, evt.user_repo_opts), │ │214 id: *const [evt.event_id_size * 2]u8, │ │214 - expected_user_id: ?*const [evt.event_id_size]u8,│ │215 author: evt.CommitAuthor, │ │216 ) !void { │ │217 const fork_id = try evt.parseEventId(id); │ ╰─────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │218 var arena = std.heap.ArenaAllocator.init(allocator); │ │219 defer arena.deinit(); │ │220 - const moment = try evt.currentMoment(evt.admin_repo_opts, admin_repo); │ │221 - const record = (try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, moment, &arena, &fork_id)) orelse return error.InvalidPatchDraft;│ │222 - if (expected_user_id) |user_id| { │ │223 - if (!std.mem.eql(u8, record.event.user_id, user_id)) return error.InvalidPatchDraft; │ │224 - } │ │225 - if (!record.removed) try evt.remove(.server, .admin, .xit, evt.admin_repo_opts, io, allocator, admin_repo, &fork_id, .fork, author); │ │220 + const moment = (try evt.userMoment(user_repo)) orelse return error.InvalidPatchDraft; │ │221 + const record = (try evt.Fork.readById(evt.UserDB, evt.user_repo_opts.hash, moment, &arena, &fork_id)) orelse return error.InvalidPatchDraft; │ │222 + if (!record.removed) try evt.remove(.server, .user, .xit, evt.user_repo_opts, io, allocator, user_repo, &fork_id, .fork, author); │ │223 │ │224 const path = try forkPath(allocator, repo_root_path, id); │ │225 defer allocator.free(path); │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │226 try std.Io.Dir.cwd().deleteTree(io, path); │ │227 } │ │228 │ │229 +// the id of the user who created the fork at `fork_path`, or null when it's gone │ │230 +pub fn readForkerId(io: std.Io, allocator: std.mem.Allocator, fork_path: []const u8) !?[evt.event_id_size]u8 { │ │231 + var fork_repo = rp.Repo(.xit, .{}).open(io, allocator, .{ .path = fork_path, .require_repo_root = true }) catch |err| switch (err) {│ │232 + error.RepoNotFound, error.FileNotFound => return null, │ │233 + else => |e| return e, │ │234 + }; │ │235 + defer fork_repo.deinit(io, allocator); │ │236 + var config = try fork_repo.listConfig(io, allocator); │ │237 + defer config.deinit(); │ │238 + const section = config.sections.get("haxy") orelse return error.InvalidPatchDraft; │ │239 + return try evt.parseEventId(section.get("forker") orelse return error.InvalidPatchDraft); │ │240 +} │ │241 + │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────╮ │src/patch.zig│ ╰─────────────╯ ╭─────────────────────────────────────────────────────────╮ │869 io: std.Io, │ │870 allocator: std.mem.Allocator, │ │871 admin_repo: *rp.Repo(.xit, evt.admin_repo_opts),│ │872 + user_repo: *rp.Repo(.xit, evt.user_repo_opts), │ │873 target_repo: *rp.Repo(.xit, repo_opts), │ │874 fork_path: []const u8, │ │875 input: PublishInput, │ ╰─────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │880 │ │881 // validate the fork ownership and publishing identity │ │882 const admin_moment = try evt.currentMoment(evt.admin_repo_opts, admin_repo); │ │882 - const fork_record = (try evt.Fork.readById(evt.AdminDB, evt.admin_repo_opts.hash, admin_moment, &arena, &patch_id)) orelse return error.InvalidPatchDraft;│ │883 - if (fork_record.removed or │ │884 - !std.mem.eql(u8, fork_record.event.user_id, &input.user_id) or │ │885 - !std.mem.eql(u8, fork_record.event.repo_id, &input.repo_id)) return error.InvalidPatchDraft; │ │883 + const user_moment = (try evt.userMoment(user_repo)) orelse return error.InvalidPatchDraft; │ │884 + const fork_record = (try evt.Fork.readById(evt.UserDB, evt.user_repo_opts.hash, user_moment, &arena, &patch_id)) orelse return error.InvalidPatchDraft; │ │885 + if (fork_record.removed or !std.mem.eql(u8, fork_record.event.repo_id, &input.repo_id)) return error.InvalidPatchDraft; │ │886 const user = (try evt.User.readById(evt.AdminDB, evt.admin_repo_opts.hash, admin_moment, &arena, &input.user_id)) orelse return error.InvalidPatchDraft; │ │887 if (user.removed or │ │888 !std.mem.eql(u8, user.event.name, input.author.name) or │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────╮ │937 }}); │ │938 } │ │939 │ │940 - // mark the fork as published in the administration repository│ │940 + // mark the fork as published in the forker's user repo │ │941 if (fork_record.event.stage == .draft) { │ │942 var published = fork_record.event; │ │943 published.stage = .publish; │ ╰───────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯