Add local cargo registry type #144

Open
unkin-agent wants to merge 3 commits from benvin/local-cargo into master
Member

Private Rust crates have nowhere to publish: artifactapi only proxies cargo registries, with no local type cargo can publish to.

  • serve config.json (dl + api) and sparse index files for local cargo repos from a new cargo_index table
  • add PUT /api/v1/crates/new, translating publish metadata into index lines (sha256 cksum, deps, features2 + v:2)
  • reject names equal up to case or -/_ and versions equal up to build metadata, under a per-crate lock and unique index
  • validate versions as strict SemVer 2.0
  • add yank/unyank and serve .crate downloads from local files
  • drop index lines when a crate file is deleted
  • add UI/README usage and an e2e-docker case
Private Rust crates have nowhere to publish: artifactapi only proxies cargo registries, with no local type cargo can publish to. - serve config.json (dl + api) and sparse index files for local cargo repos from a new cargo_index table - add PUT /api/v1/crates/new, translating publish metadata into index lines (sha256 cksum, deps, features2 + v:2) - reject names equal up to case or -/_ and versions equal up to build metadata, under a per-crate lock and unique index - validate versions as strict SemVer 2.0 - add yank/unyank and serve .crate downloads from local files - drop index lines when a crate file is deleted - add UI/README usage and an e2e-docker case
unkin-agent added 1 commit 2026-10-16 23:35:48 +11:00
Add local cargo registry type
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
4c321a089d
- serve sparse index and config.json for local cargo repos from a cargo_index table
- add Web API publish, yank and unyank under the local repo api root
- drop index lines when the crate file is deleted
Author
Member
  • internal/api/v1/cargo.go:76 — case-collision check is list-then-insert and the only uniqueness is UNIQUE(repo_name, file_path) (case-preserving path); concurrent publishes of Foo and foo (same or different version) both succeed and the shared lower-cased index file then mixes two crate names → add UNIQUE(repo_name, lower(name), version) in 0003_cargo_index.sql (map 23505 to 409) and take pg_advisory_xact_lock(hashtext(repo||lower(name))) around check+insert for the different-version case.
  • internal/provider/cargo/local.go:187 — validVersion accepts non-semver (1, 1..2, 1.0.0--) and a bad vers makes cargo fail to parse the entire index file for that crate → parse with golang.org/x/mod/semver-style strict semver (or blang/semver) and reject; also reject a second X.Y.Z+other build-metadata variant of an existing version (cargo treats them as the same version).
  • internal/provider/cargo/local.go:69 (nit) — empty crate part accepted and cksum of empty body indexed → reject len(crate)==0.
  • internal/api/v1/cargo.go:56 (nit) — up to 64 MiB buffered in memory per publish with no concurrency bound (crates.io caps at 10 MiB) → lower cap or spool to disk.
  • internal/provider/cargo/local.go:171 (nit) — foo-bar and foo_bar are distinct crates here (crates.io treats them as one) → normalize -/_ in the collision check.
  • migrations/0003_cargo_index.sql (nit) — 0003_ prefix also used by #140 (0003_helm_metadata) and #135 (0003_github_sync_state_cascade); names differ so they apply, no test enforces uniqueness → renumber to next free number on whichever merges later.
- internal/api/v1/cargo.go:76 — case-collision check is list-then-insert and the only uniqueness is UNIQUE(repo_name, file_path) (case-preserving path); concurrent publishes of `Foo` and `foo` (same or different version) both succeed and the shared lower-cased index file then mixes two crate names → add UNIQUE(repo_name, lower(name), version) in 0003_cargo_index.sql (map 23505 to 409) and take pg_advisory_xact_lock(hashtext(repo||lower(name))) around check+insert for the different-version case. - internal/provider/cargo/local.go:187 — validVersion accepts non-semver (`1`, `1..2`, `1.0.0--`) and a bad `vers` makes cargo fail to parse the entire index file for that crate → parse with golang.org/x/mod/semver-style strict semver (or blang/semver) and reject; also reject a second `X.Y.Z+other` build-metadata variant of an existing version (cargo treats them as the same version). - internal/provider/cargo/local.go:69 (nit) — empty crate part accepted and cksum of empty body indexed → reject len(crate)==0. - internal/api/v1/cargo.go:56 (nit) — up to 64 MiB buffered in memory per publish with no concurrency bound (crates.io caps at 10 MiB) → lower cap or spool to disk. - internal/provider/cargo/local.go:171 (nit) — `foo-bar` and `foo_bar` are distinct crates here (crates.io treats them as one) → normalize `-`/`_` in the collision check. - migrations/0003_cargo_index.sql (nit) — `0003_` prefix also used by #140 (0003_helm_metadata) and #135 (0003_github_sync_state_cascade); names differ so they apply, no test enforces uniqueness → renumber to next free number on whichever merges later.
unkin-agent added 2 commits 2026-10-17 12:43:47 +11:00
Guard cargo publishes against name and version collisions
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
ce8026d46e
- serialise publishes per crate with an advisory lock in one transaction
- add a unique index on normalised name and version without build metadata
- validate versions as strict SemVer 2.0 and reject an empty .crate
- renumber the migration to 0005
Author
Member
  • nit: internal/database/cargo_index.go:96 — SetCargoYanked matches lower(name) = $2 AND version = $3, but uniqueness is on replace(lower(name),'-','_') and split_part(version,'+',1); yank foo_bar on a crate published as foo-bar, or --vers 1.0.0 on a 1.0.0+a row, returns 404 → match on the same normalised expressions (at most one row can match).
- nit: internal/database/cargo_index.go:96 — SetCargoYanked matches `lower(name) = $2 AND version = $3`, but uniqueness is on `replace(lower(name),'-','_')` and `split_part(version,'+',1)`; `yank foo_bar` on a crate published as `foo-bar`, or `--vers 1.0.0` on a `1.0.0+a` row, returns 404 → match on the same normalised expressions (at most one row can match).
All checks were successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Required
Details
ci/woodpecker/pr/test Pipeline was successful
Required
Details
ci/woodpecker/pr/build Pipeline was successful
Required
Details
This pull request has changes conflicting with the target branch.
  • e2e-docker/README.md
  • ui/src/components/UsageInstructions.tsx
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin benvin/local-cargo:benvin/local-cargo
git checkout benvin/local-cargo
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unkin/artifactapi#144