Set up Dev, QA, Production environments with Linux and Git

For faster code deploy

Posted by paul on 2015.05.11

Set up Dev/QA/Production environments with Linux/Git

Created 2015.05.11 Mon

Well organized dev/qa/production environment will allow fast dev/qa/production cycle, with less bug, allowing more time to add more features. Even if you are just starting to learn website coding or running a one-man freelance shop, you should invest in learning about setting up proper environments and use tools like git. Half a day of work will save you days/weeks of your time. This tutorial may be a bit of challenge for someone relatively new to Linux, but it will help you learn and also gain tools that will save you time.

Dev, QA, Production phases

I have following three environments for coding for my website. All are similar except for a few differences. In this tutorial, all three environments are on the same server, which is on my VPS server.

  1. Dev phase: Using rsync and a shell script on the Linux server, I can cycle through code-upload-test very quickly. Uploading to website is quick because I use rsync to upload files. In my small website of 10MB hosted on my VPS server, uploading a change over the internet takes seconds. I then run a script on my Linux web server which copies the code into the web document folder and takes care of other tasks such as setting permissions. The biggest benefit is that it helps me keep the code in head. This helps me keep the code in memory in my head and I don't have to reread the code. One less context switching to deal with. This alone saves me a lot of time and energy.
  2. Git: Once I'm done with a Dev phase, I check in the code to my Git repo (bitbucket.com in this case). Then I tag the code with qa.yyyymmdd.hh (ex: qa.20150518.09). Keeping your code in a Git Repository really should be one of first things you set up as you learn about coding. Bitbucket allows unlimited free repository.
  3. QA phase: To deploy code to QA website, I run a script on my Linux server. It checks the date on the servers and tries to check out code tagged as qa.yyyymmdd.hh, ex: qa.20150518.09. Because I was testing the code frequently during the dev phase, there is really not much QA to do. If you were in a multi-person dev team, perhaps this QA site could be used. As for me, this QA site serves just as a mirror image of my production site. When I need to check something in my production site, I instead check the QA site. This ensures Google analytics of my little website doesn't get skewed. With a website of a few dozen visits a day, I think this is useful.
  4. Git: With QA done, I tag the code as production.yyyymmdd.hh, ex: production.20150518.09.
  5. Production phase: To deploy the code to my production site, I run a script which is similar to the script for deploying code to QA. One advantage in tagging the code with production.20150518.09 is the ability to redeploy an older version of code. Let's say a release went bad (bug, missing files etc) and you need to quickly redeploy a previous working version. You simply run a script to deploy code tagged with production.yyyymmdd.hh of the previous working version.

You could use tools like Jenkins to automate releasing code. But for my small website, using shell scripts works fine for me.

Tools/skills needed

Here are necessary tools/skills to complete this tutorial.

  1. Account at Linode.com or DigitalOcean, running CentOS 6 with Apache web server. Any other Linux OS will do but I use CentOS. If you do sign up with Linode.com, please go ahead and sign up using this referral code https://www.linode.com/?r=9bf80d091a35c28e9ac9378c100240a0a1a3d5f3. If you pick DigitalOcean, use this referral https://www.digitalocean.com/?refcode=739d35d9ecb8.
  2. Account on bitbucket.org to store code. Free
  3. WebStorm editor or Sublime editor.
  4. Git client such as SmartGitHg.
  5. Some shell scripts in CentOS 6.
  6. vi editor.
  7. Basic shell command (rsync) executed in OS X. Windows works but I use Mac in this tutorial.

Prerequisite configs

Signing up account at Bitbucket, signing up at a VPS host, and creating A/AAAA DNS records may be a complicated for someone new to it. But keep at it.

  1. Git Repo: Sign up for a free account at http://bitbucket.org/. You can also use http://github.com/ but my examples are written using bitbucket.org. Check in your code into a repo of yours at http://bitbucket.org.
  2. Sign up at a VPS host: Some things you can only learn in the real world, and for a budding new web developer I think having a VPS is the best way to learn how to be a web developer/master. You will want to be able to log in via ssh, run shell scripts on the server, and run Apache web server on it. You can use the server to show off your coding portfolio and gain experience with managing a web server in real world. For all these reasons, I strongly recommend that you sign up for an account at a VPS hosting company. I have a basic account at Linode.com for $20/month and I think it's well worth it. If you do sign up with Linode.com, please go ahead and sign up using this referral code https://www.linode.com/?r=9bf80d091a35c28e9ac9378c100240a0a1a3d5f3. As long as you remain a customer of Linode.com for 90 days or more, I get $20 off in my monthly bill. It does not cost you anything extra and it encourages me to write more tutorials. If you pick DigitalOcean, use this referral https://www.digitalocean.com/?refcode=739d35d9ecb8 and I get to save $10.
  3. Add new A/AAAA DNS records for dev and qa version your yoursite.
    1. devsite.yoursite.com
    2. qasite.yoursite.com
  4. I use Linode's name server as the primary DNS server to add A/AAAA records.

How folders/files are organized

HTML, apache config file, and URL.

  1. Dev
    1. HTML Dir: /data/www/yoursite_devsite/
    2. Apache Config: /etc/httpd/conf.d/yoursite_devsite.conf
    3. URL: http://devsite.yoursite.com
  2. QA
    1. HTML Dir: /data/www/yoursite_qa/
    2. Apache config: /etc/httpd/conf.d/yoursite_qasite.conf
    3. URL:http://qasite.yoursite.com
  3. Production
    1. HTML Dir: /data/www/yoursite/
    2. Apache Config: /etc/httpd/conf.d/yoursite.conf
    3. URL: http://yoursite.com

Set up Dev and QA environments

For each subdomain, you will 1) set up subdomains, 2) create HTML directories, and 3) add Apache .conf files.

  1. Subdomains: Using your hosting company's console, edit your DNS records (adding A records) so that you have devsite.yoursite.com and qasite.yoursite.com. This is also known as adding subdomains. This change may take some time to propagate through the internet DNS servers. Some say it takes 24 - 48 hours but for me it was about 30 min - 1 hour. You can view Linode's instruction on adding subdomain if your VPS is hosted at Linode.
  2. HTML Document directories: On your VPS Linux server, create directories to hold website files for Dev and QA environments. This would mean creating 2 directories: /data/www/yoursite_devsite/ AND /data/www/yoursite_qasite/. For production version, you can use /data/www/yoursite/. You can name the folder any way you like, but I recommend creating the folders with following convention: sitename_devsite/, sitename_qasite/, and sitename/. It helps keep the folders for a given website near each other. I recommend against naming folders as dev_somesite, qa_somesite, etc.
  3. Add simple index.html, data/www/yoursite_devsite/index.html. Add a line to indicate it is Dev site of yoursite.com.
  4. Add simple index.html, data/www/yoursite_qasite/index.html. Add a line to indicate it is QA site of yoursite.com.
  5. Add simple index.html, data/www/yoursite/index.html. Add a line to indicate it is the production site of yoursite.com.
  6. Apache configs: To get http://devsite.yoursite.com working on your Linux server, create /etc/httpd/conf.d/yoursite_devsite.conf with following.
  7. <VirtualHost *:80>
    ServerAdmin [email protected]
    ServerName devsite.yoursite.com
    ServerAlias devsite.yoursite.com
    DocumentRoot /data/www/paulcodr_devsite
    ErrorLog /var/log/httpd/error_devsite_paulcodr_co_log
    CustomLog /var/log/httpd/access_devsite_paulcodr_co_log combined
    </VirtualHost>
    
  8. To get http://qasite.yoursite.com working on your Linux server, create /etc/httpd/conf.d/yoursite_qasite.conf with following.
  9. <VirtualHost *:80>
    ServerAdmin [email protected]
    ServerName qasite.yoursite.com
    ServerAlias qasite.yoursite.com
    DocumentRoot /data/www/paulcodr_qasite
    ErrorLog /var/log/httpd/error_qasite_paulcodr_co_log
    CustomLog /var/log/httpd/access_qasite_paulcodr_co_log combined
    </VirtualHost>
    
  10. To get http://yoursite.com (the production version) working on your Linux server, create /etc/httpd/conf.d/yoursite.conf with following.
  11. <VirtualHost *:80>
    ServerAdmin [email protected]
    ServerName yoursite.com
    ServerAlias yoursite.com
    DocumentRoot /data/www/paulcodr
    ErrorLog /var/log/httpd/error_paulcodr_co_log
    CustomLog /var/log/httpd/access_paulcodr_co_log combined
    </VirtualHost>
    
  12. Restart Apache service on the Linux server.
  13. Try pinging devsite.yoursite.com or qasite.yoursite.com. If pinging works, from your PC try browsing http://devsite.yoursite.com or http://qasite.yoursite.com. You should see the html page. If it doesn't work, you just may have to wait awhile for DNS progagation to complete. If you can reach yoursite.com, you should eventually be able to reach devsite.yoursite.com and qasite.yoursite.com. Just give it some time.
  14. Even if you DNS propagation is not done yet, you can move onto the next step.

Deploying files to Dev site

Deployes files to Dev site requires 2 steps: 1) rsync files up and 2) run a shell script on the Linux server

Deploying files to Dev site - upload files

For uploading code to devsite, you can simply use rsync. I rsync up the files to a folder on the server. Because rsync is done via ssh connection, you should set up passwordless login from your Mac to the Linux server.

  1. Set up passwordless login using ssh key from your Mac to your Linux server. Linode has good tutorial. I rsync up files to a non-root account, in my case, an account called ftpuser. I do not like allowing root to log in directly via SSH, so I use a dedicated account.
  2. rsync -ave ssh --delete --exclude=".git" ~/code/yoursite-bitbucket [email protected]:~/devsite/
    
    1. --delete: Ensures target directory exactly mirrors the source folder on your Mac
    2. --exclude: Don't rsync up unnecessary files. .git is automatically created by SmartGit/Hg, Git client app I use.
    3. ~/code/yoursite-bitbucket: The source folder on my Mac. Note there is no trailing slash. I keep code on bitbucket and other places, so I use -bitbucket suffix to remind myself.
    4. ftpuser: I use a dedicated user account on my Linux server for rsyncing files up.
    5. ~/devsite/: Destination folder. Note the trailing slash. This folder on the Linux server holds all incoming files/folders of the websites in dev phases I host on my web server.
  3. Test uploading your code 1st time. It will take a while, as all of the files need to be uploaded. Once the initial upload is done, on the Linux server the files will be in /home/ftpuser/devsite/yoursite-bitbucket. Subsequents rsyncs will be quick.
  4. Next, we need to copy the files of yoursite.com from /home/ftpuser/devsite/yoursite-bitbucket to /data/www/yoursite_devsite/. This calls for a script on the Linux server.

Deploying files to Dev site - prep files

Once files are rsynced up, you'd run a script on the Linux server as root. This script copies the files into /data/www/yoursite_devsite/, sets permission etc.

  1. When I set out to write the script, dev-prep.sh, I wanted to make sure running the script handles prepping ALL websites I may code, not just one website. This helps me think less when deploying code during the dev phase.
  2. On the Linux server, create /root/bin/dev-prep.sh with following.
  3. #!/bin/bash
    
    ###
    # Variables
    ###
    NOW=$(date +"%Y-%m-%d_%H-%M")
    NOWSTAT=$NOW
    STAGED="home/ftpuser/devsite"
    TARGET="data/www"
    
    # Declare array to hold values: projectName1-bitbucket, projectName2-bitbucket, etc.
    PROJECTS=(/$STAGED/*-bitbucket)
    
    loopStatus=true
    
    ###
    # Script
    ###
    cd
    
    #In /$STAGED, remove folders that start with '1-'. These folders contain support files/assets
    #that should NOT be in website.
    find /$STAGED -maxdepth 1 -name "1-*" -type d -exec rm -rf {} \; > /dev/null 2>&1
    
    #Prepare folder to keep old folders
    mkdir -p /$STAGED/old/$NOWSTAT > /dev/null 2>&1
    
    #Remove references to Google Analytics from html files. QA site shouldn't touch Google Analytics.
    find /$STAGED -type f -exec sed -i '/assets\/js\/google-analytics.js/d' {} \;
    
    #Move all 'dev' sites out of /data/www/  into /home/ftpuser/old/
    # For each array value of PROJECTS, basename is performed and then sed is performed to get the
    # project name (ex.: ~/devsite/yoursite-bitbucket --> yoursite-bitbucket --> yoursite)
    # Finally if www/yoursite_d/index.html exists,  www/yoursite_d/ is moved out of www/
    # Then yoursite-bitbucket is copied to www/yoursite_d/.
    for i in ${PROJECTS[@]}
    do
    	i1=`basename $i`    # Convert devsite/projectName1-bitbucket --> projectName1-bitbucket
    	i2=`echo "$i1" | sed 's/-.*//g'`   # Convert projectName1-bitbucket --> projectName1
    	if [ -d "/$STAGED/${i2}-bitbucket" ]
    		then
    		mv -f /$TARGET/${i2}_devsite /$STAGED/old/$NOWSTAT/ > /dev/null 2>&1
    		loopStatus=true
    	else
    		loopStatus=false
    	fi
    
    	if [ "$loopStatus" = true ]
    		then
    		#echo "/$TARGET/${i2}_devsite moved to /$STAGED/old/$NOWSTAT/"
    		mkdir -p /$STAGED/${i2}_devsite               # Create *_d to receive files from *-bitbucket/*
     		rsync -a --delete /$STAGED/${i2}-bitbucket/ /$STAGED/${i2}_devsite/  > /dev/null 2>&1  # Rename folder to *_devsite
     		rsync -a --delete --exclude=.DS_Store --exclude=.git --exclude=.gitignore --exclude=.idea --exclude=.name /$STAGED/${i2}_devsite /$TARGET/ > /dev/null 2>&1
    		sed -i "s/\/${i2}.co\/blog/\/d.${i2}.co\/blog/" /$TARGET/${i2}_devsite/.htaccess  > /dev/null 2>&1  # Replace someurl.com with devsite.someurl.com in .htaccess.
    		find /$TARGET/${i2}_d -type f -name run_prettify.js -exec sed -i -e "s/${i2}/devsite.${i2}/g" {} \;  # Update run_prettify.js to work.
    		echo ${i2}:
     		echo "/$STAGED/${i2}_devsite copied to /$TARGET/"
     		echo ""
     	else
     		echo "No devsite will be updated."
    	fi
    done
    
    
    #Delete old copies of devsite (*_d) older then 10 minutes.
    find /$STAGED/old/ -maxdepth 1 -mmin +10 -exec rm -rf {} \;
    
    #Remove devsites (*_d) as cleanup.
    rm -rf /$STAGED/*_devsite
    
    # Set permissions for Apache.
    chown -R apache:apache /$TARGET/*_devsite
    chmod -R 750 /$TARGET/*_devsite
    
    exit 0
    
  4. Some details
    1. All folders in /home/ftpuser/devsite/ will be copied to /data/www/***_devsite/. Because of rsync, it will be fast.
    2. Older copies of folders are moved to /home/ftpuser/devsite/yyyy-mm-dd_hh-min/. Previous copies older than 10 minutes are deleted, to keep the HD from filling up.
    3. /home/ftpuser/devsite/yoursite-bitbucket is moved to /data/www/yoursite_d/
    4. Permission update of /data/www/yoursite_d/.
  5. Run /root/bin/dev-prep.sh to publish the dev site.
  6. Let's test browsing http://devsite.yoursite.com/. You should see your site. If not, slow DNS propagation may be the reason. Proceed to creating the QA site.
  7. As you are coding, you can cycle through upload/view process very quickly.

Deploying files to QA site

For the QA phase, you will need to check in your code to Git repo and tag the code. I use Bitbucket as my personal Git repo. Instead of using an artificial version number, I use date-hour as the unique identifier. So instead of tagging my code as 0.2 for example, I tag it as qa.yyyymmdd.hh (ex. qa.20150512.09). My shell script picks up this date-time value automatically from the Linux OS and I don't have to remember or look up any special value to enter when running the script to deploy code for QA. One less context switching to deal with. Another reason I am not using actual version number is that my website is really a blog and it's a bit hard to assign a version number when I am really publishing just a short article and/or publishing a small typo.

What if you have to release QA code twice or more in quick succession, like at 10:20am and 10:45am? For this reason, my script allows adding additional number or phrase before the script actually runs. So you can tag a code as qa.20150512.09 at 9:10am and deploy it to the QA site. After adding some change, you can tag it as qa.20150512.09.test1 at 9:35am. When you run the script, you will be prompted for a suffix value. You just enter test1 and the QA deploy script will deploy code tagged as qa.20150512.09.test1.

  1. First, you need to add an SSH key generated on your Mac to your account at Bitbucket. Or github. You can view Bitbucket tutorial here.
  2. On Linux server, create following script, /root/bin/yoursite-qasite-update.sh.
  3. #!/bin/bash
    # This script git pulls from Bitbucket, switches to a 'tag', and copies files from the tag
    # to /data/www/yoursite_qasite/.
    
    # This script is used to deploy qa ready codes to production site, qasite.yoursite.com.
    # Files tagged with 'qa.yyyymmdd.hh' will be copied into the Apache Document home directory.
    
    ############
    # Variables you MUST check/update.
    ############
    # GIT related variables
    Repo="yoursite"    #aka website
    Target="yoursite_qasite"
    Codes="root/codes"
    
    # Specify the number of set of codes you want to keep. Mainly used to keep down folder size.
    Folders_To_Keep=5
    
    #Array to hold list of folders containing old code
    Old_Folder=/data/www/$Target-old
    Old_Folder_Array=($Old_Folder/*)
    
    
    
    ############
    # Variables you do not need to update.
    ############
    TagDate=$(date +"%Y%m%d.%H")
    Tag="q.$TagDate"   #On Git repo, make sure you have ready to deploy code tagged with 'q'.
    
    # Date/time stamp
    Now=$(date +"%Y-%m-%d_%H-%M")
    NowStat=$Now
    
    # This is necessary because of the way 'tail -n +6' works. Nothing to change here.
    Folders_Value=$(($Folders_To_Keep + 1))
    
    
    
    ############
    # Script
    ############
    #Append additional name to $Tag if it is entered.
    echo "If necessary, add end designation to the Tag: "
    read -p "> " TagNote1
    
    if [ ! -z "$TagNote1" ]; then
    	TagNote=".$TagNote1"
    	Tag=$Tag$TagNote
    	echo -e "\n########"
    	echo -e "Script will attempt to publish code tagged as $Tag."
    	echo -e "If no code is tagged as $Tag, no code will be published."
    	echo -e "########\n"
    else
    	echo -e "\n########"
    	echo -e "Script will attempt to publish code tagged as $Tag."
    	echo -e "If no code is tagged as $Tag, no code will be published."
    	echo -e "########\n"
    fi
    
    
    #Get files from GIT Repo and clean up codes.
    mkdir /$Codes > /dev/null 2>&1
    cd /$Codes
    rm -rf /$Codes/$Repo
    git clone [email protected]:pauljchu/$Repo.git
    cd /$Codes/$Repo/
    
    
    #Git switches to a tag.
    git checkout $Tag
    
    
    #Remove references to Google Analytics from html files. QA site shouldn't touch Google Analytics.
    cd /$Codes/$Repo/
    find /$Codes/$Repo/ -type f -exec sed -i '/analytics/d' {} \;
    
    
    #Replace d.yoursite.co with q.yoursite.co for run_prettify.js to work.
    cd /$Codes/$Repo/assets/js/prettify
    sed -i 's/yoursite.co/qasite.yoursite.co/g' run_prettify.js
    
    # Replace d.yoursite.co with q.yoursite.co in .htaccess
    sed -i 's/\/yoursite.co\/blog/\/q.yoursite.co\/blog/' /$Codes/$Repo/.htaccess
    
    #Rename old $Repo site in Apache Documents folder
    mkdir -p /data/www/$Target-old/$NowStat/ > /dev/null 2>&1
    mv /data/www/$Target/* /data/www/$Target-old/$NowStat/ > /dev/null 2>&1
    
    
    #Delete old backup
    #find /data/www/$Target-old/* -ctime +5 -exec rm -rf {} \;
    for i in ${Old_Folder_Array[@]}
    do
    	/bin/ls -dt $Old_Folder/* | /usr/bin/tail -n +$Folders_Value | /usr/bin/xargs /bin/rm -rf  #keep only latest 3 sets and delete older ones
    done
    echo "Only  $Folders_To_Keep  newer subfolders are now in $Old_Folder/."
    
    
    #Copy in new files and sets permissions
    mkdir /data/www/$Target/ > /dev/null 2>&1
    rsync -a --exclude=.DS_Store --exclude=.git --exclude=.gitignore --exclude=.idea --exclude=.name /$Codes/$Repo/ /data/www/$Target/ > /dev/null 2>&1
    cd /data/www/$Target/
    rm -rf 1-*  #Removing copyrights, original img files, etc that should not be published on production site.
    
    #rm -f google27bbc24c440b5dd6.html  #(Google analytics tracking code needed in tagged site.)
    
    chown -R apache:apache /data/www/$Target/
    chmod -R 750 /data/www/$Target/
    
    
    #service httpd restart
    
    1. Roughly, youriste-qa-update.sh works like this.
    2. You run the script, and you will be asked to add any optional suffix. You can enter nothing and just hit Enter key and it will work fine.
    3. The script than checks out code from your Git repo using the tag, in this example qa.20150512.10.
    4. The script than removes lines/files that are not needed in QA phase. One notable example is removing from each HTML file any reference to Google Analytics. It also changes other lines/files that need to be changed for them to work in QA environment.
    5. The script than copies files to /data/www/yoursite_qa/ and sets permission.
  4. Before running yoursite-qasite-update.sh, you will need to tag your code. I tag the code as 'qa.yyyymmdd.hh'. To tag with correct date-hour, log into your Linux server and run date command. The reason I do this is because I keep my server on UTP time, meaning the clock is different from what I see on my Mac. Not a big hassle as I always have a Terminal window logged into my Linux server whenever I code.
  5. After tagging the code in your Git client, switch to the Linux server.
  6. On the Linux server, run /root/bin/yoursite-qasite-update.sh.
  7. Try browsing http://qasite.yoursite.com/ now. By this time, the DNS propagation should have finished and you should be able to browse the QA site. If not, you just will have to wait a bit longer.
  8. In my environment, having the QA site is not really for QA but for having a mirror image of my production site, to allow me to view a 'production' like site without skewing the numbers in the Google Analytics. In my case, QA is being done constantly in the Dev phase. When you don't have visitors in the tens of thousands coming to your site, such precaution is useful. I wouldn't want to fool myself into thinking I got 100 new visitors when it was just me checking a page on the site for some reason. I can just go to http://qasite.yoursite.com/.
  9. Now onto the Production phase.

Deploying files for Production site

Deploying code in production phase is very similar to what you do in QA phase.

  1. Check in code to Git repo if necessary.
  2. Tag the code as 'production.yyyymmdd.hh'. You can also tag it with extra characters at the end as 'production.yyyymmdd.hh.xyz1'
  3. On Linux server, create /root/bin/yoursite-production-update.sh
  4. #!/bin/bash
    #This script git pulls from Bitbucket, switches to a 'tag', and copies files from the tag
    #to /data/www/yoursite/
    
    #This script is used to deploy production ready codes to production site, www.yoursite.com.
    
    ############
    # Variables you MUST check/update.
    ############
    # GIT related variables
    Repo="yoursite"    #aka website
    Codes="/root/codes"
    
    # Specify the number of set of codes you want to keep. Mainly used to keep down folder size.
    Folders_To_Keep=5
    
    #Array to hold list of folders containing old code
    Old_Folder=/data/www/$Repo-old
    Old_Folder_Array=($Old_Folder/*)
    
    
    
    ############
    # Variables you do not need to update.
    ############
    TagDate=$(date +"%Y%m%d.%H")
    Tag="production.$TagDate"   #On Git repo, make sure you have ready to deploy code tagged with 'production'.
    
    # Date/time stamp
    Now=$(date +"%Y-%m-%d_%H-%M")
    NowStat=$Now
    
    # This is necessary because of the way 'tail -n +6' works. Nothing to change here.
    Folders_Value=$(($Folders_To_Keep + 1))
    
    
    
    ############
    # Script
    ############
    #Append additional name to $Tag if it is entered.
    echo "If necessary, add end designation to the Tag: "
    read -p "> " TagNote1
    
    if [ ! -z "$TagNote1" ]; then
    	TagNote=".$TagNote1"
    	Tag=$Tag$TagNote
    	echo -e "\n########"
    	echo -e "Script will attempt to publish code tagged as $Tag."
    	echo -e "If no code is tagged as $Tag, no code will be published."
    	echo -e "########\n"
    else
    	echo -e "\n########"
    	echo -e "Script will attempt to publish code tagged as $Tag."
    	echo -e "If no code is tagged as $Tag, no code will be published."
    	echo -e "########\n"
    fi
    
    
    #Clean up codes
    mkdir $Codes > /dev/null 2>&1
    cd $Codes
    rm -rf $Codes/$Repo
    git clone [email protected]:pauljchu/$Repo.git
    cd $Codes/$Repo/
    
    
    #Git switches to a tag.
    git checkout $Tag
    
    
    #No change to run_prettify.js needed.
    #Replace d.yoursite.com with yoursite.com, to enable run_prettify.js to work.
    #cd $Codes/$Repo/assets/js/prettify
    #sed -i 's/yoursite.co/yoursite.co/g' run_prettify.js
    
    
    #Rename old $Repo site in Apache Documents folder
    mkdir -p /data/www/$Repo-old/$NowStat/ > /dev/null 2>&1
    mv /data/www/$Repo/* /data/www/$Repo-old/$NowStat/ > /dev/null 2>&1
    
    
    #Delete old backup
    for i in ${Old_Folder_Array[@]}
    do
    	/bin/ls -dt $Old_Folder/* | /usr/bin/tail -n +$Folders_Value | /usr/bin/xargs /bin/rm -rf  #keep only latest 3 sets and delete older ones
    done
    echo "Only  $Folders_To_Keep  newer subfolders are now in $Old_Folder/."
    
    
    #Copy in new files and sets permissions
    mkdir /data/www/$Repo/ > /dev/null 2>&1
    rsync -a --exclude=.DS_Store --exclude=.git --exclude=.gitignore --exclude=.idea --exclude=.name $Codes/$Repo/ /data/www/$Repo/ > /dev/null 2>&1
    cd /data/www/$Repo/
    rm -rf .git .DS_Store
    
    chown -R apache:apache /data/www/$Repo/
    chmod -R 750 /data/www/$Repo/
    
    
    #service httpd restart
    
  5. When ready to deploy your code, execute /root/bin/yoursite-production-update.sh. The script takes care of checking out code from the Git Repo, copy files around and sets permissions.

Summary

That's it. You now have 3 separate environments for your website. All 3 environments are separated from each other but they all share same server.