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!
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
ordev
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"]
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
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"
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
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
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-
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
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
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
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
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
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
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
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
15. Deployment
Deployment is automated via GitHub Actions and occurs after tests pass:
-
Main Branch:
When code is pushed to themain
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 thedev
branch, a similar process runs, but targets the development server.
Deployment Steps:
- SSH into the target server.
- Change directory to
/var/www/htm/laravel-cicd
. - 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
and the main_deploy.sh
file with execute permission
chmod +x main_deploy.sh
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!"
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)