One of the ways ExtDN is trying to advance the Magento ecosystem is through promoting good practice in extension coding standards. We are happy to announce that to support this mission we have started releasing some open source tools that can be easily embedded in any Magento 2 extension development process using Github Actions.

While there is more to a great extension using these actions we believe provide a good baseline.

All you need is a repo and then copy and paste our pre-setup Github Actions.

Magento 2 Coding Standard

Back in the early days there were many differing standards in circulation in the Magento ecosystem:

  • PSR-2 (Magento is part of PHP-FIG after all)
  • MEQP-2 (Marketplace)
  • ECG-2 (Expert consulting group)
  • Standard contained in the Magento 2 code base

Fortunately all of the above have now been replaced by the unified Magento 2 Coding Standard with the core code also adhering to it.

This Github action allows you to test your own code against this standard in an automated fashion.

Screenshot Github Action with Coding Standard

Create a new file .github/workflows/coding-standard.yml then copy the following content

name: ExtDN M2 Coding Standard
on: [push, pull_request]

jobs:
  static:
    name: M2 Coding Standard
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: extdn/github-actions-m2/magento-coding-standard@master

once this is pushed to Github you are all set and any new code pushes and pull request will run through Magento’s coding standard check.

Magento 2 PHPStan

PHPStan is a static code analysis tool that understands more of your code than the Php_CodeSniffer that the Magento 2 Coding Standard above is based on, thanks in large parts being Php 7+ and having access to the underlying code syntax tree.

This allows PHPStan to catch a wide range of issues without the need to write explicit tests for it. Issues that show up in PHPStan include calling inexistent classes, methods, wrong parameter counts, etc.

Adobe has recently introduced PHPStan to the Magento core code checks. This Github action extracts this check to be run standalone against your code.

Screenshot Github Action with PHPStan

To use it create a new file .github/workflows/phpstan.yml with the following content:

name: ExtDN M2 PhpStan
on: [push, pull_request]

jobs:
  phpstan:
    name: M2 PhpStan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: extdn/github-actions-m2/magento-phpstan@master
        with:
          composer_name: foo/magento2-foobar

Magento 2 Mess Detector

This action extracts the Magento 2 mess detector ruleset into its own action. Usage is through creating .github/workflows/mess-detector.yml with this content:

name: ExtDN M2 Mess Detector
on: [push, pull_request]

jobs:
  phpmd:
    name: M2 Mess Detector
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: extdn/github-actions-m2/magento-mess-detector@master

Magento 2 Integration Tests

Integration tests are a great way to test that your code works well within a given Magento installation. This Github Action provides a blueprint on how to get your existing tests run as a Github action.

In your Github repository add .github/workflows/integration.yml

name: ExtDN M2 Integration Tests
on: [push, pull_request]

jobs:
  integration-tests:
    name: Magento 2 Integration Tests
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306:3306
        options: --tmpfs /tmp:rw --tmpfs /var/lib/mysql:rw --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
      es:
        image: docker.io/wardenenv/elasticsearch:7.8
        ports:
          - 9200:9200
        env:
          'discovery.type': single-node
          'xpack.security.enabled': false
          ES_JAVA_OPTS: "-Xms64m -Xmx512m"
        options: --health-cmd="curl localhost:9200/_cluster/health?wait_for_status=yellow&timeout=60s" --health-interval=10s --health-timeout=5s --health-retries=3
    steps:
      - uses: actions/checkout@v2
      - name: M2 Integration Tests with Magento 2 (Php7.4)
        uses: extdn/github-actions-m2/magento-integration-tests/7.4@master
        with:
          module_name: Foo_Bar
          composer_name: foo/magento2-foobar
          ce_version: '2.4.0'

Magento 2 Performance Profiling

Thanks to our ExtDN supporter Blackfire.io we have been able to work on a performance test for extensions.

The test we have come up with for the initial release is meant as a way to flag extensions that have obvious performance implications. The test runs a performance profile of the category page before and after installing an extension. We excluded the block level cache and fullpage cache to get a true representation of what happens at the application level. The profiles are then compared and will fail if memory consumption, execution time or number of sql queries goes up substantially (more than 25%).

Why the category page? For the initial implementation we picked a page that historically has been one that exhibits performance issues often. What might look like a small delay on a single product can be substantial if the category page loads a lot of products.

Why 25% as a default threshold? In our testing so far we have seen a variation of around 5-10% on the before and after snapshots without any code changes. Setting the threshold to 25% provides a bit of headroom for an extension to add something to the category page. We want to catch the outliers here.

In your GitHub repository add the below as .github/workflows/performance.yml

name: ExtDN M2 Performance Testing
on: [push, pull_request]

jobs:
  performance:
    name: M2 Performance Testing
    runs-on: ubuntu-latest
    env:
      DOCKER_COMPOSE_FILE: "./docker-compose.yml"
      EXTENSION_NAME: "Foo_Bar"
      EXTENSION_PACKAGE_NAME: "foo/magento2-foobar"

    steps:
      - uses: actions/checkout@v2
        name: Checkout files
        with:
          path: extension

      - name: Get composer cache directory
        id: composer-cache
        run: "echo \"::set-output name=dir::$(composer config cache-dir)\""
        working-directory: ./extension

      - name: Cache dependencies
        uses: actions/cache@v1
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Prepare ExtDN performance testing
        uses: extdn/github-actions-m2/magento-performance-setup@master
        env:
          BLACKFIRE_CLIENT_ID: ${{ secrets.BLACKFIRE_CLIENT_ID }}
          BLACKFIRE_CLIENT_TOKEN: ${{ secrets.BLACKFIRE_CLIENT_TOKEN }}
          BLACKFIRE_SERVER_ID: ${{ secrets.BLACKFIRE_SERVER_ID }}
          BLACKFIRE_SERVER_TOKEN: ${{ secrets.BLACKFIRE_SERVER_TOKEN }}

      - name: Install Magento
        run: >-
          docker-compose -f ${{ env.DOCKER_COMPOSE_FILE }} exec -T php-fpm
          bash -c 'cd /var/www/html/m2 && sudo chown www-data: -R /var/www/html/m2 && ls -al && id
          && php -f bin/magento setup:install --base-url=http://magento2.test/ --backend-frontname=admin --db-host=mysql --db-name=magento_performance_tests --db-user=root --db-password=123123q --admin-user=admin@example.com --admin-password=password1 --admin-email=admin@example.com --admin-firstname=firstname --admin-lastname=lastname'
      - name: Generate Performance Fixtures
        run: >-
          docker-compose -f ${{ env.DOCKER_COMPOSE_FILE }} exec -T php-fpm
          bash -c 'cd /var/www/html/m2
          && php -f bin/magento setup:performance:generate-fixtures setup/performance-toolkit/profiles/ce/small.xml
          && php -f bin/magento cache:enable
          && php -f bin/magento cache:disable block_html full_page'
      - name: Run Blackfire
        id: blackfire-baseline
        run: docker-compose -f ${{ env.DOCKER_COMPOSE_FILE }} run blackfire-agent blackfire --json curl http://magento2.test/category-1/category-1-1.html > ${{ github.workspace }}/baseline.json
        env:
          BLACKFIRE_CLIENT_ID: ${{ secrets.BLACKFIRE_CLIENT_ID }}
          BLACKFIRE_CLIENT_TOKEN: ${{ secrets.BLACKFIRE_CLIENT_TOKEN }}
          BLACKFIRE_SERVER_ID: ${{ secrets.BLACKFIRE_SERVER_ID }}
          BLACKFIRE_SERVER_TOKEN: ${{ secrets.BLACKFIRE_SERVER_TOKEN }}

      - name: Install Extension
        run: >-
          docker-compose -f ${{ env.DOCKER_COMPOSE_FILE }} exec -e EXTENSION_BRANCH=${GITHUB_REF#refs/heads/} -T php-fpm
          bash -c 'cd /var/www/html/m2
          && php -f vendor/composer/composer/bin/composer config repo.extension path /var/www/html/extension
          && php -f vendor/composer/composer/bin/composer require ${{ env.EXTENSION_PACKAGE_NAME }}:dev-$EXTENSION_BRANCH#${{ github.sha }}
          && php -f bin/magento module:enable ${{ env.EXTENSION_NAME }}
          && php -f bin/magento setup:upgrade
          && php -f bin/magento cache:enable
          && php -f bin/magento cache:disable block_html full_page'
      - name: Run Blackfire Again
        id: blackfire-after
        run: docker-compose -f ${{ env.DOCKER_COMPOSE_FILE }} run blackfire-agent blackfire --json curl http://magento2.test/category-1/category-1-1.html > ${{ github.workspace }}/after.json
        env:
          BLACKFIRE_CLIENT_ID: ${{ secrets.BLACKFIRE_CLIENT_ID }}
          BLACKFIRE_CLIENT_TOKEN: ${{ secrets.BLACKFIRE_CLIENT_TOKEN }}
          BLACKFIRE_SERVER_ID: ${{ secrets.BLACKFIRE_SERVER_ID }}
          BLACKFIRE_SERVER_TOKEN: ${{ secrets.BLACKFIRE_SERVER_TOKEN }}

      - name: Compare Performance Results
        uses: extdn/github-actions-m2/magento-performance-compare@master

Additionally please create the following Github Secrets for this repository with the values available in your blackfire.io account:

BLACKFIRE_CLIENT_ID
BLACKFIRE_CLIENT_TOKEN
BLACKFIRE_SERVER_ID
BLACKFIRE_SERVER_TOKEN
Blackfire.io secrets in Github

Summary

We hope these actions provide you with a starting point on your testing journey towards writing quality Magento 2 extensions. Any questions or suggestions please a leave a comment in our repo here.