pushonltd / magento-patchline
Checks and syncs Adobe's isolated security patches for a Magento store.
Requires
- php: >=8.2
- composer-runtime-api: ^2.0
- ext-hash: *
- ext-json: *
- ext-openssl: *
- composer/semver: ^3.2
- guzzlehttp/guzzle: ^7.4.5 || ^8.0.0
- symfony/console: ^5.4 || ^6.4 || ^7.0
- symfony/process: ^5.4 || ^6.4 || ^7.0
Requires (Dev)
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-29 10:06:43 UTC
README
Adobe's isolated security patches for Magento Open Source.
Checked, downloaded and applied on every deploy.
Quick start · Recommended wiring · Where the patches come from · Commands
Since magento/magento-cloud-patches 1.1.18, Adobe's security patches no longer reach Open Source.
Patchline finds the missing ones, drops them into m2-hotfixes/, and ece-patches apply does the rest.
A companion to magento/quality-patches.
Quick start
curl -fsSL https://github.com/pushonltd/magento-patchline/releases/latest/download/install.sh | bash php patchline.phar status # applied, queued or missing php patchline.phar sync # download the missing ones into m2-hotfixes/ php patchline.phar verify # will ece-patches apply succeed?
Commit m2-hotfixes/. To apply the patches on every composer install, add the wiring.
Recommended wiring
composer require pushonltd/magento-patchline # not --dev: the scripts also run on install --no-dev
In the store's composer.json:
"scripts": { "post-install-cmd": ["@apply-patches"], "post-update-cmd": ["@apply-patches"], "apply-patches": ["vendor/bin/patchline rewind", "vendor/bin/ece-patches apply"] }
rewindfirst: stockece-patches applyfails on an already patchedvendor/(DDEV, hosts that keepvendor/). On a fresh installrewindis a no-op.- Not
status: it goes online and exits 1 on every new Adobe patch, breakingcomposer install. Use CI.
Where the patches come from
Straight from Adobe. There is no third party in between: no mirror, no hosted service, no patches of ours.
- Registry:
repo.magento.com/patch/patch-registry.json, the one Adobe's ownpatch-statusreads. Confirmed by Adobe as meant for public, automated use. - Diffs:
repo.magento.com/patch/, each checked against the registry's sha256. - Commerce-only patches need repo.magento.com keys.
A security patch should come from the vendor who wrote it, and you should be able to check that it did. Thanks to @s-renz for finding the endpoint (and a great chat at Meet Magento Germany 2026).
Install
Magento 2.4.6+, PHP 8.2+, a patch binary. No dependencies beyond what Magento already installs.
- PHAR (check and sync): the Quick start one-liner. Verifies the sha256, plus build provenance
if
ghis logged in. Pin withPATCHLINE_VERSION=v0.0.3, relocate withbash -s -- --path=/usr/local/bin/patchline. - Composer (apply on install): see Recommended wiring.
- Update notice at most once a day; off with
--no-update-checkorPATCHLINE_NO_UPDATE_CHECK=1.
Commands
patchline status [options]
patchline sync [options]
patchline verify [options]
patchline rewind [--root=ROOT] [--dry-run]
status
Every registry patch for your Magento version and its state (screenshot above). Read-only.
sync
Downloads missing patches as m2-hotfixes/<released>_<id>.patch; the date prefix keeps the release order
ece-patches apply needs. Never overwrites, never touches vendor/.
verify
Replays the queue on a temporary copy, like a fresh deploy, and names the first patch that fails.
rewind
Reverses the queue in vendor/, newest first. It is the only command that writes to vendor/.
- Rehearses on a temporary copy first; if that fails, changes nothing and exits 1. Offline.
- Blocked by a patch applied in
vendor/but missing from the queue (usually a deleted hotfix file): runsync. vendor/is unpatched untilece-patches applyruns. Build step only, never a live docroot.
Options
| Option | Commands | Meaning |
|---|---|---|
--root=ROOT |
all | Store root (default: current directory) |
--format=FORMAT |
all but rewind |
text, json, github or junit (default github on GitHub Actions) |
--fail-on=FAIL-ON |
all but rewind |
States that exit 1 (default missing,duplicate,misordered; also cross-check) |
--registry=REGISTRY |
all but rewind |
Registry URL or file instead of repo.magento.com |
--cache-dir=CACHE-DIR |
all but rewind |
Cache directory (default <root>/var/patch_metadata, shared with patch-status) |
--no-cache |
all but rewind |
Don't use cached files |
--include-entitled |
all but rewind |
Include Commerce-only patches (needs repo.magento.com keys) |
--cross-check |
all but rewind |
Compare the result with vendor/bin/patch-status |
--no-update-check |
all but rewind |
Don't check for a newer Patchline release |
--dry-run |
sync, rewind |
Show the plan, change nothing |
--adopt |
sync |
Rename hand-copied hotfix files to the dated naming |
--prune |
sync |
Remove hotfix files that duplicate cloud-patches files |
Patch states
| State | Meaning |
|---|---|
applied |
🟢 Already in the code |
queued |
🟢 Will be applied by ece-patches apply |
missing |
🔴 Not in the code and not queued |
duplicate |
🔴 Queued more than once, which makes ece-patches apply fail |
misordered |
🔴 Queued before a patch it depends on |
entitled |
🟡 Commerce-only, skipped without --include-entitled |
unknown |
🟡 Could not be checked, e.g. locally modified files |
not_applicable |
⚪ Only touches packages your store has removed |
🔴 states fail the run by default (see --fail-on).
Exit codes
| Code | Meaning |
|---|---|
| 0 | All good |
| 1 | Needs action (see --fail-on), sync or verify failed, or rewind was blocked |
| 2 | Wrong usage |
| 3 | Could not run: no registry, no patch binary, unreadable store, or no magento-cloud-patches for rewind |
Configuration
Any flag also works as a PATCHLINE_* variable or under extra.patchline in composer.json.
Precedence: flag › environment › composer.json.
{ "extra": { "patchline": { "fail-on": ["missing", "duplicate"] } } }
Keys come from COMPOSER_AUTH or auth.json and go only to repo.magento.com. Proxy: HTTPS_PROXY.
CI
- Daily:
sync --adopt --prune, open a PR ifm2-hotfixes/changed. Adobe has no fixed release day. - Every PR:
statusandverify.--format=githubadds annotations and a job summary;junitsuits GitLab and Jenkins.
How it works
Like Adobe's patch-status: each patch is dry-run forwards and in reverse with patch on temporary copies,
then matched against the queue ece-patches apply will use.
Development
composer check # code style, PHPStan level 10, tests composer build # dist/patchline.phar composer mutation # mutation tests
Authors
- PushON Ltd
- Jiří Brada (@jbrada)
License
MIT, © 2026 PushON Ltd.