Advanced TeamCity usage

As can be seen on the TeamCity page FAKE is really easy to setup in TeamCity, it also support some advanced scenarios to integrate even deeper with it.

Displaying blocks in the log

By default each Target already is displayed as a collapsible block in the log file :

Target blocks

But blocks can be created in targets to separate operations more cleanly :

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
let printHello name =
    use __ = teamCityBlock (sprintf "Hello %s" name)
    printfn "Hello %s !" name

Target "Default" (fun () ->
    printHello "Fake"
    printHello "TeamCity"
)

Custom blocks

Reporting artifacts

While TeamCity has a great configurability in terms of artifacts, nothing beats specifying them in code.

FAKE scripts also have the advantage of being versioned along the rest of your code, avoiding the need to keep complex artifact configurations when you need to support a new branch along with old ones or the need to configure artifacts in each build if you have multiple builds on the same repository.

1: 
2: 
3: 
4: 
5: 
6: 
Target "NuGet" (fun () ->
    Paket.Pack (fun p -> { p with OutputPath = "." })

    !! "*.nupkg"
    |> Seq.iter(PublishArtifact)
)

Customizing version numbers

Each build is assigned a build number in TeamCity that is available as TeamCityBuildNumber from FAKE and that is shown in the TeamCity dashboard :

Default version numbers

But TeamCity also support that builds customize their version number by reporting it directly, using this feature from FAKE is simple and when coupled with other parameters reported by TeamCity can allow complex versioning schemes.

This code read versions from a release notes file and if TeamCity is detected label versions as pre-release when they come from a branch that isn't the default one or from a personal build :

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
// Placed outside any Target
let releaseNotes =
    let fromFile = ReleaseNotesHelper.LoadReleaseNotes ("Release Notes.md")
    if buildServer = TeamCity then
        let buildNumber = int (defaultArg TeamCityBuildNumber "0")
        let asmVer = System.Version.Parse(fromFile.AssemblyVersion)
        let asmVer = System.Version(asmVer.Major, asmVer.Minor, buildNumber)
        let prerelease =
            if TeamCityBuildIsPersonal then "-personal"
            else if getTeamCityBranchIsDefault () then "" else "-branch"
        let nugetVersion = asmVer.ToString() + prerelease

        ReleaseNotesHelper.ReleaseNotes.New(asmVer.ToString(), nugetVersion, fromFile.Date, fromFile.Notes)
    else
        fromFile

SetBuildNumber releaseNotes.NugetVersion

Custom version numbers

Reporting test results

In addition to artifacts, TeamCity also allow to report test results that will be visible in the dashboard directly from the build.

Each test runner has a specific function to send it's result that can be found in the TeamCityHelper API like here for NUnit :

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
Target "Tests" (fun () ->
    testDlls
    |> NUnit(fun p ->
        { p with
            OutputFile = outputFile
            // If the build fails immediately the
            // test results will never be reported
            ErrorLevel = DontFailBuild
        })

    sendTeamCityNUnitImport outputFile
)

Note: NUnit version 3 is a special case as it directly support TeamCity and it's enough to set TeamCity = (BuildServer = TeamCity) in it's configuration.

val printHello : name:string -> unit

Full name: todoteamcityadvanced.printHello
val name : string
val __ : System.IDisposable
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
module Seq

from Microsoft.FSharp.Collections
val iter : action:('T -> unit) -> source:seq<'T> -> unit

Full name: Microsoft.FSharp.Collections.Seq.iter
val releaseNotes : obj

Full name: todoteamcityadvanced.releaseNotes
val fromFile : obj
val buildNumber : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
val defaultArg : arg:'T option -> defaultValue:'T -> 'T

Full name: Microsoft.FSharp.Core.Operators.defaultArg
val asmVer : System.Version
namespace System
Multiple items
type Version =
  new : unit -> Version + 4 overloads
  member Build : int
  member Clone : unit -> obj
  member CompareTo : version:obj -> int + 1 overload
  member Equals : obj:obj -> bool + 1 overload
  member GetHashCode : unit -> int
  member Major : int
  member MajorRevision : int16
  member Minor : int
  member MinorRevision : int16
  ...

Full name: System.Version

--------------------
System.Version() : unit
System.Version(version: string) : unit
System.Version(major: int, minor: int) : unit
System.Version(major: int, minor: int, build: int) : unit
System.Version(major: int, minor: int, build: int, revision: int) : unit
System.Version.Parse(input: string) : System.Version
property System.Version.Major: int
property System.Version.Minor: int
val prerelease : string
val nugetVersion : string
System.Version.ToString() : string
System.Version.ToString(fieldCount: int) : string