Setting a CI/CD workflow for a Symfony project thanks to the GitHub actions

Published on 2020-03-28 • Modified on 2020-03-28

In this post, we will see how to set up a CI/CD workflow for a Symfony project thanks to the GitHub actions. It will cover from configuring PHP on the runner host to launching the unit and functional tests of the Symfony application. Let's go! 😎

» Published in "A Week of Symfony #691" (23-29 March 2020).

Prerequisite

I will assume you have a basic knowledge of Symfony, you know how to set up a Symfony application and to run tests. To be able to use GitHub actions, you must have a public repository or a PRO account for private repositories.

Configuration

  • PHP 7.4
  • Symfony 5.2.0-BETA2

Introduction

GitHub actions are available since a few months, but I didn't give a try until now. As I am using GitHub a lot and I have all my projects on it, It was logical to have my CI/CD here too.

Goal

The goal will be to be able to run the unit and functional tests of this project and also to test the code against different PHP versions. Until now, I am using PHP 7.2 on my production server, but I am planning to migrate to PHP 7.4. So, is my project ready for it? 🤔

[Edit 2020-04-03]: I have migrated to PHP 7.4 now 🙂.

If you don't know GitHub actions and want to use it for your Symfony project, the following workflow file can be a good basis to adapt to your needs.

Creating a workflow

Create a .github/workflows/symfony.yml in your project. For now, this file will contain only one job and will include all the workflow I need. I don't want to have something too complicated, so let's keep things simple. Here is the file I am currently using. As I am playing with it, it may be modified quite often, but I will reflect the changes on this blog post if something diverts from what I wrote. In the next chapter, I'll try to explain every step in a more detailed way. So here is the full file:

# https://help.github.com/en/actions
name: Full CI process for Symfony 5
on:
  push:
    branches:
    - master
env:
  ES_HTTP_PORT: 9209
jobs:
  symfony:
    name: Symfony 5.0 (PHP ${{ matrix.php-versions }})
    # https://hub.docker.com/_/ubuntu/
    runs-on: ubuntu-18.04
    services:
      # https://docs.docker.com/samples/library/mysql/
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
      # https://hub.docker.com/_/elasticsearch/
      elasticsearch:
        image: elasticsearch:6.8.6
        ports:
          - 9209:9200
        options: -e="discovery.type=single-node" --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10
    strategy:
      fail-fast: true
      matrix:
        php-versions: ['7.2', '7.4']
    steps:
      # —— Check Elasticsearch 🔎 ——————————————————————————————————————————————
      - name: Check the Elasticsearch connection from runner host
        run: |
          curl -fsSL "http://localhost:$ES_HTTP_PORT"
          curl -fsSL "http://localhost:$ES_HTTP_PORT/_cluster/health?pretty"

      # —— Setup Github actions 🐙 —————————————————————————————————————————————
      # https://github.com/actions/checkout (official)
      - name: Checkout
        uses: actions/checkout@v2

      # https://github.com/shivammathur/setup-php (community)
      - name: Setup PHP, extensions and composer with shivammathur/setup-php
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-versions }}
          extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, dom, filter, gd, iconv, json, mbstring, pdo
        env:
          update: true

      # https://github.com/zhulik/redis-action (community)
      - name: Setup Redis with zhulik/redis-action
        uses: zhulik/redis-action@1.1.0
        with:
          redis version: '5'

      # —— Composer 🧙‍️ —————————————————————————————————————————————————————————
      - name: Validate composer.json and composer.lock
        run: composer validate

      - name: Get composer cache directory
        id: composer-cache
        run: echo "::set-output name=dir::$(composer config cache-files-dir)"

      - name: Cache composer 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: Install Composer dependencies
        run: make install

      # —— Symfony 🎵 ——————————————————————————————————————————————————————————
      - name: Check Symfony requirements
        run: vendor/bin/requirements-checker

      - name: Check the Symfony console
        run: |
          php bin/console -V
          php bin/console strangebuzz:version

      ## —— Coding standards ✨ ————————————————————————————————————————————————
      - name: Coding standards checks (php_codesniffer + PHPStan)
        run: make cs

      ## —— Tests ✅ ———————————————————————————————————————————————————————————
      - name: Load Doctrine fixtures and populate the Elasticsearch indexes
        run: |
          make load-fixtures
          make populate

      - name: Run functionnal and unit tests
        run: |
          cp phpunit.xml.ci phpunit.xml
          make test

The workflow settings

Let see the most critical parameters of the file. As stated by the runs-on parameter, the runner will use the Ubuntu 18.04 LTS (Bionic) Linux distribution; it's the same as my production server. The other primary setting is the PHP matrix one:

  matrix:
    php-versions: ['7.2', '7.4']

It means that the workflow will run on both PHP 7.2 and PHP 7.4. It prevents us from duplicating the workflow file for these two PHP versions. Here, I only put two versions: the actual and the one I am about to migrate. But for an open-source project, for example, you will put all possible PHP minor (or even major) versions that your library supports. The fail-fast parameter tells if the matrix must stop as soon as a test fails.

In the service section we set the version of the components we will use (In fact, the Redis version is set in a specific action, check below):

  • MySQL 5.7
  • Elasticsearch 6.8
  • Redis 5

The workflow's steps

Several steps define the workflow. When a step fails the process stops and all the ones following aren't executed, the build is marked as failed ❌. Otherwise, it is marked as successful. In the three following sections, for each action, you can see the corresponding log output by clicking on ‣ Click here to view/hide the step output ✅. If you want to have all the logs expanded without clicking on each link, use on the button just below. To make those logs more readable, I have deleted all the extra and useless lines.

 ≪ this.expanded ? this.trans.button_collapse_all : this.trans.button_expand_all ≫

• Initialization steps

These steps are responsible for initializing the GitHub actions worker.

» Set up job

This step creates the runner and downloads the actions' repositories used by the workflow.

Click here to view/hide the step output ✅.
Current runner version: '2.165.2'
Prepare workflow directory
Prepare all required actions
Download action repository 'actions/checkout@v2'
Download action repository 'shivammathur/setup-php@v2'
Download action repository 'zhulik/redis-action@1.1.0'
Download action repository 'actions/cache@v1'

» Build zhulik/redis-action@1.1.0

It runs the action that will set up Redis. It downloads and builds the container that will provide the Redis service on port 6379. This action is available in the community market place. I wanted to test if it works well. But of course, you can set it manually in the service section of the file as we are doing it for MySQL and Elasticsearch.

Click here to view/hide the step output ✅.
Build container for action use: '/home/runner/work/_actions/zhulik/redis-action/1.1.0/Dockerfile'.
##[command]/usr/bin/docker build -t e87b52:d5b46c5afa2544c8b9b1b35120a65556 "/home/runner/work/_actions/zhulik/redis-action/1.1.0"
Sending build context to Docker daemon  7.168kB
Step 1/4 : FROM docker:stable
stable: Pulling from library/docker
c9b1b535fdd9: Already exists
cd54125436dc: Pulling fs layer
6aca4f909924: Waiting
70d1554c15cb: Verifying Checksum
70d1554c15cb: Download complete
cd54125436dc: Pull complete
Digest: sha256:5bca19bb6d8f0aea380902af97be841d5841353c04e3eda5f87b4e932a509e94
Status: Downloaded newer image for docker:stable
 ---> e036013d6d10
Step 2/4 : COPY entrypoint.sh /entrypoint.sh
 ---> c4ea3853cc97
Step 3/4 : RUN chmod +x /entrypoint.sh
 ---> Running in ab6b87d4b7a0
Removing intermediate container ab6b87d4b7a0
 ---> 87aee15fd9a8
Step 4/4 : ENTRYPOINT ["/entrypoint.sh"]
 ---> Running in 4becfb378f5b
Removing intermediate container 4becfb378f5b
 ---> f3a67891445a
Successfully built f3a67891445a
Successfully tagged e87b52:d5b46c5afa2544c8b9b1b35120a65556

» Initialize containers

This steps will be responsible for creating all the other containers declared in the workflow. It will create the resources declared in the jobs > symfony > services section of our file. In the following log output, the two are essential messages are: MySQL service is healthy, and Elasticsearch service is healthy. Those two services are ready to be used by our application.

Click here to view/hide the step output ✅.
##[command]/usr/bin/docker version --format '{{.Server.APIVersion}}'
'1.40'
Docker daemon API version: '1.40'
##[command]/usr/bin/docker version --format '{{.Client.APIVersion}}'
'1.40'
Docker client API version: '1.40'
##[command]/usr/bin/docker ps --all --quiet --no-trunc --filter "label=e87b52"
##[command]/usr/bin/docker network prune --force --filter "label=e87b52"
##[command]/usr/bin/docker network create --label e87b52 github_network_51587edd68e3421d88eaf5842fb44d03
3cfd017711d1c2c4b50be2f3db048ea812df776c39aca4d608c97410dc1d0810
##[command]/usr/bin/docker pull mysql:5.7
5.7: Pulling from library/mysql
68ced04f60ab: Pulling fs layer
702ec598d0af: Waiting
f9748e016a5c: Verifying Checksum
f9748e016a5c: Download complete
68ced04f60ab: Pull complete
Digest: sha256:f4a5f5be3d94b4f4d3aef00fbc276ce7c08e62f2e1f28867d930deb73a314c58
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
##[command]/usr/bin/docker create --name bfb02f6b5a4a454280b6be3fed6e1ba7_mysql57_ad121e --label e87b52 --network github_network_51587edd68e3421d88eaf5842fb44d03 --network-alias mysql -p 3306/tcp --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 -e "MYSQL_ROOT_PASSWORD=root" -e GITHUB_ACTIONS=true mysql:5.7
05937a8d432624612b1d06f6c174b26d6023cb9f9c0bae7efeaf36f252c5aa41
##[command]/usr/bin/docker start 05937a8d432624612b1d06f6c174b26d6023cb9f9c0bae7efeaf36f252c5aa41
05937a8d432624612b1d06f6c174b26d6023cb9f9c0bae7efeaf36f252c5aa41
##[command]/usr/bin/docker ps --all --filter id=05937a8d432624612b1d06f6c174b26d6023cb9f9c0bae7efeaf36f252c5aa41 --filter status=running --no-trunc --format "{{.ID}} {{.Status}}"
05937a8d432624612b1d06f6c174b26d6023cb9f9c0bae7efeaf36f252c5aa41 Up Less than a second (health: starting)
##[command]/usr/bin/docker port 05937a8d432624612b1d06f6c174b26d6023cb9f9c0bae7efeaf36f252c5aa41
3306/tcp -> 0.0.0.0:32768
##[command]/usr/bin/docker pull elasticsearch:6.8.6
6.8.6: Pulling from library/elasticsearch
ab5ef0e58194: Pulling fs layer
02437bb6d816: Waiting
5abe907f1981: Verifying Checksum
5abe907f1981: Download complete
ab5ef0e58194: Pull complete
Digest: sha256:be763d0b98b58a963f0496677be546211ec9aa336b0981ede98cec63325f55ef
Status: Downloaded newer image for elasticsearch:6.8.6
docker.io/library/elasticsearch:6.8.6
##[command]/usr/bin/docker create --name 7a612b8d5fa24e87830a2cc1e03aaa56_elasticsearch686_ce1133 --label e87b52 --network github_network_51587edd68e3421d88eaf5842fb44d03 --network-alias elasticsearch -p 9209:9209 -e="discovery.type=single-node" -e="http.port=9209" --health-cmd="curl http://localhost:9209/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 -e GITHUB_ACTIONS=true elasticsearch:6.8.6
64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
##[command]/usr/bin/docker start 64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
##[command]/usr/bin/docker ps --all --filter id=64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0 --filter status=running --no-trunc --format "{{.ID}} {{.Status}}"
64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0 Up Less than a second (health: starting)
##[command]/usr/bin/docker port 64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
9209/tcp -> 0.0.0.0:9209
##[command]/usr/bin/docker inspect --format="{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}" 05937a8d432624612b1d06f6c174b26d6023cb9f9c0bae7efeaf36f252c5aa41
healthy
mysql service is healthy.
##[command]/usr/bin/docker inspect --format="{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}" 64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
starting
elasticsearch service is starting, waiting 2 seconds before checking again.
##[command]/usr/bin/docker inspect --format="{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}" 64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
starting
elasticsearch service is starting, waiting 4 seconds before checking again.
##[command]/usr/bin/docker inspect --format="{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}" 64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
starting
elasticsearch service is starting, waiting 8 seconds before checking again.
##[command]/usr/bin/docker inspect --format="{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}" 64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
starting
elasticsearch service is starting, waiting 18 seconds before checking again.
##[command]/usr/bin/docker inspect --format="{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}" 64475ed7c281304c3f62e9108bcfa5b922e7c6ab2825ec4ded7937a0cc1faaf0
healthy
elasticsearch service is healthy.

• Main steps

The previous steps were automatically configured and run by GitHub. Now start the actions we have defined. They begin in the jobs > symfony > steps section of our file.

» Verify Elasticsearch connection from runner host

When I started to work on the workflow, I had problems configuring Elasticsearch. That's why I added this step very early to avoid running any other step if Elasticsearch isn't up and running. In the run command, you can see that we use an environment variable $ES_HTTP_PORT we have defined at the very beginning of the action file in the env section. With the first command, we can verify the version number, and with the second, we can check that the status of the cluster is OK (green).

Click here to view/hide the step output ✅.
Run curl -fsSL "http://localhost:$ES_HTTP_PORT/"
{
  "name" : "uwLkILk",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "_doe3REkQ4KutBwyauK1lg",
  "version" : {
    "number" : "6.8.6",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "3d9f765",
    "build_date" : "2019-12-13T17:11:52.013738Z",
    "build_snapshot" : false,
    "lucene_version" : "7.7.2",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}
Run curl -fsSL "http://localhost:$ES_HTTP_PORT/_cluster/health?pretty"
{
  "cluster_name" : "docker-cluster",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

» Checkout

This step will checkout the project from your repository.

Click here to view/hide the step output ✅.
Run actions/checkout@v2
Added matchers: 'checkout-git'. Problem matchers scan action output for known warning or error strings and report these inline.
Syncing repository: COil/strangebuzz.com
Working directory is '/home/runner/work/strangebuzz.com/strangebuzz.com'
[command]/usr/bin/git version
git version 2.25.1
Deleting the contents of '/home/runner/work/strangebuzz.com/strangebuzz.com'
[command]/usr/bin/git init /home/runner/work/strangebuzz.com/strangebuzz.com
Initialized empty Git repository in /home/runner/work/strangebuzz.com/strangebuzz.com/.git/
[command]/usr/bin/git remote add origin https://github.com/COil/strangebuzz.com
[command]/usr/bin/git config --local gc.auto 0
[command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
[command]/usr/bin/git config --local http.https://github.com/.extraheader AUTHORIZATION: basic ***
[command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +b0134f36a4f5989986a47425c7bfb0ccf7687a0e:refs/remotes/origin/master
remote: Enumerating objects: 1379, done.
remote: Counting objects:   0% (1/1379)
remote: Counting objects: 100% (1379/1379), done.
remote: Compressing objects:   0% (1/1195)
remote: Compressing objects: 100% (1195/1195), done.
Receiving objects:   0% (1/1379)
Receiving objects: 100% (1379/1379), 104.69 MiB | 35.17 MiB/s, done.
Resolving deltas:   0% (0/179)
Resolving deltas: 100% (179/179), done.
From https://github.com/COil/strangebuzz.com
 * [new ref]         b0134f36a4f5989986a47425c7bfb0ccf7687a0e -> origin/master
[command]/usr/bin/git checkout --progress --force -B master refs/remotes/origin/master
Reset branch 'master'
Branch 'master' set up to track remote branch 'master' from 'origin'.
[command]/usr/bin/git log -1
commit b0134f36a4f5989986a47425c7bfb0ccf7687a0e
Author: COil
Date:   Tue Mar 27 09:38:48 2020 +0100
    [github] pr template
Removed matchers: 'checkout-git'

» Setup PHP, extensions and Composer with shivammathur/setup-php.

This community action will setup PHP to the last available bugfix version for the current minor version. It will enable the required extensions we have specified in the action configuration (with > extensions). This action is really complete: we can even use Blackfire!

Click here to view/hide the step output ✅.
Run shivammathur/setup-php@v2
/bin/bash /opt/hostedtoolcache/linux.sh 7.4 /home/runner/work/_actions/shivammathur/setup-php/v2/dist

==> Setup PHP
✓ PHP Switched to PHP 7.4.4

==> Setup Tools
✓ composer Added

==> Setup Extensions
✓ mbstring Enabled
✓ xml Enabled
✓ ctype Enabled
✓ iconv Enabled
✓ intl Enabled
✓ pdo_sqlite Enabled
✓ dom Enabled
✓ filter Enabled
✓ gd Enabled
✓ iconv Enabled
✓ json Enabled
✓ mbstring Enabled
✓ pdo Enabled

» Start Redis with zhulik/redis-action

This step will run the Docker container that was built previously for Redis thanks to the zhulik/redis-action.

Click here to view/hide the step output ✅.
Run zhulik/redis-action@1.1.0
/usr/bin/docker run --name e87b52a634b55d4bea4f1a995d572615c8eea1_5d7367 --label e87b52
--workdir /github/workspace --rm -e ES_HTTP_PORT -e INPUT_REDIS_VERSION -e INPUT_NUMBER_OF_DATABASES
-e HOME -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER
-e GITHUB_ACTOR -e GITHUB_WORKFLOW -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GITHUB_EVENT_NAME
-e GITHUB_WORKSPACE -e GITHUB_ACTION -e GITHUB_EVENT_PATH -e RUNNER_OS -e RUNNER_TOOL_CACHE
-e RUNNER_TEMP -e RUNNER_WORKSPACE -e ACTIONS_RUNTIME_URL -e ACTIONS_RUNTIME_TOKEN
-e GITHUB_ACTIONS=true --network github_network_218d01d494464f389e98156544786390
-v "/var/run/docker.sock":"/var/run/docker.sock" -v "/home/runner/work/_temp/_github_home":"/github/home"
-v "/home/runner/work/_temp/_github_workflow":"/github/workflow" -
v "/home/runner/work/strangebuzz.com/strangebuzz.com":"/github/workspace" e87b52:a634b55d4bea4f1a995d572615c8eea1
Unable to find image 'redis:5' locally
5: Pulling from library/redis
68ced04f60ab: Already exists
7ecc253967df: Pulling fs layer
76feb725b7e3: Waiting
75797de34ea7: Verifying Checksum
75797de34ea7: Download complete
765957bf98d4: Pull complete
Digest: sha256:ddf831632db1a51716aa9c2e9b6a52f5035fc6fa98a8a6708f6e83033a49508d
Status: Downloaded newer image for redis:5
9e01d93b3b8d465e1ae8208454fc0193f93dad224750a4ad0ee002d5e336e022

» Validate composer.json and composer.lock

Now we will start all the PHP and Composer stuff. The first thing is to verify that the composer.lock file is synchronized with the composer.json one.

Click here to view/hide the step output ✅.
Run composer validate
 ./composer.json is valid

» Get Composer cache directory & cache Composer dependencies

The two following steps aren't something we would do locally as Composer itself already handles them. But as we are creating the runner from scratch, there is no existing Composer cache. That's the purpose of these two steps. The first will compute the Composer cache directory and the second one will cache the dependencies if needed.

Click here to view/hide the step output ✅.
Run echo "::set-output name=dir::$(composer config cache-files-dir)"
Run actions/cache@v1
Cache Size: ~33 MB (34280929 B)
/bin/tar -xz -f /home/runner/work/_temp/c575859b-081d-44fc-9467-3b9688b76b7c/cache.tgz -C /home/runner/.composer/cache/files
Cache restored from key: Linux-composer-472b10dc8f0c399944d4bbea30b2ac3699a936a5bbd2a87a1ec4fcbfe3845039

» Install Composer dependencies

Now we can install Composer dependencies as we are used to doing. Note that I directly use my Makefile targets to use the same commands and arguments. You can find my full Makefile in this snippet.

Click here to view/hide the step output ✅.
Run make install
php composer.phar install --no-progress --no-suggest --prefer-dist --optimize-autoloader
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 164 installs, 0 updates, 0 removals
  - Installing ocramius/package-versions (1.4.2): Loading from cache
  - Installing ... (): Loading from cache
  - Installing symfony/phpunit-bridge (v5.0.6): Loading from cache
Package zendframework/zend-eventmanager is abandoned, you should avoid using it. Use laminas/laminas-eventmanager instead.
Package zendframework/zend-code is abandoned, you should avoid using it. Use laminas/laminas-code instead.
Package guzzlehttp/streams is abandoned, you should avoid using it. No replacement was suggested.
Package guzzlehttp/ringphp is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
Deprecation Notice: Class EasyRdf_Serialiser_JsonLd located in ./vendor/easyrdf/easyrdf/lib/EasyRdf/Serialiser/JsonLd_real.php does not comply with psr-0 autoloading standard. It will not autoload anymore in Composer v2.0. in phar:///home/runner/work/strangebuzz.com/strangebuzz.com/composer.phar/src/Composer/Autoload/ClassMapGenerator.php:185
Deprecation Notice: Class EasyRdf_Parser_JsonLd located in ./vendor/easyrdf/easyrdf/lib/EasyRdf/Parser/JsonLdImplementation.php does not comply with psr-0 autoloading standard. It will not autoload anymore in Composer v2.0. in phar:///home/runner/work/strangebuzz.com/strangebuzz.com/composer.phar/src/Composer/Autoload/ClassMapGenerator.php:185
Deprecation Notice: Class Doctrine\Bundle\FixturesBundle\Tests\IntegrationTest\IntegrationTest located in ./vendor/doctrine/doctrine-fixtures-bundle/Tests/IntegrationTest.php does not comply with psr-4 autoloading standard. It will not autoload anymore in Composer v2.0. in phar:///home/runner/work/strangebuzz.com/strangebuzz.com/composer.phar/src/Composer/Autoload/ClassMapGenerator.php:185
Deprecation Notice: Class Doctrine\Bundle\FixturesBundle\Tests\IntegrationTest\IntegrationTestKernel located in ./vendor/doctrine/doctrine-fixtures-bundle/Tests/IntegrationTest.php does not comply with psr-4 autoloading standard. It will not autoload anymore in Composer v2.0. in phar:///home/runner/work/strangebuzz.com/strangebuzz.com/composer.phar/src/Composer/Autoload/ClassMapGenerator.php:185
ocramius/package-versions:  Generating version class...
ocramius/package-versions: ...done generating version class

» Check Symfony requirements

Composer packages are installed. But is the worker host has the requirements to run our Symfony application? Let's verify with the requirements-checker command.

Click here to view/hide the step output ✅.
Run vendor/bin/requirements-checker

Symfony Requirements Checker
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

> PHP is using the following php.ini file:
/etc/php/7.4/cli/php.ini

> Checking Symfony requirements:
.....................................

                                                                                                                        
 [OK]                                                                                                                   
 Your system is ready to run Symfony project                                                                            
                                                                                                                        

Note  The command console could use a different php.ini file
~~~~  than the one used with your web server. To be on the
      safe side, please check the requirements from your web
      server using the public/check.php script.

» Check the Symfony console

Great, the worker meets the Symfony 5 requirements. Let's test our application then. First, we will check that we can run the Symfony console. If it doesn't work, no way the unit nor the functional tests could run without a problem. First, we run the console only with the -V argument to check the Symfony version and then we run the strangebuzz:version command of the project to view the application version number.

Click here to view/hide the step output ✅.
Run php bin/console -V
Symfony 5.0.7 (env: dev, debug: true)

Run php bin/console strangebuzz:version

 ! [NOTE] strangebuzz 2.0.1                                                                                             
                                                                                                                        
 [OK]  -> DONE!                                                                                                         
                                                                                                                        

» Coding standards checks (php_codesniffer + PHPStan)

Before running the tests, let's check the coding standards. It will run php_codesniffer and PHPStan. I had put this step before the tests because I wanted to have the tests step to be the final one.

Click here to view/hide the step output ✅.
Run make cs
./vendor/squizlabs/php_codesniffer/bin/phpcs --standard=phpcs.xml -n -p src/
............................................................  60 / 101 (59%)
.........................................                    101 / 101 (100%)


Time: 2.43 secs; Memory: 16MB

./vendor/bin/phpstan analyse -l max --memory-limit 1G -c phpstan.neon src/
   0/101 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░]   0%
  40/101 [▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░]  39%
  80/101 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░]  79%
 101/101 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

                                                                                                                        
 [OK] No errors                                                                                                         
                                                                                                                        

» Load Doctrine fixtures and populate the Elasticsearch indexes

To be able to run the tests, we must load the Doctrine fixtures in the MySQL database and populate the Elasticsearch indexes.

Click here to view/hide the step output ✅.
Run make load-fixtures

php bin/console doctrine:cache:clear-metadata

 // Clearing all Metadata cache entries

                                                                                                                        
 [OK] Successfully deleted cache entries.                                                                               
                                                                                                                        

php bin/console doctrine:database:create --if-not-exists

Created database `strangebuzz` for connection named default

php bin/console doctrine:schema:drop --force

 Dropping database schema...

                                                                                                                        
 [OK] Database schema dropped successfully!                                                                             
                                                                                                                        

php bin/console doctrine:schema:create


 !                                                                                                                      
 ! [CAUTION] This operation should not be executed in a production environment!                                         
 !                                                                                                                      

 Creating database schema...

                                                                                                                        
 [OK] Database schema created successfully!                                                                             
                                                                                                                        

php bin/console doctrine:schema:validate

Mapping
-------

 [OK] The mapping files are correct.

Database
--------

                                                                                                                        
 [OK] The database schema is in sync with the mapping files.                                                            
                                                                                                                        

php bin/console doctrine:fixtures:load -n

   > purging database
   > loading App\DataFixtures\LoadArticleTypeData
   > loading App\DataFixtures\LoadImageObjectData
   > loading App\DataFixtures\LoadOrganizationData
   > loading App\DataFixtures\LoadArticleData

php bin/console fos:elastica:reset

Resetting app
Resetting suggest

php bin/console fos:elastica:populate --index=app

Resetting app
  0/79 [>---------------------------]   0%
 79/79 [============================] 100%
Populating app/articles
Refreshing app

php bin/console strangebuzz:populate
Populate the "suggest" Elasticsearch index
 -> TODO: 2942 -> DONE: 2942, "fr" keywords indexed.
 -> TODO: 2046 -> DONE: 2046, "en" keywords indexed.

» Run functional and unit tests

Data are loaded. Now let's run all the test, that's the foremost step that will decide if our build is successful or not. Before running the tests, I copy a specific file to set up the right variables for the CI environment.

Run cp phpunit.xml.ci phpunit.xml
./vendor/bin/phpunit --testsuite=main --stop-on-failure
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

Testing
...............................................................  63 / 235 ( 26%)
............................................................... 126 / 235 ( 53%)
............................................................... 189 / 235 ( 80%)
..............................................                  235 / 235 (100%)

Time: 19.74 seconds, Memory: 98.25 MB

OK (235 tests, 346 assertions)

Victory! 🎉 This was the last steps of the build. All unit and functional tests are green ✅. We know that we can safely deliver the code to production.

• Job shutdown

We are done with the steps we have defined. Now remain four steps internal to GitHub to finish the job properly. Here they are, I have only put one log output for this group of steps. The last one is to destroy the Docker containers.

  • Post Cache Composer dependencies
  • Post Checkout
  • Stop containers
  • Complete job
Click here to view/hide the step output ✅.
Post job cleanup.
Cache hit occurred on the primary key Linux-composer-472b10dc8f0c399944d4bbea30b2ac3699a936a5bbd2a87a1ec4fcbfe3845039, not saving cache.
Post job cleanup.
[command]/usr/bin/git version
git version 2.25.1
[command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
http.https://github.com/.extraheader
[command]/usr/bin/git config --local --unset-all http.https://github.com/.extraheader
Stop and remove container: 17a0515ca90e4d00923b6ed3b6f2e9f0_mysql57_58c726
##[command]/usr/bin/docker rm --force c4d2e8de1f3465c60085624ea5efb82e9e0a124a5bd5a8460097fbc623c62226
c4d2e8de1f3465c60085624ea5efb82e9e0a124a5bd5a8460097fbc623c62226
Stop and remove container: 93aca059f6194ee38a764314b1f45fb7_elasticsearch686_1e39f8
##[command]/usr/bin/docker rm --force e96226707f7050abcb10c740530990ab4eef0f25ff96261777c6e0eea9159c53
e96226707f7050abcb10c740530990ab4eef0f25ff96261777c6e0eea9159c53
Remove container network: github_network_218d01d494464f389e98156544786390
##[command]/usr/bin/docker network rm github_network_218d01d494464f389e98156544786390
github_network_218d01d494464f389e98156544786390
Cleaning up orphan processes

And here is what it looks like when everything runs without a problem. So good to see so much "green" ✅ ,isn't it? 😁

All GitHub actions steps are successful!

And of course, the global check mark associated with each commit:

The global check by commit

Conclusion

I've discovered GitHub actions recently; I must say I am really satisfied with this tool. It is now my primary CI/CD provider, as all my projects are on GitHub. Of course, there are many things to add and improve. But as it is now, it does the job. It ensures that the tests are OK and that the project is ready to be migrated to PHP 7.4 without having to change a single line of code.

That's it! I hope you like it. Check out the links below to have additional information related to the post. As always, feedback, likes and retweets are welcome. (see the box below) See you! COil. 😊

  Read the doc  More on the web  More on Stackoverflow

They gave feedback and helped me to fix errors and typos in this article, many thanks to Laurent Q, jmsche. 👍


Call to action

Did you like this post? You can help me back in several ways: (use the Tweet on the right to comment or to contact me )

Thank you for reading! And see you soon on Strangebuzz! 😉

COil