Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.
This repository was archived by the owner on May 14, 2026. It is now read-only.

Wire is_built skip + side-effects cache WRITE path (#397 item #10 follow-up to PR #422) #421

@zkochan

Description

@zkochan

Closed in 55f6f7684f (PR #424) — slice (B) WRITE path landed.

PR #424 wired:

  • pacquet_store_dir::add_files_from_dir — directory walker that re-CAFSes a post-build package directory. Mirrors pnpm/pnpm@7e3145f9fc:store/cafs/src/addFilesFromDir.ts:16-194 + worker/src/start.ts:312-383. Symlinks read/recurse via the resolved canonical path (TOCTOU-safe), top-level node_modules skipped, cycle-safe via recursion-stack visited set.
  • pacquet_store_dir::upload + calculate_diffupload(store_dir, built_pkg_location, files_index_file, cache_key, writer) walks the directory and queues a WriteMsg::SideEffectsUpload. The actual R/M/W (load existing row → algo == HASH_ALGORITHM check → diff → side_effects[cache_key] = diff) runs inside the writer task's transaction so concurrent uploads to the same row stay commutative.
  • StoreIndexWriter driving a WriteMsg enum (Replace vs SideEffectsUpload). The batch loop coalesces per-key updates into one PackageFilesIndex before flushing.
  • Config.side_effects_cache_readonly: bool (default false) — mirrors pnpm's sideEffectsCacheReadonly. Helpers Config::side_effects_cache_read() / side_effects_cache_write() consolidate the precedence.
  • BuildModules WRITE call site — after run_postinstall_hooks returns Ok and the gate fires, calls pacquet_store_dir::upload(...). Upload errors are swallowed with tracing::warn!, matching upstream's try { upload } catch { logger.warn } at building/during-install/src/index.ts:208-215.
  • build_deps_subgraph — the cache-hashing dep graph is bounded to the forward closure of requires_build snapshots, so pure-JS installs skip the walk entirely. Upstream's lockfileToDepGraph builds the full graph because its consumers extend beyond cache hashing; pacquet's graph is consumed only by calc_dep_state today, so the closure-bounded walk produces byte-identical cache keys with strictly less work.
  • StoreIndexWriter hoisted to InstallFrozenLockfile::run so the writer spans both the prefetch/download phase (existing Replace queues) and the build phase (new SideEffectsUpload queues). The final batch flushes once BuildModules::run returns.
  • Byte-stable msgpackencode_package_files_index now iterates files, side_effects, and SideEffectsDiff.added in sorted-key order so two pacquet writes of the same logical row produce identical bytes on disk.

Tests ported / added:

  • add_files_from_dir::tests (9 cases): top-level files, nested forward-slash paths, exec-bit propagation, top-level-vs-nested node_modules, symlink-out-of-root drop, symlink-in-root follow, directory-cycle termination, missing-root error.
  • upload::tests (6 cases): identical / added-only / deleted-only / digest change / mode change / mixed — every calculateDiff branch.
  • deps_graph::tests for build_deps_subgraph: empty-roots → empty graph, forward-closure inclusion/exclusion, cycle termination.
  • workspace_yaml::tests: sideEffectsCacheReadonly round-trip + 4-state truth table.
  • build_modules::tests:
    • write_path_populates_side_effects_row — pre-seed a base row, run a postinstall that creates generated.txt, drain the writer, assert side_effects[cache_key].added carries generated.txt and NOT pristine index.js. Mirrors 'a postinstall script does not modify the original sources added to the store' at pnpm/pnpm@7e3145f9fc:sideEffects.ts:189-223.
    • write_path_disabled_skips_upload — gate off → row's side_effects stays None.
    • upload_error_does_not_interrupt_install — postinstall produces a 0-permission file → walker fails on EACCESBuildModules swallows with warn! → install completes, base row untouched. Mirrors 'uploading errors do not interrupt installation' at sideEffects.ts:166-187.

Benchmark at merge: cold install on par with main (~2.4s), hot cache ~7% faster than main (690ms vs 738ms), pacquet beats pnpm by 2.5× cold / 3.5× hot.


Both sub-tasks of this issue are now complete:

#397 item #10 fully closed.


Written by an agent (Claude Code, claude-opus-4-7).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions