Table of Contents

R4.02 - Continuous Integration of a Symfony project with GitLab

Table of ContentsClose

1 Setting up a Symfony project and its development environment

1.1 Let's start with a new Symfony project

symfony new gitlab-symfony --full
cd gitlab-symfony

1.2 Prepare a Docker environment to deploy on your local machine

mkdir docker
cd docker
# git clone or copy the content of https://gitlab-ce.iut.u-bordeaux.fr/Pierre/gitlab-symfony/-/tree/master/docker

Let's adapt the environment to our project by modifying the docker-compose.yml file.

version: "3.8"
services:

    db_gitlab:
	image: mysql
	container_name: db_docker_gitlab
	restart: always
	volumes:
	    - db-data:/var/lib/mysql
	environment:
	    MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
	networks:
	    - dev

    www_gitlab:
	build: php
	container_name: www_docker_gitlab
	ports:
	  - "8080:80"
	volumes:
	    - ./php/vhosts:/etc/apache2/sites-enabled
	    - ../:/var/www
	restart: always
	networks:
	    - dev

networks:
    dev:

volumes:
    db-data:

We need the following containers:

  1. A MySQL container: you will have noticed the use of the MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' option, which allows us to indicate that we allow accounts without passwords. This will simplify the use of the root account when you customize your Symfony .env files. This container refers the dev network and use the db-data volume for the storage of the database.
  2. A PHP Apache container: This container is built from a Dockerfile available in php subdirectory. You will also have noticed that, in the volumes, we map a ./php/vhosts directory into the container (at the same for the website directory /var/www). This allows use to update the content or edit files outside from the container. This container also refer to the dev network.
  3. If you want, you can also add a PHPMyAdmin container with:
phpmyadmin_gitlab:
    image: phpmyadmin
    container_name: phpmyadmin_docker_gitlab
    restart: always
    depends_on:
	- db_gitlab
    ports:
	- 8080:80
    environment:
	PMA_HOST: db
    networks:
	- dev

Warning: Docker is not available at IUT department, please replace docker with podman in the following, for all the instructions related to containers on your local machine. But, of course, keep docker for instructions related to containers within Gitlab-CI.

Note: Thanks to the volumes that are managed by OverlayFS via Fuse, it is possible to use the -v option with podman, in the same way as with docker.

The vhosts.conf file need to be updated :

<VirtualHost *:80>
    ServerName localhost

    DocumentRoot /var/www/public
    DirectoryIndex /index.php

    <Directory /var/www/public>
        AllowOverride None
        Order Allow,Deny
        Allow from All

        FallbackResource /index.php
    </Directory>

    <Directory /var/www/public/bundles>
        FallbackResource disabled
    </Directory>
    ErrorLog /var/log/apache2/project_error.log
    CustomLog /var/log/apache2/project_access.log combined
</VirtualHost>

1.3 Let's start the full stack

From the docker/ subdirectory:

docker-compose up -d

And let's check that everything works by going to http://127.0.0.1:8080/

1.4 Prepare the database with Symfony

First you have set the database in .env file:

DATABASE_URL="mysql://root:@db_gitlab:3306/db_name?serverVersion=5.7"

Let's create the database from the Symfony CLI. To simplify the next commands, we will enter the shell of the www_docker_gitlab container.

Warning: In the following, all commands beginning with > /var/www# mean that they must be executed inside the container www_docker_gitlab.

docker exec -it www_docker_gitlab bash
> /var/www# php bin/console doctrine:database:create

Let's set up a Demo entity with a demo field of type string (and all the default values proposed by Symfony), create the associated migration and then run the migration.

> /var/www# php bin/console make:entity Demo
> /var/www# php bin/console make:migration
> /var/www# php bin/console doctrine:migrations:migrate

2 Setting up a GitLab repository and first commit

Symfony already initialize a Git repository. After setting up an empty project in GitLab, add the remote origin (replace the address with your own project):

# git init
git remote add origin git@gitlab.com:ramet/gitlab-symfony.git
git add .
git commit -m "Initial commit"
git push -u origin master

2.1 Write a first unit test named UnitTest

> /var/www# php bin/console make:unit-test

Let's edit the tests/UnitTest.php file and add a simple unit test:

<?php

namespace App\Tests;

use App\Entity\Demo;
use PHPUnit\Framework\TestCase;

class UnitTest extends TestCase
{
    public function testDemo()
    {
	$demo = new Demo();

	$demo->setDemo('demo');

	$this->assertTrue($demo->getDemo() === 'demo');
    }
}

2.2 Test to ensure that it works

> /var/www# php bin/phpunit

2.3 Commit this first unit test

git add .
git commit -m "add unit test"
git push

3 Configure the pipeline

Using the docker image jakzal/phpqa:php8.1 from Static Analysis Tools for PHP, we want to set up the following steps:

  • Check for security flaws in dependencies with Security Checker
  • Check the code style with PHP CS
  • Run a static analysis with PHP Stan
  • Run a static analysis of Twig files with Twig-lint
  • Run our unit tests with PHP Unit

3.1 Prepare the pipeline for static analysis

Here is a template for .gitlab-ci.yml file:

image: jakzal/phpqa:php8.1

before_script:
    - composer install

cache:
    paths:
	- vendor/

stages:
    - SecurityChecker
    - CodingStandards
    - UnitTests

security-checker:
    stage: SecurityChecker
    script:
	- local-php-security-checker  --path=./composer.lock
    allow_failure: false

phpcs:
    stage: CodingStandards
    script:
	- phpcs -v --standard=PSR12 --ignore=./src/Kernel.php ./src
    allow_failure: false

phpstan:
    stage: CodingStandards
    script:
	- phpstan analyse ./src
    allow_failure: false

twig-lint:
    stage: CodingStandards
    script:
	- twig-lint lint ./templates
    allow_failure: false

phpunit:
    stage: UnitTests
    script:
	- php bin/phpunit
    allow_failure: false

3.2 Commit this first pipeline

git add .
git commit -m "Ajout de la pipeline"
git push

gitlab_pipeline.png

4 Add functional tests with interaction with the database

The first thing we need to do is to set up the functional tests locally in our Symfony project. We need to define which database is used for testing. Let's copy the .env.test file to .env.test.local (and this one should not be committed!).

cp .env.test .env.test.local

4.1 Edit the .env.test.local file

# .env.test.local
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
DATABASE_URL="mysql://root:@db_gitlab:3306/db_test?serverVersion=5.7"

4.2 Let's create the test database, and play the migrations

> /var/www# php bin/console doctrine:database:create --env=test
> /var/www# php bin/console doctrine:migrations:migrate --env=test

4.3 Let's generate a CRUD (Demo entity) to have something to test

> /var/www# php bin/console make:crud

And let's check that everything works by going to http://127.0.0.1:8080/demo/

4.4 Let's generate a functional test named FunctionalTest

> /var/www# php bin/console make:functional-test

4.5 Edit the tests/FunctionalTest.php file

Add a first test that checks that the /demo url is responding:

<?php

namespace App\Tests;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class FunctionalTest extends WebTestCase
{
    public function testShouldDisplayDemoIndex()
    {
	$client = static::createClient();
	$client->followRedirects();
	$crawler = $client->request('GET', '/demo');

	$this->assertResponseIsSuccessful();
	$this->assertSelectorTextContains('h1', 'Demo index');
    }
}

Let's add a second test that loads the new form:

public function testShouldDisplayCreateNewDemo()
    {
	$client = static::createClient();
	$client->followRedirects();
	$crawler = $client->request('GET', '/demo/new');

	$this->assertResponseIsSuccessful();
	$this->assertSelectorTextContains('h1', 'Create new Demo');
    }

Finally, let's add a test that will insert data via the form, and then check that it exists:

public function testShouldAddNewDemo()
{
    $client = static::createClient();
    $client->followRedirects();
    $crawler = $client->request('GET', '/demo/new');

    $buttonCrawlerNode = $crawler->selectButton('Save');

    $form = $buttonCrawlerNode->form();

    $uuid = uniqid();

    $form = $buttonCrawlerNode->form([
	'demo[demo]'    => 'Add Demo For Test' . $uuid,
    ]);

    $client->submit($form);

    $this->assertResponseIsSuccessful();
    $this->assertSelectorTextContains('body', 'Add Demo For Test' . $uuid);
}

4.6 Test to ensure that it works

> /var/www# php bin/phpunit

4.7 Update the section phpunit: in the .gitlab-ci.yml file

phpunit:
    image: php:8.1-apache
    stage: UnitTests
    services:
	- name: mysql:5.7
	  alias: mysql
    variables:
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
      #MYSQL_ROOT_PASSWORD: pass_test
      MYSQL_DATABASE: myapptest
      #MYSQL_USER: myapptest
      #MYSQL_PASSWORD: myapptest
      #DATABASE_URL: 'mysql://myapptest:myapptest@mysql:3306/myapptest'
      DATABASE_URL: 'mysql://root:@mysql:3306/myapptest'
    before_script:
	- apt-get update && apt-get install -y git libzip-dev
	- curl -sSk https://getcomposer.org/installer | php -- --disable-tls && mv composer.phar /usr/local/bin/composer
	- docker-php-ext-install mysqli pdo pdo_mysql zip
	- php bin/console doctrine:database:drop --force --if-exists --env=test
	- php bin/console doctrine:database:create --env=test
	- php bin/console doctrine:migration:migrate --env=test --no-interaction
    script:
	- php bin/phpunit
    allow_failure: false

4.8 Edit the .env.test file

Let's indicate to Symfony to use the test database in the context of the tests:

# .env.test
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
APP_ENV=test
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
# DATABASE_URL="mysql://myapptest:myapptest@mysql:3306/myapptest"
DATABASE_URL="mysql://root:@mysql:3306/myapptest"

4.9 Commit this new pipeline

git add .
git commit -m "add functional tests"
git push

Warning: For some reason, the access to the test database does not work with the myapptest account, so I use the root account instead.

Note: You might have a /src/Controller/DemoController.php file that doesn't pass the PHP CS PSR-12 check. Make the modification reported (missing spaces around a "." on line 72) and do a commit + push again.

gitlab_failure.png

5 Resources

Date: 03/03/2023

Author: Pierre Ramet

Created: 2023-03-03 Fri 11:40

Validate