[1.3.1] - 2026-09-09
Changed
- npm rebuilds a packument concurrently (#956 follow-up) —
regenerate_packumentwalkedversions/anddist-tags/one key at a time. On an object store each key is a round-trip, and #956 put that walk on a read path, so the first request after a packument went missing paid one round-trip per version: 505 reads and 0.77 s for a 500-version package against a loopback S3 store, linear from there. It now reads them through the same buffered fan-out the RPM and Deb index rebuilds use, keeping the lenient behaviour — an unreadable or unparsable version is skipped rather than failing the rebuild. - Every storage round-trip is counted (#969 follow-up) —
stat,pin,listandlist_with_metareached the backend without touchingnora_storage_operations_total, which is why a handler issuing onestat()per file (tens of thousands of HEAD requests on a single PyPI index response) moved no metric and could only be found by reading code. All four now increment it; onstat/pinan absent object or an unpinned one isstatus="miss", so ordinary misses do not inflate error-rate alerting. Counters only — no behavioural change. - Changelog entries live in
changelog.d/(#971 follow-up) —## [Unreleased]inCHANGELOG.mdis no longer edited by hand. Each change addschangelog.d/<number>.<category>.md, andscripts/changelog-fragments.shassembles them (--check,--render,--apply,--release <version>). Three pull requests merged into 1.3.1 each added a bullet to the same few lines and each conflicted with the previous one; separate files cannot. A.gitattributesunion merge was the lighter alternative and was rejected: it duplicates the### Fixedheading when two branches both introduce it, and it is unclear whether the merge performed on the forge honours the driver at all, which is where the conflicts actually surfaced.
Fixed
- PyPI simple JSON no longer stats every file (#969) — the PEP 700 fields added in 1.3.0 made
GET /simple/{name}/with a JSONAcceptheader issue one storagestat()per file in the index, serially. On an object store that is a HEAD round-trip each, so the response time scaled with the number of files the index lists and uv — which always negotiates the JSON index — hit its timeout; the HTML index pip uses was never on that path. Each file’ssizenow comes from the listing the handler already performs, and the cacheddates.jsonis read once per response instead of twice. Measured on S3 against a 40 000-file proxied index:uv lockwent from 129.8 s and 59 764 storage HEAD requests, failing after three retries, to 0.5 s and 2. A second regression on the same path is fixed with it: an upstream that answers the PEP 691 JSON withoutupload-timeleft nothing cached, so every request re-downloaded the whole upstream index; it is now recorded once, while a failed fetch still caches nothing so a transient outage stays retryable. lock-auditno longer depends on gawk (#971 follow-up) — Checks 1 and 3 used gawk's three-argumentmatch(), which mawk rejects. mawk is the defaultawkon Debian, Ubuntu and the CI images, and an aborted awk prints nothing — so on any machine without gawk the audit reported no findings and looked clean. The awk programs are now POSIX, andscripts/test-lock-audit.shruns the audit under every implementation it finds, asserting they agree and that each run still produced the known finding, so "both silent because both aborted" cannot pass.lock-auditno longer reports a guard that cannot drop early (#971) — Check 2 scanned forward from the end of a guarded block to the next column-0}and flagged the first storage write it met, with no notion of branch exclusivity. Apublish_locktaken underif !is_tarballwas therefore reported against a write underif is_tarball, which no request can reach on the same path. The scan now skips a write whose enclosingifchain contains the textual negation of a condition enclosing the guard. Where the reasoning is beyond textual analysis, a// LOCK-SAFE: <reason>comment inside the block of the write exempts it, with the reason recorded next to the code; a bare marker with no reason silences nothing, and the function-level markers the repo already carries are deliberately not honoured, because a function-wide exemption would hide a genuine finding elsewhere in the same handler.scripts/test-lock-audit.shpins all four directions.- npm no longer caches a proxied packument over a locally owned key (#975) —
npm/{name}/metadata.jsonis the assembled form of keys this registry owns, but the proxy branch cached the upstream document there unconditionally. On the metadata path that branch is reached with local versions present only by falling through the rebuild — its write failed, or the read-back missed — and the cached upstream document then dropped every locally published version from the packument. It did not self-heal, because the read-path rebuild triggers on the key being absent and it now existed with the wrong content, sonpm install pkg@<locally-published-version>failed until the next publish. The write also landed unserialized against thepublish_lockreleased a few lines above, so a concurrent publish could be overwritten. The upstream bytes are still served for that request; only the write is skipped, and the next request retries the rebuild.
Install
# x86_64
curl -LO https://github.com/getnora-io/nora/releases/download/v1.3.1/nora-linux-amd64
chmod +x nora-linux-amd64
sudo mv nora-linux-amd64 /usr/local/bin/nora
# ARM64 (Apple Silicon, Graviton, Ampere)
curl -LO https://github.com/getnora-io/nora/releases/download/v1.3.1/nora-linux-arm64
chmod +x nora-linux-arm64
sudo mv nora-linux-arm64 /usr/local/bin/noraDocker
docker pull getnora/nora:1.3.1| Variant | Image | Platforms |
|---|---|---|
| Alpine (default) | getnora/nora:1.3.1
| amd64, arm64 |
| RED OS | getnora/nora:1.3.1-redos
| amd64 |
| Astra Linux SE | getnora/nora:1.3.1-astra
| amd64 |
| GHCR | ghcr.io/getnora-io/nora:1.3.1
| amd64, arm64 |
DEB / RPM
# Debian / Ubuntu / Astra Linux (amd64)
curl -LO https://github.com/getnora-io/nora/releases/download/v1.3.1/nora-amd64.deb
sudo dpkg -i nora-amd64.deb
# RHEL / Fedora / RED OS (amd64)
curl -LO https://github.com/getnora-io/nora/releases/download/v1.3.1/nora-amd64.rpm
sudo rpm -i nora-amd64.rpmChangelog
See CHANGELOG.md