Introduction

Imagine that you have few developers working on Java project. Code of the project is committed to GIT repository and there are ANT scripts implemented to facilitate builds and deployments of the system as well as additional activities that might be useful. The developers using this infrastructure push code changes to GIT repository and automatic build and deployment of the latest code version should be automatically deployed on Tomcat server. This blog describes simple automatic build system that users GIT hooks to invoke appropriate ANT tasks depending on files that were changed in the system.

Users

There are two users that are crucial to run the automatic builds:

  • git – this user is owner of GIT repository and the commits are made as git user
  • tomcat – this user should run Tomcat

To allow the deployments, which involve stopping and starting Tomcat server, git user should be able to run tomcat as tomcat user. This functionality can be enabled in /etc/sudoers file.
Run:

sudo visudo

and add the following line

git ALL = (tomcat) NOPASSWD: ALL

To run a command as Tomcat user type

sudo -u tomcat {command name}

If you set some variables you might want not to reset them once you run sudo command. To do so add variable names to sudoers file

Defaults        env_keep += "JAVA_HOME"

Project structure

Let’s assume that our project has the following structure:

  • project – source of the project to be compiled and deployed
    • src – source code
  • configuration – Tomcat configuration files
  • git – GIT configuration files
    • post-receive – script to be invoked after push is made to GIT repository
  • build.xml – ant build script

Ant script

Let’s assume that ant script has following targets defined:

  • build-deploy – build the project and deploy it on Tomcat server
  • deploy-configuration – deploy Tomcat configuration

Depending on code changes the following targets should be run:

  • on change in project/src folder build-deploy tasks should run
  • on change in configuration folder deploy-configuration task should run

Git repository

Hooks

Git allows to run actions on several events, which mechanism is implemented using hooks. There are several hooks defined in git. Some of them are client side and some of them are server side. More information regarding hooks can be found here

Bare git repository is located on the server. Hooks are located in {git repository path}/hooks/. We are going to use server-side post-receive hook to make automatic builds. The post-receive hook runs after the entire process is completed and can be used to update other services or notify users. It takes a list of references that are being pushed from stdin.

The hooks will run as user which logs into GIT therefore it is important to ensure that tasks can be run as this user or user can run tasks as other user.

Configuration

To use ant build file (build.xml) and post-receive script, the checkout of the repository has to be done on the same server:

git clone {local path to repository}

The checkout should be done as tomcat user.

Each time push will be made to the repository, the checkout repository should be updated (git pull) and then the files from the repository could be used for builds. Having both files in the repository (build.xml and post-receive) allows dynamic configuration changes and flexible build system without a need to make any changes on the server.

post-receive hook configuration

We can invoke post-receive script which is in our git repository by invoking the script from post-receive hook in GIT repository. To do so post-receive file should be created in {git repository path}/hooks/ and made executable. The file, which is in fact bash script, should include the following commands:

#!/bin/sh

TEMP_GIT_FILE="/tmp/git.txt"
# Path to cloned version of git repository
GIT_CLONE="/opt/git-clone"

cd "$GIT_CLONE"
# Update cloned version of git repository as tomcat user
sudo -u tomcat git pull > /dev/null

rm $TEMP_GIT_FILE

while read line
do
  echo $line >> $TEMP_GIT_FILE
done

# Run post-receive script located in git repository
cat $TEMP_GIT_FILE | ./git/post-receive

Automatic build script – post-receive

The following listing presents automatic build script. The script is located in GIT repository in git folder, so whenever it is changed the changes are automatically applied. The comments regarding parts of the script are within the code.

#!/bin/sh

# Log all the messages to the file
exec >> /opt/logs/git.log

# Path variables
TOMCAT="/opt/tomcat"
GIT="/opt/git"
GIT_CLONE="/opt/git-clone"

# Export variables
export JAVA_HOME="/opt/java/jdk1.7.0_07"
export PATH="$PATH":"$JAVA_HOME/bin"
export ANT_HOME="/opt/ant/apache-ant-1.8.4"
export PATH="$PATH":"$ANT_HOME/bin"
export TOMCAT_HOME=$TOMCAT

# Global variables to track code changes
configuration_change_global=1
code_change_global=1

echo "***********************************************************"
echo "BUILD"
echo $(date)
echo "***********************************************************"

cd "$GIT"

# Variables given 
while read oldrev newrev refname
do

  # Commit
  commit=$(git show $newrev | grep 'commit')
  # Date of commit
  date=$(git show $newrev | grep 'Date:')
  # Author of commit
  author=$(git show $newrev | grep 'Author:')
  
  echo "-----------------------------------------------------------" 
  echo "$commit" 
  echo "$date" 
  echo "$author" 
  echo "-----------------------------------------------------------"  
  
  # Code changes since last commit 
  git diff --name-only $newrev $oldrev | grep 'project/src' > /dev/null
  code_change=$? 
  git diff --name-only $newrev $oldrev | grep 'configuration' > /dev/null
  configuration_change=$? 
  
  if [ $code_change -eq 0 ]  
  then 
    code_change_global=0 
    echo "Source code changed" 
  fi 
  if [ $configuration_change -eq 0 ]  
  then 
    configuration_change_global=0 
    echo "Configuration changed" 
  fi  
done 
 
# Stop Tomcat 
cd "$TOMCAT/bin" 
sudo -u tomcat ./shutdown.sh
sleep 15 
 
pid=$(sudo -u tomcat ps aux | grep "tomca[t]" | awk '{print $2}') 
if [ -n "$pid" ]
then
    sudo -u tomcat kill -9 $pid
    sleep 5
fi

echo ""

cd "$GIT_CLONE"
sudo -u tomcat git pull  > /dev/null

echo "***********************************************************"
if [ $code_change_global -eq 0 ]
then
  echo ""
  echo "COMPILE AND DEPLOY CODE"
  sudo -u tomcat ant build-deploy
  success=$?
  if [ $? -eq 0 ]
  then
    echo "SUCCESSFUL"
  else
    echo "FAILED"
  fi
  echo "-----------------------------------------------------------"  
fi
if [ $configuration_change_global -eq 0 ] 
then
  echo ""
  echo "DEPLOY CONFIGURATION"
  sudo -u tomcat ant deploy-configuration
  success=$?
  if [ $? -eq 0 ]
  then
    echo "SUCCESSFUL"
  else
    echo "FAILED"
  fi
  echo "-----------------------------------------------------------"  
fi
  
echo ""   
  
#Start Tomcat  
cd "$TOMCAT/bin"  
sudo -u tomcat ./startup.sh