aboutsummaryrefslogtreecommitdiffstats
path: root/AGENTS.md
blob: 4a13a10b825debcfbb411f852db25ae6a9fd0ed4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# slackware-changelog

A widget for a [Homepage](https://gethomepage.dev) dashboard, one of the
modules under `homepage-modules/`. Siblings share the shape described below.

## Shape of a module

Homepage's `customapi` widget fetches JSON and maps fields to labels. It
cannot parse text, cannot do arithmetic, and refetches on every refresh
interval. So each module is a small stdlib Python HTTP service on localhost
that does the parsing and caching, plus:

- `<name>.py` - the service, single file, stdlib only, executable
- `test_<name>.py` - self-check, fakes the upstream, prints `ok`
- `<name>.service` - systemd unit, `DynamicUser=yes`, config via `Environment=`
- `homepage-services.yaml` / `homepage-settings.yaml` - snippets to merge

No dependencies. No framework. `ThreadingHTTPServer` and `urllib`.

## This module

Serves the latest Slackware -current ChangeLog entry on `127.0.0.1:8098`.

Endpoints: `/latest` (counts and summary), `/top` (the first `TOP_N` packages
as flat fields), `/packages` (every package with its action), `/entry` (HTML
page of the entry), `/refresh` (bypass the cache).

### Why /top exists

`customapi` maps a fixed list of field names to labels. It cannot iterate an
array and cannot scroll, so a JSON list of packages renders as nothing. `/top`
flattens the first `TOP_N` into `pkg1`, `pkg2`, ... which the widget can map
one per row, and appends a `... and N more` row in the next slot when the entry
is longer. The card links to the ChangeLog for the rest.

Mapping more `pkgN` fields than the entry has packages is harmless: Homepage
skips a field that is absent. So the snippet maps a fixed 9 rows regardless of
how many that day's entry holds.

### ChangeLog format

The file is reverse-chronological. Entries are separated by a `+--------+`
line, so the latest entry is everything before the first separator.

Inside an entry:

```
Mon Sep  7 22:56:39 UTC 2026
a/util-linux-2.42.3-x86_64-1.txz:  Upgraded.
  This update fixes bugs and security issues.
  (* Security fix *)
l/cryptopp-8.9.0-x86_64-3.txz:  Rebuilt.
  Fixed library location. Thanks to Petri Kaukasoina.
```

Gotcha: a package line is identified by **starting at column 0**, not by
ending in a period. Note lines are indented and frequently end in a period
too ("Thanks to Petri Kaukasoina."), so a period-based regex counts prose as
packages. The test asserts this specific case.

Actions seen: `Upgraded`, `Rebuilt`, `Added`, `Removed`. The parser counts
whatever word it finds rather than matching a fixed list, so a new action
verb shows up in `summary` instead of vanishing.

Security fixes are marked by the literal `(* Security fix *)` on its own
indented line. CVE ids appear in the notes as cve.org URLs.

### Caching

`CACHE_TTL` (default 1800s) governs upstream fetches; the widget's
`refreshInterval` only redraws. A failed refresh keeps the previous entry and
adds a `stale` field with the error, so an unreachable mirror degrades to an
old entry rather than a blank card. `/refresh` forces a fetch but obeys the
same fallback.

### Rendering

`/entry` escapes everything from the ChangeLog with `html.escape` before it
reaches the page. Upstream text is not trusted markup; the test asserts no
`<script` survives.

## Conventions

- GPLv2 only. Header notice in every source file.
- Snippet YAML files are merged into Homepage's config by hand, never copied
  over it; the comments at the top of each say so.
- Layout groups: any group omitted from `layout:` falls to the bottom of the
  page, so the snippet reminds the reader to list their existing groups too.