diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-01 21:55:09 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-01 21:55:09 +0900 |
| commit | baa40325659a44938ad2e9ad6525ea3b3aaacfe2 (patch) | |
| tree | b2d69a005c79c4d272443602235afcf5d602b062 /crates/mozart-registry/src/lockfile.rs | |
| parent | 740eb5b55804134c1977dd39cd8170e2fa615d35 (diff) | |
| download | php-mozart-baa40325659a44938ad2e9ad6525ea3b3aaacfe2.tar.gz php-mozart-baa40325659a44938ad2e9ad6525ea3b3aaacfe2.tar.zst php-mozart-baa40325659a44938ad2e9ad6525ea3b3aaacfe2.zip | |
fix(registry): accept composer.lock without content-hash
Composer's `Locker` treats `content-hash` as optional with BC support
(see Locker::isLocked() / isFresh() lines 142-147): if a lock predates
the field — or, in the case of installer fixtures, deliberately omits
it — Composer simply considers the lock "not fresh" against any
composer.json. Mozart's deserializer was strict, rejecting the lock
with `missing field content-hash` before any of the install-time
checks could run.
Default the field to empty via `#[serde(default)]`. With an empty
hash, `is_fresh()` returns false (matching Composer's BC behavior, so
the freshness warning still fires) and downstream code that overwrites
`content_hash` continues to work unchanged.
Closes the parsing barrier exercised by the
updating-dev-from-lock-removes-old-deps installer fixture. Note:
matching Composer's exact operations trace ("Upgrading a/devpackage
…", alias-removal lines) requires a `compute_operations` that compares
package source references — out of scope for this change and tracked
in .ken/test_design.md §7.2 under "EXPECT (operations trace) 比較".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-registry/src/lockfile.rs')
| -rw-r--r-- | crates/mozart-registry/src/lockfile.rs | 24 |
1 files changed, 23 insertions, 1 deletions
diff --git a/crates/mozart-registry/src/lockfile.rs b/crates/mozart-registry/src/lockfile.rs index 19e721c..331f58e 100644 --- a/crates/mozart-registry/src/lockfile.rs +++ b/crates/mozart-registry/src/lockfile.rs @@ -21,7 +21,10 @@ pub struct LockFile { #[serde(rename = "_readme", default = "LockFile::default_readme")] pub readme: Vec<String>, - #[serde(rename = "content-hash")] + /// Composer lock files written before content-hash existed (or fixtures + /// covering BC behavior) may omit this field; mirror Composer's BC support + /// in `Locker::isLocked()` by defaulting to empty. + #[serde(rename = "content-hash", default)] pub content_hash: String, pub packages: Vec<LockedPackage>, @@ -704,6 +707,25 @@ mod tests { assert!(readme[0].contains("locks the dependencies")); } + #[test] + fn parses_lock_without_content_hash() { + // Composer fixtures (and historical lock files) may omit content-hash; + // mirror Composer's BC handling by accepting it and treating the lock + // as not-fresh against any composer.json. + let raw = r#"{ + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false + }"#; + let lock: LockFile = serde_json::from_str(raw).unwrap(); + assert_eq!(lock.content_hash, ""); + assert!(!lock.is_fresh(r#"{"require": {}}"#)); + } + // ──────────── Lock file generation tests ──────────── fn make_packagist_version( |
