Patching Magento 2 vendor directory

Posted December 17, 2016

During the last Magento meet up in Manchester hosted by Push On I was asked about and we all had a conversation about how people are handling patching of files installed via composer.

Lets re wind a little, why would we want to patch a file that lives in vendor directory? Well in Magento 2 there are some bugs, Often there is a pull request waiting in github ready to fix this problem but forking and maintaining another repo is not ideal when it comes to the underlying framework.

One of the ideas presented that works is to commit the entire vendor directory and make the changes to the file and commit to the repo. Lets analyze this for a moment. It works and on deployment the vendor directory will be pushed with changes, Yet the git repo has all the files from vendor in it. Ideally this should be excluded and composed as part of a build. Also if we run a composer update then there is a chance that unless we ping our composer versions the changes just get nuked every time. There feels like a potential for merge conflicts on changing packages and its just another area of the project that ideally we use composer to manage.

So how would we handle not committing vendor to the git repo but still patch our files? Well thankfully I faced this problem while working for Inviqa and while there we developed a package that can be found at my github repo. This works by using the composer hooks system and adds some additional configuration to the composer.json manifest file.

 "extra": {
        "magento-root-dir": "public",
        "patches": {
            "patch-group-1": {
                "patch-name-1": {
                    "type": "patch",
                    "title": "Allow composer autoloader to be applied to Mage.php",
                    "url": "https://url/to/file1.patch"
                }
            },
            "patch-group-2": {
                "patch-name-1": {
                    "title": "Fixes Windows 8.1",
                    "url": "https://url/to/file2.patch"
                }
            },
            "shell-patch-group-1": {
                "magento-shell-patch-name-1": {
                    "type": "shell",
                    "title": "Magento security fix",
                    "url": "https://url/to/magento/shell/patch.sh"
                }
            }
        }
    }

Here is an example of it in action. Under the extra node we define where magento root is installed and then define an array of patches to apply. What is cool here is that if you are using Magento 1 then its possible to use the SUPEE patches here as that gets detected, otherwise it will use the default git patching. So what you need to do is define some info about the patch type and title, and then a URL to where the patch can be found. This can be a full web url or a path on disk. I tend to use a patches folder and store the patches there.

The process to create a patch is just as you would patch any file with git format-patch master --stdout > some_patch.patch add this path to extra and commit it to the repo and make sure that vendor is not committed.

Any time a composer action is then ran the patcher will kick in and patch the files you specify in your patches array. This then enables each of the pipeline instances moving forward to compile the code as we would expect.

Summary

So there you have it, my summary to how I work with patches compose and Magento 2. I would really like to hear how everyone else is working with this and feedback on the patcher in general.

You may also find these related posts interesting: Technical Insights into Magento 2 Continously delivering Learn to love testing an agile journey into Behat, PHPSpec and Magento Magento Live 2016 - OOP Module development