Commit 8839e2cc95

8839e2cc95d19fe316869d48d4f96dc3bd9b4e8d

parent: 34360906d0

Verified · cmc ci/build: success ci/test: success ci/vuln: success

cmc <hello@cleberg.net> · 2026-09-04 16:01 UTC

CHANGELOG: v1.12.0

Layout: unified · split

CHANGELOG.org +56
@@ -4,6 +4,62 @@ Versioning follows semver from v0.1.0. Database migrations run
44automatically on daemon start; upgrade notes appear per release when
55anything beyond "replace the binary and restart" is needed.
66
7* v1.12.0 — 2026-09-04
8
9The store and the push path. Dashboard queries walk an index instead of
10sorting the table, concurrent writers wait instead of failing, expired
11rows are swept, and a large first push no longer forks a process per
12commit.
13
14- Every dashboard list scanned its table and sorted the result to take
15 fifty rows. Reachability is correlated subqueries and cannot be
16 indexed; the index supplies the =ORDER BY= instead, so the walk stops
17 at =LIMIT=. On 20k issues and 20k merge requests: open issues 11.8ms
18 to 0.8ms, open merge requests 12.1ms to 0.8ms, the review queue 15.5ms
19 to 0.3ms, assigned issues 10.6ms to 0.04ms. The last of those also
20 drives from =issue_assignees= rather than testing =EXISTS= against
21 every issue. #137
22- Concurrent writers serialise rather than failing. Every transaction in
23 the store writes, and a deferred one takes the write lock at its first
24 write, by which point another may hold it; SQLite answers
25 =SQLITE_BUSY= and does not run the busy handler for that case, so
26 =busy_timeout= could not help. Measured with eight concurrent
27 read-then-write transactions, 44% of them failed. Beginning immediate
28 takes the lock up front, where the timeout applies. #121
29- =repo settings= updates no longer overwrite each other.
30 =settings_json= is one blob, and every caller read it off a repository
31 loaded earlier, changed a field and wrote it all back; two admins at
32 once and the later write put back what it had read for the other's
33 field. The read and the write happen together now, under one
34 immediate transaction. #123
35- Expired sessions and tokens are swept, and =[retention]= caps how long
36 the audit log, the activity feed, webhook deliveries and the mail
37 queue keep a row. Unset means forever, which is what every existing
38 instance gets. #122
39- The audit entry records flag names without their values. Secrets never
40 reached argv — they travel on stdin — but prose did: =--body <the
41 whole issue>= was stored verbatim, in a table nothing pruned, for a
42 repository that may be private. Identifiers are positional and
43 survive. #122
44- pre-receive on a require-signed repository forked a =cat-file= per
45 incoming commit and built one JSON message holding the whole push. A
46 50k-commit first push forked 50k processes and held the history in
47 memory twice. One =cat-file --batch= serves the push now, and the
48 daemon verifies each commit as it arrives. #100
49- The payloads other surfaces decode are named types in =control=. httpd
50 had its own copies of what the build, profile and dashboard commands
51 emit, so a field added to a command was silently absent on the page.
52 #126
53
54Migration 0035 adds three indexes; it runs on daemon start.
55
56Two changes in behaviour rather than configuration. A heavily contended
57write now blocks for up to =busy_timeout= (five seconds) instead of
58returning an error immediately — waiting is the point, but a caller that
59treated the error as normal will see a pause instead. And anything
60parsing =audit --json= for command arguments will find flag values gone;
61the flag names and the positional arguments are still there.
62
763* v1.11.0 — 2026-09-04
864
965The web catches up with the rest of the forge: search across the