DDEV and Magento 2

Local development and GitHub actions

Introduction

I would like to speak here about a tool that I discovered a few months ago: DDEV.

To summarize, DDEV makes it easier to set up a Docker stack for your PHP developments. I was surprised by how quickly it could be picked up, and I use it, among other things, for my Magento 2 developments.

I won't go into detail about everything that can be done with DDEV. There are a large number of resources (especially awesome-ddev) and its documentation seems clear enough to facilitate its installation and customization.

I simply prefer to share my experience here by evoking two types of tasks for which DDEV has simplified my life:

  • install Magento 2 and develop modules locally
  • set up GitHub actions to test several versions of Magento 2 and PHP in an automated way.

The first step is to install everything you need:

Step 1: Installations

Installing DDEV

The best way to install DDEV is to follow the official documentation. On a Linux distribution, you could run this type of command:

sudo apt install linuxbrew-wrapper
brew tap drud/ddev && brew install ddev

Preparing DDEV Magento 2 environment

The final structure of the project will look like below.:

m2-sources
│
│ (Magento 2 sources installed with composer)
│
└───.ddev
│   │
│   │ (DDEV configuration files)
│
└───my-own-modules (only if you want to test some of your module(s))
    │
    │
    └───yourVendorName-yourModuleName
       │
       │ (Module sources)

To populate the above .ddev folder, I will use here specific DDEV configurations. It's, in fact, configurations that DDEV automatically generates when you tell it that you want to work on a Magento 2 project, to which I added some DDEV files and commands. For example, I have added docker-compose.yaml files when I wanted to work with other Docker containers, Varnish, PHPSTAN or Nginx configuration files for some very specific uses and I created commands to simulate crons or run custom scripts.

For now, you could use my sources without trying to understand in detail, but the idea is that everyone should do according to their needs by adding their own DDEV files and commands. I will give below some more specific examples of custom commands.

So if you want to see what it looks like on your host:

  • Create an empty m2-sources folder:
mkdir m2-sources
  • In this folder, create an empty hidden .ddev folder and clone my GitHub repository:
mkdir m2-sources/.ddev && cd m2-sources/.ddev && git clone git@github.com:julienloizelet/ddev-m2.git ./
  • In this example, we will install Magento 2.4.3 with PHP 7.4. To do this, copy a small configuration file:
cp .ddev/config_overrides/config.m243.yaml .ddev/config.m243.yaml
  • Finally, launch DDEV
cd .ddev && ddev start

This should take some times on the first launch as this will download all necessary docker images. Later, it will be much faster.

If you are curious, you could then run a ddev describe which will give you some information on the different Docker services.

Installing Magento 2

To install Magento 2 you will need your private and public Magento 2 keys. You will be asked for them during the installation with composer.

DDEV has some basic commands that allow you to launch usual commands in the right docker container. For example, ddev composer runs composer in the web container where the sources are located. So, to install Magento 2.4.3, you can do:

ddev composer create --repository=https://repo.magento.com/ magento/project-community-edition:2.4.3

Likewise, ddev magento launches the CLI executable bin/magento. So, to complete the installation, run:

ddev magento setup:install \
--base-url=https://m243.ddev.site \
--db-host=db \
--db-name=db \
--db-user=db \
--db-password=db \
--backend-frontname=admin \
--admin-firstname=admin \
--admin-lastname=admin \
--admin-email=admin@admin.com \
--admin-user=admin \
--admin-password=admin123 \
--language=en_US \
--currency=USD \
--timezone=America/Chicago \
--use-rewrites=1 \
--elasticsearch-host=elasticsearch

Magento 2 is now installed and operational.

For convenience purpose, I sometimes add commands like:

ddev magento config:set admin/security/password_is_forced 0
ddev magento config:set admin/security/password_lifetime 0
ddev magento module:disable Magento_TwoFactorAuth
ddev magento indexer:reindex
ddev magento c:c

Or, to add test data:

ddev magento setup:performance:generate-fixtures setup/performance-toolkit/profiles/ce/small.xml

The installations are complete. In the second step, I describe how I develop my modules locally:

Step 2: Local development of a module

Module sources

There are several ways to develop a module locally. Perhaps the easiest is to place your sources in app/code. As far as I'm concerned, I prefer to modify the composer.json of the project to add a repository of type path which point to the module sources:

mkdir -p m2-sources/my-own-modules/yourVendorName-yourModuleName
cd m2-sources/my-own-modules/yourVendorName-yourModuleName
git clone git@github.com:yourGitHubName/yourGitHubModule.git ./
ddev composer config repositories.yourVendorName-yourModuleName path my-own-modules/yourVendorName-yourModuleName/
ddev composer require yourComposerModuleName:@dev
ddev magento module:enable yourVendorName_yourModuleName
ddev magento setup:upgrade
ddev magento cache:flush

For the rest of this presentation, I will take as an example one of my modules called Okaeli_CategoryCode. This one adds a code attribute to the categories, but whatever. If you want to see what it looks like:

mkdir m2-sources/my-own-modules
mkdir m2-sources/my-own-modules/okaeli-category-code
cd m2-sources/my-own-modules/okaeli-category-code
git clone git@github.com:julienloizelet/magento2-category-code.git ./
ddev composer config repositories.okaeli-category-code path my-own-modules/okaeli-category-code/
ddev composer require okaeli/magento2-category-code:@dev
ddev magento module:enable Okaeli_CategoryCode
ddev magento setup:upgrade
ddev magento cache:flush

Static and unit tests

As mentioned above, one of the strengths of DDEV is that it allows you to add your own commands. I was thus able to define the phpcs, phpmd and phpstan commands which launch the tools of the same name:

  • PHP Code Sniffer: ddev phpcs my-own-modules/yourVendorName-yourModuleName
  • PHP Mess Detector: ddev phpmd my-own-modules/yourVendorName-yourModuleName
  • PHP Stan: ddev phpstan my-own-modules/yourVendorName-yourModuleName

If you have cloned the example module Okaeli_CategoryCode in m2-sources/my-own-modules/okaeli-category-code, just replace yourVendorName-yourModuleName by okaeli-category-code.

I also added a command to launch PHPUNIT:

ddev phpunit my-own-modules/yourVendorName-yourModuleName/Test/Unit

Sorry, the example module does not have unit tests :).

More tests: Cron and Varnish

If you want to see how I used DDEV to test crons and Varnish, you can watch here.

We have just seen how to test a module locally. The implementation of GitHub actions results directly from this way of doing:

Step 3: Implementing GitHub actions

I started using GitHub actions at the same time as DDEV. It was while creating the DDEV commands that I realized that it would be "easy" to launch them in a GitHub action. And I found that it also saves time: the commands that I create locally are directly applicable in my GitHub actions.

This is why the GitHub action I use to run the static tests (PHPCS, PHPMD and PHPSTAN) follows precisely the steps that I have just described above:

  • we install DDEV
  • we install Magento with DDEV
  • we install a module with DDEV
  • we launch tests with DDEV

The main difference is that we can play on the strategy.matrix entry of the yaml action file to test several versions of Magento and PHP at the same time.

Here is what it looks like for the example module:

Small bonus if you have held up until then: these static and Varnish tests are close to those performed for the technical review during a module submission to the marketplace (see here and here). If you have a module to submit, it might save you from a failed on this step.

Conclusion

This presentation is a use case and only covers a small part of DDEV's possibilities. I hope you find it useful if you ever want to give this tool a chance.

OKAELI