Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docs(vendor): reflect final service coverage (npm/pypi/cargo/golang/c…
…omposer); gem gated

gem stays build-local: a path-sourced gem needs a stub gemspec that the `.gem`
archive doesn't carry in bundler's required eval-able form (it's metadata.gz
YAML; RubyGems generates the stub into specifications/). A clean service path
can't produce it without the local install or Ruby-specific serialization.

- dispatch_vendor_one gate comment + detail message updated to the final set
- CLI_CONTRACT.md "Coverage today" + README.md flag doc updated; note Tier-B
  build-equivalence is exercised by the toolchain-backed e2e suites

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
  • Loading branch information
mikolalysenko and claude committed Jun 24, 2026
commit 36995a999d14c7df1e57b16cb67283683c6a9e93
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Each flag has a matching `SOCKET_*` environment variable. **Precedence is CLI ar
| `--proxy-url <url>` | `SOCKET_PROXY_URL` | Public proxy URL used when no API token is set. |
| `-e, --ecosystems <list>` | `SOCKET_ECOSYSTEMS` | Restrict to specific ecosystems (comma-separated, e.g. `npm,pypi`). |
| `--download-mode <mode>` | `SOCKET_DOWNLOAD_MODE` | Artifact to fetch when local files are missing: `diff` (default, smallest delta), `package` (full per-package tarball), or `file` (legacy per-file blobs). |
| `--vendor-source <mode>` | `SOCKET_VENDOR_SOURCE` | How `vendor` acquires the installable artifact: `auto` (default — download the prebuilt package from patch.socket.dev, fall back to a local build on any miss), `service` (require the service, fail-closed), or `build` (always build locally). Covers npm, pypi, and cargo today; other ecosystems build locally. |
| `--vendor-source <mode>` | `SOCKET_VENDOR_SOURCE` | How `vendor` acquires the installable artifact: `auto` (default — download the prebuilt package from patch.socket.dev, fall back to a local build on any miss), `service` (require the service, fail-closed), or `build` (always build locally). Covers npm, pypi, cargo, golang, and composer today; gem builds locally. |
| `--vendor-url <url>` | `SOCKET_VENDOR_URL` | Base host for the vendoring service's package-reference request (default: the active `--api-url`/`--proxy-url` base). Point at staging / local dev for testing. |
| `--patch-server-url <url>` | `SOCKET_PATCH_SERVER_URL` | Override the host of the prebuilt-archive download URL the service returns (default: as returned). Mainly for local-dev / testing. |
| `--offline` | `SOCKET_OFFLINE` | Strict airgap: never contact the network. Operations that need remote data fail loudly. |
Expand Down
16 changes: 11 additions & 5 deletions crates/socket-patch-cli/CLI_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,11 +351,17 @@ per service outcome:
| 401 / 403 grant / 5xx / network error | local build + `vendor_prebuilt_unavailable` | refuse |
| `--offline` | local build | refuse (`vendor_service_offline_conflict`) |

Coverage today: **npm** (all lock flavors), **pypi** (wheel — sdist falls back / refuses), and
**cargo** (download + extract the `.crate`). For ecosystems not yet covered (golang, gem, composer,
maven, nuget) `auto`/`build` build locally as before, and `service` refuses with
`vendor_service_unsupported_ecosystem`. A successful service vend emits `vendor_prebuilt_downloaded`.
Unrelated to `--download-mode` (which selects the patch-CONTENT format for the local build).
Coverage today: **npm** (all lock flavors), **pypi** (wheel — sdist falls back / refuses), **cargo**
(download + extract the `.crate`), **golang** (download + extract the module zip, verify the `h1:`
dirhash, wire the `replace`), and **composer** (download + extract the dist zip). The Tier-B
ecosystems (cargo/golang/composer) download the patched archive and extract it into the vendor
directory — the same source tree the local build commits — then run the existing path-dep wiring;
their build-equivalence is exercised by the toolchain-backed e2e suites (which skip when the package
manager is absent). **gem** is NOT covered (it builds locally): a path-sourced gem needs a stub
gemspec that the `.gem` archive doesn't carry in bundler's required form. For not-covered ecosystems
`auto`/`build` build locally as before, and `service` refuses with `vendor_service_unsupported_ecosystem`.
A successful service vend emits `vendor_prebuilt_downloaded`. Unrelated to `--download-mode` (which
selects the patch-CONTENT format for the local build).

**Patch sources stay in memory (v3.4)**: vendoring never writes `.socket/blobs/`, `.socket/diffs/`,
or temporary patch files. Pre-existing `.socket/` artifacts (from a prior `apply`/`get`/`repair`)
Expand Down
15 changes: 8 additions & 7 deletions crates/socket-patch-cli/src/commands/vendor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,20 @@ pub(crate) async fn dispatch_vendor_one(
) -> Option<VendorOutcome> {
let eco = ecosystem_dir_for_purl(purl)?;

// Prebuilt service downloads currently cover npm, pypi, and cargo; the
// remaining ecosystems vendor by building locally. Under fail-closed
// `service` mode, refuse the not-yet-covered ones with a clear message
// rather than silently building (which would violate the contract). Under
// `auto`/`build` they fall through to the local build as before.
// Prebuilt service downloads cover npm, pypi, cargo, golang, and composer;
// the rest (gem) vendor by building locally — gem needs a stub gemspec the
// `.gem` archive doesn't carry in the form bundler's path source wants.
// Under fail-closed `service` mode, refuse the not-covered ones with a clear
// message rather than silently building (which would violate the contract).
// Under `auto`/`build` they fall through to the local build as before.
const SERVICE_ECOSYSTEMS: &[&str] = &["npm", "pypi", "cargo", "golang", "composer"];
if let Some(cfg) = service {
if cfg.source.requires_service() && !SERVICE_ECOSYSTEMS.contains(&eco) {
return Some(VendorOutcome::Refused {
code: "vendor_service_unsupported_ecosystem",
detail: format!(
"--vendor-source=service is not yet supported for `{eco}` \
(prebuilt downloads currently cover npm, pypi, and cargo); \
"--vendor-source=service is not supported for `{eco}` \
(prebuilt downloads cover npm, pypi, cargo, golang, and composer); \
use --vendor-source=auto or --vendor-source=build"
),
});
Expand Down