Howto automate simple Windows build server.

Requirements:

  • cygwin — windows bash scripting utility
  • mailsend — free email notification utility
  • msbuild — standard .NET Framework build utility
  • team explorer — source control utility (tfs)

Features:

  • auto build with Debug and Release configurations
  • builds separately every commited TFS changeset
  • current date appended to MSI file name
  • different email notifications for successful and failed builds with description

Algorithm structure:

  1. This script should be called periodically, for example, every 10 minutes to check for the new changesets with Windows task scheduler.
  2. In case changeset hasn’t built yet (there are no folders with a given building changeset number), we have to start build process.
  3. Get source code from  TFS server for the given changeset number.
  4. Build Debug and Release configurations for the given changeset. Log files are stored in destination folders.
    1. Debug/[changesetnumber]
    2. Release/[changesetnumber]
  5. Send notifications emails for the selected users at the end of a build process for the given changeset:
    1. in case of successful build, email contains network links to the destination folders with msi installation files. Also changeset commiter user name included, comment and edit files list.
    2. in case of failed build, debug.log and release.log build process files attached to the email letter. Also log files copies are stored in destination folders of build server.

Main projects autobuild.bat build file example:

c:\cygwin\bin\bash.exe --login -i /cygdrive/c/WT_Build/firstproject.sh >>C:\WT_Build\log.txt 2>&1
c:\cygwin\bin\bash.exe --login -i /cygdrive/c/WT_Build/secondproject.sh >>C:\WT_Build\log.txt 2>&1

Script file example firstproject.sh:

#!/bin/bash
SERVER="http://tfsserver:portnumber/"
SERVERPATH='$/TFSProjetDirectory/trunk/ProjectName/'
WORKSPACE="WORKSPACENAME"

BUILD_PARAMETERS="/p:PostBuildEvent=""" #skip post build events

CC_SUCCESS_BUILD="firstemail@mailserver.com,secondemail@mailserver.com"
CC_FAILED_BUILD="firstemail@mailserver.com"

PROJECT_BUILD_DIR="ProjectNameSuffix"

GETPATH="$(cygpath -u 'C:\WT_Build\TFSProjetDirectory\trunk\ProjectName')"
DOTNETPATH="$(cygpath -u 'C:\WINDOWS\Microsoft.NET\Framework\v3.5\')"
SETUPPATH="$GETPATH"/SetupProjectDirectory
SETUPPROJECTPATH="$(cygpath -w $SETUPPATH/SetupProjectName.wixproj)"

SETUP_DEBUG_EN_RESULT="$SETUPPATH"/bin/Debug/en-US/MsiOutputName*.msi
SETUP_DEBUG_RU_RESULT="$SETUPPATH"/bin/Debug/ru-RU/MsiOutputName*.msi
SETUP_RELEASE_EN_RESULT="$SETUPPATH"/bin/Release/en-US/MsiOutputName*.msi
SETUP_RELEASE_RU_RESULT="$SETUPPATH"/bin/Release/ru-RU/MsiOutputName*.msi

#include file with auto build utilities
source /cygdrive/c/WT_Build/auto_build_utils.sh

echo "----------------------------------------------------------"
echo "Checking TFS for the new changesets...."

CreatePath $GETPATH

#we load last solution history. 15 latest changesets are taken
$TF history $SERVERPATH /version:T /recursive /stopafter:15 /noprompt /server:$SERVER |
tail -n+3 |
sort |
while read changeset commiter other
do
    if [ ! -d "$BUILDPATH"/Debug/"$changeset" ] && [ ! -d "$BUILDPATH"/Release/"$changeset" ] ; then
    # Control will enter here if destination DIRECTORY doesn't exist
		DeleteCode $GETPATH
		CreatePath $GETPATH

		CreatePath "$BUILDPATH"/Debug/"$changeset"
		CreatePath "$BUILDPATH"/Release/"$changeset"

		getCodeLogfile=$BUILDPATH"/Release/"$changeset"/getcode.log"
		debugBuildLogfile=$BUILDPATH"/Debug/"$changeset"/debug.log"
		releaseBuildLogfile=$BUILDPATH"/Release/"$changeset"/release.log"

		GetCode $changeset $getCodeLogfile
		BuildChangeset $changeset $debugBuildLogfile $releaseBuildLogfile

#Project name considered to be like MyProjectSetupFileName.0.9.4102.30983_110326_171041_EN_Debug.msi
		setupDebugEnOutput="$BUILDPATH"/Debug/"$changeset"/`basename $SETUP_DEBUG_EN_RESULT | cut -d'.' -f1,2,3,4,5`_"$DATE"_EN_Debug.msi
		setupDebugRuOutput="$BUILDPATH"/Debug/"$changeset"/`basename $SETUP_DEBUG_RU_RESULT | cut -d'.' -f1,2,3,4,5`_"$DATE"_RU_Debug.msi
		setupReleaseEnOutput="$BUILDPATH"/Release/"$changeset"/`basename $SETUP_RELEASE_EN_RESULT | cut -d'.' -f1,2,3,4,5`_"$DATE"_EN_Release.msi
		setupReleaseRuOutput="$BUILDPATH"/Release/"$changeset"/`basename $SETUP_RELEASE_RU_RESULT | cut -d'.' -f1,2,3,4,5`_"$DATE"_RU_Release.msi

		CopyResults $SETUP_DEBUG_EN_RESULT $setupDebugEnOutput
		CopyResults $SETUP_DEBUG_RU_RESULT $setupDebugRuOutput
		CopyResults $SETUP_RELEASE_EN_RESULT $setupReleaseEnOutput
		CopyResults $SETUP_RELEASE_RU_RESULT $setupReleaseRuOutput

		MailBuildReport $changeset $commiter $debugBuildLogfile $releaseBuildLogfile

	echo -e "\n"
    fi
done

echo Done.

Auto build utilities file auto_build_utils.sh:

#!/bin/bash
TF="TF.exe"
MSBUILD="MSBuild.exe"

#http://www.muquit.com/muquit/software/mailsend/mailsend.html">http://www.muquit.com/muquit/software/mailsend/mailsend.html
MAILSEND="$(cygpath -u 'C:\WT_Build\mailsend.exe')"

DATE=`date +%y%m%d_%H%M%S`
TFSPATH="$(cygpath -u 'C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE')"
BUILDPATH="$(cygpath -u 'C:\WT_Build\Builds\'$PROJECT_BUILD_DIR)"

PATH=${PATH}:"$TFSPATH":"$GETPATH":"$DOTNETPATH"

function CreatePath {
    echo -e "\n"
    echo Checking directory: $1

    mkdir -p $1
}

function BuildChangeset {

    echo -e "\n"
    echo Building Release changeset $1...
    $MSBUILD $SETUPPROJECTPATH /t:Rebuild /p:Configuration=Release $BUILD_PARAMETERS >$3 2>&1

    echo -e "\n"
    echo Building Debug changeset $1...
    $MSBUILD $SETUPPROJECTPATH /t:Rebuild /p:Configuration=Debug $BUILD_PARAMETERS >$2 2>&1
}

function GetCode {
    echo -e "\n"
    echo Getting code for changeset $1...

	pushd $GETPATH
    $TF get $SERVERPATH /version:C$1 /recursive /noprompt /overwrite /force >$2 2>&1
	popd
}

function DeleteCode {
    echo Deleteting path: $1

    rm -r -f $1
}

function CopyResults {
    echo -e "\n"
    echo Copying $1
    echo '   'to $2
    cp $1 $2
}

function MailBuildReport {
    if (cat $3 | grep -q "Build succeeded") && (cat $4 | grep -q "Build succeeded")
    then
		( echo 'Release build link \\Vm-wt-build\Builds\'$PROJECT_BUILD_DIR'\Release\'$1 &&
		echo 'Debug build link \\Vm-wt-build\Builds\'$PROJECT_BUILD_DIR'\Debug\'$1  &&
		echo -e "\n\n" &&
		$TF changeset /server:$SERVER /noprompt $1 ) |
		iconv -f CP1251 -t UTF-8 |
		$MAILSEND -d share.server.local -smtp share.server.local -cc $CC_SUCCESS_BUILD -t $2@server.com -f fromuser@server.com -sub "$2: $PROJECT_BUILD_DIR Build for changeset $1 READY"
    else
		firstLog="$(cygpath -w $3)"
		secondLog="$(cygpath -w $4)"

		$TF changeset /server:$SERVER /noprompt $1 |
		iconv -f CP1251 -t UTF-8 |
        $MAILSEND -d share.server.local -smtp share.server.local -cc $CC_FAILED_BUILD -t $2@server.com -f fromuser@server.com -sub "$2: $PROJECT_BUILD_DIR Build changeset $1 FAILED." -a "$firstLog,text/plain" -a "$secondLog,text/plain"
    fi
}
This entry was posted in Programming and tagged , , , , , . Bookmark the permalink.

Leave a comment