Search code examples
c#tfsbuild

Can't set keepforever if the build is still running


I have a simple c# client that's fully working IF the build has completed:

WebClient client = new WebClient();
client.Encoding = System.Text.Encoding.UTF8;
client.Headers.Add("Content-Type", "application/json");
client.UploadString(url, "PATCH", "{keepForever : true}");
Console.WriteLine(reply);

However: When run as part of a build step, the code does not throw any error, and the JSON from UploadString suggests that keepForever has been changed, but it is not persisted.

Only after the build completes, is this working reliably.

Before I write lengthy work arounds, anything obvious that I'm missing? Is it possible to force it to update, when the build is running?


Solution

  • 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}");
             }
    
          }
       }
    }