# Continuous delivery with FAKE, Paket and TeamCity

Efficient continuous delivery process can speed up greatly development cycle, improve feedback loop and help to manage automatic deployments.
In this entry I'll present how one can configure a continuous delivery chain using combination of following tools: FAKE, Paket and TeamCity.

## Assumptions

Let's start with defining goals of the process as described in this post:

• Each push to master branch has to trigger a build on the build server,
• The build server builds up a package, runs a suite of tests upon that package and exposes the package as a build artifact with its specific version,
• The package can be deployed to TEST environment only if the build succeeded,
• The package can be deployed to PROD environment only if it was already deployed to TEST before (and tested there),
• In order to verify which version is in what environment, all runs are marked with the build version,
• There are scripts for both build and deploy steps, written with help of common tools.

For the sake of this example, we'll build a very simple package containing a single file. The deploy part boils down to firing a HTTP POST request with contents of the file in the request's body. This minimal setup can be later extended to more sophisticated use cases.

Some TeamCity concepts are used throughout the entry, which are not explained, so in case of any doubts refer to the docs.

## Build scripts

In the first turn, we need to bootstrap Paket. Then let's create paket.dependencies to pull FAKE library as well as other packages for testing:

 1: 2: 3: 4: 5: 6: 7: 8: 9:  source http://nuget.org/api/v2 nuget FAKE // Tests nuget FsCheck nuget FsCheck.Xunit nuget xunit nuget xunit.runners 

Next, let's add build.fsx FAKE script:

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36:  #r @"packages/FAKE/tools/FakeLib.dll" open Fake open Fake.TeamCityHelper let releaseNotes = File.ReadAllLines "RELEASE_NOTES.md" let version = releaseNotes |> parseAllReleaseNotes |> List.head |> fun x -> x.SemVer.ToString() let teamCityIsPresent = TeamCityVersion.IsSome Target "Clean" (fun _ -> CleanDirs ["./build/"] ) Target "Test" (fun _ -> // not relevant here DoNothing () ) Target "Build" (fun _ -> if teamCityIsPresent then SetBuildNumber (sprintf "%s.%s" version TeamCityBuildNumber.Value) "./src/file" CopyFile "./build/file" ) "Clean" ==> "Test" ==> "Build" RunTargetOrDefault "Build" 

What "Build" target does in above snippet is copying file from src to build directory, but you can imagine that there occurs some process of building instead. In addition to that, "Build" target sets the TeamCity build number to X.X.X.X format, where first three numbers (major, minor, patch) are read from the first line of RELEASE_NOTES.md and the last number is taken from the TeamCity build counter (always incremented).

Finally add build.cmd helper script (for Unix you can create corresponing .sh script):

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:  @echo off cls .paket/paket.bootstrapper.exe if errorlevel 1 ( exit /b %errorlevel% ) .paket/paket.exe restore if errorlevel 1 ( exit /b %errorlevel% ) packages/FAKE/tools/FAKE.exe build.fsx %* 

Alternatively, we could have used ProjectScaffold to bootstrap the repository to use Paket + FAKE. I deliberately chose to configure it myself, as ProjectScaffold by default contains some funky stuff I didn't need, e.g. creating and publishing NuGet package for your project.

## Deployment scripts

Now, let's move to creating scripts for automatic deployment. For that reason, at the end of paket.dependencies file we'll add a dependency group:

 1: 2: 3: 4: 5: 6:  group Deploy source http://nuget.org/api/v2 nuget FAKE nuget Http.fs-prerelease 

This dependency group will allow to restore packages needed for deploy part only, i.e. FAKE to run the deploy script and a helper HTTP client library, Http.fs-prerelease.

Deploy script written in FAKE can look like something between those lines:

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:  #r @"packages/deploy/FAKE/tools/FakeLib.dll" #r @"packages/deploy/Http.fs-prerelease/lib/net40/HttpFs.dll" open Fake open HttpFs.Client Target "Deploy" (fun _ -> // read optional parameters let host = getBuildParamOrDefault "host" "localhost" let port = getBuildParamOrDefault "port" "80" // Take the file from build and send a HTTP POST request to target machine ) RunTargetOrDefault "Deploy" 

And the corresponding deploy.cmd (note the additional group deploy for restore command and deploy in packages directory):

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:  @echo off cls .paket/paket.bootstrapper.exe if errorlevel 1 ( exit /b %errorlevel% ) .paket/paket.exe restore group deploy if errorlevel 1 ( exit /b %errorlevel% ) packages/deploy/FAKE/tools/FAKE.exe deploy.fsx %* 

## Building package on TeamCity

Creating appropriate build configuration on TeamCity gets pretty easy now:

1. Attach VCS Root,
3. Define a single Command Line build step,
4. Specify Artifacts path.

Command for the build step is just build (runs the build.cmd script):

To create the package, we need:

• deploy scripts deploy.* (includes fsx and cmd)
• paket files paket.* (includes dependencies and lock file)
• artfiacts from build/* directory
• paket.bootstrapper.exe

The resulting package will look like below:

## TeamCity Deploy chain

Having passed tests and built the package on TeamCity, we can now create a following deployment chain:

This can be achieved by defining new TeamCity configurations Deploy TEST and Deploy PROD (below list applies to both configurations):

1. Specify artifact dependency,
2. Specify snapshot dependency,
3. Add single Command Line build step,
4. Fill in appropriate parameters.

Both TEST and PROD environment need the same artifact dependency built from Build configuration:

Note the "Build from the same chain" option. It ensures that the same package is used for both Deploy configurations. In order to unzip contents of the package.zip in working directroy, we have to type package.zip!** in the "Artifacts Path" field.

Deploy configurations will differ with regards to snapshot dependency. The Deploy TEST configuration should depend on Build:

and Deploy PROD should depend on Deploy TEST:

For the command line step, we'll just have to call deploy (possibly with passing parameters for target environment host and port):

## Summary

With this setup, every push to the master branch will trigger Build configuration. If the tests pass, package.zip gets created and exposed as the configuration's artifact. Successful Build enables next step, which is Deploy TEST. It can be done either manually, or in automatic fashion as well (for instance by attaching a build trigger). Deploy PROD behaves in similar way - it can be run only if Deploy TEST was executed successfully.

It's also useful to subscribe to TeamCity notifications upon successful deployment, so that we're always up-to-date with latest deployments. As of version 8.1.3 TeamCity supports Email, IDE Notifier, Jabber and Windows Tray notifiactions.

Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
| ( [] )
| ( :: ) of Head: 'T * Tail: 'T list
interface IEnumerable
interface IEnumerable<'T>
member GetSlice : startIndex:int option * endIndex:int option -> 'T list
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val head : list:'T list -> 'T