feat: pin shared packs to commit SHAs via rac-lock.json - #41
Merged
Merged
Conversation
Adds PackLockEntry / PackLockFile types, extends PackRuntime with resolvedSha, and introduces src/core/pack-lock.ts (loadPackLock, writePackLock, findLockEntry) with full test coverage.
…t to ensureSharedPack
Changes GitRunner return type from Promise<void> to Promise<{ stdout: string }>,
updates defaultGitRunner to capture stdout, adds lockedSha option to
ensureSharedPack for deterministic SHA checkout, and captures resolvedSha
from rev-parse in resolving mode. Updates all mock call sites accordingly.
Adds FrozenLockfileError class, integrates loadPackLock/writePackLock into resolvePacks, and routes each pack through locked or resolving mode based on whether a matching lock entry exists. Extends pack-lock.test.ts with eight resolvePacks integration scenarios covering first install, locked repeat install, refresh, frozen-lockfile violations and success, overrides exclusion, ref-change re-resolve, and stale-entry cleanup.
Enforce that git rev-parse HEAD returns a valid 40-char hex SHA in ensureSharedPack; throw a descriptive error if not, preventing empty strings from being written to the lockfile. Update two parsers tests whose stub gitRunners were returning empty stdout for rev-parse, and add drifted-entry frozen error and frozen+stale-only tests to the pack-lock integration suite. Export FrozenLockfileError from src/index.ts.
Plumbs frozen?: boolean through InstallOptions/DiffOptions → computeInstallPlan → resolvePacks, and surfaces FrozenLockfileError as exit code 2 in both the install and diff commands. Also guards against --refresh-packs + --frozen-lockfile being combined (exit code 2, clear error message).
- addProjectPack now accepts opts.gitRunner, resolves the pack via ensureSharedPack after writing config.toml, and upserts the lockfile entry. On resolve failure, config.toml stays mutated but the lockfile is not touched, matching today's partial-failure behaviour. - removeProjectPack drops the matching lockfile entry when the lockfile exists; skips the write when the entry was absent (no spurious mtime change). - setProjectPackOverride / clearProjectPackOverride are unchanged. - Add test/pack-config.test.ts covering all 7 required scenarios. - Update test/parsers.test.ts to pass a fake gitRunner to the three existing addProjectPack call sites so they stay hermetic.
Extends doctor() with three new project-scope lockfile checks: - lockfile_malformed (error): emitted when rac-lock.json cannot be parsed - missing_lockfile_entry (error, frozen mode only): emitted for each pack in config.toml that lacks a matching (id, repo, ref) lockfile entry - stale_lockfile_entry (warn): emitted for each lockfile entry whose (id, repo, ref) triple is no longer present in config.toml When the lockfile is malformed, resolvePacks is skipped to avoid masking the diagnostic with a thrown exception; the rest of doctor's checks still run. Also adds --frozen-lockfile flag to the doctor CLI command, and accepts an optional gitRunner in doctor() opts for test isolation. Six new test cases in test/doctor-overrides.test.ts cover all diagnostic branches.
- README.md: short paragraph under Shared Packs about the lockfile, --frozen-lockfile, --refresh-packs, and overrides skipping it. - docs/configuration.md: new Lockfile subsection (format, write triggers, --refresh-packs, --frozen-lockfile, overrides, doctor checks). - docs/architecture.md: one sentence noting SHA pinning happens at pack resolution (stage 1) via the lockfile. - .changeset/rac-lock-json.md: minor changeset for @raniejade/rac. - scripts/harness-smoke.mjs: setupLockfileSmoke verifies rac-lock.json is written after pack add + install; setupPackOverrideScope now uses a local bare git repo + GIT_CONFIG_GLOBAL insteadOf redirect so pack add succeeds without real GitHub network access.
Add noWrite option to resolvePacks that suppresses both writePackLock and FrozenLockfileError; pass noWrite:true from doctor() to preserve its read-only contract. Correct docs: overridden pack lockfile entries are pruned on next install, not left untouched.
Extends setupLockfileSmoke to exercise all 7 spec verification scenarios: lockfile creation with SHA matching upstream HEAD, pinning across upstream advances, --refresh-packs rewrite, --frozen-lockfile no-op, stale-entry doctor warning and pruning, pack override prunes then re-resolves on clear, and two-clone byte-identical output with --frozen-lockfile.
raniejade
pushed a commit
that referenced
this pull request
May 14, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @raniejade/rac@0.4.0 ### Minor Changes - [#39](#39) [`c6f501b`](c6f501b) Thanks [@raniejade](https://github.com/raniejade)! - Add local pack overrides for dev cycle. A new `.rac/config.local.toml` (gitignored) with `[[pack_overrides]]` entries redirects a configured `[[packs]]` id to a local directory, bypassing git and cache so packs can be iterated on locally without commit/push churn. - New CLI: `rac pack override <id> <path>` and `rac pack override <id> --clear`. - `rac pack list` decorates overridden packs with `(override → <path>)`. - `rac doctor` and `rac install` emit a `WARN` per active override (install still exits 0). - `rac init` writes `.rac/.gitignore` containing `config.local.toml`. - [#41](#41) [`33037ca`](33037ca) Thanks [@raniejade](https://github.com/raniejade)! - Pin shared packs to resolved commit SHAs via `.rac/rac-lock.json`. The lockfile is committed alongside `config.toml`; future installs check out the locked SHA instead of re-resolving the floating `ref`. Two machines installing the same project now produce identical outputs, and CI can gate on lockfile drift. - New flag `--frozen-lockfile` for `rac install`, `rac diff`, and `rac doctor`: errors (exit code 2) if the lockfile would change. - Existing `--refresh-packs` now also re-resolves and rewrites the lockfile. - `rac pack add` / `rac pack remove` keep the lockfile in sync. - Pack overrides skip the lockfile entirely. - `rac doctor` reports malformed lockfiles, stale entries, and (with `--frozen-lockfile`) missing entries. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces
.rac/rac-lock.json, a committed lockfile that pins each shared pack to a resolved commit SHA. Today two installs of the sameconfig.tomlcan diverge because every run re-resolves the floatingref(main,v2, …) viagit fetch ... && checkout FETCH_HEAD. With the lockfile, locked SHAs are checked out directly and CI can gate on lockfile drift.What changed
.rac/rac-lock.json— committed JSON, sorted by pack id,version: 1. Schema:{ id, repo, ref, resolved }per entry.rac install/rac diff— default now uses locked SHAs when an entry exists; otherwise resolves and writes the lockfile. New--frozen-lockfileflag errors (exit 2) if a sync would mutate the lockfile. Existing--refresh-packsis widened to re-resolve and rewrite all entries.--refresh-packs --frozen-lockfileis rejected.rac pack add/rac pack remove— keep the lockfile in sync alongsideconfig.toml.rac pack override— unchanged. Overridden packs are excluded from the lockfile entirely.rac doctor— read-only on the lockfile; adds three new diagnostics:lockfile_malformed(error),missing_lockfile_entry(error,--frozen-lockfileonly),stale_lockfile_entry(warn).GitRunnertype — now returnsPromise<{ stdout: string }>(wasPromise<void>) so resolving mode can capturegit rev-parse HEAD. Not re-exported fromsrc/index.tstoday, so no public-API break.docs/configuration.mdLockfile subsection, one-liner indocs/architecture.md.Test plan
npm run typechecknpm run test— 367 unit/integration testsnpm run lintnpm run test:harness— 7 end-to-end scenarios against a local bare git repo, matching the spec verification list:--refresh-packsrewrites the lockfile--frozen-lockfilepost-refresh: succeeds, no diff[[packs]]block +rac doctorwarns;rac installprunes the entryrac pack overrideprunes lockfile entry;--clear+ install re-resolves--frozen-lockfileproduce byte-identical generated outputsUser-visible workflow