Problem
This happen only when using Distribution Mirroring.
When a package is required as a branch version (dev-main), any composer.lock that pins a commit older than the branch head fails to install with a 404 error, you have to re-run composer update to download the new version.
Steps to reproduce
- Require a package from a repository synced by Pricore at a branch version (
"acme/pkg": "dev-main"), and commit the resulting composer.lock.
- Push a new commit to
main and let Pricore sync it.
- Run
composer install from the unchanged lock file → 404 on the dist URL.
Root cause
A branch keeps a single package_versions row and SyncRefAction updates that row in place on every sync, moving source_reference and dist_path to the new commit.
DistController::download() then looks the archive up by source_reference, so the reference recorded in the lock no longer matches any row and it returns 404.
The archive itself is often still there: CreateDistArchiveAction stores it at a path that includes the short ref, {org}/{vendor}/{package}/{version}_{ref12}.zip, and CleanupDistArchivesAction only prunes stable() versions. So the old zip stays on disk as an orphan that nothing can reach.
Proposed Solution
- Resolve the archive by its deterministic path before giving up. The orphaned zip is already on disk at
{org}/{package}/{version}_{ref12}.zip; checking for it costs one exists() and recovers every archive built by an earlier sync.
- Re-archive the requested reference from the git provider when the file is not on disk, instead of returning 404. This makes lock files reproducible for as long as the commit exists upstream, which matches the packagist.org behaviour. Worth putting behind a config flag.
- Keep a record of detached archives. When a branch row moves forward, retain the previous row (e.g. with a null
version, reachable only by source_reference, behind a global scope so it never leaks into metadata or the UI). This would also enable retention policies and storage accounting for dev archives, which CleanupDistArchivesAction does not currently cover.
Caveat for (2): Composer verifies the dist shasum from the lock file, and a regenerated zip is not guaranteed to be byte-identical to the original. When that happens the failure moves from a 404 to a checksum error. Omitting the shasum for dev versions, as packagist does, would close that gap.
If we keep PackageVersion rows for unversioned archives we can implement the cleanup for versions without downloads in the latest x days to reduce the re-archive cases.
I think this feature should be optional but the old dist archive cleanup should be implemented anyway to reduce storage usage for unreachable archives.
Alternatives Considered
No response
Additional Context
No response
Problem
This happen only when using Distribution Mirroring.
When a package is required as a branch version (
dev-main), anycomposer.lockthat pins a commit older than the branch head fails to install with a 404 error, you have to re-runcomposer updateto download the new version.Steps to reproduce
"acme/pkg": "dev-main"), and commit the resultingcomposer.lock.mainand let Pricore sync it.composer installfrom the unchanged lock file → 404 on the dist URL.Root cause
A branch keeps a single
package_versionsrow andSyncRefActionupdates that row in place on every sync, movingsource_referenceanddist_pathto the new commit.DistController::download()then looks the archive up bysource_reference, so the reference recorded in the lock no longer matches any row and it returns 404.The archive itself is often still there:
CreateDistArchiveActionstores it at a path that includes the short ref,{org}/{vendor}/{package}/{version}_{ref12}.zip, andCleanupDistArchivesActiononly prunesstable()versions. So the old zip stays on disk as an orphan that nothing can reach.Proposed Solution
{org}/{package}/{version}_{ref12}.zip; checking for it costs oneexists()and recovers every archive built by an earlier sync.version, reachable only bysource_reference, behind a global scope so it never leaks into metadata or the UI). This would also enable retention policies and storage accounting for dev archives, whichCleanupDistArchivesActiondoes not currently cover.Caveat for (2): Composer verifies the dist
shasumfrom the lock file, and a regenerated zip is not guaranteed to be byte-identical to the original. When that happens the failure moves from a 404 to a checksum error. Omitting the shasum for dev versions, as packagist does, would close that gap.If we keep
PackageVersionrows for unversioned archives we can implement the cleanup for versions without downloads in the latest x days to reduce the re-archive cases.I think this feature should be optional but the old dist archive cleanup should be implemented anyway to reduce storage usage for unreachable archives.
Alternatives Considered
No response
Additional Context
No response