DEV Community

Cover image for Continuous Deployment on Shared Hosting with GitHub Actions
Madalitso Nyemba
Madalitso Nyemba

Posted on • Updated on

Continuous Deployment on Shared Hosting with GitHub Actions

Introduction

In this fast-paced world, where everything seems to be happening quickly, it is paramount for early as well as frequent deployment of websites/apps to keep up with the competition. There are various ways of deploying your web apps to your server but will shall be looking at how GitHub actions can help us continuously deploy our web app with minimal effort.

What is Continuous Deployment

First of all, what is continuous deployment you ask? According to Search IT operations

Continuous deployment is a strategy for software releases wherein any code commit that passes the automated testing phase is automatically released into the production environment, making changes that are visible to the software's users.

We will not cover tests in this post (but in a later post). As you can see from the definition, simply commit and then all your changes are on the production server.

What is Shared Hosting

There are different hosting choices with the popular ones now being shared hosting and cloud hosting.

A shared web hosting service is a web hosting service where many websites reside on one web server connected to the Internet.
This is the cheapest way to host your website since the different users split the cost of the web server but it also has drawbacks since the resources are split across a number of users as well.

I have been using shared hosting for a long time and I got bored with transferring my files via FTP every time I made changes. I then resorted to using GitHub where I pushed my code to the repo then pulled it from my shared hosting. I still was not satisfied. I used GitFtp which was alright. Until I learned about CI/CD in my Azubi Africa class.

GitHub Actions to the rescue

After further research, came across GitHub actions. According to their site:

Automate, customize, and execute your software development workflows right in your repository with GitHub Actions. You can discover, create, and share actions to perform any job you'd like, including CI/CD, and combine actions in a completely customized workflow.

The Workflow

Let us get to the juicy bit by doing some actual work.

Setting up GitHub actions

(This is assuming you already have a repository in your GitHub account that you want to link to your shared hosting. if not, click here).

  • Open your repository on GitHub and head over to Actions tab.
    Actions tab

  • Click on the set up a workflow yourself → as shown below.
    Set up workflow yourself

  • Delete all the contents of main.yml on the page as shown below:
    Delete contents

  • Replace the file with below contents (note this is for a project in Laravel with a Vue frontend)

name: Deploy Site on push
on:
  push:
    branches:
      - master
jobs:
  web-deploy:
    name: Deploy
    runs-on: ubuntu-latest
    steps:
    - name: Get the latest code
      uses: actions/checkout@v2.3.2
    - uses: actions/setup-node@master
    - name: Installing project dependencies
      run: npm install
    - name: Building the project
      run: npm run production
    - name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.example', '.env');"
    - name: Install Dependencies
      run: composer update --ignore-platform-reqs
    - name: Generate key
      run: php artisan key:generate
    - name: Directory Permissions
      run: chmod -R 777 storage bootstrap/cache
    - name: 📂 Sync files
      uses: SamKirkland/FTP-Deploy-Action@4.0.0
      with:
        server: ${{ secrets.LARAVEL_SITE_SERVER}}
        username: ${{ secrets.LARAVEL_SITE_USER}}
        password: ${{ secrets.LARAVEL_SITE_PASS}}
Enter fullscreen mode Exit fullscreen mode

Let me now explain block by block what is going on

name: Deploy Site on push
Enter fullscreen mode Exit fullscreen mode

This is the name of the workflow. GitHub displays the names of your workflows on your repository’s actions page after you write it.

on:
  push:
    branches:
      - master
Enter fullscreen mode Exit fullscreen mode

This is where the GitHub action system is told when to run the workflow. The above snippet triggers the workflow when one pushes to the master branch. For more on the On key, click here

jobs:
  web-deploy:
    name: Deploy
    runs-on: ubuntu-latest
Enter fullscreen mode Exit fullscreen mode

jobs - Groups together all the jobs that run in the workflow file.

web-deploy - Defines the name of the web-deploy job stored within the jobs section.

runs-on: ubuntu-latest - Configures the job to run on an Ubuntu Linux runner. This means that the job will execute on a fresh virtual machine hosted by GitHub.

steps:
    - name: Get the latest code
      uses: actions/checkout@v2.3.2
    - uses: actions/setup-node@master
    - name: Installing project dependencies
      run: npm install
    - name: Building the project
      run: npm run production
Enter fullscreen mode Exit fullscreen mode

Remember that the steps are inside the job block.

steps - Groups together all the steps that run in the web-deploy job. Each item nested under this section is a separate action or shell command.

name - For identification of the separate action.

uses: actions/checkout@v2.3.2 - The uses keyword tells the job to retrieve v2 of the community action named actions/checkout@v2.3.2. This is an action that checks out your repository and downloads it to the runner, allowing you to run actions against your code (such as testing tools). You must use the checkout action any time your workflow will run against the repository's code or you are using an action defined in the repository.

uses: actions/setup-node@master - This action installs the node software package on the runner, giving you access to the npm command.

run: npm install - The run keyword tells the job to execute a command on the runner. In this case, you are using npm to install the package node dependecies.

run: npm run production - This builds the Vue frontend project.


- name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.example', '.env');"
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.0'
    - name: Install Dependencies
      run: composer update --ignore-platform-reqs
    - name: Generate key
      run: php artisan key:generate
    - name: Directory Permissions
      run: chmod -R 777 storage bootstrap/cache
Enter fullscreen mode Exit fullscreen mode

run: php -r "file_exists('.env') || copy('.env.example', '.env'); - This creates a .env file if it does not already exist (this is important as this is automatically added to the .gitignore file).

run: composer update --ignore-platform-reqs - This is used to install and update the composer packages.

run: php artisan key:generate - This generates a key for the Laravel project.

run: chmod -R 777 storage bootstrap/cache - This changes permissions for the specified folder.

- name: 📂 Sync files
      uses: SamKirkland/FTP-Deploy-Action@4.0.0
      with:
        server: ${{ secrets.LARAVEL_SITE_SERVER}}
        username: ${{ secrets.LARAVEL_SITE_USER}}
        password: ${{ secrets.LARAVEL_SITE_PASS}}
Enter fullscreen mode Exit fullscreen mode

This is where the files are now transferred to the shared hosting server. Get your FTP details from your shared hosting. Then go to your repo>settings>secrets then add the three secrets namely: server, username and then password. This action is courtesy of SamKirkland.

For more information on GitHub actions, click here

Enjoy 😎

Top comments (19)

Collapse
 
eflames profile image
eflames

I have a question... In my shared host the public folder is named public_html but in Laravel is just public... How to set up all files inside public goes to public_html in my shared folder?.... Do I explain well?, sorry for my english btw... hehe

Collapse
 
madalitsonyemba profile image
Madalitso Nyemba

Hi, the way I would propose this is done is that you should have your ftp user point t public_html as the directory. Then all application files will be uploaded there. Then point your domain name to the public folder inside public_html. I hope that makes sense.

Collapse
 
jovialcore profile image
Chidiebere Chukwudi

Thanks for the tutorial...Let me add that, if you follow this tutorial and your GitHub Action workflow is not running, always make sure that your branch name in the yml file is same with the branch name on your github repo.
For example, make sure main branch on your github repo is same with the specified branch in yaml and not a different branch name like master

Collapse
 
codetroll profile image
Claus Paludan

Nice tutorial. However there is a few things I don't understand.
1) why use composer update? I would assume that part would be done locally and any CI/CD stuff would use composer install to ensure that only verified and testet versions are used?
2) why the step with the .env file - as per best practice it won't exists in the repo to begin with and is probably already uploaded to the server (or will be done manually at first deploy)
3) why generate the laravel key each time? wouldn't this be a one time thing at the first deploy? It shouldn't be updated each time?

Collapse
 
madalitsonyemba profile image
Madalitso Nyemba

Hi, thank you for the feedback. I apologize for the late one. Looking and reviewing the tutorial based on your comments got me thinking and you are right on all points. Will update it accordingly. For the .env, it was there as my goal for this whole action was to limit my access to the server thus would want to do everything locally then deploy. The key generation bit is supposed to be at first deploy and subsequent deploys need not have that. I will update it accordingly. Thank you.

Collapse
 
codetroll profile image
Claus Paludan

My pleasure :)

Collapse
 
artalus profile image
Artalus

How is that you first run: php -r "file_exists, but only then uses: shivammathur/setup-php@v2 ? 🤔
Does GHA come with "some abstract php" preinstalled, and then you override it with the specific version? Even if so, these two actions should probably be switched, install and then run.

Collapse
 
madalitsonyemba profile image
Madalitso Nyemba

In fact, you are right in the observation. GHA used to come with php 7.4 as default that is why I changed to 8.0 but these should be switched indeed. In fact, now GHA comes with php 8.0 as default so removing the specific version would not hurt. Thanks for pointing it out.

Collapse
 
sadiazia profile image
Sadia Zia

Hi Madalitso!

Thanks for sharing such an informative piece. You know I have completely ditched shared hosting because it comes with so many limitations. As a web developer, I always look to have a lot of choices and flexibility when it comes to hosting providers. I am someone who is managing a lot of clients at a time, so I had to choose the best hosting that offered the choice of having unlimited websites on one platform. I tried out Cloudways managed cloud hosting - 3 day free trial and was mindblown. It is so easy to use and gave me the choice of choosing from 5 hosting providers like AWS, Digitalocean, Vultr, and Linode. It’s been 8 months and I am super satisfied with Cloudways performance, my website’s speed, and the flexibility I get to scale the serve according to my needs. I was also looking for a pay as you go model and thank God Cloudways offers that too.

Collapse
 
rahulhuilgol profile image
Rahul Huilgol

Great article! Well, I just carried out this process following these steps that you've written so well. Somehow the whole execution took 61 minutes. At the end I found that all the folders have been created on the server but contain no files whatsoever. Very surprising! Any idea why this should happen?

Collapse
 
madalitsonyemba profile image
Madalitso Nyemba

Thanks for the feedback. First, took that long due to the node_modules as well as vendor folders as they have alot of files. Second, after V2 of the FTP deploy action. It only uploads tracked files by default. This link provides a detailed explanation of the issue .github.com/SamKirkland/FTP-Deploy-...
Hopefully that works.

Collapse
 
arthurnamz profile image
Arthur Mwang'onda

Nice work boss, continue to do a good work

Collapse
 
madalitsonyemba profile image
Madalitso Nyemba

Thanks

Collapse
 
drdee23 profile image
drdee23

Amazing work chief

Collapse
 
madalitsonyemba profile image
Madalitso Nyemba

Thanks boss

Collapse
 
bornadev profile image
Borna Grilec • Edited

@madalitsonyemba thank you for great tutorial, but it seems that I'm missing something...

I tried to do everything like it's described, and workflow works fine, files of initial Laravel App are successfully transferred to the subdomain in the shared hosting.

However, when I try to access my subdomain, application is not running, only files and directories are listed.

From my previous experience of manual deployment of Laravel App to the Shared Hosting there was need to modify and move index.php file to the root directory from /public and modify all paths in asset() functions in project. I've also tried to define Document Root of the subdomain to the /project-dir/pubilc/ on the server and I still just can't run the app, now HTTP 500 ERROR is shown.

I'm not using Vue.js, only basic/initial Laravel App, do you know where is the problem?

Collapse
 
madalitsonyemba profile image
Madalitso Nyemba

Probably late but for the sake of others. I found this as I once have met such before. stackoverflow.com/questions/643334...

Collapse
 
sevenreup profile image
Christopher Seven Phiri

Very insightful

Collapse
 
madalitsonyemba profile image
Madalitso Nyemba

Glad to hear that.