DRIFT
DRIFT = 'code.vendor_drift'
Code raised when the tree and the lock disagree.
Notices when `vendor/` stops matching the lockfile that is supposed to reproduce it.
Referencing composer.lock instead of storing 241 MB of dependencies rests on one assumption:
that running composer install against that lock reproduces the tree. That assumption is true right up
until someone patches a dependency in place, and then the backup is quietly missing the only copy
of a change the site depends on.
So the lock is stored WITH a fingerprint of the tree it produced, and the fingerprint is checked.
When it moves without the lock moving, code.vendor_drift fires and the files that drifted are
captured as bytes - only those files, so the cost is the patch rather than the tree.
The fingerprint is a bounded sample, and that is stated rather than implied. Hashing 241 MB on
every cron would cost more than the realm it protects, so a fixed number of files is sampled in a
deterministic order. A patch to a file outside the sample is not seen by the fingerprint; what
catches that is composer.lock itself changing, or an operator running a full check. The sample
size is the dial between cost and confidence.
compare(string $expected, string $lockDigest) : array{drifted: bool, reason: string, lockChanged: bool, fingerprint: string}
Compares the tree against a fingerprint taken earlier.
| string | $expected | The stored fingerprint. |
| string | $lockDigest | The stored digest of |
Whether the tree drifted, why, whether the lock itself moved, and the current fingerprint.
drifted(array$expected) : list<string>
The files whose content differs from what the fingerprint recorded.
Called only after drift is detected, and only over the sample, so the answer is what to capture rather than a complete account of the tree.
| array |
$expected | Path keyed to the description recorded earlier. |
Paths relative to the project root.
sample() : array<string,\SplFileInfo>
The sampled files, in a deterministic order.
Sorted by path and truncated, so two runs on the same tree sample the same files and a difference means the tree moved rather than the iteration order did.
Path relative to the project root, keyed to the file.