DEV Community

Maksym Kulikovskiy
Maksym Kulikovskiy

Posted on • Updated on

Continuous deployment without dedicated CI/CD tools

This article may be interesting to people looking for a quick, albeit limited version (imitation) of a true CI/CD pipeline. Once I had to figure out a way to deploy an app while I was still working with the infra people on configuring our Gitlab CI/CD and Kubernetes stuff.
When it comes to deploying apps, the most basic way is to move the project build to the deployment server and start a server process to serve the build to clients (user machines).
But what if you don't want to ssh directly into a virtual machine server to deploy an app? Or what if there's a security constraint preventing a direct ssh connection to a virtual machine server hosting the application? Maybe there's a gateway server in which you have to use CLI to go to the server of interest (some variety of gatekeeping access I've seen). Instead you'd have to ssh into another "proxy" server first. Tedious manual process hard to nearly impossible to automate...

I needed a way to have the app redeploy automatically every time the main/master branches gets updated. [Eventually I did set up Gitlab CI/CD with Docker and Kubernetes]

I found a workaround.

Create a deploy.sh script in the project folder of the deployment server.

#!/bin/sh

# add missing PATH env vars for crontab
PATH=/pathtoyournodedir/bin:/usr/bin

cd /usr/share/nginx/myprojectdir/;

git fetch;

LOCAL=$(git rev-parse HEAD);
REMOTE=$(git rev-parse @{u});

if [ $LOCAL != $REMOTE ]; then
    #pull and merge changes
    git pull origin main;

    yarn build;
   # touch "$(date +"%Y_%m_%d_%I_%M_%p").log"
    pm2 restart nextapp

    #change back to home directory
    cd ~;

fi
Enter fullscreen mode Exit fullscreen mode

#!/bin/sh line tells it's a bash script to the interpreter.\

Why PATH=/pathtoyournodedir/bin:/usr/bin ?

Here's a good explanation why we had to "import" PATH variables for crontab: https://askubuntu.com/a/23438

Note that it became essential to include PATH for all basic ubuntu utilities like cd or touch. I think we're not just adding PATH variables, but rather replacing crontab's default PATH "import" with _only _what we need. Because once I added PATH to node, it started complaining it didn't know what cd is :)

cd /usr/share/nginx/myprojectdir/; - go to your project dir - that's where your hidden .git folder is.
git fetch; will get the updated list with the latest commits without pulling them in just yet.

LOCAL=$(git rev-parse HEAD);
REMOTE=$(git rev-parse @{u});
Enter fullscreen mode Exit fullscreen mode

gets the latest commit id's to compare to know whether to rebuild.

git pull origin main; - main or whatever branch you want to deploy.

yarn build; - or however you build your application. Note we can't move the build folder to the server for the above reasons. We do it all on the deployment server.

pm2 restart nextapp restarts the Node.js process serving the build (if you use pm2) or however you restart your process.

# touch "$(date +"%Y_%m_%d_%I_%M_%p").log" if you remove # to uncomment the line, it will create a dummy file with the date and time that the script ran to debug cases if the build didn't go through when the script ran.

Make the script executable (won't run otherwise):

sudo chmod u+x rebuild.sh

Set up a recurring job (script).
Run crontab -e to edit the file with scheduled commands.
Press i to enter vim edit mode.
Add a line
*/5 * * * * /usr/share/nginx/myprojectdir/rebuild.sh (will run every 5 min) and press ENTER to go to a new line. (heard a new line was essential)
Press ALT+h to exit vim edit mode.
Press left SHIFT + : to bring up command prompt in the bottom left corner of vim editor.
Enter wq to write and save (or qa if you simply ever wanted to exit without making changes)

Run crontab -l to make sure your job has been saved.

Wait for 5 min to see if auto deployment happened (if needed update interval to 2 min for faster results)

If deployment didn't happen successfully, but git pull executed, you need to run git reset --hard HEAD~1 to drop the most recent commit so that the script's if condition will allow it to run again.
After this build a build manually with yarn build or similar command to restore the original build if needed (if it got corrupted), so you can await next auto deployment attempt.

What code change to make to your project to reflect successful deployment and be able to observe results easily? I would recommend a simple UI text change (misspell a word or make some word uppercase)

Hope this helps! Let me know if you have questions!

Top comments (0)