DEV Community

Mahmud Ibrahim
Mahmud Ibrahim

Posted on • Edited on

Step-by-Step Guide: Laravel CI/CD with GitHub Actions

Continuous Integration (CI) is a must-have for modern web development. It helps you catch bugs early, ensures code quality, and saves you from manual testing headaches. In this article, I’ll walk you through a real-world GitHub Actions workflow for a Laravel project. Whether you’re new to CI/CD or just want to see how it works for Laravel, this guide is for you!

CI/CD Workflow Overview


What is GitHub Actions?

GitHub Actions is a free automation tool built into GitHub. It lets you run scripts and commands automatically when certain events happen in your repository—like pushing code or opening a pull request.


Workflow Overview

Our workflow will:

  • Run on every push or pull request to the main or dev branches.
  • Set up a MySQL database for testing.
  • Install PHP and all required extensions.
  • Cache Composer dependencies for faster builds.
  • Install your Laravel app’s dependencies.
  • Run static analysis and code quality checks.
  • Run your automated tests.

Let’s break down each step!


1. Workflow Triggers

on:
  push:
    branches: ["main", "dev"]
  pull_request:
    branches: ["main", "dev"]
Enter fullscreen mode Exit fullscreen mode

This tells GitHub to run the workflow every time you push code or open a pull request to the main or dev branches.


2. Define the Job and Environment

jobs:
  tests:
    runs-on: ubuntu-latest
Enter fullscreen mode Exit fullscreen mode

We define a job called tests that runs on the latest Ubuntu Linux environment provided by GitHub.


3. Set Up MySQL Database

services:
  mysql:
    image: mysql:8.0
    ports:
      - 3306:3306
    env:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: test_db
    options: --health-cmd="mysqladmin ping"
Enter fullscreen mode Exit fullscreen mode

We spin up a MySQL 8.0 database in Docker, set the root password, and create a database called test_db. The health check ensures MySQL is ready before tests run.


4. Checkout Your Code

- uses: actions/checkout@v4
Enter fullscreen mode Exit fullscreen mode

This step pulls your repository’s code into the workflow environment so the next steps can work with it.


5. Setup PHP with All Laravel Extensions

- name: Setup PHP with PECL extension
  uses: shivammathur/setup-php@v2
  with:
    php-version: '8.4'
    extensions: mbstring, bcmath, ctype, fileinfo, json, tokenizer, xml, pdo, pdo_mysql, openssl, curl, zip, imagick, swoole
Enter fullscreen mode Exit fullscreen mode

We install PHP 8.4 and all the extensions Laravel and its ecosystem commonly need. This ensures your app runs just like it would in production.


6. Cache Composer Dependencies

- name: Cache Composer dependencies
  uses: actions/cache@v4
  with:
    path: vendor
    key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
    restore-keys: |
      ${{ runner.os }}-composer-
Enter fullscreen mode Exit fullscreen mode

This is “smart caching.” If your dependencies haven’t changed, Composer can skip downloading and installing them, making your workflow much faster.


7. Copy the CI Environment File

- name: Copy .env file
  run: |
    cp .env.ci .env
Enter fullscreen mode Exit fullscreen mode

We copy a special .env.ci file to .env. This file should contain settings for your test database and other CI-specific configs.


8. Install Composer Dependencies

- name: Install dependencies
  run: |
    composer install -q --no-ansi --no-interaction --no-scripts --no-progress
Enter fullscreen mode Exit fullscreen mode

We install all PHP dependencies your Laravel app needs, using flags to make the process faster and quieter.


9. Generate Laravel Application Key

- name: Generate application key
  run: |
    php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

Laravel needs an application key for encryption and sessions. This command generates it.


10. Set Directory Permissions

- name: Directory permissions
  run: |
    sudo chown -R $USER:$USER storage bootstrap/cache
    chmod -R 775 storage bootstrap/cache
Enter fullscreen mode Exit fullscreen mode

Laravel needs write access to storage and bootstrap/cache for logs and cache files. This step ensures the permissions are correct.


11. Run Static Analysis with PHPStan

- name: phpstan
  run: |
    ./vendor/bin/phpstan analyse --level=5 --memory-limit=1G
Enter fullscreen mode Exit fullscreen mode

PHPStan checks your code for bugs and potential issues without running it. This helps you catch problems early.


12. Run Code Quality Checks with PHP Insights

- name: phpinsights
  run: |
    php artisan insights --no-interaction \
    --min-quality=90 --min-complexity=90 \
    --min-architecture=90 --min-style=90 \
    --ansi --format=github-action
Enter fullscreen mode Exit fullscreen mode

PHP Insights analyzes your code for quality, complexity, architecture, and style. The workflow enforces minimum scores for each metric.


13. Show Database Configuration

- name: Show DB config
  run: |
    grep DB_ .env
Enter fullscreen mode Exit fullscreen mode

This step prints out the database settings being used, so you can verify your tests are running against the correct database.


14. Run Your Tests!

- name: Run tests
  run: |
    php artisan test
Enter fullscreen mode Exit fullscreen mode

15. Deployment

Deployment is automated via GitHub Actions and occurs after tests pass:

  • Main Branch:

    When code is pushed to the main branch, the workflow connects to the main server using SSH and runs the deployment script (main_deploy.sh).

    • SSH credentials are securely managed with GitHub Secrets.
    • The script puts the application into maintenance mode, pulls the latest code, runs migrations, sets permissions, clears and caches configuration, and brings the application back up.
  • Dev Branch:


    When code is pushed to the dev branch, a similar process runs, but targets the development server.

Deployment Steps:

  1. SSH into the target server.
  2. Change directory to /var/www/htm/laravel-cicd.
  3. Execute ./main_deploy.sh main.

You can customize the deployment script (main_deploy.sh) as needed for your environment.

- name: Deployment to Main Server
              if: github.ref == 'refs/heads/main'
              uses: appleboy/ssh-action@master
              with:
                host: ${{ secrets.MAIN_SSH_HOT }}
                username:  ${{ secrets.SSH_USER }}
                password:  ${{ secrets.SSH_PASS }}
                script: |
                  cd /var/www/htm/laravel-cicd && ./main_deploy.sh main
- name: Deployment to Dev Server
              if: github.ref == 'refs/heads/dev'
              uses: appleboy/ssh-action@master
              with:
                host: ${{ secrets.SSH_HOST }}
                username:  ${{ secrets.SSH_USER }}
                password:  ${{ secrets.SSH_PASS }}
                script: |
                  cd /var/www/htm/laravel-cicd && ./main_deploy.sh main
Enter fullscreen mode Exit fullscreen mode

and the main_deploy.sh file with execute permission

chmod +x main_deploy.sh
Enter fullscreen mode Exit fullscreen mode
set -e
echo "Deploying the application..."

php artisan down
echo "Putting the application into maintenance mode..."

git pull origin main
echo "Pulling the latest changes from the main branch..."

php artisan migrate --force
echo "Running database migrations..."

php artisan up
echo "Bringing the application back up..."

chmod -R 775 storage bootstrap/cache
echo "Setting permissions..."

php artisan optimize:clear
echo "Clearing caches..."
php artisan config:cache
echo "Caching configuration..."

echo "Deployment completed successfully!"

Enter fullscreen mode Exit fullscreen mode

Finally, we run all your Laravel tests. These tests will use the MySQL test database you set up earlier.


Conclusion

With this workflow, every push or pull request is automatically tested in a fresh environment, with a real database, and all the tools you need for code quality. This helps you catch bugs early, maintain high standards, and move faster as a team.

Tip:

Make sure your .env.ci file is committed and contains the correct database settings for CI!


Happy automating!

If you have questions or want to see more CI/CD tips for Laravel, let me know in the comments.


Github Link: https://github.com/rafi021/laravel-cicd

Feel free to copy, modify, and use this workflow in your own Laravel projects!

Top comments (0)