╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ try.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
220 defer repo_dir.close(io); 221 222 const readme = try repo_dir.createFile(io, "README.md", .{}); 223 defer readme.close(io); 224 const readme_content = try std.fmt.allocPrint(arena.allocator(), "# {s}\n\n{s}\n", .{ r.name, r.description }); 225 try readme.writeStreamingAll(io, readme_content); 226 227 try repo_dir.createDirPath(io, "docs/dev"); 228 const doc = try repo_dir.createFile(io, "docs/dev/contribute.md", .{}); 229 defer doc.close(io); 230 try doc.writeStreamingAll(io, "To contribute, please make a pull request"); 231 } 232 233 try repo_i.add(io, allocator, &.{ "README.md", "docs/dev/contribute.md" }); 234 _ = try repo_i.commit(io, allocator, .{ .message = "let there be light" }); 235 236 // a batch of commits so the commits tab has more than one page to 237 // paginate through. each rewrites a few files with scattered line 238 // edits, so every commit is a multi-file diff with several separate 239 // hunks to look at. stepped timestamps vary the date column. 240 const base_ts: u64 = 1_700_000_000; // 2023-11-14 241 const edit_files = [_][]const u8{ "src/alpha.txt", "src/beta.txt", "src/gamma.txt" }; 242 // cycled through to pad each line into prose-like content. 243 const words = [_][]const u8{ 244 "lorem", "ipsum", "dolor", "sit", "amet", "consectetur", 245 "quantum", "vector", "matrix", "buffer", "kernel", "socket", 246 "falcon", "otter", "badger", "walrus", "ferret", "marmot", 247 "scatter", "gather", "encode", "decode", "render", "commit", 248 }; 249 // commit subjects, cycled then padded to a varying length. 250 const subjects = [_][]const u8{ 251 "fix off-by-one in scatter loop", 252 "encode and decode buffers", 253 "tune kernel socket timeouts", 254 "render matrix vector product", 255 "gather falcon and otter stats", 256 "refactor badger walrus module", 257 "drop dead ferret marmot branch", 258 }; 259 var c: usize = 0; 260 while (c < 30) : (c += 1) { 261 { 262 var repo_dir = try cwd.openDir(io, repo_path, .{}); 263 defer repo_dir.close(io); 264 try repo_dir.createDirPath(io, "src"); 265 for (edit_files, 0..) |path, fi| { 266 const file = try repo_dir.createFile(io, path, .{}); 267 defer file.close(io); 268 var writer = std.Io.Writer.Allocating.init(allocator); 269 defer writer.deinit(); 270 // every 8th line is edited by this commit; the stride 271 // offset shifts with c so consecutive commits touch 272 // different lines, and the edited text carries a 273 // c-dependent word, so each commit's diff is a distinct 274 // scatter of hunks. untouched lines stay byte-identical 275 // across commits (their padding ignores c) so they don't 276 // all change every commit. each line is padded with 277 // cycled words to a per-line length, capped at 120. 278 for (0..40) |line| { 279 const start = writer.written().len; 280 if ((line + fi + c) % 8 == 0) { 281 const tag = words[(line + fi + c * 3) % words.len]; 282 try writer.writer.print("line {d}: rev {d} {s}", .{ line, c, tag }); 283 } else { 284 try writer.writer.print("line {d}", .{line}); 285 } 286 const target = 40 + (line * 17 + fi * 23) % 70; 287 var w = line + fi; 288 while (true) : (w += 1) { 289 const word = words[w % words.len]; 290 const len = writer.written().len - start; 291 if (len >= target or len + 1 + word.len > 120) break; 292 try writer.writer.print(" {s}", .{word}); 293 } 294 try writer.writer.writeByte('\n'); 295 } 296 try file.writeStreamingAll(io, writer.written()); 297 } 298 } 299 try repo_i.add(io, allocator, &edit_files); 300 // a cycling subject padded to a c-varying length so the commit 301 // list shows messages of different widths, capped at 120. 302 var msg_writer = std.Io.Writer.Allocating.init(allocator); 303 defer msg_writer.deinit(); 304 try msg_writer.writer.print("{s}", .{subjects[c % subjects.len]}); 305 const msg_target = 16 + (c * 41) % 96; 306 var mw = c; 307 while (true) : (mw += 1) { 308 const word = words[mw % words.len]; 309 const len = msg_writer.written().len; 310 if (len >= msg_target or len + 1 + word.len > 120) break; 311 try msg_writer.writer.print(" {s}", .{word}); 312 } 313 const message = try arena.allocator().dupe(u8, msg_writer.written()); 314 _ = try repo_i.commit(io, allocator, .{ .message = message, .timestamp = base_ts + c * std.time.s_per_day }); 315 } 316 } 317 318 break :blk try ui.Session.init(&session_arena, &repo, .{}); 319 }; 320 session.is_terminal = true; 321 // try uses the default serve options below, so the footer's url points at 322 // the default web UI port. 323 session.web_port = try (srv.Options{}).wuiPort(); 324 325 // start the server 326 327 var cli = false; 328 329 var arg_it = try init.minimal.args.iterateAllocator(allocator); 330 defer arg_it.deinit(); 331 _ = arg_it.skip(); 332 while (arg_it.next()) |arg| { 333 if (std.mem.eql(u8, "--cli", arg)) { 334 cli = true; 335 } 336 } 337 338 const server_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "server" }); 339 defer allocator.free(server_path); 340 341 // let the native TUI's page builders open the on-disk repos for the file tree 342 session.io = io; 343 session.repos_dir = try std.fs.path.join(session_arena.allocator(), &.{ server_path, "repos" }); 344 345 if (cli) { 346 var stdout_writer = std.Io.File.stdout().writer(io, &.{}); 347 var stderr_writer = std.Io.File.stderr().writer(io, &.{}); 348 const run_opts = hx.main.RunOpts{ .out = &stdout_writer.interface, .err = &stderr_writer.interface }; 349 350 const key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); 351 defer allocator.free(key_path); 352 353 const Runnable = struct { 354 io: std.Io, 355 key_path: []const u8, 356 357 pub fn run(self: @This()) !void { 358 std.debug.print( 359 \\ 360 \\connect to the TUI with: 361 \\ ssh -p 8022 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null localhost 362 \\ 363 \\create a git repo and push over SSH: 364 \\ mkdir -p temp-try/client/test 365 \\ cd temp-try/client/test 366 \\ git init 367 \\ echo "hello" > hello.txt 368 \\ git add hello.txt 369 \\ git commit -m "let there be light" 370 \\ GIT_SSH_COMMAND='ssh -p 8022 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -i {s}' git push localhost:admin/test HEAD:master 371 \\ 372 \\to quit, press enter. 373 \\ 374 , .{self.key_path}); 375 // portable stdin read via std.Io — equivalent to the 376 // std.posix.read(STDIN_FILENO, ...) that doesn't compile 377 // on windows. 378 var buf: [1]u8 = undefined; 379 var stdin_reader = std.Io.File.stdin().reader(self.io, &buf); 380 _ = stdin_reader.interface.takeByte() catch {}; 381 } 382 }; 383 384 try srv.run(.xit, .{}, io, allocator, cwd_path, .{ 385 .data_dir = server_path, 386 }, run_opts.err, Runnable{ .io = io, .key_path = key_path }); 387 } else { 388 var null_writer = std.Io.Writer.Discarding.init(&.{}); 389 const run_opts = hx.main.RunOpts{ .out = &null_writer.writer, .err = &null_writer.writer }; 390 391 const Runnable = struct { 392 io: std.Io, 393 allocator: std.mem.Allocator, 394 session: *ui.Session, 395 396 pub fn run(self: @This()) !void { 397 // launch the TUI 398 try hx.ui.run(self.io, self.allocator, self.session); 399 } 400 }; 401 402 try srv.run(.xit, .{}, io, allocator, cwd_path, .{ 403 .data_dir = server_path, 404 }, run_opts.err, Runnable{ .io = io, .allocator = allocator, .session = &session }); 405 } 406 } 407