tfstfs-sdkgated-checkin

How can a Gated check-in be triggered programmatically?


I am getting errors using the Workspace.Checkin command in TFS because the files I am trying to check in are part of a Gated build definition. There are lots of people asking how to override a gated check-in using the TFS API. But a very different question is this: what if I actually want to perform the gated check-in, programmatically? How can I trigger this in TFS API?

What I want to do is:

  1. Perform a normal check-in operation of a set of pending changes
  2. Receive information about the gated build that was launched
  3. Either subscribe to a completion event for that build, or poll the build until it is complete.
  4. Continue if the build was successful, and changes were committed (get the resulting changeset if possible)
  5. Stop if the build was not successful, and report the error.

I could not find anything out there to answer this question; perhaps it is such an edge case that I'm the only one crazy enough to want to do this. That said, my need for this is legitimate. The only thing I can figure is that I would need to go through the lower-level functions of the gated check-in process: 1) Walk through the build definitions to see if the path of any of the files coincide with any of the paths in any of the gated builds. 2) If an intersection exists, create a shelveset of the pending changes 3) Queue a new build of the gated definition using the shelveset 4) Query the build definition and refresh the information until the build status is completed 5) If the build was successful, unshelve the pending changes, and then check-in using an override.

However, note that even if I do these steps, the resulting build won't look like a gated build at all. Policy override notifications will be sent, and the only way of letting someone know that a build was actually performed would be to include such information in the policy override comments. Is there a more direct way to do this that would make it look like any other gated build?


Solution

  • Prompted by the post by Scordo I was able to figure out how to do this. Here is the code:

    //Initialize connection to server
    TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri(ServerAddress));
    try
    {
        tpc.EnsureAuthenticated();
    }
    catch (Exception e)
    {
        System.Environment.Exit(-1);
    }
    VersionControlServer vcServer = tpc.GetService<VersionControlServer>();
    IBuildServer buildServer = tpc.GetService<IBuildServer>();
    
    //.
    //.
    //Program logic goes here...
    //.
    //.
    
    //Get the workspace and the respective changes
    Workspace workspace = vcServer.TryGetWorkspace(LocalProjectPath);  
    PendingChange[] changes = workspace.GetPendingChanges();  
    
    //Perform the check-in
    bool checkedIn = false;
    
    //Either wait for the gated check-in or continue asynchronously...  
    bool waitForGatedCheckin = true;  
    
    //Attempt a normal check-in
    try
    {
        //First, see what policy failures exist, and override them if necessary
        CheckinEvaluationResult result = workspace.EvaluateCheckin(CheckinEvaluationOptions.All, 
        changes, changes, "Test", null, null);
    
        //Perform the check-in, with overrides if necessary, including Work Item policies
        //or include additional code to comply with those policies
        PolicyOverrideInfo override = null;
        if (result.PolicyFailures != null)
        {
            override = new PolicyOverrideInfo("override reason", result.PolicyFailures);
        }
        workspace.CheckIn(changes, comment, null, null, override);
        checkedIn = true;
    }
    catch (GatedCheckinException gatedException)
    {
        //This exception tells us that a gated check-in is required.
        //First, we get the list of build definitions affected by the check-in
        ICollection<KeyValuePair<string, Uri>> buildDefs = gatedException.AffectedBuildDefinitions;
    
        if (buildDefs.Count == 1)
        {
            //If only one affected build definition exists, then we have everything we need to proceed
            IEnumerator<KeyValuePair<string, Uri>> buildEnum = buildDefs.GetEnumerator();
            buildEnum.MoveNext();
            KeyValuePair<string, Uri> buildDef = buildEnum.Current;
            String gatedBuildDefName = buildDef.Key;
            Uri gatedBuildDefUri = buildDef.Value;
            string shelvesetSpecName = gatedException.ShelvesetName;
            string[] shelvesetTokens = shelvesetSpecName.Split(new char[] { ';' });
    
            //Create a build request for the gated check-in build
            IBuildRequest buildRequest = buildServer.CreateBuildRequest(gatedBuildDefUri);  
            buildRequest.ShelvesetName = shelvesetTokens[0]; //Specify the name of the existing shelveset
            buildRequest.Reason = BuildReason.CheckInShelveset; //Check-in the shelveset if successful 
            buildRequest.GatedCheckInTicket = gatedException.CheckInTicket; //Associate the check-in
    
            //Queue the build request
            IQueuedBuild queuedBuild = buildServer.QueueBuild(buildRequest);
    
            //Wait for the build to complete, or continue asynchronously
            if (waitForGatedCheckin)
            {
                while (!queuedBuild.Build.BuildFinished)
                {
                    //Get the latest status of the build, and pause to yield CPU
                    queuedBuild.Refresh(QueryOptions.Process);  
                    System.Threading.Thread.Sleep(1000)
                }
                if (queuedBuild.Build.Status == BuildStatus.Succeeded)
                {
                    checkedIn = true;
                }
            }
        }
        else
        {
            //Determine a method for specifying the appropriate build definition
            //if multiple build definitions are affected
        }
    }
    catch (CheckinException checkinException)
    {
        //Handle other checkin exceptions such as those occurring with locked files, 
        //permissions issues, etc.
    }
    
    if (checkedIn)
    {
        //Additional logic here, if the check-in was successful
    }