Sync the golangci-lint version, note the new govulncheck workflow, drop
the removed codeql.yml reference, and record that the pkg/storage test
suites are run manually against cloud resources.
Add golang.org/x/vuln/cmd/govulncheck to the go.mod tool set and run it
in a dedicated workflow with the project build tags. The report is
written to GITHUB_STEP_SUMMARY and the step is non-blocking so findings
are reported without failing CI.
Verification:
- go tool govulncheck -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
Replace context.Background() with t.Context() so test contexts are
canceled when the test ends, which surfaces leaked background work. The
pkg/storage suites are intentionally left unchanged because they run
manually against cloud resources.
Verification:
- go vet ./...
Thread the caller context through ClientsFactory.New and
Clients.AuthToken so proxied blob and manifest requests keep
cancellation and trace propagation instead of starting from
context.Background(). Regenerate the mock and update callers and tests.
Document the intentionally background contexts in the one-shot builder
CLI.
Verification:
- go generate ./pkg/distribution/clients/... (no diff)
- go vet ./...
Adopt modern Go and stdlib idioms surfaced by the modernize analyzer:
promote embedded fields in struct literals, and replace hand-written
equivalents with maps.Copy, min, atomic.Int64, new(expr) and
slices.SortStableFunc.
Verification:
- go fix -diff ./... (no remaining changes)
- go vet ./...
Reformat the page, switch relative imports to the @ alias, replace the
inline update/delete buttons with a dropdown row-actions menu, and use
exponentiation for the TiB/GiB/MiB size conversion.
Verification:
- cd web && bunx tsc --noEmit
- cd web && bun run lint
JavaScript bitwise shifts operate on 32-bit integers, so `1 << 40` evaluates
to `1 << 8` (256) instead of 1 TiB, corrupting TiB size limits and the
calcUnit TiB branch. Replace the shifts with exponentiation.
Verification:
- cd web && bunx tsc --noEmit
- cd web && bun run lint
- Update go.mod/go.sum (grype, syft, aws-sdk-go-v2, distribution,
gin contrib, validator and others).
- Update web/package.json and web/bun.lock (shikijs, react-router-dom,
lucide-react, oxlint, esbuild, vite and others).
- Ignore the local `.delta/` directory in .gitignore.
- Add a shared NamespaceTabs component and use underline tabs across the
namespace summary, repository, member, daemon task and webhook pages.
- Redesign the pagination footer with theme tokens and a page indicator,
and give previous/next proper pointer and disabled states.
- Align the daemon task and webhook table headers with the namespace list
style, drop bold text, and use a muted header color.
- Convert the namespace member list to the shadcn Table component and
match the search/create toolbar height.
- Modernize the webhook create dialog (Input, Label, Switch, two-column
events) and unify button and input styles.
- Make the namespace summary markdown editor fill the available height.
- Stay silent on 404/empty responses for the namespace summary, member
and webhook requests instead of showing an error toast.
- Add standard `appearance` declarations and update deprecated Tailwind
classes to clear editor warnings.
Verification: bunx tsc --noEmit; bunx oxlint; bun run build (web)
Drop the coverage_test.go catch-all and distribute its cases to the test
files matching the units they exercise: logger_test.go for logger.go,
glog_test.go for glog.go, and a new trace_handler_test.go for the trace
handler.
Verification:
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/logger/
- golangci-lint run --build-tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/logger/...
The testkit package only provides test helpers, so skip it in the unit-test
package matrix and coverage run, matching the other infrastructure-only
excludes.
The rs3 server binary never reads RS3_ENDPOINT; it is a client-only setting
for rs3-cli, which only the old healthcheck used. Switch the rs3 healthcheck
to `curl --fail .../readyz` with a start period (matching scripts/run_rs3.sh
and CI) and remove the now-dead RS3_ENDPOINT from compose.yml, the demo-server
compose, run_rs3.sh, and the test workflow.
Verification:
- git grep RS3_ENDPOINT -> no matches
- docker compose config for compose.yml and tools/demo-server/compose.yml
Delete the checked-in ci.txt and test.txt fixtures and write them into
t.TempDir() inside the hash and compress tests, keeping the fixture content
in package constants so the expected digests stay unchanged.
Verification:
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/utils/hash/ ./pkg/utils/compress/
Move the cache, counter, lock, and workq mock files from their package
directories into a `mocks` subpackage, matching the existing service and
storage layout. Update the `//go:generate` directives to emit into
`mocks/*_mocks.go` with package `mocks`, regenerate the files, and document
the convention in AGENTS.md.
Verification:
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/infra/...
- go vet -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/infra/...
Add tests for SetLevel branches, source trimming, the log writer error path,
formatLogLine error branches, gorm trace logging, the Logger methods, both
Fatal helpers (via a re-exec subprocess) and the trace handler.
Split databaseTraceCall into a thin wrapper plus databaseTraceCallFrom(caller)
so the stack walk can be tested with a fake caller; the behavior is unchanged.
pkg/validators is the gin request-validation layer: it registers gin's
binding.Validator and is used by the HTTP handlers, so move it to
pkg/server/validators where the rest of the HTTP layer lives.
The only non-server consumer was pkg/background/daemon, which called
validators.Initialize() even though the worker never uses gin binding
validation. Drop that call so the worker does not depend on the server
package; the server still initializes validators through
handlers.Initialize.
Move pkg/storage/storage_mocks.go to pkg/storage/mocks/ (package mocks) so the
generated mock is not counted in the storage package coverage, and update the
//go:generate directive and the tests that referenced the mock.
Add tests for every context helper and for Init across disabled, http, grpc
and invalid protocol configurations, raising coverage from 21.6% to 98.6%.
This also surfaced a real bug: Init failed when tracing was enabled because
resource.Default() (schema 1.43.0) conflicted with semconv.SchemaURL
(1.26.0). Build the service name resource schemaless so Init succeeds.
Move every pkg/service/<domain>/*_mocks.go into pkg/service/<domain>/mocks/
(package mocks) so generated mocks are no longer part of the packages under
test and stop depressing their coverage. Update the //go:generate directives
and the tests that referenced the mocks.
Raise the validators package coverage from 49.2% to 97.1% with table-driven
tests for every validation function and fold the repetitive RegisterValidation
checks in newValidator into one loop.
Move the repository mocks out of the packages under test into per-domain mocks
subpackages, update the //go:generate directives and test imports, skip the
mocks packages in the CI test matrix and drop the unused Codecov config.
Default InitializeIntegration to the turso dialect so integration tests run
locally without a PostgreSQL server.
Replace the hard-coded library namespace seed with an idempotent bootstrap
task driven by a new namespace.initialize config list, so users can declare
any number of namespaces and set the visibility of each one (public or
private). The task only creates namespaces that do not exist yet.
Remove the seed INSERT from the four 0001 migrations and wire the new config
into the example configs, the docs and the Helm chart (values + configmap).
Add an explicit TableName() for UserRecoverCode and document that the
password recovery backend is wired up (endpoints, service, repository and
generated query) but the code is never delivered to users and the web UI has
no recover pages. Track the remaining work in TODO.md.
pkg/dal/models/locker.go is dead code: it is absent from the gorm-gen model
list in pkg/dal/cmd/gen.go, has no generated query, and no migration creates
a lockers table. Locking is handled by pkg/infra/lock instead.
Run the self-hosted rs3 service instead of Minio in test.yml and swap the
local helper script, Makefile helm values, and AGENTS.md notes accordingly.
Fix the E2E workflow (refs #426): it only triggered on the `main` branch,
which does not exist in this repository, so the job never ran. Trigger it
on `dev` for both push and pull_request like test.yml, build the image via
the consolidated `make docker-build`, bump the checkout/setup-go actions,
drop the hosted-toolcache cleanup, and refresh the E2E tooling to k6
v2.3.0 and skopeo v1.24.1.
Repair the image startup path as well: start nginx with an explicit
/etc/nginx/nginx.conf and a /dev/stderr error log so the non-root user does
not touch the compiled-in log path, create /etc/sigma with mode 0755 before
copying config.yaml (COPY --chmod made the directory non-traversable), and
disable redis in the shipped default config since cache, locker and work
queue are inmemory.
Replace the codecov/codecov-action step with coverallsapp/github-action,
using COVERALLS_REPO_TOKEN and the golang format for the Go coverprofiles
emitted by `go test -coverprofile`. Update the README badges and the CI
workflow notes so no references to Codecov remain.
Verification:
- Ruby YAML parse of .github/workflows/test.yml
Add a `Lint web` step to the lint workflow so the frontend gets the same
treatment as the Go code. It reuses the dependencies installed by the
existing `Build web` step (same job, same bun cache) and runs
`bun run lint`, which is the oxlint invocation configured in
web/package.json and web/.oxlintrc.json — no extra setup or duplicated
dependency install required.
Verification: parsed the workflow YAML and confirmed the step order is
Checkout -> Setup golang -> Setup bun -> Cache bun -> Build web ->
Lint web -> Run golangci-lint -> Lint Dockerfile; `cd web && bun run lint`
exits 0 locally (0 warnings, 0 errors).
Turn on the react, react-hooks, suspicious and perf rule sets in oxlint and
fix the 2665 diagnostics they report instead of muting the rules:
- validation/limit state is derived during render instead of being synced
in an effect (namespace/repository/tag limits, size limit + unit,
username/email/password checks in Menu, Users, Webhook and Member)
- refetch triggers are gone: mutation handlers now call the memoized fetcher
directly (Namespace list and members, Menu token timer) instead of bumping
a `refresh` state that only existed to re-run an effect
- data fetchers (fetchNamespace/fetchTags/fetchRepository/fetchWebhook/
fetchMembers/logout) are memoized with useCallback and their effects use
correct dependency arrays; the async profile seeding uses an override state
instead of an effect
- fix 39 shadowed variables (resp/err/*ListData/configData/requestUrl/...),
use stable list keys instead of array indexes, hoist `cutDigest` to module
scope and memoize the ToggleGroup context value
- replace the ported HTML5 email pattern with a zod based `isEmail` helper
(zod 4, `z.email()`), dropping the control-character regex
- use-mobile seeds its state lazily and Shiki derives the highlighted html
with useMemo
.oxlintrc.json pins the setup: plugins react/typescript/unicorn/oxc,
correctness as error, suspicious/perf as warn, `_`-prefixed names ignored by
no-unused-vars, and react-in-jsx-scope off because the app uses the React 19
automatic JSX runtime (that rule only applies to React < 17).
Verification: web: `bun run lint` (0 warnings, 0 errors, no
oxlint-disable directives left in src); `tsc --noEmit`; `vite build`;
unused-import scan reports 0.
Fix the diagnostics that oxlint reports on the codebase instead of
silencing the rules:
- unused event parameters (41): `onClick={e => ...}` -> `onClick={() => ...}`
and `.catch(err => ...)` -> `.catch(() => ...)` where the argument was
never used
- unused state members: keep only the half that is used
(`const [sortOrder] = useState(...)`, `const [, setUserObj] = ...`), and
drop the sort state in CodeRepository/List that was fully unused
- unused props/locals: drop `index` from TableItem/TableItemDropdown (plus
interface and call sites), `namespace`/`repository` from Tag TableItem,
two dead `const user = ...` in Login/Callback and the unused `u` URL in
Webhook/List, along with the imports that became unused (IUserSelf, IOrder)
- one-line effects (10): `useEffect(() => { cond && setX() }, [dep])` ->
explicit `if (cond) { setX(); }`
- utils/index.ts: keep the HTML5 email pattern as-is with a scoped
`oxlint-disable-next-line no-control-regex` and a reason
Add .oxlintrc.json to pin the setup: correctness rules are errors,
no-unused-vars ignores `_`-prefixed names, browser/es2024 globals,
dist/node_modules/public ignored. The rule set matches the previous
defaults, so nothing is disabled to hide a warning.
Verification: web: `bun run lint` (0 warnings, 0 errors); `tsc --noEmit`;
`vite build`; unused-import scan reports 0.
Swap every headlessui usage for the shadcn/base-ui components that are
already bundled in the app, then drop the dependency:
- menus (8): Menu/MenuButton/MenuItems/MenuItem + Transition -> DropdownMenu;
role checks that guarded the original clicks are preserved as
DropdownMenuItem `disabled`
- dialogs (14): Dialog/DialogPanel/DialogTitle + Transition* -> Dialog,
DialogContent, DialogTitle, DialogDescription, DialogFooter and Button
- listboxes (6): Listbox -> Select; the multiple-selection one becomes a
DropdownMenu with DropdownMenuCheckboxItem
- comboboxes (9): Combobox -> Popover + Command, wiring the existing search
state into CommandInput and keeping shouldFilter={false} for server-side
search
- remove @headlessui/react from package.json/bun.lock
Also included:
- drop imports that became unused (Table/Button in Builder/RunnerList,
Separator, useSidebar, dayjs, Link/useParams, Check/ChevronsUpDown and the
leftover shadcn imports in the webhook pages)
- replace Tailwind v4 arbitrary sizes that have spacing-scale equivalents
(w-[350px] -> w-87.5, h-[36px] -> h-9, after:top-[2px] -> after:top-0.5, ...);
h-[18.4px] is intentionally kept since 4.6 is not a multiple of 0.25
- fix the webhook log delete-confirm label (was "Resend")
- add oxlint with `lint` and `lint:fix` scripts
Verification: web: `tsc --noEmit`; `vite build`; `oxlint` (0 errors,
72 pre-existing warnings); scripted scan reports 0 unused imports and no
remaining @headlessui imports.
Follow up the shadcn/ui migration with a round of layout and UX fixes:
- layout: unify the horizontal padding of the page header, page toolbars,
table headers/cells and pagination on `px-4`, so titles, search boxes and
the first/last table columns share one left/right baseline while the table
itself stays full width.
- sidebar: stop the SidebarProvider wrapper (`w-full`) from squeezing the
page content, enlarge the menu rows, lighten the active background through
a new `--sidebar-active` token, drop the bold active label, and always show
a pointer cursor for buttons and menu items.
- user menu: fold API docs, theme and language into the bottom dropdown,
persist username/email on login, callback and token refresh, fall back to
"sigma" when no username is known, and expose a Login entry for guests.
- tables: replace the row action dropdown with inline update/delete icon
buttons (delete in red), right align the Action column, only render sort
arrows for the active sort, keep namespace/repository names readable on
hover, and render created/updated columns as relative times.
- components: add `RelativeTime` (dayjs relativeTime + tooltip) and the
`Field`/`FieldLabel` primitives, rebuild the login page on top of them,
and bump the shared button/input/select/toggle/tabs presets to h-9.
- home: lay the summary cards out in a responsive grid instead of a fixed
1600px wide row that overflowed the viewport.
- utils: merge the short helper modules into `utils/index.ts` (trimHTTP,
calcUnit, Regex, Tooltip) and `utils/request.ts` (axios interceptors plus
token refresh), leaving only index/request/distros.
Verification:
- `npx tsc --noEmit`
- `npx vite build`
Replace every `// <Identifier> ...` placeholder doc comment with a real
description of the declared symbol. 276 comments in 71 files were rewritten:
149 top-level placeholders first, then a follow-up scan caught another 127
indented ones (interface methods and const blocks) that a column-anchored
match had missed.
Comments now open with the declared identifier and record the actual
contract - behaviour, side effects, error/panic conditions, units, HTTP
endpoints, lock keys and cache prefixes - instead of restating the
signature. Three placeholders carried a stale identifier and were corrected
to the declared symbol: ConfigurationDatabasePostgresql, GetImageRef and
Initialize.
No functional change: every modified line is a comment.
Verification:
- grep '^\s*//\s+\S+\s+\.\.\.\s*$' over all *.go returns no matches
- gofmt -l over the changed files prints nothing
- CGO_ENABLED=1 go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./... exits 0
- git diff HEAD -U0 filtered to non-comment changed lines is empty
Add login failure rate limiting with per-username tracking, supporting both delay-based throttling and lockout via configurable backend.
Update all sample configs, Helm templates, and documentation to include the new auth loginRateLimit settings.
Verification:
All config files, Helm charts, and docs are updated to match the new configuration schema.
Rename the cache module package name from `cacher` to `cache` across all files, including:
- Update all internal package declarations
- Fix import aliases in dependent modules
- Correct mockgen package configuration
- Update cache interface and error type references
This aligns the package naming with the module directory structure and improves consistency across the codebase.
Add a cache-backed failed-login throttler for Basic auth. Once a username
reaches auth.loginRateLimit.maxFailures failures within window, each
subsequent authentication attempt is delayed by delay (capped at 5s)
instead of being rejected; the counter is cleared on a successful login.
- add pkg/infra/ratelimit backed by pkg/infra/cache (inmemory/redis) with
Count/Incr/Reset and Prometheus metrics for failures, delayed attempts,
and cache errors
- wire ConfigurationAuthLoginRateLimit with defaults (5 failures, 5m window,
1s delay) and document it in conf/config.yaml
- integrate the limiter into the authn middleware, counting all Basic
failures including /v2/, and honor request context cancellation
- add unit tests for threshold behavior, delay, and cancellation
- mark the login rate limit item complete in TODO.md
Update direct and indirect Go module versions, including aws-sdk-go-v2,
goose, ClickHouse, Azure SDK and golang.org/x/exp, and refresh the web
toolchain (react 19.3, vite 8.3, typescript 7, shadcn, tailwind).
Switch the web Vite `@` alias from `__dirname` to `import.meta.dirname`
to align with the ESM config runtime.
Verification: not run (dependency-only change).
Use the tools declared in go.mod's tool block (go-enum, addlicense,
swag, mockgen) through the `go tool` prefix, so they resolve via the
module tool directives instead of relying on globally installed
binaries:
- rewrite //go:generate mockgen/go-enum directives to
`go tool mockgen` / `go tool go-enum` (42 source files)
- switch the Makefile addlicense target to `go tool addlicense`
- regenerate GoMock files with `go generate ./...`, which updates
the generated-by headers, fixes stale mock signatures, and drops
the unused MockFactory from runtime_mocks.go
Verification:
- go tool lists go-enum/addlicense/swag/mockgen from go.mod
- go generate ./... completes on enums.go
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" \
./pkg/background/build/runtime ./pkg/dal/repository/analytics ./pkg/service/token
Move generated mocks out of the `mocks/` subpackages and into the
source packages as `*_mocks.go` files for `pkg/distribution/clients`
and `pkg/service/password`, updating the go:generate directives to
emit `-package=clients` / `-package=password`. Update the import in
`pkg/service/users/users_test.go` to reference the new location.
Also:
- add `go.uber.org/mock/mockgen` to the tool directives and refresh
module dependencies
- bump CI service images (mysql 9, postgres 18-alpine) and drop the
removed mocks packages from the coverage exclusion list
- clean up .gitignore and ignore local tool artifacts (.pi/,
skills-lock.json)
Verification:
- go vet -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/users/ ./pkg/service/password/ ./pkg/distribution/clients/
- go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/service/users/
Replace the hand-rolled authz cache (sync.RWMutex + map + dual-index
namespace cache) with the shared pkg/infra/cache Cacher, matching the
existing namespace_cache / repository_cache pattern.
- Inject Config and RedisClientFactory into the authorizer and build the
Cachers via cacher.NewWithOptions in NewAuthorizer (now returns
(Authorizer, error))
- Use read-through Cachers for role and namespace lookups; represent
non-members with negative caching instead of the empty-string sentinel
- Keep the 30s TTL to preserve multi-instance eventual consistency, while
gaining LRU 2Q, singleflight and an optional Redis backend
- Drop pkg/authz/cache.go and rewrite the cache tests as construction
tests (inmemory success, redis-disabled failure)
- Document the New convenience wrapper in pkg/infra/cache/cacher.go
Verification: CI_DATABASE_TYPE=sqlite3 go test ./pkg/authz/... passes
Update all direct and indirect Go module dependencies to their latest compatible versions, including security patches and feature updates across the codebase.
Replace all usages of standard encoding/json with encoding/json/v2 and jsontext
where applicable, update decoding logic to use json.UnmarshalRead and
json.MarshalWrite instead of manual encoder/decoder instances.
Verification:
go test -race ./pkg/... ./cmd/...