Versioning your SharePoint Apps using Team Foundation Server Team Builds

One of the cool features of Team Foundation Server is the ability to schedule and execute builds. This feature is simply known as “Team Builds” (or even shorter, just “Builds”) and provides some interesting extensibility points for doing Build Management.

When it comes to build management, enabling  versioning on my SharePoint Apps is usually the first thing I do when creating a new build.

Quick introduction to Builds

Builds allow you to create a Build Definition and configure the Build Process. After the definition is ready and saved back to TFS, you can queue new builds on demand, scheduled or as part of your Continuous Integration setup:

Trigger

The second neat thing about Builds is, they can output your build to a drop location, ready for you to collect and distribute:

Drop

On top of all this builds provide a way to trace your output back to a certain build. So if you have a certain application that contains an assembly (like a SharePoint Full Trust Solution or a SharePoint Provider Hosted App), Builds can brand these files with a configurable build number. Usually this number contains things like a build date and sequence number and matches the build report name:

Report

Note: in case of assemblies it was common practice for SharePoint custom solutions to only update the AssemblyFileVersion attribute of an assembly. If we kept the AssemblyVersion attribute at 1.0.0.0 we steered away from unneeded web.config SafeControl modifications.

This supplied script also uses this technique if you supply the “-FixedVersion” build argument. See Modification 2 how to achieve this.

The Script

Before Team Foundation Server 2013 customized assembly versioning was usually done by creating a custom build activity and updating (or creating your own) Build Process Template. With TFS 213 we still have those options, but we also have the ability to provide pre- and postbuild PowerShell scripts. These configurable build process parameters are the ideal place to extend and customize our build process. No heavy lifting required, all PowerShell!

See MSDN for more information on these parameters: http://msdn.microsoft.com/en-us/library/vstudio/dn376353.aspx

There is an awesome project on Codeplex that provides a great starting point for our customized build: Community TFS Build Extensions. These extensions also include PowerShell scripts with many, many activities. Most of these are easy to read, well documented and their usage obvious.

See Codeplex for more information: https://tfsbuildextensions.codeplex.com/documentation

So, with those scripts at hand we can start to customize our build process. Since I only made 2 small modifications, I will jump right in.

Modification 1: Version your AppManifest.xml (internally)

Versioning your app is pretty simple, you can do that through the Visual Studio UI of course.

AppVersioning

But there are a couple of issues with this:

  • This value can be changed by developers through the Visual Studio UI. You could of course crack open the AppManifest and overwrite this value upon team build, but…..
  • …. updating this value triggers the App update cycle and that might not always be what you want.

This usually only concerns me if I am dealing with SharePoint Hosted Apps. Because those type of apps don’t have an assembly and commonly contain only HTML, CSS and JS it is pretty hard to track an app back to a certain build.

So we use the Team Foundation Server pre-build script option to version our AppManifest.xml internally by inserting a XML comment. In this case “BuildNumber”.

<?xml version="1.0" encoding="utf-8"?>
<!--Published:920014D5-5E5A-442D-934A-437E4FB0A4DA-->
<!--Created:cb85b80c-f585-40ff-8bfc-12ff4d0e34a9-->
<!--BuildNumber: Demo App MAIN Build_2014.04.18.1-->
<App xmlns="http://schemas.microsoft.com/sharepoint/2012/app/manifest" Name="DemoApp" ProductID="{91a961f1-88fd-4675-b3b4-48d0de3ed086}" Version="1.0.0.0" SharePointMinVersion="15.0.0.0">
  <Properties>
    <Title>Demo App</Title>
    <StartPage>~appWebUrl/Pages/Default.aspx?{StandardTokens}</StartPage>
  </Properties>
  <AppPrincipal>
    <Internal />
  </AppPrincipal>
</App>

To get the comment in the AppManifest, I started with the scripts from the Community TFS Build Extensions mentioned earlier. This kit already contains an ApplyVersionToFiles.ps1, but that script only targets assemblies. By adding a few lines, we make it SharePoint App aware 🙂

# Modification: support SharePoint App Manifest Versioning
# Apply buildnumber to manifest files
$NewBuildNumber = $Env:TF_BUILD_BUILDNUMBER

# Apply the buildnumber to the manifest files
$files = gci $Env:TF_BUILD_SOURCESDIRECTORY -recurse -include AppManifest.xml
if($files)
{
Write-Verbose "Will apply $NewBuildNumber to $($files.count) files."

foreach ($file in $files) {

if(-not $Disable)
{
[System.Xml.XmlDocument] $doc = new-object System.Xml.XmlDocument
$doc.load($file)

attrib $file -r

$newComment = $doc.CreateComment("BuildNumber: $NewBuildNumber");
$root = $doc.DocumentElement
$doc.InsertBefore($newComment,$root)

$doc.Save($file)

Write-Verbose "$file.FullName - version applied"
}
}
}
else
{
Write-Warning "No Manifest files found."
}

This block of code locates the AppManifest.xml in the Build Sources directory and adds the build number as a XML comment.

Modification 2: Fixed versioning your assembly

Next modification we make to the build process is the ability to set the assembly file version and to leave the assembly version unchanged. This is of course not targeted at SharePoint Hosted Apps (since there is no assembly), but mainly for the legacy (Full Trust/ Sandbox) stuff. As mentioned, it was common practice for SharePoint custom solutions to only update the AssemblyFileVersion attribute of an assembly and keep the AssemblyVersion at 1.0.0.0. We still like to track back an assembly to a certain build, so we create a second modification:

# Modification: support SharePoint fixed assembly versioning
# We don't want to match the AssemblyVersion, just the AssemblyFileVersion
if($Fixedversion)
{
$VersionRegex = "\d+\.\d+\.\d+\.\d+(?&lt;!AssemblyVersion.+)"
}

This supplied script enables this if you supply the “-FixedVersion” build argument. After you build your solution and fetch it from the DROP location your assembly properties will look like this:

Version1

But the assembly version will stay at 1.0.0.0 as shown here (using JetBrains dotPeek which is a great tool by the way):

Version2

The Setup

So how do we get all this to work?

Step 1: get the scripts from Codeplex: https://tfsbuildextensions.codeplex.com/documentation

Step 2: apply the modifications or get the scripts attached to this post.

Step 3: upload the script files to source control. I usually place them in a BUILD folder outside of code branches.

Build3

Step 4: Create a new build definition. There are many good posts about how to do this so I will only include the basic steps here.

Supply a build definition name

Build1

Configure the drop location

Build2

Set build process parameters

Build

Step 5: kick off your team build and check the results in your DROP folder.

Happy building!

You can download the scripts here.

/Y.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s