Skip to content

feat(lock): wippy.lock.local overlay for local replacement overrides#461

Open
skhaz wants to merge 3 commits into
mainfrom
feat/lock-local-overlay
Open

feat(lock): wippy.lock.local overlay for local replacement overrides#461
skhaz wants to merge 3 commits into
mainfrom
feat/lock-local-overlay

Conversation

@skhaz

@skhaz skhaz commented Jul 9, 2026

Copy link
Copy Markdown
Member

Why

Module replacements: only lived in the tracked wippy.lock, so local development against sibling checkouts forced editing the committed lock:

  • machine-local paths leaked into commits;
  • a contributor whose checkout layout differs got replacement path ... does not exist and had to fork the lock;
  • a repo could not keep one canonical hub lock while a developer overrode a couple of modules locally.

What

A gitignored wippy.lock.local overlay next to wippy.lock that carries only replacements:. Per module, overlay entries win over the tracked lock. Honored by install, update, and run; its content is never written back into the tracked lock.

  • lock.New reads a sibling <lockpath>.local overlay (strict decode — non-replacement keys rejected; empty overlay tolerated). Overlay entries merge over the tracked lock and win by from.
  • Read side (GetReplacement, GetReplacements, GetModuleLoadPaths) uses the effective view; Write and the mutators stay base-only, so nothing overlay-derived is persisted. A new GetTrackedReplacements exposes the base-only set.
  • update uses GetTrackedReplacements at its five replacement consumers, so the overlay never leaks into the regenerated lock.
  • run needs no flag — the overlay is discovered automatically next to the resolved lock.
  • wippy init idempotently adds wippy.lock.local to .gitignore.

Testing

  • TDD throughout; go test -race green across boot/... + cmd/... (24 packages); golangci-lint clean; gremlins mutation 91% (lone survivor is an equivalent capacity-hint mutant).
  • End-to-end with the built wippy binary:
# overlay redirects a vendored module to a local checkout
$ cat wippy.lock            # tracked: module wippy/fsdir-fixture, no replacement
$ cat wippy.lock.local      # replacements: [{from: wippy/fsdir-fixture, to: ./modules/fsdir-fixture}]

$ wippy registry list       # WITH overlay -> loaded from local path
wippy.fsdir:assets  fs.directory   (2 entries)
$ mv wippy.lock.local /tmp; wippy registry list   # WITHOUT overlay
(1 entry — fixture absent)

$ wippy install             # overlay-replaced module is skipped, lock unchanged
INFO install  module is replaced by local source; skipping install {"module":"wippy/fsdir-fixture"}

$ printf 'modules:\n  - name: a/b\n    version: v1\n' > wippy.lock.local; wippy registry list
Error: wippy.lock.local may only contain replacements

$ : > wippy.lock.local; wippy registry list   # empty overlay tolerated (1 entry)

$ wippy update              # regenerated wippy.lock has NO replacements; overlay untouched
$ wippy init                # .gitignore now contains wippy.lock.local (idempotent)

skhaz added 3 commits July 9, 2026 16:21
Why: module replacements only lived in the tracked wippy.lock, so local
development against sibling checkouts leaked machine-local paths into
commits and broke contributors whose checkout layout differed.

What: lock.New now reads a sibling "<lockpath>.local" overlay that carries
only replacements (strict decode; non-replacement keys are rejected, an
empty overlay is tolerated). Overlay entries merge over the tracked lock
and win by "from". GetReplacement, GetReplacements and GetModuleLoadPaths
return the effective view; a new GetTrackedReplacements exposes the
base-only set. Write and the mutators stay base-only, so overlay content
is never persisted. Validate checks the effective replacement paths.
Why: update regenerates the canonical wippy.lock; consuming the effective
(overlay-merged) replacements would copy developer-local overrides back
into the tracked lock and scan their sources into the resolved graph.

What: switch the five replacement consumers in the full and targeted
update flows to GetTrackedReplacements so the overlay never influences or
leaks into the regenerated lock. The targeted skip check keeps the
effective view, since it only decides whether to hub-update a locally
overridden module, never what to persist.
Why: the overlay is meant to stay local; without a default ignore rule
developers would commit machine-local replacement paths.

What: wippy init idempotently ensures the .gitignore next to the lock
file lists wippy.lock.local, creating the file when absent and appending
the entry on its own line when missing.
@skhaz skhaz requested a review from wolfy-j July 9, 2026 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant