GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix vendored outcomes a failed commit or rolled-back eject still repo… · SocketDev/socket-patch@d73c27c

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • crates/socket-patch-cli
    • CLI_CONTRACT.md
    • src
      • commands
        • vendor.rs
      • json_envelope.rs
    • tests
      • covgap_commands_vendor.rs
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎crates/socket-patch-cli/CLI_CONTRACT.md‎

Copy file name to clipboardExpand all lines: crates/socket-patch-cli/CLI_CONTRACT.md
+4-4Lines changed: 4 additions & 4 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ For a **9.0 root lock**, the CLI ensures `pnpm-workspace.yaml` carries `trustLoc
148148

149149
**Vendored write durability (v5.0)**: every write is atomic (stage + rename), but only the durable commit points — lockfiles, `go.mod`/`go.sum`, `pom.xml`, `nuget.config`, `package.json`, `pnpm-workspace.yaml`, `.cargo/config.toml`, the Python/Ruby manifests, `.socket/vendor/state.json` and `redirect-state.json` — are fsynced on write. The content-verified artifacts under `.socket/vendor/<eco>/<uuid>/` (patched copies, packed/rebuilt archives and sidecars, markers) are written without an fsync and made durable by one barrier (file + directory fsync, one `F_FULLFSYNC` per device on macOS) ahead of the next commit point — and, for an artifact rebuilt in place that no commit point follows, at the end of the vendored run's commit and when the command releases the apply lock — so a crash can only lose an artifact that no durable commit point names yet, which the next run redownloads.
150150

151-
**Vendored group commit (v5.0)**: `vendor`, `scan --mode vendored` and `get --mode vendored` capture every lockfile / manifest / config edit and every ledger save of the run in memory (reads inside the run see them) and commit them ONCE after the per-package loop — including the packages that succeeded in a run where others failed, so a completed run leaves the same files per-package commits would. Captured: every file under the project root outside `.socket/`, plus `.socket/vendor/state.json` and `.socket/vendor/redirect-state.json`; artifacts are written directly (see the durability note). A multi-file commit goes through a roll-forward journal, `.socket/vendor/.commit-journal.json` (the new bytes of every changed file, plus the bytes each replaces and their sha256; deleted once the commit completes). **Crash semantics**: before the journal is durable, nothing is committed — the lockfiles and ledgers are the pre-run ones and the run's artifacts are unreferenced orphans; after it, the next command that takes the apply lock replays the journal before reading anything (files already at their new bytes are left alone), so a locked command never observes a half-committed run. A journal that matches neither side of some file (edited by hand since the crash) is renamed to `.socket/vendor/.commit-journal.set-aside-<uuid>.json` (keeping every file's pre-commit bytes) and stderr says what was done (`Warning: an interrupted vendored run's commit could not be finished as written: …`): the edited files are never written over; when they all still carry the commit's own lines the rest of the commit is finished around them, when none of them does the files the crash had already replaced are put back to their pre-commit bytes, and otherwise nothing is applied. A journal that is unreadable, names a path outside the lockfiles and ledgers, or would write through a symbolic link is set aside with nothing applied. A replay that fails on I/O keeps the journal and fails the lock acquire (`lock_io`, naming the journal). Read-only commands that take no lock (`vex`, `list`) may observe the interrupted state until then. A re-vendor under a newer uuid removes the replaced uuid's dir only after the commit (its `vendor_stale_artifact_removed` event follows the run's per-package events), and a golang takeover removes the `.socket/go-patches/` copy only after the commit that repoints `go.mod`. A commit never renames over a symbolic link: when a changed file is a symlink, the whole commit is refused before anything is written, with the top-level error `redirect_symlinked_file_unsupported` (exit 1; a `--dry-run` predicts it with a `vendor_would_refuse_symlinked_file` advisory) — the artifact dirs the run's loop added under `.socket/vendor/` are removed, and every package it vendored is reported as a `failed` event with that code instead of `applied`. A commit write failure is the top-level error `vendor_commit_failed` (exit 1; the pre-run lockfiles and ledger stay — unless putting back the files already replaced failed too, in which case the journal is kept and the next locked command finishes the commit). `repair`, `vendor --revert` and `rollback` still save per entry.
151+
**Vendored group commit (v5.0)**: `vendor`, `scan --mode vendored` and `get --mode vendored` capture every lockfile / manifest / config edit and every ledger save of the run in memory (reads inside the run see them) and commit them ONCE after the per-package loop — including the packages that succeeded in a run where others failed, so a completed run leaves the same files per-package commits would. Captured: every file under the project root outside `.socket/`, plus `.socket/vendor/state.json` and `.socket/vendor/redirect-state.json`; artifacts are written directly (see the durability note). A multi-file commit goes through a roll-forward journal, `.socket/vendor/.commit-journal.json` (the new bytes of every changed file, plus the bytes each replaces and their sha256; deleted once the commit completes). **Crash semantics**: before the journal is durable, nothing is committed — the lockfiles and ledgers are the pre-run ones and the run's artifacts are unreferenced orphans; after it, the next command that takes the apply lock replays the journal before reading anything (files already at their new bytes are left alone), so a locked command never observes a half-committed run. A journal that matches neither side of some file (edited by hand since the crash) is renamed to `.socket/vendor/.commit-journal.set-aside-<uuid>.json` (keeping every file's pre-commit bytes) and stderr says what was done (`Warning: an interrupted vendored run's commit could not be finished as written: …`): the edited files are never written over; when they all still carry the commit's own lines the rest of the commit is finished around them, when none of them does the files the crash had already replaced are put back to their pre-commit bytes, and otherwise nothing is applied. A journal that is unreadable, names a path outside the lockfiles and ledgers, or would write through a symbolic link is set aside with nothing applied. A replay that fails on I/O keeps the journal and fails the lock acquire (`lock_io`, naming the journal). Read-only commands that take no lock (`vex`, `list`) may observe the interrupted state until then. A re-vendor under a newer uuid removes the replaced uuid's dir only after the commit (its `vendor_stale_artifact_removed` event follows the run's per-package events), and a golang takeover removes the `.socket/go-patches/` copy only after the commit that repoints `go.mod`. A commit never renames over a symbolic link: when a changed file is a symlink, the whole commit is refused before anything is written, with the top-level error `redirect_symlinked_file_unsupported` (exit 1; a `--dry-run` predicts it with a `vendor_would_refuse_symlinked_file` advisory) — the artifact dirs the run's loop added under `.socket/vendor/` are removed, and every package it vendored is reported as a `failed` event with that code instead of `applied` (its per-package `skipped` advisories from the same run, such as `vendor_prebuilt_downloaded`, are dropped). A commit write failure is the top-level error `vendor_commit_failed` (exit 1; the pre-run lockfiles and ledger stay, the artifact dirs the run added are removed and its vendored packages are `failed` events with that code, the same way — unless putting back the files already replaced failed too, in which case the journal is kept, the next locked command finishes the commit, and the per-package events stand). `repair`, `vendor --revert` and `rollback` still save per entry.
152152

153153
`scan --sync` is sugar for `--mode agent --prune` — the canonical single-flag agent-mode bot invocation. `scan --json --sync` discovers, applies, and reconciles state in one pass.
154154

@@ -635,7 +635,7 @@ package then fails, exactly the files the eject wrote are put back (the pins' fi
635635
restore's files, and every file the vendored apply committed, each only when its bytes changed) —
636636
the project stays hosted exactly as before, and every other file (a `--json > report.json` redirect
637637
target, a log another process appends to) is left alone, with the
638-
`eject_rolled_back` warning (printed to stderr on a human run, which prints no "Vendored N packages" summary and no "Next steps:") and `partial_failure`, exit 1 — each package the vendored apply had vendored is re-reported as a `skipped` event with `errorCode: "eject_rolled_back"`, never `applied` (#1005); if putting the snapshot back itself fails,
638+
`eject_rolled_back` warning (printed to stderr on a human run, which prints no "Vendored N packages" summary and no "Next steps:") and `partial_failure`, exit 1 — each package the vendored apply had vendored is re-reported as a `skipped` event with `errorCode: "eject_rolled_back"`, never `applied`, and the run's other `skipped` advisories for it (`vendor_prebuilt_downloaded`, `vendor_takeover_reverted_redirect`, …) are dropped (#1005); if putting the snapshot back itself fails,
639639
the error is `eject_rollback_failed` naming the files to `git checkout`. The eject does not emit the
640640
per-purl `vendor_takeover_reverted_redirect` warning (the restore is its own planned step).
641641
`--offline` (or `SOCKET_OFFLINE`) refuses the eject up front with `offline_eject_unavailable` —
@@ -1235,8 +1235,8 @@ Every `--json` invocation emits a single JSON object that follows the **unified
12351235
| `out_of_scope_copies_restored` | rollback `warnings[]` | path-scoped rollback (v5.0): a selected patch had installed copies outside the given patterns; ALL copies were restored (patches are per-package). Informational — never flips the exit. |
12361236
| `vendor_ledger_entry_unwired` | scan `warnings[]` | a vendored entry's dependency left the lockfile (upgraded or removed), so the ledger supplement skipped it; the detail names the purls and points at `scan --prune`, which reverts them (no warning on a pruning non-hosted run). An entry that prune drift-keeps (its lock entries were re-resolved since vendoring, e.g. an npm uninstall re-locked it away) is reported on the prune's `GC: kept` line and keeps being warned about. |
12371237
| `path_scope_excluded_supplements` | scan `warnings[]` | path-scoped scan (v5.0): lockfile-only / vendor-ledger supplement packages have no installed path and were excluded from the scoped scan; the detail carries the count. |
1238-
| `vendor_commit_failed` | top-level error (`vendor`, and the nested vendor envelope of `scan` / `get --mode vendored`) | v5.0 group commit: the run's lockfile / manifest / ledger edits could not be written (the detail names the I/O error). Exit 1; the project's lockfiles and `.socket/vendor/state.json` are left as they were before the run (a partially-applied commit is put back), and the per-package events describe the uncommitted outcome. When putting a partially-applied commit back fails too, the journal is kept instead and the detail says the next socket-patch command in the project finishes the commit. |
1239-
| `redirect_symlinked_file_unsupported` (vendored) | top-level error (`vendor`, and the nested vendor envelope of `scan` / `get --mode vendored`) | v5.0 group commit (#627): a file the run would rewrite — a lockfile, `package.json`, `pnpm-workspace.yaml`, `nuget.config`, … — is a symbolic link. The commit stages each file and renames it over the path, which would replace the link with a detached copy and leave its target (the lock other checkouts read) unpatched, so it refuses before writing anything — the same code and message as the hosted guard. Exit 1; the link, its target and `.socket/vendor/state.json` are left as they were, the artifact dirs the run added under `.socket/vendor/` are removed (any that cannot be are named in the message, with `vendor --revert` as the remedy), and each package the run vendored is a `failed` event with this code, not `applied` (`summary.applied` does not count it). The human run prints no "Vendored N packages" count for them and no "Next steps:" (#898). Backends that check their own targets first (bun.lockb, Hatch, uv, Poetry, Pipenv, requirements, Cargo) keep their own codes. |
1238+
| `vendor_commit_failed` | top-level error (`vendor`, and the nested vendor envelope of `scan` / `get --mode vendored`); also `failed` (per package) | v5.0 group commit: the run's lockfile / manifest / ledger edits could not be written (the detail names the I/O error). Exit 1; the project's lockfiles and `.socket/vendor/state.json` are left as they were before the run (a partially-applied commit is put back). As for `redirect_symlinked_file_unsupported` (#898), the artifact dirs the run added under `.socket/vendor/` are removed (any that cannot be are named in the detail), each package the run vendored is a `failed` event with this code, not `applied`, its `skipped` advisories from the run are dropped, and the human run prints no "Vendored N packages" count for them and no "Next steps:". When putting a partially-applied commit back fails too, the journal is kept instead, the detail says the next socket-patch command in the project finishes the commit, and the per-package events stand (that commit does complete). |
1239+
| `redirect_symlinked_file_unsupported` (vendored) | top-level error (`vendor`, and the nested vendor envelope of `scan` / `get --mode vendored`) | v5.0 group commit (#627): a file the run would rewrite — a lockfile, `package.json`, `pnpm-workspace.yaml`, `nuget.config`, … — is a symbolic link. The commit stages each file and renames it over the path, which would replace the link with a detached copy and leave its target (the lock other checkouts read) unpatched, so it refuses before writing anything — the same code and message as the hosted guard. Exit 1; the link, its target and `.socket/vendor/state.json` are left as they were, the artifact dirs the run added under `.socket/vendor/` are removed (any that cannot be are named in the message, with `vendor --revert` as the remedy), and each package the run vendored is a `failed` event with this code, not `applied` (`summary.applied` does not count it); the `skipped` advisories the run recorded for it (`vendor_prebuilt_downloaded`, `vendor_artifact_reused`, …) are dropped, since they describe the vendoring that was refused. The human run prints no "Vendored N packages" count for them and no "Next steps:" (#898). Backends that check their own targets first (bun.lockb, Hatch, uv, Poetry, Pipenv, requirements, Cargo) keep their own codes. |
12401240
| `vendor_would_refuse_symlinked_file` | `skipped` (advisory event) under `vendor --dry-run`; a `warnings: [{code, detail}]` entry on the `would_vendor` / `would_revendor` row of the `vendor` preview under `scan` / `get --mode vendored --dry-run` (human: an `[warning] <purl> (<code>): <detail>` line) | dry run (#627): a dry run captures no writes, so for each package it would vendor (not one previewed as in sync, whose re-run writes nothing) it names every symlinked file of that package's ecosystem a vendored run may rewrite (the registry's vendored rewrite targets plus `pnpm-workspace.yaml`, `nuget.config`, `packages.lock.json`, the root `pom.xml`, `.mvn/maven.config` and `hatch.toml`; files a vendored run only reads, such as `.yarnrc.yml` or `vlt.json`, are never named); the wet run refuses with `redirect_symlinked_file_unsupported` if it must rewrite one. Does not change the exit code. |
12411241
| `vendor_state_unreadable` | rollback `warnings[]`; remove top-level error | corrupt-ledger containment (v5.0). Rollback: an unreadable vendor ledger skips the vendored leg + manifest cleanup + GC and drives `partial_failure` exit 1 while the agent and hosted legs still run. Remove: a hard top-level error before any mutation. Also the Bun vendored preflight's refusal code: `get` / `scan --mode vendored`, `vendor`'s pre-takeover check and the `--dry-run` `would_refuse` preview report an unreadable `.socket/vendor/state.json` as itself (`errorCode` in `patches[]` / `download.patches[]`, or `get <uuid>`'s top-level `error.code`), fail-closed — nothing is exempt — instead of a Bun lock code. (v4's `redirect_state_unreadable` is no longer emitted: v5 never reads the redirect ledger on these paths.) |
12421242
| `manifest_write_failed` | rollback `warnings[]` | rollback (v5.0): the post-rollback manifest update could not be written; no entries were removed (`manifest.removedEntries: []`) and the run exits `partial_failure` 1. |
Collapse file

‎crates/socket-patch-cli/src/commands/vendor.rs‎

Copy file name to clipboardExpand all lines: crates/socket-patch-cli/src/commands/vendor.rs
+26-10Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -494,10 +494,7 @@ async fn unwired_check_failure(
494494
/// entry (fail-safe): ecosystems other than npm, cargo and pypi (whose
495495
/// probe covers the requirements flavor only) have no in-use probe yet,
496496
/// and a missing/unreadable lockfile proves nothing.
497-
pub(crate) async fn dispatch_in_use_one(
498-
entry: &VendorEntry,
499-
project_root: &Path,
500-
) -> Option<bool> {
497+
pub(crate) async fn dispatch_in_use_one(entry: &VendorEntry, project_root: &Path) -> Option<bool> {
501498
match entry.ecosystem.as_str() {
502499
"npm" => vendor::npm_flavor::vendored_entry_in_use(entry, project_root).await,
503500
// Cargo probes the lock entry's shape: detached + `[patch]` pointing
@@ -1289,8 +1286,7 @@ async fn run_check(args: &VendorArgs) -> i32 {
12891286
// know (the ledger was ignored or dropped from the commit along with the
12901287
// manifest) leaves every fresh install failing; the manifest keys above
12911288
// cannot see it, so the references are read from the wiring itself.
1292-
let references =
1293-
crate::commands::vendored_backend::repair::scan_vendor_references(root).await;
1289+
let references = crate::commands::vendored_backend::repair::scan_vendor_references(root).await;
12941290
for (eco, uuid, rel) in references {
12951291
let ledgered = state
12961292
.entries
@@ -2770,8 +2766,9 @@ pub(crate) async fn vendor_records_reusing(
27702766
let mut status = StatusLine::stderr(common.json, common.silent);
27712767
let total = all_packages.len();
27722768
// The vendored artifact dirs before this run wrote any: a commit the
2773-
// symlink check refuses removes the ones the loop added, so the refusal
2774-
// leaves no orphan artifact behind (#898). A dir the pre-run ledger
2769+
// symlink check refuses (or that fails with nothing written) removes
2770+
// the ones the loop added, so it leaves no orphan artifact behind
2771+
// (#898). A dir the pre-run ledger
27752772
// already names (an artifact redownloaded in place) is kept: it needs
27762773
// no commit to be referenced.
27772774
let vendor_dirs_before = (!common.dry_run).then(|| VendorDirsBefore {
@@ -3544,11 +3541,30 @@ pub(crate) async fn vendor_records_reusing(
35443541
this project finishes it"
35453542
)
35463543
} else {
3547-
format!(
3544+
// Nothing was committed: like the symlink refusal
3545+
// (#898), the artifacts the loop downloaded are removed
3546+
// and its packages are reported failed, not applied.
3547+
let mut detail = format!(
35483548
"could not commit the vendored lockfile, manifest and ledger edits: \
35493549
{e}; the project's lockfiles and .socket/vendor/state.json are \
35503550
unchanged"
3551-
)
3551+
);
3552+
let leftovers =
3553+
remove_new_vendor_dirs(common, vendor_dirs_before.as_ref()).await;
3554+
if !leftovers.is_empty() {
3555+
detail = format!(
3556+
"{detail} (except the downloaded artifacts that could not be \
3557+
removed: {}; `socket-patch vendor --revert` removes them)",
3558+
leftovers.join("; ")
3559+
);
3560+
}
3561+
env.retract_applied(
3562+
events_start,
3563+
PatchAction::Failed,
3564+
"vendor_commit_failed",
3565+
&detail,
3566+
);
3567+
detail
35523568
};
35533569
if !common.json {
35543570
eprintln!("Error: {detail}");

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.