SharePoint Config

Ari Bakker's thoughts on customising and configuring SharePoint

Building SharePoint 2013 Apps with TFS 2013

with 2 comments

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.

tfs2012 vs 2013 build 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.

clip_image002

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:

  1. Ensure you have a SharePoint 2013 App project checked into TFS 2013 (either Server or Service) and are using Visual Studio 2013.
  2. In Team Explorer select Builds and click New Build Definition
    vs2013-tfs2013-builds
  3. In the Source Settings page enter the location of your project in TFS
    tfs2013-build-source
  4. In the Build Defaults page either select your on prem build controller or if you are using TF Service select the Hosted Build Controller
    tfs2013-build-controller
  5. In the Process page select TfvcTemplate.12.xaml (TF Service) or the Default Template (TfvcTemplate.12.xaml) if you are using TF Server.
    tfs2013-build-process
  6. 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."
    }
  7. 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).
  8. On the Process page expand the Build > Advanced section and enter the following details:
    1. MSBuild arguments: /p:IsPackaging=true /p:AppSpecificPublishOutputs=true
    2. Post-build script path: <path to file created in step 6>tf2013-build-sp2013app
  9. In Team Explorer under Builds right click the new build definition and select ‘Queue New Build…’queue build
  10. Once the build is complete click on the build in Team Explorer and select ‘Open Drop Folder’tfs2013-build-complete
  11. 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.tfs-build-online
  12. 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.vs2013-drop-folder-app
  13. 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.

 

Resources

Post to Twitter Post to Delicious Post to Digg Post to Reddit Post to StumbleUpon

Written by Ari Bakker

October 22nd, 2013 at 1:00 pm

2 Responses to 'Building SharePoint 2013 Apps with TFS 2013'

Subscribe to comments with RSS or TrackBack to 'Building SharePoint 2013 Apps with TFS 2013'.

  1. Very nice Mr Bakker!

    Rich

    29 Oct 13 at 11:10 am

Leave a Reply