Building SharePoint 2013 Apps with TFS 2013
This article contains details of how to take advantage of the new features in TFS 2013 build to automate the packaging of SharePoint 2013 Apps.
TFS 2013 new build features
TFS 2013 includes several changes to the build process that are relevant in automating SharePoint 2013 App project builds. These features are available in both the on-prem version (Team Foundation Server 2013) and the cloud based Team Foundation Service when using Visual Studio 2013.
Simplified build process
Previous versions of TFS builds included an extremely verbose .xaml file. In TFS 2013 this has been completely rewritten and is more concise. The diagram below shows the default TFS 2012 build process template compared to the default TFS 2013 build process template.
As you can see a lot of the detail has been abstracted away. While this makes the process a lot easier to read it is a big change from previous versions so many previous build process customisation techniques no longer work. In particular the customisations in the excellent Office/SharePoint 2013 Continuous Integration with TFS 2012 project on CodePlex don’t work with this new model as the MSBuildArguments variable is no longer available. As you will see later in this article there are improvements in the build functionality that let us achieve similar results without having to customise the build process template.
Another change in TFS 2013 is that the default build process template is not stored in source control so you do not have to have a copy of the template for each project in TFS. The global default build process template is stored centrally in TFS so all projects can refer to the same instance. This removes the need for .xaml files stored in a BuildProcessTemplates folder for each TFS project if you are using the default build process.
Pre and post build scripts
TFS 2013 allows you to run batch and PowerShell scripts before and after compilation, and before and after your tests run. This allows you to perform actions such as versioning assemblies, collecting deployment files such as .app and .ps1 files into the drop folder using PowerShell rather than having to customise the build process template .xaml file. In the next part of this article I will show how to take advantage of this feature to automate the packaging of SharePoint 2013 App projects.
Building SharePoint 2013 App Projects with TFS 2013 build
The changes to the build process offer new ways to package SharePoint 2013 app projects. In particular we can use the default template without any customisations and use PowerShell to ensure our .app package and related files are copied to the drop folder.
Recap on packaging SharePoint 2013 Apps
The process to package a SharePoint 2013 App project using msbuild is detailed in the article “How to build Office developer tools projects with TFS Team Build 2012”.
For app for Office and app for SharePoint projects, you must set the following parameters:
To create package in build: /p:IsPackaging=true
To set the publish directory in the same drop output location: /p:PublishDir=<droplocation>
When your solution contains multiple app projects, set the following parameter to produce app specific publish directories: /p:AppSpecificPublishOutputs=true
As I mentioned earlier the Office/SharePoint 2013 Continuous Integration with TFS 2012 project on CodePlex includes steps to set these parameters. Unfortunately this does not work in TFS 2013 but we can take advantage of the new post build PowerShell script option to avoid having a custom build process template.
Creating a TFS 2013 Build Definition to Package SharePoint 2013 Apps
The steps to create a build definition in TFS 2013 to create a SharePoint app package are shown below:
- Ensure you have a SharePoint 2013 App project checked into TFS 2013 (either Server or Service) and are using Visual Studio 2013.
- In Team Explorer select Builds and click New Build Definition
- In the Source Settings page enter the location of your project in TFS
- In the Build Defaults page either select your on prem build controller or if you are using TF Service select the Hosted Build Controller
- In the Process page select TfvcTemplate.12.xaml (TF Service) or the Default Template (TfvcTemplate.12.xaml) if you are using TF Server.
- Create a PowerShell script with the following contents and save this into source control:
# Specify file types to include $FileTypes = $("*.app","*.ps1","*.zip","*.xml","*.cmd") # Specify the sub-folders to include $SourceSubFolders = $("*app.publish*","*deployscripts*") # If this script is not running on a build server, remind user to # set environment variables so that this script can be debugged if(-not $Env:TF_BUILD -and -not ($Env:TF_BUILD_SOURCESDIRECTORY -and $Env:TF_BUILD_BINARIESDIRECTORY)) { Write-Error "You must set the following environment variables" Write-Error "to test this script interactively." Write-Host '$Env:TF_BUILD_SOURCESDIRECTORY - For example, enter something like:' Write-Host '$Env:TF_BUILD_SOURCESDIRECTORY = "C:\code\FabrikamTFVC\HelloWorld"' Write-Host '$Env:TF_BUILD_BINARIESDIRECTORY - For example, enter something like:' Write-Host '$Env:TF_BUILD_BINARIESDIRECTORY = "C:\code\bin"' exit 1 } # Make sure path to source code directory is available if (-not $Env:TF_BUILD_SOURCESDIRECTORY) { Write-Error ("TF_BUILD_SOURCESDIRECTORY environment variable is missing.") exit 1 } elseif (-not (Test-Path $Env:TF_BUILD_SOURCESDIRECTORY)) { Write-Error "TF_BUILD_SOURCESDIRECTORY does not exist: $Env:TF_BUILD_SOURCESDIRECTORY" exit 1 } Write-Verbose "TF_BUILD_SOURCESDIRECTORY: $Env:TF_BUILD_SOURCESDIRECTORY" # Make sure path to binary output directory is available if (-not $Env:TF_BUILD_BINARIESDIRECTORY) { Write-Error ("TF_BUILD_BINARIESDIRECTORY environment variable is missing.") exit 1 } if ([IO.File]::Exists($Env:TF_BUILD_BINARIESDIRECTORY)) { Write-Error "Cannot create output directory." Write-Error "File with name $Env:TF_BUILD_BINARIESDIRECTORY already exists." exit 1 } Write-Verbose "TF_BUILD_BINARIESDIRECTORY: $Env:TF_BUILD_BINARIESDIRECTORY" # Tell user what script is about to do Write-Verbose "Will look for and then gather " Write-Verbose "$FileTypes files from" Write-Verbose "$Env:TF_BUILD_SOURCESDIRECTORY and copy them to " Write-Verbose $Env:TF_BUILD_BINARIESDIRECTORY # Find the files $files = gci $Env:TF_BUILD_SOURCESDIRECTORY -recurse -include $SourceSubFolders | ?{ $_.PSIsContainer } | foreach { gci -Path $_.FullName -Recurse -include $FileTypes } if($files) { Write-Verbose "Found $files.count files:" foreach ($file in $files) { Write-Verbose $file.FullName } } else { Write-Warning "Found no files." } # If binary output directory exists, make sure it is empty # If it does not exist, create one # (this happens when 'Clean workspace' build process parameter is set to True) if ([IO.Directory]::Exists($Env:TF_BUILD_BINARIESDIRECTORY)) { $DeletePath = $Env:TF_BUILD_BINARIESDIRECTORY + "\*" Write-Verbose "$Env:TF_BUILD_BINARIESDIRECTORY exists." if(-not $Disable) { Write-Verbose "Ready to delete $DeletePath" Remove-Item $DeletePath -recurse Write-Verbose "Files deleted." } } else { Write-Verbose "$Env:TF_BUILD_BINARIESDIRECTORY does not exist." if(-not $Disable) { Write-Verbose "Ready to create it." [IO.Directory]::CreateDirectory($Env:TF_BUILD_BINARIESDIRECTORY) | Out-Null Write-Verbose "Directory created." } } # Copy the binaries Write-Verbose "Ready to copy files." if(-not $Disable) { foreach ($file in $files) { Copy $file $Env:TF_BUILD_BINARIESDIRECTORY } Write-Verbose "Files copied." }
- Note the script above is a modification of the script in the article Get started with some basic TFBuild scripts. It scans the source directory for app.publish and deployscripts folders and copies all .app, .ps1, .xml, .cmd, .zip files to the drop folder. You can customise this to fit your project by changing the $FileTypes and $SourceFolders parameters variables. This is effectively doing the same thing the /p:PublishDir msbuild parameter does and copies the .app package to the drop location, but is able to also include other files (e.g. .ps1 files to deploy the package).
- On the Process page expand the Build > Advanced section and enter the following details:
- In Team Explorer under Builds right click the new build definition and select ‘Queue New Build…’
- Once the build is complete click on the build in Team Explorer and select ‘Open Drop Folder’
- This will either open the drop folder if you are using Team Foundation Server 2013, or open the Team Foundation Service build page if you are using the cloud version (shown below). In this case select the Download drop as zip option to download the build output.
- If everything is set up correctly you will see an .app package with any other files you have selected to be copied to the drop folder in step 6.
- You can then either manually deploy this to your target environment or potentially create a PowerShell script to automatically deploy this to your target environment as a post-build or post-test script argument of the build definition. If you want to do this the PowerShell scripts included in the Office/SharePoint 2013 Continuous Integration with TFS 2012 project on CodePlex provide a great starting point.
Very nice Mr Bakker!
Rich
29 Oct 13 at 11:10 am
[…] Building apps with TFS 2013: http://www.sharepointconfig.com/2013/10/building-sharepoint-2013-apps-with-tfs-2013/ […]
Packaging SharePoint 2013 App | tillander6
11 Feb 14 at 3:29 pm
Hi Ari,
great resource for sp developers. Thanks. Just shared it in the Advatera network as well.
-Volker
Volker
24 Apr 15 at 10:22 am