I implemented a work around:
Firstly created a console application, and this is what will be called in the build step:
private static void Main(string[] args)
{
// All this does is dispatch the call to a new process, so that the build can complete independently
// before attempting to update the keepForever field
// This is because you cannot update this field while the build is running
if (args.Length < 1)
{
throw new Exception("No Build Number Provided");
}
var buildId = args[0];
Console.WriteLine("Dispatching task to retain build: " + buildId);
var workingDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
Console.WriteLine("Working Directory Set: " + workingDir);
if (workingDir == null)
{
throw new Exception("Working directory is null");
}
var p = new Process
{
StartInfo =
{
WorkingDirectory = workingDir,
FileName = "RetainBuildIndefinitely.exe",
Arguments = buildId,
RedirectStandardOutput = false,
UseShellExecute = true,
CreateNoWindow = false
}
};
p.Start();
}
Now you might notice it calls RetainBuildIndefinitely.exe in it's own process. This is so it can dispatch this task, and then quit and the build can finish.
RetainBuildIndefinitely.exe is also a console application:
namespace RetainBuildIndefinitely
{
class Program
{
static void Main(string[] args)
{
var client = new WebApiCalls();
client.RetainBuildIndefinately(args[0]);
}
}
}
And then the implementation :
namespace RetainBuildIndefinitely
{
public class WebApiCalls
{
public void RetainBuildIndefinately(string buildId)
{
// Max retry attempts
var retryCount = 30;
// The Tfs Url
var url = [YourURL eg: http://server:8080/tfs/TeamCollection/Project/_apis/build/builds/{buildId}?api-version=2.0";
// Poll until the build is finished
// Since this call should be made in the last build step, there shouldn't be too much waiting for this to complete
using (var client = new WebClient {UseDefaultCredentials = true})
{
client.Headers.Add("Content-Type", "application/json");
client.Encoding = Encoding.UTF8;
var completed = false;
var attempt = 1;
while (completed == false)
{
if (attempt == retryCount)
{
// Couldn't complete? Clearly something went very wrong
// TODO: Sent out a notification email, but to who?
return;
}
var source = client.DownloadString(url);
dynamic data = JObject.Parse(source);
if (data.status == "completed")
{
// Completed, let's move on!
completed = true;
}
attempt = attempt + 1;
Thread.Sleep(2000);
}
} ;
// Set the keepForever property
using (var client2 = new WebClient {UseDefaultCredentials = true})
{
client2.Headers.Add("Content-Type", "application/json");
client2.Encoding = Encoding.UTF8;
client2.UploadString(url, "PATCH", "{keepForever : true}");
}
}
}
}