The composer.lock that only builds on your machine
You develop a contrib module against a local checkout, commit, and CI goes red on every job — or worse, the production image build fails. The culprit is one line in composer.lock that only makes sense on your laptop.
The setup
To hack on a module that is published to a registry, it is common to point Composer at a local checkout with a path repository in a git-ignored override:
// composer.local.json (git-ignored)
"repositories": { "my_module": { "type": "path", "url": "../my_module" } }Composer symlinks the module from the sibling directory. Great for development. The trap is what it writes into the lock.
The trap
With the override active, composer.lock records the module with a path dist pointing at ../my_module:
"dist": { "type": "path", "url": "../my_module" }That path exists only on your machine. Commit the lock and every environment that runs composer install from it — CI, the image build, a teammate — looks for a sibling directory that is not there, and fails. The registry version never gets installed, because the lock says to use the path.
The fix
Regenerate the lock with the override out of the way, so it records the registry dist:
mv composer.local.json composer.local.json.aside
composer update my_module --no-install # relock from the registry
mv composer.local.json.aside composer.local.jsonConfirm the lock now shows a real registry dist ("type": "zip", an https URL), not a path. Then commit only the lock — never the override.
Guard it in CI
This is easy to do by accident, so make it impossible to merge. A one-line check that fails the build when the lock contains a path dist catches every case before it lands:
grep -q '"type": "path"' composer.lock && { echo 'path dist in lock'; exit 1; } || trueTakeaway
A local path override is a development convenience that must never reach the committed lock. Relock with it set aside, verify the dist is a registry URL, and add a lockfile guard so "works on my machine" cannot become "breaks every build."