This is an overview of how we got our e2e cypress tests running for our Laravel backend, and React frontend. 🚀
Even if your stack is identical to ours, your environment may still slightly differ depending on versions, and providers. This post is only meant to serve as a guide, and not as a copy-paste solution.
Sections
-
.gitlab-ci.yml
- Gitlab CI config - API Docker Image - Laravel app
TL;DR, Create a single Docker image for your Laravel App + webserver, and use it as a Gitlab service
Motivation
I've spent the last 2 days struggling to make this happen. Through a series of small undocumented wins, we finally reached that sweet ✅ pipeline. Seeing as I've had to piece together information from various sources, and travel back in time, I thought I'd summarize my findings here in case it helps anyone with a similar stack.
.gitlab-ci.yml
E2E Local Tests:
image: cypress/browsers:node16.13.0-chrome95-ff94
services:
- mysql:5.7
- name: registry.gitlab.com/your_project/api_ci:latest
alias: api
variables:
# Create separate network, required for services to talk to each other
# Reference: https://docs.gitlab.com/ee/ci/services/#connecting-services
FF_NETWORK_PER_BUILD: 1
MYSQL_DATABASE: myapp
MYSQL_ROOT_PASSWORD: secret
DB_USERNAME: root
DB_DATABASE: myapp
DB_PASSWORD: secret
DB_HOST: mysql
REACT_APP_API_URL: http://api:8000
script:
# Verify the api is up, and running (optional)
- curl http://api:8000
# Install npm packages, and start server in background
- npm install
- npm run start&
- sleep 120 # Wait for server to be up
- npm run e2e:local # run tests
- Set
FF_NETWORK_PER_BUILD
to tell Gitlab to create a netowrk for our job. This is required if you want your services to talk to each other, ie., api, and db, which we do. - registry.gitlab.com/your_project/api_ci:latest would be your API Laravel app, bundled in a single docker image with your web-server. In our case it was our app + nginx (with php-fpm).
- Aliased the API service to
api
- Make sure you set the
DB_HOST
to the service name as well, in this casemysql
. - Tell React app the API url is now
http://api:8000
, whereapi
is the alias we gave our service above. - Start react app with
&
to run in background.
API Docker Image - Laravel app
To get the API up, and running, 2 solutions came to mind:
- Use
git
to pull in the project, and setup volume mounts + nginx service - Bundle everything together in a single Docker image
Option 2. is definitely the simpler option, so that's the one we went with.
Dockerfile
# Build
FROM php:7.4-fpm as build
# Install PHP dependencies to get Laravel up, and running
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip \
libfreetype6-dev \
libjpeg62-turbo-dev \
libzip-dev \
cron \
openssh-client
# Install xdebug for code coverage
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype=/usr/include/ --with-jpeg=/usr/include/
RUN docker-php-ext-install -j$(nproc) gd pdo_mysql zip bcmath pcntl
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Where php-fpm expects the project files to live
WORKDIR /var/www
# FPM default user (www-data) must own the files,
# or we'll hit a permission error in Laravel
RUN chown -R www-data:www-data /var/www
# Include a docker-init.sh (optional)
COPY ./Docker/dev/docker-init.sh /usr/local/bin/docker-init.sh
RUN chmod +x /usr/local/bin/docker-init.sh
# Bake our entire project into the image
COPY . .
# Copy configs
COPY supervisord.conf /etc/supervisord.conf
COPY nginx-site.conf /etc/nginx/conf.d/default.conf
# Copy start script
COPY start.sh /start.sh
RUN chmod 755 /start.sh
# IMPORTANT - need to tell gitlab which port to check, otherwise it will timeout at 'waiting at services'
EXPOSE 8000
ENTRYPOINT [ "docker-init.sh" ]
CMD ["/start.sh"]
- Installed nginx & supervisor to run the app.
- Copied all files into the image.
-
docker-init.sh
custom script that does prep stuff like run migrations, and seeders. -
start.sh
is the final docker command, in this case we're starting supervisor
Bonus: docker-init.sh
Here's where we init the app.
#!/bin/sh
# Exit if any fails
set -e
# Install dependencies / upgrade packages
composer install
# Clear cache/config to make sure env is read
php artisan route:clear
php artisan config:clear
# Migrate
php artisan migrate:refresh --seed
php artisan storage:link
# execute default entrypoint
docker-php-entrypoint $@
Next we have our start script, which is really only just starting supervisor.
start.sh
#!/bin/bash
# Start supervisord and services
exec /usr/bin/supervisord -n -c /etc/supervisord.conf
Build & Run
Once you've got your Dockerfile
ready, build, and push it to your private Gitlab repository.
Note that the image name has to be the full URL of the repository.
Other Issues
App reading wrong ENV
If you find your API not reading the ENV set in either .gitlab-ci.yaml
or the Dockerfile, it's because php-fpm
aren't reading those values. Most likely because it was started by supervisor.
The fix is to make sure you use the same values in a .env
file.
Top comments (0)