October 30th, 2024 · Read time 6 min

FTP-based CI/CD with GitHub actions

So today, I cleaned up the repo (hosted on GitHub) for one of our recent projects at 3d7 Tech by actioning existing PRs and deleting stale/unnecessary branches. It's a multi-tenant application built with Laravel and requires a lot of server-side configurations for a full deployment, something I love (and hate). We have a VPS and as much as I love to ssh from time to time, I also love the ease that comes with server management via a UI like cPanel.

I had always maintained separate repos for staging (or testing) and production, basically because I wanted that extra level of human effort to deploy to production. I had implemented CI/CD on both repos such that whenever each's master branch receives a push action, the code is automatically deployed to their respective server via FTP. This went on for months and the project received more and more development efforts as the team grew and It became difficult to manage both repos.

So I cleaned up to make a protected branch in the primary repo and only merged into it from master after a developed branch has passed QA and been merged into it. The protected branch now serves as the production branch while the master branch serves as staging.

Explained all these to the team and my product owner goes “We can’t start thinking of CI-CD?” I was dumb-founded for a second and asked him to explain what he understood by the concept of CI-CD, and he goes “One pipeline…you build local and deploy, if nothing breaks, it’s accepted, if something breaks, it’s rejected…my knowledge is all AWS based and might have not been scalable”.

It then occured to me that while my approach might be efficient, it may not be common and so I thought to share this knowledge.

Continuous Integration (CI) is a development practice where developers regularly integrate their code changes into a shared repository. This practice involves building and testing the software.

The Shared repo could be hosted on GitHub, Bitbucket, or GitLab. The primary goal is to detect and fix integration issues early in the development process.

Continuous Deployment (CD) extends CI by automatically deploying code changes to a REMOTE environment after passing through the CI pipeline. This practice involves automation of some sort.

The remote environment could be Production or Staging for UAT. The primary goal is to deliver new features or fixes to end-users as quickly as possible.

In essence, CI/CD (Continuous Integration/Continuous Deployment) stands as a cornerstone in modern software development, revolutionizing the way teams build, test, and deploy code. By automating these crucial processes, CI/CD not only accelerates development cycles but also elevates the quality of code and reliability of deployments. Its significance in today’s software landscape cannot be overemphasized, shaping the way teams deliver value to users with efficiency and confidence.

To achieve FTP-based CI/CD with cPanel, FTP, and GitHub actions follow these steps carefully.

Setup FTP account via cPanel

  1. Log in to your cPanel account.
  2. Scroll down to the Files section and click FTP Accounts (or just search for it).
  3. Fill the form to Add an FTP Account providing a valid username and password. Also, depending on your app, choose the directory that the FTP account will have access to. This is where your codes will be shipped from the repo. For example: public_html if you want to deploy to the www directory.
  4. Keep these details handy as we’d need it in the next step.

Establish communication between your repo and server with GitHub repository secrets

To establish communication between your repo and server via FTP, follow the steps below:

  1. Go to your GitHub repository and click on Settings.
  2. Click on Secrets and variables on left sidebar.
  3. Click on Actions.
  4. Click on New repository secret.
  5. Enter a name for the secret, such as FTP_USER_STAGING. In the Value field, enter your FTP credentials.
  6. Click on Add secret to save the secret.

Following the steps above, create FTP_SERVER_SECRET and FTP_PASSWORD secret variables with the appropriate values. You may also add the credentials for as many servers you want to deploy to. In may case, I’d set up for staging and production severs.

Setup GitHub actions and workflows in the repository

This is where the magic happens. In this step, we will write a github workflow(s) to deploy to our server(s). This workflow will be triggered by github actions. These steps will get us all set.

  1. Go to your GitHub repository and click on Actions.
  2. On the left pane, click on New workflow.
  3. Click Skip this and set up a workflow yourself.
  4. Copy and paste the script below
name: 🚀 Deploy Changes to cPanel
on:
push:
branches:
master
jobs:
FTP-Deploy-Action:
nameFTP-Deploy-Action
runs-onubuntu-latest
steps:
- usesactions/checkout@v2.1.0
with:
fetch-depth2
- nameFTP-Deploy-Action
usesSamKirkland/FTP-Deploy-Action@4.3.3
with:
server$
username$
password$
  1. Save and commit.
  2. This creates the folder workflows within the .git folder in your repo.

Note: You may create as many workflows for as many FTP accounts you have set up. If you have a branch for production called Production all you ned to do is tweak the script above and replace master with production . Also feel free to update the server, username and password parameters with the names you had given them when you created the secrets.

Perform push actions onto desired branch

Lastly, To deploy the codes from the master or desired branch of your repo to the remote server, you need to perform a push action. This can either be through a merge or you may use the git push command. Here are the basic steps:

git add .
git commit -m "Your commit message"
git push origin <your-desired-branch>

This process will send your chosen local branch to the remote repository and synchronize the main branch on the remote repository with your modifications. Visit your application or website, and you’ll notice the new code in place, automatically updating the site. Going forward, every time you push changes to the main branch, the website will refresh automatically.

What do you think of this approach? Let me know on Twitter.

Originally posted here.