Files
mesh-catalog/modules/gitea
jschoubben 1f48a29495
mesh/merge-gate pass: builds gitea, mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
mesh-delivery: a check that answers keeps the hour running, and a started head is unanswered (issue 438)
Review of #195: the clock restarted on any change in what a head waits on, so a
check answering pushed the line back by the time it took; it now restarts only on
a newer ask. A head whose check started was announced, so its line no longer says
unannounced; pending times are given in UTC like the module's other lines.
2026-10-11 02:22:16 +02:00
..

gitea

The forge, and the mesh's npm package registry: it claims the git and npm-package-registry seats. Its tools, its events and the provisioner of registry accounts are the TypeScript bundle (index.ts, tools/, provisioner/). This README covers the Go bundle beside it.

Announcing pull requests to the merge check (novox/hq to-be 45 §9, issue 347)

Every new head of an open pull request is announced as pull.updated, once, and the controller has it checked.

  • Which repositories a pass looks at. Each that moved since the last pass, less a minute's margin (issue 250); each holding a pull request updated since then, found by the forge's own search — opening, reopening or retargeting a pull request moves its own update time and not its repository's, and a pull request opened more than a minute after its push was never announced (issue 347); and every repository on a sweep, every ten minutes, or when the search fails.
  • A pull request nobody checks is said — by the delivery's owner, not here. gitea_open_pulls answers every open pull request from the forge itself, with the statuses its base requires and the ones its head has. The mesh-delivery module asks it every five minutes, and a head on a base requiring mesh/merge-gate or mesh/repo-check with neither for ten minutes since it first saw it so is a delivery held past its bound (delivery.<id>.stalled, state unannounced), which the controller raises like any other (issue 347). Each of the head's statuses comes with its state and when it was set, so a head whose check started and never answered — a required check pending, or never set beside one that is — is said as a stalled line in the state unanswered once that has lasted the delivery table's bound for a check asked and not answered, an hour, counted from its first sight and again only when a check is asked anew (issue 438).
  • How to see it work. A pull request opened more than a minute after its branch was pushed — this fix's own was one, and the announcer it fixes never saw it — has mesh/merge-gate pending within a pass of the next sweep at the latest, and mesh-controller.conditions names none for it.
  • A pull request or issue number that is not one is refused before the forge is asked: a tool called with a misnamed argument sent pulls/NaN.

The package registry's retention (novox/hq ADR 0251 §4, to-be 51)

Every publish adds a version to the registry, and until this nothing removed one. The Go bundle cmd/npm-registry says which versions each package holds, what keeps each one, and — asked with a why — deletes the versions nothing keeps.

tool what
npm_packages r every package of the registry's owner, each version newest first with its publish time, its size and why it is kept
npm_retention a the versions retention would delete, and the bytes. A dry run unless dry_run is false; a real run needs why

A version is kept when:

  • a lockfile on the default branch of any repository on the forge names it — package-lock.json (every lockfile version), npm-shrinkwrap.json, yarn.lock (classic and berry) and pnpm-lock.yaml (by each resolved name@version or name/version), whatever the version's age;
  • it is the highest published version satisfying a range that a package.json there names in its dependencies, development, peer or optional dependencies (an npm: alias counts for the package it stands for; a path, a URL, git or a workspace is not a range on this registry). npm itself installs the latest dist-tag when it satisfies the range; that version is kept by its dist-tag;
  • a dist-tag names it — and a range that is a dist-tag's name keeps that tag's version;
  • it is among the newest keep of its package, five by default (the artifact store's five builds, ADR 0189 §3), by semantic-version order. Pre-releases count among the newest.

A version that is not a semantic version is never ordered, so it is kept. A range that cannot be read, or a package whose dist-tags cannot be read, keeps every version of its package, and the answer says why under keeps_all.

Nothing is deleted on partial knowledge. A repository whose files could not be listed, or a manifest or lockfile that could not be fetched, is named under repositories_unread in every answer, and a real run then deletes nothing at all. A file that was fetched but is not readable (broken JSON) is named under files_not_read, and so is a lockfile entry of a shape the reader does not know (the rest of that file is still read); what it would have named is not known, so a real run then deletes nothing at all either. Under node_modules nothing is read: that is what a lockfile already says. An empty repository, or one without its default branch, holds nothing to read and is not a failure.

A real run deletes each version through the forge's own interface (DELETE /api/v1/packages/{owner}/npm/{name}/{version}), says what it deleted and what the forge refused, and a version already gone counts as deleted.

How it reaches the forge

Over the forge's HTTP interface on the machine (MESH_GITEA_URL), as the admin account the vault delivered (MESH_GITEA_ADMIN_USER, the password in MESH_GITEA_ADMIN_PASSWORD_FILE — the same file the TypeScript bundle mints its token with), by basic authentication: this bundle mints no token and keeps nothing. The password is read per call and never logged, answered or put in an error. The registry's owner is MESH_NPM_OWNER, the owner in the seat's npm-path.

Calls to the forge run eight at a time, and one tool call reads for at most 50 seconds, inside a module's 60-second ask.

Why the manifest lists no tools

The TypeScript bundle's tools are served without a tools list, and a claim without serves offers the module's tools as the seat's verbs — so a list here would make these two tools verbs of both the git and the npm-package-registry seats. The two names are kept in ToolNames, and a test holds them to this README and to what the bundle serves.

Tests

go test ./...

Against a fake forge (httptest): what keeps a version (a lockfile whatever its age, the highest version satisfying a range, a dist-tag, the newest keep); a dry run deleting nothing; a real run without why refused; a real run deleting only what nothing keeps; an unread repository stopping a real run before anything is deleted; an unreadable range keeping its whole package; a wrong password failing without saying the password. And the range matcher against npm's rules — exact, ^, ~, x and *, comparisons with partial versions, hyphen ranges, ||, and the pre-release rule — and each lockfile format.