Why do you copy all those binaries?

· by giulio · Read in about 3 min · (555 Words)

Some days ago I replied to StackOverflow question «TFS 2010 Build, constant drop location, random access issue», and it reminded of an approach used in the past that revealed being effective.

The scenario

TFS Build creates a new folder in the Drop share at each new Build, no matter if it was successful or completely failed. The folder name is taken from the build identifier generated during the build run, e.g. MyBuild_14102.3.

This is good to keep all the recent builds, but it has the drawback that the path to a build that you want to deploy is variable.

A common solution pattern is to customize the Build Template and add a final step to copy the artefacts to a know location. In TFS 2013 could be a one-line batch file.

    robocopy "%TF_BUILD_DROPLOCATION%" "%TF_BUILD_DROPLOCATION%\..\MyBuild_LastGreen" /E

Simple, but inconvenient, as it waste disk space and lengthen build time. Much worse it overlaps the old content, and it is hard knowing which build was copied into.

Important note: all this happens only when the Drop location is a network share.

The solution is to have a pointer, a reference to the last green build.

Local solution

The simplest case happens when you have both the Build Agent and the Drop folder on the same machine. In this case you create a directory junction with the mklink command.

    mklink /J D:\Drops\MyBuild_LastGreen D:\Drops\MyBuild\MyBuild_14102.3

rmdir is the command that remove the junction.

    rmdir D:\Drops\MyBuild_LastGreen

Junctions are nicely processed at the server, that is to the client requesting \\BuildServer\Drops\MyBuild_LastGreen is completely transparent.

Remote solution

Assume now, that the Drop folder lives on another Windows machine different from the Build server.

One option is to use the same technique above, i.e. creating a directory junction, this time through a remote call.

    Invoke-Command -ComputerName MYFILESERVER -ScriptBlock { mklink /J "G:\Drops\MyBuild_LastGreen" "G:\Drops\MyBuild\MyBuild_14102.3" }

Consider that the account running the Build Agent must have adequate permissions on the target machine.

An alternative is to create a new network share pointing to the desired folder. Powershell (again) makes this easy, even if the disk is remote.

    $cs = New-CimSession –ComputerName "MYFILESERVER"
    New-SmbShare –Name "MyBuild_LastGreen" –Path "G:\Drops\MyBuild\MyBuild_14102.3" -CimSession $cs

If you have PowerShell 2.0 this will not work, but you can use the good tutorial at Managing File Shares with Windows PowerShell; as an alternative, you may use wmic (see KB295622 - How to Create a Share Remotely By Using the Windows Management Instrumentation Command-Line Tool).

The most complex case is a Drop folder living on some storage you cannot act upon, for example a Linux NAS. This situation has no easy solution, so I limit hinting some directions. One option is to mount the storage on a Windows machine where you apply the technique above. If you are confident with *nix command like mount, chmod and bash script, you may use Community TFS Build Extensions to ssh in the remote machine, like I exemplified in Integrating Linux builds in TFS.

Conclusions

I like much the approach of using file system links instead of copying files. It is more efficient, self-descriptive and you reach the goal of fixed names for important folders.

One can be interested in marking interesting build setting the Build Quality property and automatically creating the corresponding link leveraging a tool like TFS Deployer or DYI like my friend Pieter shows in his post.