A repository whose first clone was still running answered 404, since
its directory is moved into place only at the end; it is in use, so it
now answers 409.
Removing a repository clears its failed wants: one the old copy could
not fit may fit in the next clone.
The delete reads the repository from the decoded request path, as the
clone handler does. chi's wildcard stays encoded when the request
escapes a character, so team%2Frepo named another repository.
With cache_delete set, DELETE /api/git/{upstream}/{repo} removes one
repository from the git cache and /ui/git gets a Delete button per
repository. The repository is moved aside under the cache's lock, as
eviction does, so the next clone fetches it again. One that a clone, a
fetch or a client is using answers 409. The response names the
repository and the bytes freed.
The runtime counters on the analytics page (requests, in flight, latency,
status classes, served bytes, per-client and per-source breakdowns) skipped
only /metrics. Every /ui page load, with ~800 KB of vendored scripts, was
counted as "other" traffic, so viewing the analytics page inflated the
numbers it reports. Skip /ui the same way as /metrics; requests are still
logged and written to the access log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Embedded files have no modification time, so http.FileServer sent no
Last-Modified or ETag for /ui/static and browsers downloaded the vendored
tailwind.js and lucide.min.js (~780 KB) again on every page load. Give each
file an ETag from its contents and Cache-Control: no-cache, so repeat loads
are answered with 304.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The state shown on the git page was read again after every check of the
upstream, and a check runs on every client info/refs. On a busy proxy
each view then read the same repositories again and never got past what
fits in two seconds.
The state is now kept until the repository's refs or HEAD are written: by
a clone, a fetch, a fetch of wanted commits, or a change of the default
branch. A check that finds nothing new leaves it in place.
A state that fails to read for another reason than the view's deadline is
now logged at warn with the repository key.
Retention evicts every file that has not been served for the configured
time, also the newest version of a package. keep_latest: N holds back
the files of the N highest cached versions of each package from
eviction by age. Versions are ordered by the ecosystem's version rules.
It applies to cargo, golang, npm and pypi, where the cache stores a
release under its version. Other ecosystems ignore it for now.
A view ran four git processes for each repository whose state wasn't
cached, with no limit, and each concurrent view did the same.
RepositoryState now runs one read at a time and gives up with its
context. The page reads for at most two seconds, lists the rest without
branches and commits, and says how many are left; the cache keeps what
was read, so each reload reads more.
Also test that an upstream, a repos pattern and a repository with
uppercase letters are listed as spelled, and correct two comments.
A Debian package was cached under its repository and file name, but
fetched from its full path. Docker's apt repository has a pool under each
suite with the same file names, so a bookworm client could get the
bullseye build that was fetched first, and fail its hash check. Packages
are now cached under their path.
Conan joined revision, package reference, package revision and file name
with underscores, which revisions can contain, so a recipe file and a
package file could share a cache entry. The parts are now joined with
slashes.
Entries cached under the old names are still served, but only for the
URL they were fetched from, so warm caches keep working.
Since main gained the repos allowlist and the refusal of repositories the
upstream refused, the page could still list a repository the allowlist no
longer allows and show the cached commit of one the upstream refused.
Repositories now reports Refused, and the page leaves out what the
allowlist doesn't allow and shows no cached state for a refused one.
The docs say refreshing Debian, Alpine and RPM packages needs
cache_metadata, but nothing said so when it was off. Rejecting the
setting would also stop the refresh of every other ecosystem, so the
server logs a warning from the new Config.Warnings at startup.
Repository paths can hold spaces, '#', '?' or '%', as Azure DevOps
project and repository names do, so each segment is escaped as the LFS
URLs are. The requests label also uses plural() now.
Git LFS objects are artifacts under ecosystem git, so adding the
repositories to the per-ecosystem gauges mixed the two and broke the
per-ecosystem figures adding up to the totals. The git cache now has
proxy_git_cache_size_bytes and proxy_git_cached_repositories, updated
on each tick apart from the database gauges so a failed stats query
leaves them current.
RPM packages were cached under their bare filename. With upstream.rpm
pointing at a mirror root that serves several distributions, Rocky and
Alma both publish bash-5.1.8-9.el9.x86_64.rpm with different bytes, and
whichever was fetched first was served for both, so dnf failed the
checksum or signature check.
Use the path under the upstream as the cache filename, as the APK and
Arch handlers do. Packages cached before this change are no longer
found and are fetched again once; retention still matches them by
package name.
The summary said "1 repositories" and counted the size of repositories it
didn't list. A repository whose state can't be read is no longer read on
every page view, only after its next upstream check.
/ui/git lists the git cache's repositories with their upstream, clone URL,
size, last check and use, requests served and the last refresh error. The
per-ecosystem cache size and artifact gauges report the git cache as
ecosystem="git".
proxy mirror, mirror API jobs and SBOMs take pkg:git/{host}/{path}@{commit}
and cache the repository and commit in the cache /git serves, so a later
clone is a cache hit, also with the upstream down. Part of #403.
The PURL namespace names the upstream.apk repository, and the distro and
arch qualifiers syft writes give the release and architecture. The package
is looked up in the main and community indexes of that release and cached
under the path the APK handler serves it from.