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:
- This script should be called periodically, for example, every 10 minutes to check for the new changesets with Windows task scheduler.
- In case changeset hasn’t built yet (there are no folders with a given building changeset number), we have to start build process.
- Get source code from TFS server for the given changeset number.
- Build Debug and Release configurations for the given changeset. Log files are stored in destination folders.
- Debug/[changesetnumber]
- Release/[changesetnumber]
- Send notifications emails for the selected users at the end of a build process for the given changeset:
- 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.
- 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 }