Interested in generating passive income? Join our partnership program and receive a commission on each new client referral. Learn more.
12 min read
Interested in generating passive income? Join our partnership program and receive a commission on each new client referral. Learn more.
This article provides a detailed guide for setting up a simple CI/CD pipeline with Github Actions for your Laravel project.
However, let's start by getting familiar with what continuous integration and continuous deployment pipelines are all about.
(if you are already familiar with the concepts, you can head straight to the guide below)
CI stands for Continuous Integration. It's an automated approach for continuously integrating software development updates with the codebase. It automates the source code's building, linting, and testing to ensure its validity. Using CI, development teams can discover errors and security problems more quickly and efficiently. Bugs are easier and cheaper to fix in the earlier stage.
Continuous integration places high importance on testing automation to ensure that the application works as expected when new commits are merged.
CD stands for continuous deployment. It's an automated approach for deploying the software to different environments like development, staging, and production. With continuous deployment, all the steps for deploying a new software version, like pulling the new codebase version from the source control, linting, testing, building, installing dependencies, running database migrations, etc., get fully automated.
Continuous deployment might also involve automation of infrastructure provisioning and environment setup and configuration.
Making the deployment process consistent, robust, and effortless allows development teams to deploy the updates frequently, which is probably the most important thing for ensuring the effective development of software products.
To implement Continuous Integration and Continuous Deployment for your project, you will need to set up pipelines. In simple terms, a pipeline consists of two main parts: a trigger event and a sequence of actions executed after the trigger event happens.
The trigger can be any event, like making a pull request, merging one branch into another, creating a new branch, or pushing a commit to the repository. If needed, you may also define custom trigger endpoints.
You can already see how executing a particular sequence of actions can be helpful after those kinds of trigger events. For example, you might need to run a test suite, a linter, the build process, the deployment process, send a notification on success or failure, etc. You can fully automate all of that once you implement CI and CD pipelines.
Several tools help us to set up pipelines with their triggers and sequences. Some examples are Jenkins, Github Actions, and Gitlab CI/CD. Of course, there are many more, but let's concentrate on the ones provided by Github and Gitlab.
Because both host git repositories, Github Actions, and Gitlab CI/CD natively provide support for the git repository triggers. They are configurable with YML files, where you define the trigger events and describe pipeline actions in a declarative manner.
The CI/CD tools give options to select the environment the pipeline will run in. For example, you can choose the Ubuntu server, and the pipeline will be executed in an Ubuntu environment. This means that your pipeline can execute Ubuntu shell commands, e.g., 'docker build -t test-tag .' to build a docker image and 'docker push' to push it to the Docker registry.
With the Ubuntu server, you can do so much more. Besides building and testing the code, you can, for example, use AWS CLI to communicate with AWS for resource provisioning and configuration. So with the suitable trigger event and the environment, you can automate anything you do manually with the pipeline.
Now let's look at how you can set up a simple Continuous Deployment pipeline for your Laravel project with Github Actions.
Let's start with the SSH key. We have to enable GitHub to access our server. We can achieve this without the SSH key, but this way is safer and less complicated.
So let's get started.
First of all, we should indeed access the server ourselves through SSH:
ssh root@127.0.0.1
Then we should create an SSH key with the following command:
ssh-keygen
It will ask you several questions here; however, you are recommended to choose default in each case.
This is the command which creates public and private SSH keys. But we will need a public one just yet. To see the latter, you should write the following command in the terminal:
cat ~/.ssh/id_rsa.pub
You should copy the output of the command and then execute the command:
sudo vim ~/.ssh/authorized_keys
You should add previously created keys in several places on Github. Adding public keys to our profile goes as follows:
Now you should go to the repository where you want to set up CI/CD Pipeline.
Switch to the repository settings and find the Secrets tab.
Add several repository secrets:
1) SSH_HOST, which will be our server IP address.
2) SSH_USERNAME, our username (one that we use in the SSH command).
3) SSH_KEY, which is our private key (do not paste it anywhere else!); You can access your private SSH key by running this command:
cat ~/.ssh/id_rsa
Then copy the entire output of that command.
And now, it's time to configure the CD pipeline.
Create two folders in the project: First, create it in the .github project route and put the .workflows in the .github. Next, you should create a file in .workflows. (you can name it whatever you want, but it should be clear). So let's create the yml file called deploy.yml.
Write the following configuration in this file:
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: mirromutth/mysql-action@v1.1
with:
mysql database: laravel-test-db
mysql user: laravel_test_user
mysql password: example
- name: Copy .env
run: cp .env.example .env
- name: Install composer Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: Install node dependencies
run: npm ci
- name: Setup Project
run: |
php artisan config:clear
php artisan cache:clear
php artisan key:generate
npm run build
- name: Directory Permissions
run: chmod 755 -R storage bootstrap/cache
- name: Run Unit tests
env:
APP_ENV: testing
DB_CONNECTION: mysql
DB_USERNAME: laravel_test_user
DB_PASSWORD: super_secret
DB_DATABASE: laravel_test_db
run: php artisan test
- name: Deploy to Server
if: ${{ success() }}
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
script_stop: true
script: |
cd apps
cd laravel-example
git pull
npm ci
npm run prod
composer i
php artisan migrate --force
If you use laravel mix instead of vite, you should replace the npm run build with the npm run prod
So let's explain the process in sections:
First, here is declared which event triggers the Github Actions workflow.
on:
push:
branches: [main]
In this case, the Github Actions workflow will run on the main branch on any push(commit, merge, etc...) event.
runs-on: ubuntu-latest
We tell our job what operating system should be set up with the docker container, which will run the Github Actions workflow.
steps:
- uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: mirromutth/mysql-action@v1.1
with:
mysql database: laravel-test-db
mysql user: laravel_test_user
mysql password: example
- name: Copy .env
run: cp .env.example .env
- name: Install composer Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: Install node dependencies
run: npm ci
- name: Setup Project
run: |
php artisan config:clear
php artisan cache:clear
php artisan key:generate
npm run build
- name: Directory Permissions
run: chmod 755 -R storage bootstrap/cache
- name: Run Unit tests
env:
APP_ENV: testing
DB_CONNECTION: mysql
DB_USERNAME: laravel_test_user
DB_PASSWORD: super_secret
DB_DATABASE: laravel_test_db
run: php artisan test
This is a workflow for testing our project.
First, we use a few helping tools:
Next, we declare which commands should run to:
- name: Deploy to Server
if: ${{ success() }}
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
script_stop: true
script: |
cd apps
cd laravel-example
git pull
npm ci
npm run prod
composer i
php artisan migrate --force
And here is the part of the workflow that deploys to the server. appleboy/ssh-action@master is an assistant tool for running SSH commands.
if: ${{ success() }}
First, we ensure that our changes will not be deployed to the server if the test suite fails.
Host, key, and username use the values we added in secrets. Ensure that the variable names you write here are the same ones you wrote in secrets.
Script_stop ensures that if one command fails, it will not execute the other commands. We have written in the script the commands that should be executed on a server line by line. This example shows the commands necessary for the Laravel application to reflect the updates. You can fit this part to your application's specific needs.
Then push the changes to deploy.yml to Github, and that's it. ???
If everything goes well, the workflow should run, and the changes should be illustrated on a server.
So if this is so, then congratulations?
But still, let's not stop reading the blog here; I will show you some helpful things below.
When something fails in the workflow, we will receive it in the e-mail. However, we can also check this on Github - go to the repository -> actions tab, and we will see all the workflows. If any of them fails, we can choose them and see the logs. ?
For example, this workflow failed because we did not insert a public key on our profile.
After inserting the public key, we can rerun this action by clicking the rerun button.
That's it. I hope you found those instructions helpful.
Written by Tornike Khachidze & Giga Giorgadze
We are a 200+ people agency and provide product design, software development, and creative growth marketing services to companies ranging from fresh startups to established enterprises. Our work has earned us 100+ international awards, partnerships with Laravel, Vue, Meta, and Google, and the title of Georgia’s agency of the year in 2019 and 2021.