- Published on
Automating Laravel CI/CD with GitHub Actions
- Authors
- Name
- Nguyen Duc Xinh
In modern software development, continuous integration and continuous deployment (CI/CD) have become essential practices. Automating the testing and deployment processes ensures faster and more reliable software delivery. If you're working with Laravel and hosting your code on GitHub, GitHub Actions provides a powerful CI/CD solution.
In this guide, we'll walk through setting up a GitHub Actions workflow for a Laravel application, including dynamically updating the Laravel .env file with MySQL credentials stored as GitHub Secrets.
Prerequisites
Before we begin, make sure you have the following:
- A Laravel application hosted on GitHub
- LEMP Stack on Your Server. If you need guidance on setting up a LEMP stack for Laravel, refer to A Quick Setup Guide for LEMP Stack, Laravel and Supervisord with custom AWS EC2 AMI
GitHub Actions Workflow CI(Continuous Integration)
1. Create CI Workflow
Now, let's create a GitHub Actions workflow file that automates the testing process. Create a .github/workflows/laravel-ci.yml
file with the following content:
cd /path/to/your/project
mkdir -p .github/workflows
vim .github/workflows/laravel-ci.yml
name: Laravel CI
on:
pull_request:
branches:
- main
- develop
- staging
jobs:
build:
name: CI
runs-on: ubuntu-latest
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: laravel
MYSQL_USER: laravel-user
MYSQL_PASSWORD: secret
services:
mysql:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: ${{ env.MYSQL_ROOT_PASSWORD }}
MYSQL_DATABASE: ${{ env.MYSQL_DATABASE }}
MYSQL_USER: ${{ env.MYSQL_USER }}
MYSQL_PASSWORD: ${{ env.MYSQL_PASSWORD }}
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
- name: Install dependencies
run: composer install
- name: Install Frontend dependencies
run: npm install
- name: Build Frontend
run: npm run build
- name: Copy environment file
run: cp .env.example .env
- name: Update Laravel .env
run: |
echo "DB_CONNECTION=mysql" >> .env
echo "DB_HOST=127.0.0.1" >> .env
echo "DB_PORT=3306" >> .env
echo "DB_DATABASE=${{ env.MYSQL_DATABASE }}" >> .env
echo "DB_USERNAME=${{ env.MYSQL_USER }}" >> .env
echo "DB_PASSWORD=${{ env.MYSQL_PASSWORD }}" >> .env
- name: Generate application key
run: php artisan key:generate
- name: Run database migrations
run: php artisan migrate
- name: Validate Format code
run: ./vendor/bin/pint
- name: Run tests
run: vendor/bin/phpunit
Understanding the Workflow
on.pull_request.branches:
: This workflow is triggered on each pull requet to the main/staging/develop branch.jobs.build
: build is job name. You can rename to an another namejobs.build.runs-on[ubuntu-latest]
. We specify that this workflow will run on ubuntu latest versionjobs.build.env:
: Declare some environment variables to use in this workflow.jobs.build.services.mysql
: We use Containerized service Mysql to connect database to this workflow. More detail Containerized servicejobs.build.steps[Checkout code]
: This is action checking out the repo. More detail actions/checkout@v2jobs.build.steps[Setup PHP]
: This is action to set up PHP. You can add parameter inwith
to specify php version, composer version, add extensions, php.ini configuration, etc.. . More detail shivammathur/setup-php@v2
2. Demo CI Workflow
- Create a new branch:
feature/setup-ci-cd
- Add somme code changes
- Push code to Github
- Create a pull request from branch
feature/setup-ci-cd
to branchdevelop
- Check the list of running workflows . In this tutorial you can navigate to this link https://github.com/ducxinh/laravel-sample/actions
- Check the detail of running workflow. You should see that the workflow executed successfully
GitHub Actions Workflow CD(Continuous Deployment)
1. Generate deployment key
ssh-keygen -t rsa -b 4096 -f ~/.ssh/awesome-laravel-deployment -P ""
cat ~/.ssh/awesome-laravel-deployment.pub
# Copy the public key and add to server
More info for SSH keys: https://www.ssh.com/ssh/public-key-authentication
2. Add deployment key to server
Add deployment key to server by add the public key to ~/.ssh/authorized_keys
file on the server
# Connect Server
ssh -i awesome-laravel-web.pem ubuntu@35.77.229.224
ssh -i /path/to/your-key.pem ubuntu@your-instance-public-ip
# Add public key to ~/.ssh/authorized_keys
sudo vim ~/.ssh/authorized_keys
3. Verify the connection to the server ok.
In this tutorial we use file awesome-laravel-deployment
ssh -i ~/.ssh/awesome-laravel-deployment ubuntu@your-instance-public-ip
4. Create CD Workflow
Let's automate the deployment process by creating a GitHub Actions workflow. Craft a .github/workflows/laravel-cd-dev.yml
file with the provided content.
cd /path/to/your/project
mkdir -p .github/workflows
vim .github/workflows/laravel-cd-dev.yml
The .github/workflows/laravel-cd-dev.yml
should looks like the following:
name: Laravel CD to EC2
on:
push:
branches:
- develop
jobs:
build:
name: Deploy Dev
runs-on: ubuntu-latest
env:
SOURCE_DIR: "./"
APP_KEY: base64:d3dLgjYPsyYu8KJQQoFj9XPlbX5soiLcaJvjIX2lIj0=
APP_URL: https://ducxinh.com
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Copy environment file
run: cp .env.example .env
- name: Update Laravel .env
run: |
echo '${{ secrets.LARAVEL_ENV_DEV }}' > .env
- name: Deploy to EC2
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i --delete"
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: ${{ secrets.REMOTE_TARGET_DIR }}
EXCLUDE: "/storage/logs/, /vendor, /node_modules/"
SCRIPT_BEFORE: |
whoami
SCRIPT_AFTER: |
echo "SCRIPT_AFTER1"
whoami
cd ${{ secrets.REMOTE_TARGET_DIR }}
composer install --optimize-autoloader --no-dev
php artisan migrate --force
php artisan db:seed --force
echo $RSYNC_STDOUT
Understanding the Workflow
on.push.branches:
: Trigger deployment branch when only on push to thedevelop
branchjobs.deploy
: deploy is job name. You can rename to an another namejobs.deploy.runs-on[ubuntu-latest]
. We specify that this workflow will run on ubuntu latest versionjobs.deploy.env:
: Declare some environment variables to use in this workflow.jobs.deploy.steps[Checkout code]
: We need to checkout the pushed code to the runner by using a predefined action namedactions/checkout@v2
. More detail actions/checkout@v2jobs.deploy.steps[Deploy to EC2]
: We are deploying the code to the server, in order to do this we need to access the EC2 using ssh and perform rsync from the runner. For this we are going to use another GitHub action easingthemes/ssh-deploy
5. Add GitHub Secret Keys
Navigate to your repository on GitHub: Setting > Security > Secrets and variables > Actions. Add the following secret keys:
- REMOTE_HOST
- REMOTE_USER
- SSH_PRIVATE_KEY =
Content: cat ~/.ssh/awesome-laravel-deployment
- REMOTE_TARGET_DIR
- LARAVEL_ENV_DEV
6. Trigger Deployment
- Modify
resources/views/welcome.blade.php
.
<title>{{ config('app.name') }}</title>
- Update
APP_NAME
in the environment file fromLaravel
toLaravelCICD
. - Push the
develop
branch to Github to trigger automated deployment workflow
7. Check Workflow Details
Monitor the progress of your running workflow for detailed insights
8. Verify the Deployment
Open your web browser and navigate to your server's IP address or domain to confirm the successful deployment of your Laravel application.
Handling IP Limitations in CD with GitHub Actions
In certain server configurations, IP whitelisting is enforced to enhance security. However, this can pose a challenge when using GitHub Actions for Continuous Deployment (CD).
If you encounter the error message ssh: connect to host *** port 22: Connection timed out
during your GitHub Actions workflow, it likely indicates that the GitHub Actions runner's IP is not whitelisted.
Identifying GitHub Actions Runner IP
GitHub provides a range of IP addresses that their GitHub-hosted runners might use. To get a list of IP address ranges that GitHub Actions uses, you can use the GitHub REST API. For more information, see the actions
key in the response of the "Meta" endpoint https://api.github.com/meta.
Authorizing GitHub Actions Runner IP
To handle this situation, you can dynamically authorize GitHub Actions IP addresses using AWS CLI commands authorize-security-group-ingress
. This step allows you to add the GitHub Actions runner's IP to the security group associated with your EC2 instance.
- Create AWS IAM user and set following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:GetCallerIdentity",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": "*"
}
]
}
- Add Github Action secret keys
- AWS_ACCESS_KEY
- AWS_SECRET_ACCESS_KEY
- EC2_SECURITY_GROUP_ID
- Update GitHub Actions workflow step:
- name: Public IP
id: ip
uses: haythem/public-ip@v1.3
- name: Print Runner Public IP
run: |
echo ${{ steps.ip.outputs.ipv4 }}
echo ${{ steps.ip.outputs.ipv6 }}
- name: AWS set Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- name: Add Github actions Runner IP to WhiteList
run: |
aws ec2 authorize-security-group-ingress \
--group-id ${{ secrets.EC2_SECURITY_GROUP_ID }} \
--protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32
# - name: Deploy to EC2...
# uses: easingthemes/ssh-deploy@main
# ...
- name: Revoke security group
if: always()
run: |
aws ec2 revoke-security-group-ingress --group-id ${{ secrets.EC2_SECURITY_GROUP_ID }} --protocol tcp --port 22 --cidr "${{ steps.ip.outputs.ipv4 }}/32"
- Verifying and Monitoring
After implementing these changes, run your GitHub Actions workflow and monitor the logs to ensure that the authorization step is successful. You should observe the authorized IP addresses in your AWS security group.
- Result
- Observe the public IP of the GitHub Actions runner: 20.57.43.165
- You can see the public IP of the GitHub Actions runner has been incorporated into your AWS security group.
- Your GitHub Actions runner is now able to establish a connection and deploy to your server via port 22
By incorporating these steps into your CD process, you ensure a secure and smooth deployment workflow, even in environments with strict IP limitations.
Conclusion
With this GitHub Actions workflow, you've automated the setup of your Laravel environment for CI/CD. GitHub Secrets keep your sensitive information secure, and each push to your repository triggers the workflow, ensuring consistent testing and deployment.
As you implement these practices, always consider security best practices and regularly monitor your workflows for any adjustments needed.
This guide provides a foundation; feel free to expand it with additional steps or any other actions that fit your development workflow.
Happy coding and deploying!