Skip to content

Reject versions with unparsable constraints instead of stripping them - #467

Merged
maantje merged 3 commits into
mainfrom
fix/invalid-metadata-constraints
Oct 25, 2026
Merged

maantje merged 3 commits into
mainfrom
fix/invalid-metadata-constraints

Conversation

@maantje

@maantje maantje commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Builds on #458 by @codearachnid and fixes #435.

What changed

The first commit is the original change from #458. The only difference is that its unrelated composer.lock changes are left out, because they would have rolled back the dependency upgrades from #463.

The rest changes the approach. Stripping an unparsable require or conflict entry makes that version installable without the constraint (e.g. a laravel/framework: ^^12 requirement disappears, so the version installs on Laravel 10). Composer and Packagist skip such versions instead, so we now do the same:

  • On import: CreateFromZip runs the new ConstraintValidator over require, require-dev, conflict, provide and replace. If a constraint can't be parsed it throws InvalidConstraintException, which is rendered as a 422 validation error naming the invalid links (e.g. invalid version constraint in require.laravel/framework). Nothing is stored and an existing version is left untouched. Queued tag imports fail just that job, since the import batch allows failures.
  • Existing data: php artisan versions:clean removes already stored versions with invalid constraints, including their archives. It supports --dry-run, like archives:clean.
  • Matches Composer: like Composer's ArrayLoader, the validator ignores link keys that aren't an array and constraints that aren't a string. Only a string constraint that won't parse counts as invalid.

Upgrading

Installs that already have broken versions run this once after upgrading:

php artisan versions:clean --dry-run
php artisan versions:clean

codearachnid and others added 3 commits October 23, 2026 17:51
Add MetadataSanitizer to strip composer link constraints that Composer cannot parse (require, require-dev, conflict, provide, replace) while preserving valid strings and special 'self.version'. Use it in CreateFromZip so imported version metadata is cleaned before saving (including re-imports). Add unit tests for MetadataSanitizer and feature tests for CreateFromZip behavior. Aims to resolve #435 and fix similar issue around ver contraints.
Stripping an unparsable require or conflict entry makes the version
installable without that constraint. Composer skips such versions
instead, so do the same:

- CreateFromZip rejects the version with a validation error naming the
  invalid links, so uploads and webhook pushes get a 422 and nothing is
  stored
- ComposerPackageResource leaves already stored versions with invalid
  constraints out of the p2 metadata, so existing packages load again
  without a re-import
Filtering invalid versions on every p2 request only matters for installs
that already stored them, so remove them once with versions:clean
(supports --dry-run) instead.

Also align the validator with Composer's ArrayLoader: link keys that are
not an array and constraints that are not a string are ignored by
Composer, so they no longer count as invalid.
@maantje
maantje merged commit 0a8c751 into main Oct 25, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Typo in previous composer.json package gives error with composer require

2 participants