Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TFS2010 - Wrong changeset appearing at SourceGetVersion

Tags:

I am currently setting up a Team Foundation Server 2010 and I found a very strange behavior when performing a build:

The situation explained: We have 2 Branches

  • Development
  • Main

All developers check in code into the Development branch only. Once per day, the build manager merges some changesets over to the Main branch. On the Development brach, a continuous build at each check in is running. On the Main branch, once per day (in the night) a build is triggered.

Now suppose that the changesets 1-100 are being merged into the Main brach at 5pm, giving changeset 101 as the merge operation. Some developers check in changesets 102-106 after 5 o'clock into the Development branch. Now at 11pm the daily build is automatically triggered and runs on the Main branch. The last changeset of the Main branch is changeset 101. However, the Build details shows changeset 106:

enter image description here

I could imagine that this behavior is intended, because if you check out changeset 106 on the Main branch, you will in fact get the content of changeset 101. But it would be much more readable if this Build summary showed the correct number.

Question 1: Is there a way of manipulating the ouput of the SourceGetVersion information? Maybe through the Build Process Template?

The second scenario, where the TFS behaves strange is even worse: When queuing a new build, there is the option of entering the "Get Version" Parameter, as shown in the following picture:

enter image description here

If I now click on "queue", the build is triggered and AGAIN the build detail outputs the changeset 106 although I specifically set it to get changeset 76.

Question 2: Is this a bug? Is there a hotfix or something to fix this? Or is there any option flag that has to be set?

I hope someone knows more about this. I don't really believe that this is a bug, because it is such a vital functionality that other people must have encountered it before.

Thanks for any help!! Christian

EDIT 1

The folder structure of the Team Project is:

$ProjectName

  • BuildProcessTemplates
  • Documentation
  • SourceCode
    • Development <-- this is a branch
      • 3rdParty
      • Source
    • Main <-- this is a branch
      • 3rdParty
      • Source

The build only pulls the Main branch and everything below it.

EDIT 2

Here is a picture of the Workspace tab in the build definition: enter image description here

like image 787
Christian Avatar asked Nov 30 '11 07:11

Christian


1 Answers

Finally I found out what is going on:

Basically The changeset that can be seen in my picture 1 is always the latest changeset of the entire Team Project Collection. It is the property "SourceGetVersion" on the object "BuildDetails" of type "IBuildDetails".

I think this is a bug which can be worked around: If you change the BuildDetails.SourceGetVersion (which is a string) to some other value, then the build summary will show the updated string. Furthermore, it is then saved correctly to the collection database.

What I have done in order to add the correct changeset number is I have created a custom build activity that takes the branch which should be build as input parameter. It outputs the correct changeset. The activity finds out the correct changeset by connecting to the TFS and downloading the History. Then it looks at all the items in the history and outputs the largest changeset number. Here is the code of that activity:

using System.Activities;
using System.Collections;
using System.Net;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.Build.Client;

namespace SourceGetVersionActivity
{
    [BuildActivity(HostEnvironmentOption.All)]
    public sealed class SourceGetVersionActivity : CodeActivity<string>
    {
        // Define an activity input argument of type string
        public InArgument<string> Branch { get; set; }

        // If your activity returns a value, derive from CodeActivity<TResult>
        // and return the value from the Execute method.
        protected override string Execute(CodeActivityContext context)
        {
            // Obtain the runtime value of the Text input argument
            string branch = context.GetValue(this.Branch);

            ICredentials account = new NetworkCredential("Useranme", "password", "domain");

            // connect / authenticate with tfs
            TeamFoundationServer tfs = new TeamFoundationServer("http://tfs:8080/tfs/CollectionName", account);
            tfs.Authenticate();

            // get the version control service
            VersionControlServer versionControl = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));

            IEnumerable changesets = versionControl.QueryHistory(branch, VersionSpec.Latest, 0, RecursionType.Full,
                null, null, null, int.MaxValue, false, false, false, false);

            int maxVersion = 0;

            foreach (Changeset c in changesets)
            {
                if (c.ChangesetId > maxVersion)
                {
                    maxVersion = c.ChangesetId;
                }
            }

            return string.Concat('C', maxVersion.ToString());
        }
    }
}

I call this activity as soon as possible (after the GetBuild activity).

Basically in the BuildProcessTemplate I have added an Argument (string) "Branch" which needs to be filled with a string that points to the top folder that is being build. The custom activity takes that as input and outputs a string which is the correct changeset id. The BuildDetail.SourceGetVersion property will then be overriden by the correct changeset id.

I find it really strange that no-one else seems to have encountered this problem. I could not find any person on the internet with the same problem. Anyway, I hope this answer helps someone else in the future as well.

EDIT - Writing the above code directly in Workflow Foundation:

To get the correct changeset using more compact code and avoiding custom activites, it is also possible to use Workflow Foundation directly. Below is the "code" (doing exactly what is done in above C# code):

enter image description here

(1) The GetTeamProjectCollection activity gets the current collection. I am saving it inside the TeamProjectCollection variable (see bottom of the picture). Important: The variable needs to be defined inside this sequence, if you define it in outer scope, an error will occur: "Unable to serialize type 'Microsoft.TeamFoundation.Client.TfsTeamProjectCollection'. Verify that the type is public and either has a default constructor or an instance descriptor."

(2) Foreach "changeset" in "TeamProjectCollection.GetService(Of VersionControlServer).QueryHistory(Branch, VersionSpec.Latest, 0, RecursionType.Full, Nothing, Nothing, Nothing, Integer.MaxValue, False, False, False).Cast(Of Changeset)()" The TypeArgument of the Foreach loop is "Microsoft.TeamFoundation.VersionControl.Client.Changeset". This expression gets the version control object from the collection, calls it "QueryHistory" method which returns an IEnumerable with all changesets.

(3) So we are iterating over all changesets and looking at the ChangesetId. Then saving the maximum ChangesetId to the variable "maxId".

(4) At the end, BuildDetails.SourceGetVersion = "C" + maxId.ToString(). The "C" indicates, that the version is a changeset.

I hope someone finds this piece of "Code" useful!

Christian

like image 171
Christian Avatar answered Jan 05 '23 04:01

Christian