Repository navigation
Commit 9b7b5c2
authored
feat(scan): unified auto-update engine — --sync, --prune, --dry-run (v3.0) (#79)
* feat(scan): add --apply + structured updates for auto-update bot workflows
Enables `socket-patch scan` as the engine for an automated "update all
patches" workflow — a cron job or PR check that runs scan, detects new
or updated patches against the local manifest, applies them, and either
commits the change or opens a PR. Today this isn't quite possible because:
* `scan --json` is read-only — it prints the discovery JSON and exits
before the apply path runs, so there's no clean way to make it
mutate the manifest from a bot.
* Updates aren't reported in JSON — update detection (existing
manifest entry with same PURL but different UUID) only runs in the
non-JSON table-print path, so a `--json` consumer can't tell which
patches would be updates vs net-new additions.
* Per-patch JSON records lose the added-vs-updated distinction — every
successful download is reported as `action: "added"` even when it's
replacing an existing entry with a newer UUID.
Three additive (semver-MINOR) changes resolve all of the above:
1. `commands/get.rs` — `download_and_apply_patches` now emits per-patch
`{action: "updated", oldUuid}` when the PURL already had a different
UUID before insert. A new pure helper `decide_patch_action(manifest,
purl, new_uuid)` returns `Added | Updated{old_uuid} | Skipped` and is
unit-tested independently.
2. `commands/scan.rs` — new `--apply` flag (default `false`) opts JSON
callers into the full discover → select → apply pipeline. Without
`--apply`, `scan --json` keeps its prior read-only contract; with it,
`scan --json --apply` runs the same selection + download path the
non-JSON branch uses and emits one combined JSON object with an
`apply` sub-object reporting per-patch outcomes. The JSON discovery
emission also now always includes a top-level `updates` array (with
`purl`, `oldUuid`, `newUuid`) computed via a new pure helper
`detect_updates`. `severity_order` is exposed as `pub(crate)` so it
can be unit-tested.
3. `CLI_CONTRACT.md` documents the new `--apply` flag, the full
`scan` discovery and `--apply` JSON shapes, and pins the per-patch
action vocabulary (`added`/`updated`/`skipped`/`failed`) with semver
policy clauses for adding (MINOR) or renaming/removing (MAJOR) values.
## Tests
* scan.rs inline #[cfg(test)] mod tests — 4 severity_order cases +
8 detect_updates cases covering: no manifest, empty packages, no
overlap, same UUID, different UUID, multiple updates, empty patch
list, first-patch candidate selection.
* get.rs inline test module — 4 decide_patch_action cases covering
Added (no existing entry), Skipped (same UUID), Updated (different
UUID with oldUuid populated), and Added-for-different-PURL (keying
on PURL not UUID).
* tests/cli_parse_scan.rs — `--apply` parser tests (defaults false,
long form, combines with --json/--yes) + a subprocess JSON-shape
test that runs the compiled binary against an empty tempdir and
asserts the new `updates: []` key is present in stdout.
All 416 lib tests pass, all integration tests pass, clippy clean.
## How a bot uses this
```bash
socket-patch scan --json --apply --yes > scan-result.json
jq '.apply.patches[] | select(.action == "updated") | {purl, oldUuid, uuid}' scan-result.json
# Pipe into peter-evans/create-pull-request with a PR body summarizing the diff.
```
Exit code: 0 on full success (every selected patch added/updated/skipped),
1 if any `failed` records are present (and top-level `status` becomes
`"partial_failure"`).
Assisted-by: Claude Code:claude-opus-4-7
* feat(scan)!: garbage-collect on scan + hide gc subcommand (v3.0)
After PR #79's --apply work, scan applied patches but didn't reconcile
state. Orphan blob files accumulated and manifest entries for
uninstalled packages stayed forever, forcing bots to chain `scan --apply`
with `repair` themselves. This commit makes scan the single command
needed for the auto-update workflow:
* Default GC after every scan run that has scanned packages. Removes
manifest entries for PURLs no longer in the crawl results, then
sweeps orphan blob/diff/package-archive files via the existing
cleanup_unused_blobs / cleanup_unused_archives helpers.
* New --no-prune flag opts OUT of GC entirely. Useful when a missing
package reflects a temporary uninstall the user wants to preserve.
* The `gc` subcommand alias (and `repair` itself) is hidden from
socket-patch --help. `socket-patch gc` still parses for backwards
compat, just no longer listed. Existing scripts unaffected.
* Workspace version bumped 2.1.4 → 3.0.0. scripts/version-sync.sh
propagated the bump to every npm/socket-patch-* package.json and to
pypi/socket-patch/pyproject.toml.
## JSON output additions
In `scan --json` (read-only): new `gc` sub-object reports what *would*
be pruned/reaped without mutating anything (preview mode). Fields:
prunableManifestEntries, orphanBlobs, orphanDiffArchives,
orphanPackageArchives, bytesReclaimable.
In `scan --json --apply`: `gc` switches to mutation mode. Fields:
prunedManifestEntries, removedBlobs, removedDiffArchives,
removedPackageArchives, bytesFreed.
With --no-prune: gc is emitted as { "skipped": true } in both modes.
In the empty-crawl case (no packages found at all), gc is { "skipped":
true } — pruning every manifest entry on the assumption the user
"uninstalled everything" is too destructive.
## Tests
* 5 new detect_prunable unit tests covering empty manifest, all
present, missing entries, and full prune.
* --no-prune parser tests in tests/cli_parse_scan.rs (default false,
long form, combines with --apply/--json/--yes).
* 4 new tests in tests/cli_parse_repair.rs locking the v3.0
deprecation: top-level --help doesn't list `repair` or `[aliases:
gc]`, but `socket-patch gc` still resolves to Repair and
`socket-patch repair --help` still works directly.
* CleanupResult gains #[derive(Default)] so scan can build empty
summaries when the cleanup helpers report errors.
cargo build/clippy/test --workspace --all-features all clean.
100 lib tests in CLI (+5), 19 in cli_parse_repair (+4), 26 in
cli_parse_scan (+2). 416 lib tests in core unchanged.
## Breaking changes (MAJOR bump 2.1.4 → 3.0.0)
* scan --apply prunes manifest entries for uninstalled packages by
default. Scripts that ran `scan --apply --yes` and relied on
manifest entries surviving across an uninstall break unless they
add --no-prune.
* scan --apply removes orphan blob/archive files on every run
(non-breaking in practice — the apply path simply re-fetches
anything it needs — but a visible filesystem change).
* `socket-patch gc` no longer appears in top-level --help. The
subcommand still works.
Assisted-by: Claude Code:claude-opus-4-7
* test(scan): add e2e_scan test suite + CI matrix entry
End-to-end tests for the scan + GC pipeline that uses the real Socket
API. Mirrors the structure of tests/e2e_npm.rs — every test is
#[ignore] so it only runs with --ignored, matching the existing e2e
gating in .github/workflows/ci.yml. Uses the minimist@1.2.2 patch
fixture (CVE-2021-44906) that the other e2e tests already share.
## Coverage (9 scenarios)
* test_scan_apply_json_adds_new_patch — fresh install,
`scan --json --apply --yes` reports action: "added" and patches
the file on disk.
* test_scan_apply_json_skips_existing — re-run shows action:
"skipped".
* test_scan_apply_json_updates_existing — seed manifest with a fake
UUID, re-run shows action: "updated" with oldUuid populated.
* test_scan_json_read_only_emits_updates_array — read-only mode
surfaces the manifest-vs-API drift in the `updates` array.
* test_scan_json_read_only_no_mutation — `scan --json` never
creates a manifest or modifies files.
* test_scan_apply_prunes_uninstalled_package_by_default — uninstall
minimist, re-scan, manifest entry is gone + blobs are reaped.
* test_scan_apply_no_prune_keeps_uninstalled_entries — same
scenario with --no-prune leaves manifest + blobs intact, gc reports
{ skipped: true }.
* test_scan_apply_cleans_orphan_blobs — plant a stray orphan blob,
next scan run removes it and reports gc.removedBlobs >= 1.
* test_scan_json_read_only_gc_preview — preview mode lists
prunableManifestEntries and counts orphanBlobs without mutating.
## CI integration
* Added e2e_scan to the e2e job matrix on ubuntu-latest and
macos-latest (mirrors how e2e_npm is matrixed).
* Setup Node.js step's `if:` predicate extended to also run for
e2e_scan — the suite shells out to `npm install` for fixture setup.
Each #[ignore] test self-skips with a SKIP message if `npm` is not
on PATH, so a future runner without npm doesn't fail spuriously.
Assisted-by: Claude Code:claude-opus-4-7
* docs(scan): document v3.0 GC behavior + repair deprecation
CLI_CONTRACT.md changes:
* Add --no-prune row to the scan flag table with a description of
the v3.0 GC default.
* Extend the scan JSON output shape with the new `gc` sub-object.
Document the split between preview-mode field names
(prunable*/orphan*/bytesReclaimable) and apply-mode field names
(pruned*/removed*/bytesFreed). Document that --no-prune emits
gc: { skipped: true } in both modes.
* Mark `repair` as "(deprecated since v3.0)" at the section heading.
Spell out the demotion: `hide = true` on the Repair variant and
`alias = "gc"` (was `visible_alias`). Removing repair or
unhiding it would be a MAJOR bump.
* Add semver-policy row: "Change `scan`'s default behavior (e.g.
pruning, GC, apply) — MAJOR." Notes the v3.0 flip is the one
grandfathered instance; future flips also MAJOR.
README.md changes:
* Remove the `repair`/`gc` section from the public command list (still
documented in CLI_CONTRACT.md for advanced users).
* Expand the `scan` section: add --apply and --no-prune flags,
-y/--yes, --download-mode rows. New "Bot mode" example with
`scan --json --apply --yes`. Add "Apply without pruning" example.
Brief note about scan being the single command for the
auto-update workflow.
Assisted-by: Claude Code:claude-opus-4-7
* fix(scan): auto-select in JSON --apply when multiple free patches exist
The free-tier patch API may serve multiple free patches for the same
PURL (e.g., minimist@1.2.2 currently has 2 free patches). The
`scan --json --apply` path was calling `select_patches(... is_json =
true)` which returns `Err(JsonModeNeedsExplicit)` with
`status: "selection_required"` in that scenario — no forward progress,
the bot can't apply anything.
For scan-driven workflows there's no "specify --id" option (we're
scanning the whole project), so the right behavior is to auto-select
the newest patch and continue. Pass `is_json = false` so the
non-TTY branch inside `select_one` auto-selects index 0 — which is the
most-recently-published patch (the group is sorted by `published_at`
descending before `select_one` runs).
Also relaxed the e2e_scan test assertions so they don't pin a specific
upstream UUID/hash:
* added/updated/skipped tests assert action vocabulary, PURL match,
and "file was patched" (not exact AFTER_HASH).
* updated test asserts the new UUID differs from the seeded oldUuid
rather than matching a hardcoded constant.
* read-only updates test similarly asserts `newUuid != oldUuid`.
These changes make the e2e suite robust to API churn — the contract
is "an apply happened", not "this specific patch was selected".
Assisted-by: Claude Code:claude-opus-4-7
* fix(scan): run apply-mode GC even when no packages have patches
The post-uninstall scenario hit an edge case: after a user uninstalls
the only patched package and installs a new (unpatched) one, the next
`scan --apply --yes` would crawl successfully, find no packages with
patches, and skip the entire `--apply` block. The read-only preview GC
ran instead, emitting `gc.prunableManifestEntries` (preview field name)
but never actually pruning anything from the manifest.
A bot relying on `scan --apply` to reach a clean state would loop
forever — the stale manifest entry never gets removed.
Fix: when `--apply` is set but no packages have patches, still run the
mutating GC pass and emit an empty `apply` sub-object plus the
`gc.prunedManifestEntries` (apply field name). Bots can now trust
`scan --apply --yes` to converge to a clean state in one pass even when
the crawl has no patched packages.
Also dropped the now-unused `NPM_UUID` and `AFTER_HASH` constants from
the e2e_scan test file (warning noise from relaxing the assertions in
the previous commit).
Assisted-by: Claude Code:claude-opus-4-7
* feat(scan): pivot GC to opt-in via --prune + add --sync and --dry-run
Reverses the v3.0 GC-by-default decision. After feedback, default-on
GC was too aggressive: scripts running `scan --apply --yes` against a
project with a temporarily-uninstalled package would silently destroy
the manifest entry, breaking dev workflows where the package gets
reinstalled later.
The new opt-in model:
* `--prune` (new, default false) opts into garbage collection.
Manifest entries for packages no longer in the crawl are removed,
then `cleanup_unused_blobs` + `cleanup_unused_archives` sweep
orphan files. Without `--prune`, scan leaves `.socket/` alone.
* `--sync` (new) is sugar for `--apply --prune`. The canonical bot
invocation becomes `scan --json --sync --yes` (3 flags; `--json`
and `--yes` are workflow scaffolding).
* `--dry-run` / `-d` (new) previews what `--apply`/`--prune`/`--sync`
would do without mutating disk. The `apply.patches[*]` array is
populated via `decide_patch_action`, and `gc.prunable*` /
`gc.orphan*` field names are emitted (instead of `pruned*` /
`removed*`). The `apply.dryRun: true` flag explicitly marks the
output for bots that need a single signal.
* `--no-prune` field removed (it was the inverse of the now-default
behavior).
## Implementation
* `ScanArgs.no_prune` → `ScanArgs.prune` (semantics inverted). New
`sync` and `dry_run` fields.
* At the top of `scan::run`, `let apply = args.apply || args.sync;`
and `let prune = args.prune || args.sync;` — derive once, use
everywhere downstream. `--sync` is purely additive sugar.
* `run_apply_gc` no longer takes a `no_prune: bool` parameter —
callers always gate on `prune` before calling it. When GC isn't
requested, the `gc` JSON field is omitted entirely (no
`{ "skipped": true }` placeholder).
* New `preview_apply_gc` helper for the dry-run path. Runs
`cleanup_unused_blobs` / `cleanup_unused_archives` with
`dry_run=true` and emits preview field names via
`GcSummary::to_preview_json`.
* Dry-run apply path synthesizes per-patch `apply.patches[]`
records via `super::get::decide_patch_action` against the on-disk
manifest — accurately reports added/updated/skipped for the
selected patches without actually calling
`download_and_apply_patches`.
* Empty-cwd JSON branch drops `gc: { skipped: true }` (no `gc`
field at all when GC wasn't requested).
Drive-by fix: `tests/ecosystem_dispatch::partition_purls_allow_list_excludes_one`
now uses `!map.contains_key(&Ecosystem::Pypi)` instead of the
`unnecessary_get_then_check` lint trigger.
Assisted-by: Claude Code:claude-opus-4-7
* revert(repair): restore gc as a documented subcommand
The prior v3.0 iteration demoted `Commands::Repair`'s `gc` alias to a
hidden `alias = "gc"` and added `hide = true` on the subcommand itself,
banking on `scan` becoming the all-in-one command for both apply and
GC. With GC now opt-in via `--prune`/`--sync` (see prior commit),
`repair`/`gc` is the right answer for users who want to clean up
without an apply pass.
Restore `#[command(visible_alias = "gc")]` and drop `hide = true` so
the subcommand appears in `socket-patch --help` again with its
`[aliases: gc]` hint.
Update the four hidden-help tests in `tests/cli_parse_repair.rs`:
* `repair_is_hidden_from_top_level_help` →
`repair_appears_in_top_level_help` (assertion inverted).
* `gc_alias_is_hidden_from_top_level_help` →
`gc_alias_is_visible_in_top_level_help` (assertion inverted).
* `gc_alias_still_parses_for_backwards_compat` →
`gc_alias_parses_as_repair` (simplified — alias is no longer
deprecated, so the "backwards compat" framing is gone).
* `repair_subcommand_help_still_works_directly` dropped (was a
deprecation-era assertion).
These tests now lock the *opposite* contract: removing or hiding the
`gc` alias is a MAJOR bump.
Assisted-by: Claude Code:claude-opus-4-7
* test(scan): update parser + e2e tests for opt-in GC
cli_parse_scan.rs:
* defaults_match_contract now asserts !args.prune, !args.sync,
!args.dry_run (replacing the old !args.no_prune line).
* no_prune_flag_long_form → prune_flag_long_form; assertion
inverted (passing --prune sets prune=true).
* no_prune_combines_with_apply_and_json → prune_combines_with_apply_and_json.
* NEW: sync_flag_long_form — --sync sets sync=true; does NOT
auto-derive --apply/--prune at parse time (that derivation
happens inside scan::run).
* NEW: sync_combines_with_json_and_yes.
* NEW: dry_run_long_form (--dry-run sets dry_run=true).
* NEW: dry_run_short_form (-d sets dry_run=true).
e2e_scan.rs:
* Module docstring updated to describe opt-in GC.
* test_scan_apply_prunes_uninstalled_package_by_default →
test_scan_apply_prune_prunes_uninstalled_package — now passes
--prune explicitly.
* test_scan_apply_no_prune_keeps_uninstalled_entries →
test_scan_apply_default_keeps_uninstalled_entries — drops the
--no-prune flag (it no longer exists); asserts the gc field is
omitted entirely.
* test_scan_apply_cleans_orphan_blobs →
test_scan_apply_prune_cleans_orphan_blobs — passes --prune.
* test_scan_json_read_only_gc_preview split into:
- test_scan_dry_run_sync_previews_apply_and_gc — exercises the
new --dry-run flag combined with --sync; verifies preview
output is populated AND nothing on disk changed.
- test_scan_json_no_gc_field_without_prune — locks the contract
that `gc` is omitted when --prune isn't set.
* NEW: test_scan_sync_yes_full_lifecycle — installs minimist, runs
--sync (adds patch), uninstalls + plants orphan, runs --sync again
(prunes + sweeps). End-to-end exercise of the canonical bot mode.
Total e2e_scan scenarios: 11 (was 9).
Assisted-by: Claude Code:claude-opus-4-7
* docs(scan): document --prune/--sync/--dry-run + un-deprecate gc
CLI_CONTRACT.md:
* Scan flag table: replace --no-prune row with three new rows —
--prune, --sync, -d/--dry-run. Add a paragraph explaining each
plus the canonical bot-mode invocation.
* JSON output shape: drop the --no-prune-emits-{skipped:true} note.
Clarify that `gc` is omitted ENTIRELY when --prune/--sync isn't
set. Document --dry-run behavior including the explicit
`apply.dryRun: true` marker for bots.
* New "scan — --sync (bot mode)" section with the canonical
`scan --json --sync --yes | jq '{applied, pruned, bytes_freed}'`
recipe.
* New "scan — --dry-run" section explaining that --dry-run is a
no-op without one of the mutating flags.
* Restore the `repair` section's normal heading (drop the
"*(deprecated since v3.0)*" suffix and the deprecation paragraph).
Note that the `gc` visible_alias is now contract-guarded.
* Semver-policy table: drop the GC-default row, add explicit rows
for "flipping --prune to opt-out" and "demoting `gc` from
visible_alias" — both MAJOR.
README.md:
* Restore the `### repair` / `gc` section that was removed during
the deprecation iteration. Wording clarifies that `repair`/`gc` is
the right answer for cleanup-without-apply and points users at
`scan --sync` for the combined workflow.
* `### scan` section: replace --no-prune row with --prune, --sync,
--dry-run, --yes. Bot-mode example becomes `scan --json --sync --yes`
(the user's "one or two flags" target). Add a `scan --json --sync
--yes --dry-run` example.
* `## Scripting & CI/CD`: lead with the new `--sync` recipe piped
through jq into `peter-evans/create-pull-request`. Keep the old
`scan --json --ecosystems npm` read-only example as the second
use case.
Assisted-by: Claude Code:claude-opus-4-7
* chore(hardening): pin toolchain, deps, actions, and install bootstrap
- rust-toolchain.toml: pin channel to exact version with components
- Cargo.toml: exact-pin all workspace dependencies via =X.Y.Z spec
- crates/{cli,core}/Cargo.toml: wire dev-deps through workspace pins
- npm/socket-patch/package.json: exact-pin runtime + dev deps; commit
package-lock.json so downstream installs are deterministic
- scripts/install.sh: download SHA256SUMS, verify tarball digest
before extraction; accept SOCKET_PATCH_VERSION env override
- scripts/version-sync.sh: preserve the leading = on exact-pin specs;
refresh the npm lockfile on every version bump
- .github/workflows/release.yml: SHA-pin actions, pin npm@version,
pin language toolchain versions for setup-* actions
- .github/workflows/pin-check.yml: new fail-closed workflow that
greps every uses: line and rejects non-SHA-pinned action refs
* feat(cli)!: non-mutating apply + unified JSON envelope (v3.0)
Two related changes that together complete the v3.0 contract:
* apply no longer writes to .socket/. When the manifest is missing
blobs in offline mode, apply bails with a partial_failure envelope.
When online and missing blobs need fetching, the bytes go to an OS
tempdir overlay for the duration of the run; .socket/ stays read-
only. Garbage collection moves out of apply entirely (now lives in
scan --prune / repair / gc).
* New crates/socket-patch-cli/src/json_envelope.rs defines a shared
Envelope/PatchEvent/Status/Summary shape that every --json
invocation now emits. The action vocabulary (added/updated/skipped/
applied/downloaded/removed/failed/verified) is the single contract
downstream consumers route on. CLI_CONTRACT.md is updated with the
unified shape + jq recipes.
Migrated commands: apply, list, repair, remove. (scan, get, rollback,
setup retain their pre-v3.0 shapes for now and are documented as
pending in CLI_CONTRACT.md.)
Also removes three dead-code items the audit confirmed have zero
callers:
- crates/socket-patch-core/src/utils/enumerate.rs (whole module)
- crates/socket-patch-core/src/utils/global_packages.rs (whole
module; npm crawler ships the live copy)
- path_to_group_id() in maven_crawler (test-only inverse helper)
- false-positive allow(dead_code) on get.rs::DownloadParams
BREAKING: every migrated subcommand's --json output is reshaped to the
new envelope (camelCase status, events array, summary block).
* test: comprehensive in-process + subprocess test suite
Adds ~120 new tests across the apply/scan/get/list/remove/repair/
rollback/setup CLI commands. Tests drive socket-patch in-process
(commands::*::run) and via subprocess against wiremock-backed API
fixtures, asserting on disk state, JSON envelope shape, and exit
codes. Includes:
* apply: invariants test (no .socket/ mutation), network tests with
wiremock, edge cases (read-only files, nested dirs, multi-file,
hash mismatch, idempotent re-apply, missing files, force overrides)
* scan: invariants, sync end-to-end, dry-run preview, --apply +
--prune combinations
* get: identifier-type detection (UUID/CVE/GHSA/PURL/package),
--save-only path, paid_required path, error paths, edge cases
* repair: download-mode variants (file/diff/package), offline mode,
blob cleanup verification
* remove: PURL + UUID identifiers, rollback chain, blob cleanup
* rollback: real bytes restore for all 8 ecosystems via handcrafted
fixtures + real installer paths
* setup: package.json detection, pnpm monorepo handling, dry-run
* PTY-driven interactive prompt tests (portable-pty)
* Alternate installer configs: yarn, pnpm, npm workspaces, bundler
* Python venv variants: 3.11/3.12/3.13, .env/venv/.venv layouts,
VIRTUAL_ENV override, canonical name normalization, egg-info legacy
Real package managers are used where available on host (npm, pip,
gem, cargo); ecosystems without host toolchains (go/maven/composer/
nuget) use handcrafted fixtures that exactly mirror what their
native installers produce on disk.
* ci: add coverage job, language version pins, and e2e-docker matrix
* New 'coverage' job: cargo-llvm-cov (LLVM source-based instrumentation
via taiki-e/install-action), uploads lcov.info as a workflow artifact
and prints the summary to the GitHub Actions job summary. Report-only
(no --fail-under threshold) so contributors get visibility without
flaky CI when coverage shifts.
* Language toolchain pins on every setup-* action invocation: Node
20.20.2, Python 3.12.13, Ruby 3.2.11. dtolnay/rust-toolchain now
reads from rust-toolchain.toml (the toolchain: stable input is
dropped from every step).
* New 'e2e-docker' matrix: ubuntu-latest x { npm, pypi, gem, cargo,
golang, maven, composer, nuget }. Each slot builds the shared base
image and the per-ecosystem layer via docker/build-push-action with
scope-cached layers (type=gha,scope=test-<eco>), then runs the
corresponding 'cargo test --features docker-e2e --test
docker_e2e_<eco>'. Triggered on every PR. The existing 'e2e' job
(real Socket API, --ignored) stays for nightly/manual real-API
smoke runs.
* test(docker-e2e): infrastructure + npm full install→apply chain
Adds the Docker-driven e2e test infrastructure:
* tests/docker/Dockerfile.base: multi-stage build (rust:1.93-slim-
bookworm builder → debian:12-slim runtime + compiled socket-patch).
Base layer shared by every ecosystem image. Both base images pinned
by sha256 digest.
* tests/docker/Dockerfile.npm: FROM base + Node 20 LTS via NodeSource.
* tests/docker/README.md: how to build images locally, run tests with
Docker or with SOCKET_PATCH_TEST_HOST=1 host mode, and how to add a
new ecosystem.
* tests/docker/fixtures/npm/README.md: documents the synthetic fixture
approach (--force apply against any installed bytes).
* docker_e2e_npm.rs: real 'npm install minimist@1.2.2' inside the
container, wiremock served patch, scan --sync writes manifest +
blob, apply --force overwrites the on-disk file, then grep-verifies
SOCKET-PATCH-E2E-MARKER in node_modules/minimist/index.js. Hermetic
(no Socket API contact); reproducible in CI.
This is the working template every other ecosystem extends.
* test(e2e): full install→apply chain for pypi in Docker (+ global)
Upgrades docker_e2e_pypi.rs from scan-discovery-only to the full
chain, twice: once for local (venv) install and once for global
(pip --break-system-packages).
* Switches the fixture package from pydantic-ai (heavy transitive
deps, ~60s install) to six 1.16.0 (single-file, ~1s install).
* pypi's file-path convention has NO `package/` prefix — the
python crawler returns site-packages root as pkg_path, so the
patch's file path is just `six.py` (lands at site-packages/six.py).
* `pypi_local_install_full_apply_chain`: venv install at .venv/lib/
python3.X/site-packages/six.py, scan --sync writes manifest +
blob, apply --force --offline overwrites the file. Grep verifies
SOCKET-PATCH-E2E-MARKER on disk.
* `pypi_global_install_full_apply_chain`: pip install --break-
system-packages installs into Debian's system Python site-
packages. scan + apply with --global. Same marker verification at
the system-site-packages path discovered via `python3 -c "import
six; print(six.__file__)"`.
The Dockerfile.pypi already has python3 + pip + venv from prior
infrastructure work; no Dockerfile change.
* test(e2e): full install→apply chain for gem in Docker (+ global)
Two tests for the Ruby ecosystem:
* gem_local_install_full_apply_chain: `gem install --install-dir
vendor/bundle/ruby/<ver> colorize -v 1.1.0` produces the bundle-
style layout that the Ruby crawler scans in local mode. scan --sync
+ apply --force overwrites lib/colorize.rb with the synthetic
patched content; marker verified on disk.
* gem_global_install_full_apply_chain: plain `gem install colorize
-v 1.1.0` (no --install-dir) installs to `$(gem env gemdir)`. The
--global flag drives the Ruby crawler to scan the system gem dir.
Same marker check at the discovered path.
gem patches use the `package/<rel>` convention; apply strips the
`package/` prefix and joins with the gem's directory.
Dockerfile.gem is unchanged from prior infrastructure work.
* test(e2e): full install→apply chain for cargo in Docker
`cargo fetch` against a minimal project with `cfg-if = "=1.0.0"`
populates `\$CARGO_HOME/registry/src/<index>/cfg-if-1.0.0/`. scan
--sync writes the manifest + blob, apply --force --offline overwrites
the registry-source `src/lib.rs` with patched bytes containing
SOCKET-PATCH-E2E-MARKER. grep verifies on disk.
Pre-chmods the registry source file to writable — cargo's source
files are read-only by default and apply's own fix-permissions code
covers the same path, but the chmod up-front keeps the test robust
against changes there.
Single test (no global variant): cargo's registry is the only cache,
so local-vs-global is a no-op.
Dockerfile.cargo unchanged from prior infrastructure work; it has
rustup-installed Rust 1.93.1 with CARGO_HOME set.
* test(e2e): full install→apply chain for golang in Docker
`go mod download github.com/gin-gonic/gin@v1.9.1` populates
`\$GOMODCACHE/github.com/gin-gonic/gin@v1.9.1/`. scan --sync writes
the manifest + blob, apply --force --offline overwrites gin.go with
synthetic patched bytes containing SOCKET-PATCH-E2E-MARKER. grep
verifies on disk.
Pre-chmods the cache file to writable — `go mod download` extracts
to read-only files, similar to cargo registry.
Single test (no global variant): golang's module cache is the only
cache; --global is a no-op.
Dockerfile.golang ships Go 1.21.13 from the official tarball; GOPATH
and GOMODCACHE are set at image build time.
* test(e2e): full install→apply chain for maven in Docker
Upgrades docker_e2e_maven.rs from scan-only to the full chain.
`mvn dependency:get -Dartifact=org.apache.commons:commons-lang3:3.12.0`
downloads the artifact into ~/.m2/repository, the wiremock fixture
overwrites the .pom file with synthetic patched bytes, and the test
grep-verifies SOCKET-PATCH-E2E-MARKER on disk. Single test
(local-only) since ~/.m2 is always global.
* test(e2e): full install→apply chain for composer in Docker
Upgrades docker_e2e_composer.rs to the full chain plus a global
variant. Real `composer require monolog/monolog:3.5.0` installs into
vendor/monolog/monolog/, the wiremock fixture overwrites
src/Monolog/Logger.php with synthetic patched bytes, and the test
grep-verifies SOCKET-PATCH-E2E-MARKER on disk.
Adds composer_global_install_full_apply_chain: `composer global
require` installs to $COMPOSER_HOME/vendor, socket-patch scans +
applies with --global, marker verified there.
* test(e2e): full install→apply chain for nuget in Docker
Upgrades docker_e2e_nuget.rs to the full chain plus a global variant.
The local test redirects `dotnet add package` to a project-local
./packages dir via NUGET_PACKAGES, then scan + apply patch the
package's LICENSE.md with a synthetic blob; the global test uses the
default ~/.nuget/packages and --global mode.
Note: the wiremock fixture uses the lowercased package name in the
PURL ("newtonsoft.json") so scan's GC pass (--sync = --apply --prune)
doesn't prune the freshly-saved manifest entry — the crawler reports
installed packages by their lowercased directory name and GC keys
against that.
* test(e2e): add npm global install variant in Docker
Adds npm_global_install_full_apply_chain alongside the existing local
install/apply/rollback test. The variant runs `npm install -g`,
locates the file at $(npm root -g)/minimist/index.js, then runs scan
+ apply with --global and grep-verifies SOCKET-PATCH-E2E-MARKER.
Host-mode skips the global variant (no safe host npm prefix to
mutate); Docker is the canonical run path.
* test(e2e): add composer/maven/nuget Dockerfiles
Adds the three Dockerfile recipes the docker_e2e_{composer,maven,nuget}
tests panic-message instruct users to build.
- Dockerfile.composer: base + PHP 8 + Composer 2
- Dockerfile.maven: base + default-jdk-headless + maven
- Dockerfile.nuget: mcr.microsoft.com/dotnet/sdk:8.0 (sdk image)
with socket-patch COPY'd in from the base
* ci(coverage): include docker-e2e in the coverage map
The host `coverage` job ran with `--all-features`, which enabled the
docker-e2e feature, but the job never built the per-ecosystem Docker
images — every docker_e2e_<eco> test would panic on `assert_image`
and the job failed (or, if it ever passed, only the surviving
in-process tests contributed). The Docker tests exercise the real
socket-patch binary inside a Linux container, and that subprocess's
coverage wasn't captured at all.
Changes:
* Each `docker_e2e_<eco>.rs` now reads SOCKET_PATCH_COV_BIN +
SOCKET_PATCH_COV_PROFRAW_DIR. When both are set, the docker run
mounts an llvm-cov-instrumented socket-patch binary over the
image's baked-in /usr/local/bin/socket-patch and points
LLVM_PROFILE_FILE into a host-visible volume. Empty Vec when
unset → tests behave exactly as before for local dev and the
existing e2e-docker matrix.
* `coverage` job: drops `--all-features` for an explicit feature
list (cargo,golang,maven,composer,nuget) that excludes
docker-e2e. Produces `coverage-host.lcov`.
* New `coverage-docker` matrix job: per ecosystem, builds the base
+ ecosystem Docker images, eval-sources `cargo llvm-cov show-env`
to build an instrumented `target/debug/socket-patch`, sets the
SOCKET_PATCH_COV_* hooks, runs `cargo llvm-cov --no-report --test
docker_e2e_<eco>`, and emits a per-ecosystem lcov artifact.
* New `coverage-merge` job: gathers `coverage-host` + all 8
`coverage-docker-*` artifacts and unions them via `lcov
--add-tracefile` into a single `coverage-lcov` artifact. Same
artifact name as before so downstream consumers keep working.
Result: lines hit by ANY test (host in-process, host harness, or
in-container binary execution) show up in the final coverage map.
* ci: remove cargo cache from docker-image-building jobs
zizmor's cache-poisoning audit (high) flagged the cargo `actions/cache`
steps in `e2e-docker` and `coverage-docker` because both jobs also
invoke `docker/build-push-action`. The risk model: a PR could poison
the cargo cache (target/, ~/.cargo) with a backdoored crate or
compiled object, and a later run on a trusted ref could load the
poisoned cache and produce a compromised binary that gets mounted
into the docker container or baked into the published image.
Drop the cargo cache from both jobs. The Docker buildx `cache-from:
type=gha` remains, so image-layer rebuilds are still fast. Cargo
deps refresh from the registry per run — about a one-minute cost
that's worth it to eliminate the attack surface.
The other jobs (clippy, test, test-release, coverage, e2e) keep
their cargo caches — none of them build Docker images, so the audit
doesn't trigger for them.
* ci: provide explicit toolchain input to dtolnay/rust-toolchain
The action requires a `toolchain` input — when SHA-pinned (which is
our policy), the action can't infer the channel from action_ref the
way `@stable`/`@1.93.1` ref pins would, so it errors out with
"'toolchain' is a required input".
The original comments ("toolchain version is read from
rust-toolchain.toml") referred to rustup's behavior after install,
not the action's pre-install resolution — the action doesn't read
rust-toolchain.toml itself.
Set `toolchain: "1.93.1"` on every Install Rust step, matching the
channel in rust-toolchain.toml. The duplication is intentional: if
they drift, rustup will reconcile by installing the toolchain.toml
channel on first cargo invocation, just at a small extra cost.
* ci: drop dtolnay/rust-toolchain in favor of inline rustup
Removes the third-party Rust toolchain action and replaces every
"Install Rust" step with `rustup show`. rustup is pre-installed on
GitHub-hosted runners; `rustup show` consumes rust-toolchain.toml,
auto-installs the pinned channel if missing, and applies the listed
components (rustfmt, clippy). For coverage jobs that additionally
need llvm-tools-preview, `rustup component add llvm-tools-preview`
follows the show step.
Benefits:
- One less third-party action to audit and SHA-pin.
- No duplication between rust-toolchain.toml and ci.yml.
- Toolchain bumps are one-file changes (just edit toolchain.toml).
* refactor(cli): unify CLI args + env-var bindings across every subcommand
Define a single `GlobalArgs` clap struct and `#[command(flatten)]` it into
every subcommand's args. Every flag now has a matching `SOCKET_*` env var
binding (precedence: CLI > env > default). Legacy `SOCKET_PATCH_PROXY_URL`,
`SOCKET_PATCH_DEBUG`, `SOCKET_PATCH_TELEMETRY_DISABLED` are still honored at
runtime via a one-shot deprecation warning that fires even under `--silent` /
`--json`.
Behavior changes:
- `--offline` now means strict airgap on every command (was three different
things across apply / repair / rollback). On `repair`, `--offline` and
`--download-only` are mutually exclusive.
- `repair --download-mode` default flipped from `file` to `diff` to match
every other command. Users who need the legacy per-file blob behavior
opt in with `--download-mode file`.
- `apply` and `repair` gain `--api-url` / `--api-token` / `--org` for free
via the flatten (previously only readable via env).
- `--debug` and `--no-telemetry` promoted from env-only toggles to CLI flags.
CLI_CONTRACT.md rewritten around a single global-args table plus a small
per-subcommand section for local flags. New tests: `cli_global_args.rs`
(compose test: every global flag × every subcommand) and
`cli_env_deprecation.rs` (legacy-env warning fires under `--silent` /
`--json`).
Assisted-by: Claude Code:opus-4-7
* docs(changelog): add CHANGELOG.md + CI guard blocking publish without entry
Adds a Keep-a-Changelog-style CHANGELOG.md at the repo root, backfilled
with concise summaries for every published tag (v1.1.0 → v2.1.4) and a
detailed v3.0.0 entry covering the breaking changes in the in-flight v3
release (unified `--offline`, `repair --download-mode` default flip,
`SOCKET_PATCH_*` → `SOCKET_*` env-var renames with one-shot deprecation
warning, shared `GlobalArgs` flatten across every subcommand, etc.).
Wires a new step into the `Release` workflow's `version` job that fails
the workflow when `CHANGELOG.md` lacks an entry for the version in
Cargo.toml. Because every downstream job (tag, build, github-release,
cargo/npm/pypi-publish) transitively depends on `version`, a missing
changelog entry blocks the entire publish pipeline.
Accepts both `## [X.Y.Z]` and `## X.Y.Z` heading styles to keep the
format requirement loose for future contributors.
Assisted-by: Claude Code:opus-4-7
* ci(test): unbreak test/coverage/e2e-docker matrix on feat/scan-apply-json
This commit fixes the pre-existing CI red on the v3.0 branch. Four
unrelated root causes:
1. `cargo test --workspace --all-features` enables the `docker-e2e`
feature, which compiles the 8 `docker_e2e_<eco>.rs` tests on every
`test (ubuntu/macos/windows)` runner. Those tests `assert_image()`
on a docker image that only exists in the dedicated docker-building
jobs, so every test runner failed. Replaced each `assert_image()`
panic with a `skip_if_no_image()` early return that prints a stderr
skip notice. Tests now report `ok` on hosts without docker / images.
`cargo test --workspace --all-features` is green everywhere.
2. The `coverage` job (cargo-llvm-cov, --all-features) failed three
`in_process_remove_repair_lifecycle` tests that set
`SOCKET_API_URL`/`SOCKET_API_TOKEN`/`SOCKET_ORG_SLUG` via
`std::env::set_var` after constructing `RepairArgs` via
`..GlobalArgs::default()`. The refactor's `api_client_overrides()`
was always forwarding the resolved api_url/proxy_url as
`Some(...)`, which short-circuited the env-var fallback inside
`get_api_client_with_overrides`. Made `GlobalArgs::default()` leave
`api_url`/`proxy_url` empty (clap always populates them in
production via `default_value`, so the production path is
unchanged) and `api_client_overrides()` filters empty values to
`None`. The env-var fallback now fires for these tests.
3. `repair_download_only_skips_cleanup` (in `repair_invariants.rs`)
used the shared `run_repair()` helper which injects `--offline`.
v3.0 made `--offline` and `--download-only` mutually exclusive
(exit code 2). Inlined the binary invocation without `--offline`
for this one test — the manifest's referenced blob is already on
disk so the download phase is a no-op even without `--offline`.
4. The `e2e-docker` and `coverage-docker` matrix jobs failed at
"Build <eco> image" with `pull access denied` on
`socket-patch-test-base:latest`. setup-buildx-action defaults to
the `docker-container` driver, which runs BuildKit in a sandboxed
container that cannot see the host docker daemon's image store —
so the per-ecosystem Dockerfile's `FROM socket-patch-test-base:latest`
tries to pull from docker.io and fails. Switched both jobs to
`driver: docker` so buildx talks to the host daemon directly.
Dropped the `type=gha` cache directives (not supported under the
docker driver) — we trade build cache for image visibility.
Local: `cargo test --workspace --all-features` → 965 passed, 0 failed.
Assisted-by: Claude Code:opus-4-7
* ci(coverage-docker): pin to ubuntu-22.04 for glibc compatibility
The coverage-docker matrix builds an instrumented socket-patch binary
on the host and mounts it into the debian:12-slim test container.
ubuntu-latest is currently 24.04 (glibc 2.39); debian:12 ships glibc
2.36. Result: every coverage-docker matrix job failed with
socket-patch: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39'
not found (required by socket-patch)
Pin to ubuntu-22.04 (glibc 2.35) — the highest base that's
forward-compatible with debian:12. e2e-docker is unaffected because it
runs the binary that was baked into the image by the base Dockerfile's
internal builder stage, not a host-mounted one.
Assisted-by: Claude Code:opus-4-7
* test(npm): connect via 127.0.0.1 in infrastructure smoke (Windows fix)
wiremock binds to 0.0.0.0 (the wildcard). Linux and macOS quietly
route a connect to 0.0.0.0 onto the loopback interface, so the test
worked on those runners. Windows refuses the connect with
WSAEADDRNOTAVAIL (winsock error 10049) because 0.0.0.0 is a valid
bind target but not a valid destination address.
Use 127.0.0.1 explicitly for the smoke-check URL — the bound port
from `server.address().port()` is still what we need.
Assisted-by: Claude Code:opus-4-7
* test(pypi): skip in_process_pypi_apply on Windows (Unix venv layout)
`install_six()` hardcodes `venv/bin/pip` and `find_site_packages()`
walks `venv/lib/pythonX.Y/site-packages/`. Both layouts are
Unix-only — on Windows the venv puts pip at `Scripts\pip.exe` and
site-packages at `Lib\site-packages\` (no per-version subdirectory).
Rather than forking the helpers per platform, gate every test in
this file behind `skip_unsupported_platform()` which prints a skip
notice on Windows and returns early. The same code paths get
exercised by the Linux test runner and the docker_e2e_pypi suite,
so coverage isn't lost.
Assisted-by: Claude Code:opus-4-7
* test(pypi): make in_process_pypi_apply helpers platform-aware (Windows)
Reverts the earlier Windows-skip in favor of real Windows coverage.
Three changes to the helpers:
1. find_python() probes `python3` → `python` → `py` (mirrors the
crawler's `find_python_command` in
core/src/crawlers/python_crawler.rs:15). On Windows the canonical
name is `python` (the `py` launcher is also installed); `python3`
is rare. Without this the venv-creation step calls `python3` and
fails on every Windows runner.
2. venv_pip() returns `Scripts\pip.exe` on Windows vs `bin/pip` on
Unix, matching PEP-405's documented venv layout.
3. find_site_packages() branches on cfg!(windows):
* Windows: `<venv>\Lib\site-packages\` — no version subdirectory.
* Unix: glob `<venv>/lib/python3.X/site-packages/` for whatever
interpreter version pip used.
The four in-process tests now exercise the same install→scan→apply
chain on Windows that they already cover on Linux/macOS. The core
crawler is already Windows-aware (python_crawler.rs:182) so the
package-discovery path it tests is real, not synthetic.
Assisted-by: Claude Code:opus-4-7
* test(rollback): use Windows venv layout when cfg!(windows)
rollback_pypi_restores_original_content set up a synthetic
`.venv/lib/python3.11/site-packages/` tree by hand. That's the Unix
layout — on Windows the pypi crawler at
core/src/crawlers/python_crawler.rs:182 looks for
`.venv\Lib\site-packages\`, so on Windows runners the crawler found
nothing and the patched file was never rolled back.
Branch on `cfg!(windows)` when building the path so the synthetic
package sits where the crawler actually probes on each platform.
The crawler logic itself is unchanged.
Assisted-by: Claude Code:opus-4-7
* feat(json): enrich added/updated patch records with description, severity, vuln IDs
When `get` or `scan --apply` adds or updates a patch in the manifest,
the per-patch JSON record now carries the metadata consumers need to
render the patch to a human without a second API round-trip:
```jsonc
{
"purl": "pkg:npm/minimist@1.2.2",
"uuid": "11111111-...",
"action": "added",
"description": "Fixes prototype pollution in minimist",
"license": "MIT",
"tier": "free",
"exportedAt": "2024-01-01T00:00:00Z",
"severity": "high",
"vulnerabilities": [
{
"id": "GHSA-xvch-5gv4-984h",
"cves": ["CVE-2024-12345"],
"severity": "high",
"summary": "Prototype Pollution",
"description": "merge() does not check Object.prototype"
}
]
}
```
Highlights:
- Top-level `severity` is the max across the vulnerabilities array,
using the ordering critical > high > medium=moderate > low.
- `vulnerabilities[]` is sorted by `id` so consumer diffs and test
snapshots don't flap on HashMap iteration order.
- Metadata is intentionally omitted on `action: skipped` (consumer
already has it from the original add) and on `action: failed`.
- `scan --apply` benefits automatically — both flows go through
`download_and_apply_patches`.
Helpers `severity_rank`, `max_vuln_severity`, `patch_event_metadata`
are pub(crate) and unit-tested. CLI_CONTRACT.md gains a new
"`patches[]` entry shape" subsection documenting the schema.
Assisted-by: Claude Code:opus-4-7
* test(e2e): walk the v3.0 envelope for `list --json` output
The e2e (real-registry) suite was asserting on `list["patches"]` —
the pre-v3 ad-hoc shape. v3.0 migrated `list --json` to the unified
envelope, which emits `{command, status, events, summary}` with one
`discovered` event per manifest entry. Patch metadata
(vulnerabilities, tier, license) lives under `details`.
Updated four sites (e2e_npm × 2, e2e_pypi × 1, e2e_gem × 1) to
filter events by `action == "discovered"` and walk
`details.vulnerabilities[]` for CVE assertions.
Closes the `e2e (ubuntu/macos, e2e_npm|e2e_pypi|e2e_gem)` matrix
failures surfaced once the e2e workflow started passing on the v3.0
branch.
Assisted-by: Claude Code:opus-4-7
* ci(e2e_gem): bump pinned ruby from 3.2.11 to 3.2.10
`ruby/setup-ruby` dropped 3.2.11 from its catalog at some point —
the action errors with "Unknown version 3.2.11 for ruby on
ubuntu-24.04" and lists 3.2.10 as the newest 3.2.x available.
3.2.x is API-stable so 3.2.10 is a drop-in replacement.
Assisted-by: Claude Code:opus-4-7
* refactor(cli): drop -d/-m short aliases; loosen version pins
Two unrelated changes in one commit:
1. Drop the `-d` short for `--dry-run` and `-m` short for
`--manifest-path` from `GlobalArgs`. We want those letters free
for future flags. The long forms are unaffected, and a new
`reserved_short_forms_are_not_assigned` compose test locks in
that no subcommand reassigns either letter. Per-subcommand
short-form tests (`*_short`, `manifest_path_short_form`, etc.)
are deleted; the long-form counterparts cover the contract.
2. Loosen `python-version` and `ruby-version` pins in ci.yml from
exact patch (`3.12.13`, `3.2.10`) to minor.x (`3.12.x`, `3.2.x`).
setup-python and setup-ruby's catalogs keep retiring older patch
versions and breaking the workflow — minor.x auto-resolves to
whatever patch is currently available.
CLI_CONTRACT.md updated to remove `-d`/`-m` from the global args
table and the env-var cross-reference.
Assisted-by: Claude Code:opus-4-7
* feat(apply): preserve mode + ownership across patches
`apply_file_patch` now treats target-file permissions as a strict
round-trip:
1. **Existing file**: snapshot mode + uid + gid before writing.
- If read-only, temporarily grant owner-write so the overwrite
succeeds (Go module cache, npm linked symlinks, etc.).
- After writing, restore the *exact* pre-patch mode (idempotent
`set_permissions(from_mode(...))`) and chown back to the
pre-patch uid/gid. `tokio::fs::write` truncates + rewrites the
file in place, so owner usually survives, but pinning
ownership explicitly stops a theoretical race where another
process opens the file between truncate and write.
2. **New file** (created by the patch): chown to inherit owner/group
from the parent directory, mode = `0o444` (read-only for all).
Matches how a freshly-unpacked package tarball treats its files.
Windows: no uid/gid concept; preserve the readonly attribute for
existing files and force it on new ones.
`restore_file_permissions` and the `chown_blocking` helper are split
out of `apply_file_patch` for readability and unit testing. Four
new tests pin the policy: readonly-mode preservation, executable
(0o755) mode preservation, new-file default mode + parent ownership
inheritance, and uid/gid round-trip on existing files.
Assisted-by: Claude Code:opus-4-7
* ci(e2e_gem): revert ruby-version to exact 3.2.10
setup-ruby (unlike setup-python) does NOT support the `3.2.x`
wildcard pin — it errors with "Unknown version 3.2.x for ruby on
ubuntu-24.04". Revert to an exact patch that's in the catalog.
When this patch eventually drops off, bump it manually per the
list at https://github.com/ruby/setup-ruby.
Assisted-by: Claude Code:opus-4-71 parent b96a13f commit 9b7b5c2
111 files changed
Lines changed: 19945 additions & 1903 deletions
File tree
- .github/workflows
- crates
- socket-patch-cli
- src
- commands
- tests
- socket-patch-core
- src
- api
- crawlers
- patch
- utils
- npm
- socket-patch-android-arm64
- socket-patch-darwin-arm64
- socket-patch-darwin-x64
- socket-patch-linux-arm-gnu
- socket-patch-linux-arm-musl
- socket-patch-linux-arm64-gnu
- socket-patch-linux-arm64-musl
- socket-patch-linux-ia32-gnu
- socket-patch-linux-ia32-musl
- socket-patch-linux-x64-gnu
- socket-patch-linux-x64-musl
- socket-patch-win32-arm64
- socket-patch-win32-ia32
- socket-patch-win32-x64
- socket-patch
- pypi/socket-patch
- scripts
- tests/docker
- fixtures/npm
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
39 | 54 | | |
40 | 55 | | |
41 | 56 | | |
| |||
126 | 141 | | |
127 | 142 | | |
128 | 143 | | |
129 | | - | |
| 144 | + | |
130 | 145 | | |
131 | 146 | | |
132 | 147 | | |
133 | 148 | | |
134 | | - | |
| 149 | + | |
135 | 150 | | |
136 | 151 | | |
137 | 152 | | |
| |||
181 | 196 | | |
182 | 197 | | |
183 | 198 | | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
184 | 207 | | |
185 | 208 | | |
186 | 209 | | |
| |||
206 | 229 | | |
207 | 230 | | |
208 | 231 | | |
209 | | - | |
210 | | - | |
| 232 | + | |
211 | 233 | | |
212 | 234 | | |
213 | 235 | | |
| |||
258 | 280 | | |
259 | 281 | | |
260 | 282 | | |
261 | | - | |
| 283 | + | |
262 | 284 | | |
263 | 285 | | |
264 | 286 | | |
| |||
341 | 363 | | |
342 | 364 | | |
343 | 365 | | |
344 | | - | |
| 366 | + | |
345 | 367 | | |
346 | 368 | | |
347 | 369 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
0 commit comments