Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33

Open
lilleman wants to merge 966 commits from worktree-todo-relative-dates-exact-counts into main
6 changed files with 26 additions and 47 deletions
Showing only changes of commit 5a6928750c - Show all commits
+3 -3
View File
@@ -30,7 +30,7 @@ jobs:
else else
base='${{ github.event.before }}' base='${{ github.event.before }}'
fi fi
if git diff --quiet "$base"...HEAD -- '*.go' go.mod go.sum Dockerfile .dockerignore .github data release-tooling testdata README.md todo.md; then if git diff --quiet "$base"...HEAD -- '*.go' go.mod go.sum Dockerfile .dockerignore .github data release-tooling testdata vulnerability-filing README.md todo.md; then
echo "code=false" >> "$GITHUB_OUTPUT" echo "code=false" >> "$GITHUB_OUTPUT"
else else
echo "code=true" >> "$GITHUB_OUTPUT" echo "code=true" >> "$GITHUB_OUTPUT"
@@ -82,7 +82,7 @@ jobs:
run: docker build --no-cache-filter vulnerabilities --target vulnerability-report --output "$RUNNER_TEMP/vulnerabilities" . run: docker build --no-cache-filter vulnerabilities --target vulnerability-report --output "$RUNNER_TEMP/vulnerabilities" .
- name: File each one in todo.md - name: File each one in todo.md
id: file id: file
run: python3 release-tooling/file_vulnerabilities.py "$RUNNER_TEMP/vulnerabilities/report.json" todo.md run: python3 vulnerability-filing/file_vulnerabilities.py "$RUNNER_TEMP/vulnerabilities/report.json" todo.md
- name: Propose the filed items - name: Propose the filed items
if: failure() && steps.file.outcome == 'failure' && github.ref == 'refs/heads/main' if: failure() && steps.file.outcome == 'failure' && github.ref == 'refs/heads/main'
env: env:
@@ -97,7 +97,7 @@ jobs:
git commit -qam "File the known vulnerabilities govulncheck reports" git commit -qam "File the known vulnerabilities govulncheck reports"
git push -qf "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" vulnerabilities git push -qf "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" vulnerabilities
gh pr create --head vulnerabilities --title "File the known vulnerabilities govulncheck reports" \ gh pr create --head vulnerabilities --title "File the known vulnerabilities govulncheck reports" \
--body "govulncheck reports known vulnerabilities in the Go version or a required module, and no release is cut until they are gone. This files them atop the next release in todo.md; each item names the bump that fixes it." --body "govulncheck reports known vulnerabilities in the Go version or a required module, and no release is cut until they are gone. This files them atop the next release in todo.md, scored 0.0 until rank_todo.py scores them; each item names the bump that fixes it."
# A pull request the workflow's token opens starts no workflow. # A pull request the workflow's token opens starts no workflow.
gh workflow run test.yml --ref vulnerabilities gh workflow run test.yml --ref vulnerabilities
+6 -1
View File
@@ -21,9 +21,13 @@ COPY --from=vulnerabilities /report.json /
FROM python:3.14.8-slim AS release-tooling FROM python:3.14.8-slim AS release-tooling
COPY release-tooling/ /release-tooling/ COPY release-tooling/ /release-tooling/
RUN python3 -m unittest discover -s /release-tooling -p '*_test.py' && touch /tested
FROM python:3.14.8-slim AS vulnerability-filing
COPY vulnerability-filing/ /vulnerability-filing/
# The filer's test files an item into the plan itself. # The filer's test files an item into the plan itself.
COPY todo.md /todo.md COPY todo.md /todo.md
RUN python3 -m unittest discover -s /release-tooling -p '*_test.py' && touch /tested RUN python3 -m unittest discover -s /vulnerability-filing -p '*_test.py' && touch /tested
FROM goreleaser/goreleaser:v2.18.3 AS packaging FROM goreleaser/goreleaser:v2.18.3 AS packaging
WORKDIR /check WORKDIR /check
@@ -33,6 +37,7 @@ RUN git init -q && git remote add origin https://github.com/larvit/fejkdata.git
FROM portable FROM portable
# BuildKit skips a stage nothing copies from. # BuildKit skips a stage nothing copies from.
COPY --from=release-tooling /tested /tmp/release-tooling-tested COPY --from=release-tooling /tested /tmp/release-tooling-tested
COPY --from=vulnerability-filing /tested /tmp/vulnerability-filing-tested
COPY --from=packaging /tested /tmp/packaging-tested COPY --from=packaging /tested /tmp/packaging-tested
RUN unformatted="$(gofmt -l .)"; test -z "$unformatted" || \ RUN unformatted="$(gofmt -l .)"; test -z "$unformatted" || \
{ echo "unformatted files:"; echo "$unformatted"; exit 1; } { echo "unformatted files:"; echo "$unformatted"; exit 1; }
+2 -1
View File
@@ -1170,8 +1170,9 @@ internal/rows/ a table's rows: the TSV, the options proved over it, the links b
data/ the shipped modules, each a Go package embedding its categories (JSON, and a TSV per table) and its manifest; and Modules, which lists them data/ the shipped modules, each a Go package embedding its categories (JSON, and a TSV per table) and its manifest; and Modules, which lists them
data-import/ the scripts that rebuild each sourced table (see DATA-LICENSES.md), and the modules they share data-import/ the scripts that rebuild each sourced table (see DATA-LICENSES.md), and the modules they share
docs/ the decision log, the register research behind the shipped data, the survey of other libraries, panels' scores and rulings on how readable the code is, and what each chunk cost an agent to read docs/ the decision log, the register research behind the shipped data, the survey of other libraries, panels' scores and rulings on how readable the code is, and what each chunk cost an agent to read
release-tooling/ the release CI publishes from the changelog heading, and the binaries, packages, image and package repositories built for it; and the filing of known vulnerabilities release-tooling/ the release CI publishes from the changelog heading, and the binaries, packages, image and package repositories built for it
testdata/ the pinned shipped shape (see Versioning) testdata/ the pinned shipped shape (see Versioning)
vulnerability-filing/ the script CI runs to file each known vulnerability govulncheck reports as a `todo.md` item
``` ```
## Development ## Development
+3 -3
View File
@@ -1001,9 +1001,9 @@ protection does not require. So a vulnerability published in Go, or an outage of
vuln.go.dev, holds the release and leaves unrelated pull requests mergeable. Each vuln.go.dev, holds the release and leaves unrelated pull requests mergeable. Each
vulnerability is filed as a `security` item atop the next release in `todo.md`, by a pull vulnerability is filed as a `security` item atop the next release in `todo.md`, by a pull
request CI opens, so the plan names it before anyone reads a red run. request CI opens, so the plan names it before anyone reads a red run.
`release-tooling/file_vulnerabilities.py` writes that item in `todo.md`'s shape and scores it `vulnerability-filing/file_vulnerabilities.py` writes that item in `todo.md`'s shape with a
by `todo.md`'s formula, because CI cannot reach `rank_todo.py`; a test files one into the Score of 0.0, since CI cannot reach `rank_todo.py`, which scores it on the next commit to the
repository's own `todo.md`. When the next release has no item left and waits to be cut, plan; a test files one into the repository's own `todo.md`. When the next release has no item left and waits to be cut,
`todo.md` names no version for it, so the item lands in the release after; the maintainer `todo.md` names no version for it, so the item lands in the release after; the maintainer
moves it. Where no Go version fixes a vulnerability yet, govulncheck has no ignore list: the moves it. Where no Go version fixes a vulnerability yet, govulncheck has no ignore list: the
maintainer drops the job from the release's `needs` until one does, and records why here. maintainer drops the job from the release's `needs` until one does, and records why here.
@@ -4,25 +4,24 @@
Usage: file_vulnerabilities.py <govulncheck -format json report> <todo.md> Usage: file_vulnerabilities.py <govulncheck -format json report> <todo.md>
Exits 1 while any vulnerability is known, filed now or before, and 0 on a clean report. Exits 1 while any vulnerability is known, filed now or before, and 0 on a clean report.
The item is written the way ~/.claude/bin/rank_todo.py writes one, which CI cannot run: todo.md's `Next ID = N` gives the item's ID, and the release of the plan's top row, the next
todo.md's `Next ID = N` gives its ID, the release of the plan's top row (the next release) release, gives its release. Its Score is 0.0 until rank_todo.py, which CI cannot run, scores
gives its release, and its Score is todo.md's `Score = …` formula over the scores below and it; a security item ranks first in its release whatever it scores. The test that files into
the weight todo.md's goal table gives goal 1.1. The test that files into the repository's the repository's own todo.md fails the gate when the plan's shape drifts from what this
own todo.md fails the gate when the plan's shape drifts from what this script reads. script reads.
A security item scores R, S and C 1, since the fix is a version bump, and A and G 8, since a Scores, as todo.md's table spells them: R (risk), S (size) and C (difficulty) are 1, since
known vulnerability in the binaries works against README goal 1.1: installing fejkdata the the fix is a version bump; A (value to the audience) and G (value to the goal) are 8, the goal
way one installs other tools, which image scanners then flag. being README goal 1.1, installing fejkdata the way one installs other tools, which a flagged
binary works against.
""" """
import ast
import json import json
import re import re
import sys import sys
from dataclasses import dataclass, field from dataclasses import dataclass, field
ROW = re.compile(r"^\| (\d+) \| ([^|]+?) \| [^|]*\| \*\*", re.M) ROW = re.compile(r"^\| (\d+) \| ([^|]+?) \| [^|]*\| \*\*", re.M)
WEIGHT = re.compile(r"^\| ([\d.]+) \| ([\d.]+) \|$", re.M)
NEXT_ID = re.compile(r"`Next ID = (\d+)`") NEXT_ID = re.compile(r"`Next ID = (\d+)`")
SCORES = {"R": 1, "S": 1, "C": 1, "A": 8, "G": 8} SCORES = {"R": 1, "S": 1, "C": 1, "A": 8, "G": 8}
GOAL = "1.1" GOAL = "1.1"
@@ -81,32 +80,6 @@ def version_key(version: str):
return [int(part) for part in re.findall(r"\d+", version)] return [int(part) for part in re.findall(r"\d+", version)]
def evaluate(node, values):
"""The value of todo.md's score formula: numbers, letters, unary minus and + - * /."""
match node:
case ast.Constant(value=value):
return value
case ast.Name(id=name):
return values[name]
case ast.UnaryOp(op=ast.USub(), operand=operand):
return -evaluate(operand, values)
case ast.BinOp(left=left, op=ast.Add(), right=right):
return evaluate(left, values) + evaluate(right, values)
case ast.BinOp(left=left, op=ast.Sub(), right=right):
return evaluate(left, values) - evaluate(right, values)
case ast.BinOp(left=left, op=ast.Mult(), right=right):
return evaluate(left, values) * evaluate(right, values)
case ast.BinOp(left=left, op=ast.Div(), right=right):
return evaluate(left, values) / evaluate(right, values)
raise ValueError(f"todo.md's formula holds {ast.dump(node)}, which this script cannot evaluate")
def score(todo: str) -> str:
formula = re.search(r"`Score = (.+?)`", todo).group(1)
values = dict(SCORES, W=float(dict(WEIGHT.findall(todo))[GOAL]))
return f"{evaluate(ast.parse(formula, mode='eval').body, values):.1f}"
def tagline(module: Module) -> str: def tagline(module: Module) -> str:
if module.unfixed: if module.unfixed:
return f"Ship no release while fejkdata carries {module.carried}, some of whose known vulnerabilities no version fixes yet." return f"Ship no release while fejkdata carries {module.carried}, some of whose known vulnerabilities no version fixes yet."
@@ -137,7 +110,7 @@ def file_items(modules: list[Module], todo: str) -> tuple[str, list[int]]:
next_id = int(NEXT_ID.search(todo).group(1)) next_id = int(NEXT_ID.search(todo).group(1))
line = tagline(module) line = tagline(module)
scores = " | ".join(str(SCORES[k]) for k in "RSCAG") scores = " | ".join(str(SCORES[k]) for k in "RSCAG")
row = f"| {next_id} | {first.group(2)} | security | **{line}** | {scores} | {GOAL} | {score(todo)} |\n" row = f"| {next_id} | {first.group(2)} | security | **{line}** | {scores} | {GOAL} | 0.0 |\n"
todo = todo[: first.start()] + row + todo[first.start() :] todo = todo[: first.start()] + row + todo[first.start() :]
todo = todo.replace(f"`Next ID = {next_id}`", f"`Next ID = {next_id + 1}`", 1) todo = todo.replace(f"`Next ID = {next_id}`", f"`Next ID = {next_id + 1}`", 1)
todo = todo.replace("## Details\n\n", f"## Details\n\n### {next_id}. {line}\n\n{detail(module)}\n", 1) todo = todo.replace("## Details\n\n", f"## Details\n\n### {next_id}. {line}\n\n{detail(module)}\n", 1)
@@ -77,7 +77,7 @@ class FileItems(unittest.TestCase):
todo, filed = file(TODO, *STDLIB) todo, filed = file(TODO, *STDLIB)
self.assertEqual(filed, [7]) self.assertEqual(filed, [7])
self.assertIn("`Next ID = 8`", todo) self.assertIn("`Next ID = 8`", todo)
self.assertEqual(rows(todo)[0], f"| 7 | 0.2.0 | security | **{GO_TAGLINE}** | 1 | 1 | 1 | 8 | 8 | 1.1 | 29.1 |") self.assertEqual(rows(todo)[0], f"| 7 | 0.2.0 | security | **{GO_TAGLINE}** | 1 | 1 | 1 | 8 | 8 | 1.1 | 0.0 |")
self.assertTrue(rows(todo)[1].startswith("| 5 |")) self.assertTrue(rows(todo)[1].startswith("| 5 |"))
self.assertTrue( self.assertTrue(
todo.split("## Details\n\n", 1)[1].startswith( todo.split("## Details\n\n", 1)[1].startswith(
@@ -133,7 +133,7 @@ class FileItems(unittest.TestCase):
row = rows(todo)[0] row = rows(todo)[0]
self.assertEqual(row.count("|"), header.count("|")) self.assertEqual(row.count("|"), header.count("|"))
self.assertEqual(row.split(" | ")[1], rows(plan)[0].split(" | ")[1]) self.assertEqual(row.split(" | ")[1], rows(plan)[0].split(" | ")[1])
self.assertRegex(row, rf"^\| {filed[0]} \| .* \| \d+\.\d \|$") self.assertRegex(row, rf"^\| {filed[0]} \| .* \| 0\.0 \|$")
self.assertIn(f"`Next ID = {filed[0] + 1}`", todo) self.assertIn(f"`Next ID = {filed[0] + 1}`", todo)