╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ try.zig ──────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
140 .{ .user_index = 2, .name = "scikit-learn", .description = "Machine learning in Python" }, 141 .{ .user_index = 3, .name = "nginx", .description = "High performance HTTP server and reverse proxy" }, 142 .{ .user_index = 4, .name = "redis", .description = "In-memory data structure store, used as a database, cache, and message broker" }, 143 .{ .user_index = 5, .name = "postgres", .description = "The world's most advanced open source relational database" }, 144 .{ .user_index = 6, .name = "sqlite", .description = "Self-contained, serverless, zero-configuration SQL database engine" }, 145 .{ .user_index = 7, .name = "mongodb", .description = "The MongoDB Database" }, 146 .{ .user_index = 8, .name = "elasticsearch", .description = "Free and open, distributed, RESTful search engine" }, 147 .{ .user_index = 9, .name = "kafka", .description = "Distributed event streaming platform" }, 148 .{ .user_index = 10, .name = "terraform", .description = "Infrastructure as code tool" }, 149 .{ .user_index = 11, .name = "svelte", .description = "Cybernetically enhanced web apps" }, 150 .{ .user_index = 12, .name = "vue", .description = "The progressive JavaScript framework" }, 151 .{ .user_index = 13, .name = "flask", .description = "The Python micro framework for building web applications" }, 152 .{ .user_index = 14, .name = "django", .description = "The web framework for perfectionists with deadlines" }, 153 .{ .user_index = 15, .name = "rails", .description = "Ruby on Rails web framework" }, 154 .{ .user_index = 16, .name = "phoenix", .description = "Peace of mind from prototype to production for Elixir web apps" }, 155 .{ .user_index = 17, .name = "laravel", .description = "The PHP framework for web artisans" }, 156 .{ .user_index = 18, .name = "prometheus", .description = "The Prometheus monitoring system and time series database" }, 157 .{ .user_index = 19, .name = "grafana", .description = "The open and composable observability and data visualization platform" }, 158 .{ .user_index = 20, .name = "ansible", .description = "Simple, agentless IT automation" }, 159 }; 160 161 var user_ids: [user_data.len][evt.event_id_size]u8 = undefined; 162 for (&user_ids) |*id| id.* = evt.EventWithId.randomId(prng.random()); 163 164 var repo_event_ids: [repo_data.len][evt.event_id_size]u8 = undefined; 165 for (&repo_event_ids) |*id| id.* = evt.EventWithId.randomId(prng.random()); 166 167 var password_hash_buf: [evt.User.password_hash_max_len]u8 = undefined; 168 const password_hash = try evt.User.hashPassword("password", &password_hash_buf, io); 169 170 // public key matching temp-try/key, given to admin so we can push as admin 171 const admin_ssh_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKeIs8mJqigBZ5y84J4COgnAJJ5bHPKy+lM2SliMXbYm radar@roark"; 172 173 var events_to_consume: [user_data.len + repo_data.len]evt.EventWithId = undefined; 174 for (user_data, 0..) |u, i| { 175 events_to_consume[i] = .{ 176 .id = std.fmt.bytesToHex(user_ids[i], .lower), 177 .event = .{ 178 .user = .{ 179 .name = u.name, 180 .display_name = u.display_name, 181 .email = u.email, 182 .password_hash = password_hash, 183 .ssh_keys = if (i == 0) admin_ssh_key else "", 184 }, 185 }, 186 }; 187 } 188 for (repo_data, 0..) |r, i| { 189 events_to_consume[user_data.len + i] = .{ 190 .id = std.fmt.bytesToHex(repo_event_ids[i], .lower), 191 .event = .{ 192 .repo = .{ 193 .user_id = &user_ids[r.user_index], 194 .name = r.name, 195 .description = r.description, 196 .enable_issue = true, 197 }, 198 }, 199 }; 200 } 201 202 // commit the seed events and consume them into the database 203 try evt.commitAndConsume(evt.admin_repo_opts, io, allocator, &repo, evt.events_ref, &events_to_consume); 204 205 // create the actual repos on disk, named by their hex-encoded event id 206 for (repo_data, 0..) |r, i| { 207 const repo_id = std.fmt.bytesToHex(repo_event_ids[i], .lower); 208 const repo_path = try std.fs.path.join(arena.allocator(), &.{ cwd_path, temp_dir_name, "server", "repos", &repo_id }); 209 210 var repo_i = try rp.Repo(.xit, .{}).init(io, allocator, .{ .path = repo_path }); 211 defer repo_i.deinit(io, allocator); 212 213 try repo_i.addConfig(io, allocator, .{ .name = "user.name", .value = "haxy" }); 214 try repo_i.addConfig(io, allocator, .{ .name = "user.email", .value = "admin@haxy" }); 215 216 // write the repo's name and description into a README, plus a 217 // nested doc so the file tree has a directory to descend into 218 { 219 var repo_dir = try cwd.openDir(io, repo_path, .{}); 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