]> git.kaiwu.me - haproxy.git/commitdiff
DEV: patchbot: update: return the stored overlay as JSON on GET
authorWilly Tarreau <w@1wt.eu>
Mon, 6 Jul 2026 21:12:22 +0000 (23:12 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 7 Jul 2026 12:45:32 +0000 (14:45 +0200)
This adds the read side of the review persistence CGI: GET
update.cgi?branch=X.Y now returns the current overlay for that branch
as a JSON array of {"cid", "state", "notes"} objects with absent fields
omitted; a missing or empty file yields "[]". The raw storage format
never travels: the notes are unescaped by the parser and JSON-escaped
on output, so the client can JSON.parse() the response and insert the
notes via textContent directly. Unparseable lines or fields are
silently skipped as everywhere else.

Reads are lockless: the atomic rename on the write side guarantees
that the file is always a complete valid version. The response carries
Cache-Control: no-store so that a browser never reuses a stale overlay
on refresh.

dev/patchbot/cgi/update.awk

index 449ac939563b0896a40a2c72e39b7d8247e87c63..9700be027f8eadfe69f3b1cf8c8fa5ac4616b2ac 100755 (executable)
 #     the result is committed to git. Neither directive carries a base/old
 #     value: state is last-write-wins and notes are append-only, which is
 #     what keeps concurrent edits conflict-free.
-#   - GET update.cgi?branch=3.5 returns the overlay as a JSON array
-#     (not implemented yet).
+#   - GET update.cgi?branch=3.5 returns the current overlay as a JSON array
+#     of {"cid": ..., "state": ..., "notes": ...} objects, with absent
+#     fields omitted and notes fully unescaped (an empty overlay yields
+#     "[]"), directly usable with JSON.parse() on the client.
 #
 # Requires GNU awk (PROCINFO, systime); the -b flag in the shebang makes all
 # string operations byte-based regardless of the locale, which the escaping
@@ -113,7 +115,7 @@ BEGIN {
        if (ENVIRON["REQUEST_METHOD"] == "POST")
                handle_post()
        else if (ENVIRON["REQUEST_METHOD"] == "GET")
-               die("501 Not Implemented", "GET not implemented yet")
+               handle_get()
        else
                die("405 Method Not Allowed", "unsupported method")
        exit 0
@@ -546,6 +548,57 @@ function lock_release(   p)
        lock_held = 0
 }
 
+# The GET handler: returns the current overlay for <branch> as a JSON array
+# of {"cid","state","notes"} objects with absent fields omitted; a missing
+# or empty file yields "[]". The raw storage format never travels: notes
+# are unescaped by the parser and JSON-escaped here, so the client can
+# JSON.parse() the result and insert notes via textContent directly.
+# Unparseable content is silently skipped. Reads are lockless: the atomic
+# rename on the write side guarantees the file is always a complete valid
+# version.
+function handle_get(   i, first, out)
+{
+       load_file(repo "/" branch)
+
+       printf "Content-Type: application/json\r\nCache-Control: no-store\r\n\r\n"
+       out = "["
+       first = 1
+       for (i = 1; i <= nb_lines; i++) {
+               if (L_cid[i] == "" || !parse_line(L_raw[i]))
+                       continue
+               if (P_state == "" && !P_has_notes)
+                       continue        # nothing stored for this commit
+               if (!first)
+                       out = out ","
+               first = 0
+               out = out "\n{\"cid\":" json_str(P_cid)
+               if (P_state != "")
+                       out = out ",\"state\":\"" P_state "\""
+               if (P_has_notes)
+                       out = out ",\"notes\":" json_str(P_notes)
+               out = out "}"
+       }
+       printf "%s%s]\n", out, first ? "" : "\n"
+}
+
+# emits <s> as a JSON string; control chars are defensively encoded and
+# UTF-8 sequences pass through verbatim
+function json_str(s,   out, i, n, c)
+{
+       out = "\""
+       n = length(s)
+       for (i = 1; i <= n; i++) {
+               c = substr(s, i, 1)
+               if (c == "\"" || c == "\\")
+                       out = out "\\" c
+               else if (ORD[c] < 32)
+                       out = out sprintf("\\u%04x", ORD[c])
+               else
+                       out = out c
+       }
+       return out "\""
+}
+
 # the POST handler: parse directives, and if any survives, apply them to the
 # branch file under the lock, atomically replace it and commit it to git.
 function handle_post(   body, i, fname, attempt, renamed)