about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ci-builds.nix1
-rw-r--r--web/todolist/default.nix113
-rw-r--r--web/todolist/extract-todos.jq25
3 files changed, 139 insertions, 0 deletions
diff --git a/ci-builds.nix b/ci-builds.nix
index 2e4e403920..349ce9e988 100644
--- a/ci-builds.nix
+++ b/ci-builds.nix
@@ -74,6 +74,7 @@ in lib.fix (self: {
     tools.cheddar
     tools.nsfv-setup
     web.cgit-taz
+    web.todolist
     web.tvl
     (drvify "getBins-tests" nix.getBins.tests)
   ]
diff --git a/web/todolist/default.nix b/web/todolist/default.nix
new file mode 100644
index 0000000000..187830e736
--- /dev/null
+++ b/web/todolist/default.nix
@@ -0,0 +1,113 @@
+# Generates a simple web view of open TODOs in the depot.
+#
+# Only TODOs that match the form 'TODO($username)' are considered, and
+# only for users that are known to us.
+{ depot, lib, ... }:
+
+with depot.nix.yants;
+
+let
+  inherit (depot.third_party)
+    jq
+    ripgrep
+    runCommandNoCC
+    writeText
+    ;
+
+  inherit (builtins)
+    elem
+    filter
+    fromJSON
+    head
+    readFile
+    ;
+
+  inherit (lib) concatStringsSep;
+
+  # We should extract this from TVL slapd, but that data is not easily
+  # accessible right now.
+  knownUsers = [
+    "tazjin"
+    "riking"
+    "Profpatsch"
+    "grfn"
+    "lukegb"
+  ];
+
+  todo = struct {
+    file = string;
+    line = int;
+    todo = string;
+    user = string;
+  };
+
+  allTodos = fromJSON (readFile (runCommandNoCC "depot-todos.json" {} ''
+    ${ripgrep}/bin/rg --json 'TODO\(\w+\):.*$' ${depot.depotPath} | \
+      ${jq}/bin/jq -s -f ${./extract-todos.jq} > $out
+  ''));
+
+  knownUserTodos = filter (todos: elem (head todos).user knownUsers) allTodos;
+
+  fileLink = defun [ todo string ] (t:
+    ''<a style="color: inherit;"
+         href="https://cs.tvl.fyi/depot/-/blob/${t.file}#L${toString t.line}">
+      //${t.file}:${toString t.line}</a>'');
+
+  todoElement = defun [ todo string ] (t: ''
+    <p>At ${fileLink t}:</p>
+    <blockquote>${t.todo}</blockquote>
+
+  '');
+
+  userParagraph = todos:
+  let user = (head todos).user;
+  in ''
+    <p>
+      <h3>${user}</h3>
+      ${concatStringsSep "\n" (map todoElement todos)}
+    </p>
+    <hr>
+  '';
+
+  todoPage = writeText "index.html" ''
+    <!DOCTYPE html>
+    <head>
+      <meta charset="utf-8">
+      <meta name="viewport" content="width=device-width, initial-scale=1">
+      <meta name="description" content="TVL's todo-list">
+      <link rel="stylesheet" type="text/css" href="static/tazjin.css" media="all">
+      <link rel="icon" type="image/webp" href="static/favicon.webp">
+      <title>TVL's todo-list</title>
+      <style>
+        svg {
+          max-width: inherit;
+          height: auto;
+        }
+      </style>
+    </head>
+    <body class="dark">
+      <header>
+        <h1><a class="blog-title" href="/">The Virus Lounge's todo-list</a> </h1>
+        <hr>
+      </header>
+      <main>
+      ${concatStringsSep "\n" (map userParagraph knownUserTodos)}
+      </main>
+      <footer>
+        <p class="footer">
+          <a class="uncoloured-link" href="https://tvl.fyi">homepage</a>
+          |
+          <a class="uncoloured-link" href="https://cs.tvl.fyi/depot/-/blob/README.md">code</a>
+          |
+          <a class="uncoloured-link" href="https://cl.tvl.fyi">reviews</a>
+        </p>
+        <p class="lod">ಠ_ಠ</p>
+      </footer>
+    </body>
+  '';
+
+in runCommandNoCC "tvl-todos" {} ''
+  mkdir $out
+  cp ${todoPage} $out/index.html
+  ln -s ${depot.web.tvl}/static $out/static
+''
diff --git a/web/todolist/extract-todos.jq b/web/todolist/extract-todos.jq
new file mode 100644
index 0000000000..cda717fceb
--- /dev/null
+++ b/web/todolist/extract-todos.jq
@@ -0,0 +1,25 @@
+# Simple jq script to extract all TODO comments in the code base from
+# ripgrep's JSON output.
+#
+# This assumes that the filter used is something like 'TODO\(\w+\):'
+
+# Construct a structure with only the fields we need to populate the
+# page.
+def simplify_match:
+  .data.submatches[0].match.text as $todo
+  | {
+     file: (.data.path.text | sub("/nix/store/.+-depot/"; "")),
+     line: .data.line_number,
+     todo: $todo,
+     user: ($todo | capture("TODO\\((?<user>\\w+)\\)") | .user),
+     };
+
+# Group all matches first by the user and return them in sorted order
+# by the file in which they appear. This matches the presentation
+# order on the website.
+def group_by_user: .
+    | group_by(.user)
+    | map(sort_by(.file));
+
+# main:
+map(select(.type == "match") | simplify_match) | group_by_user