In some TFS 2010 work items, there is a drop down that lists the automated builds that have been run. An example field would be the “Integrated in Build” field on the Task Work Item under the Implementation tab. Even when you delete a build, the build number still shows up in these drop downs. While playing around with the TFS 2010 RC Power Tools, I came across the global list editor, and saw that all those build numbers I had been wanting to delete where listed. After deleting a few and going back to my work items, I confirmed that they no longer showed up. To delete the build numbers from the work item drop down lists: 1.) Get the Power Tools Install the TFS 2010 Power Tools for your version (RC or Beta2) 2.) Open the Global List In Visual Studio, go to Tools\Process Editor\Global List\Open Global List from Server. 3.) Delete - Expand the root folder, which should be named “Builds – ProjectName”.
- Right click on the build and then click delete. It looks like you have to select each item individually, but it goes pretty fast.
- When you have deleted everything you want, click OK.
Even if you delete the item from the list, if you have already selected the build in a work item, the value will remain for that work item.
This is my first attempt at editing a build template in Team Build 2010. Previously I have expressed a desire to see Team Build move away from cryptic XML files and go to a GUI. Well I got my wish, but instead of cryptic XML, we get a cryptic GUI in the form of Windows Workflow foundation. Now maybe I’m not supposed to go in a edit the default build template, but that’s how I learn. I start with what I know works and start tweaking stuff. How bad is it? Well the entire workflow will not fit on my screen at 25% zoom. Here are a couple of screen shots. Now to be fair, the default build template allows you to configure a lot of settings using a property grid like GUI so you never have to look at the WF diagram. It’s only when you need to add additional functionality that you have to go into the land of WF. I guess I, along with Microsoft are expecting the community to step up and provide some new build templates. I also found out that if you double click on the blocks in the WF designer view, you jump “into” the block and that’s all you see. None the less, I set about the task of modifying the default build template to execute XUnit tests instead of MS Tests. Before I go into that, let me explain why I am setting myself up for this torture. My immediate alternatives are to use a different build server, such as Team City, or to use MSTest. If I use Team City, I loose the tight integration with TFS and I do not wish to explain to people about multiple tools. If I use MSTest, I’ll still have to edit the build template to get automated deployment working, as so far I haven't seen a MS Deploy activity. I’m not sure if this is the best solution. Maybe I’ll get some feedback, but more then likely, I’ll end up borrowing someone else's solution down the road. So what do I have to start with? The default build template has an option to scan for assemblies that match a certain naming convention (**\*test*.dll) which does match my naming convention (this pattern is also configurable in the build definition GUI under Basic\Automated Tests\Test Assembly). Looking at the log file for the build, I can see that my two test assembly projects are loaded, but no tests are executed. Once I found where in the WF sequence this was occurring, I realized I had a pretty good starting point. Where to Start First I had to find where I could switch out the call to MSTest with a call to Xunit using the list of test assemblies found. It’s buried pretty deep, so keep double clicking along the following path. - Sequence
- Run On Agent
- Try Compile, Test, and Associate Changesets and Work items
- Sequence
- Compile, Test, and Associate Changesets and Work items
- Try Compile and Test
- Compile and test
- For Each Configuration in BuildSettings.PlatformConfigurations
- Compile and Test for Configuration
- If Not DisableTests
- Run Tests
- If Not TestSpecs Is Nothing
- For Each TestSpec in TestSpecs
- Try Run Tests
- If spec Is TestMetadataFileSpec
- Run MSTest for Test Assemblies
- If Test Assemblies Found (started inserting here)
- If testAssembly.HasTestSettingsFile (deleted)
Run XUnit on all test assemblies Since I don’t have a test settings file with XUnit, I delete the check for HasTestSettingsFile and added an ForEach with an Invoke Process activity to use the xunit console runner. So my “If Test Assemblies Found” block now looks like: The syntax to use the console runner is xunit.console Assembly.dll /nunit <filename>, which looks pretty easy to use. To get the Xunit console on my build server, I could either add it to source control, or put it in a known location on the build server. I opted for the later because it was the easiest. To get the XUnitPath into the build template, I created a new Argument called XUnitPath and took the easy way out by specifying the default as where I had installed XUnit. I did this because I couldn’t figure out how to get it to show in the GUI initially. You need to check your build template in and re-open the build definition. After doing this, my new argument showed up under section 4. Misc. I then went back to my Invoke Process activity and set the file name property to my argument “variable” XUnitPath.
For the arguments, I specified item, which is the ForEach variable for the assembly, as well as an option to output the results as an nUnit formatted xml file. Before I attempt to merge the XML results file, I need to be able to trigger my build to fail if my tests fail. In the invoke process, you can grab the output using the stdOut variable, which I did. I then added an If statement to check for the text “Tests failed”, which the XUnit console outputs to when it encounters a failed test. Unfortunately, every line output fires off my If statement, so my build log file looks a little verbose. However, this works, and after fixing my tests, my build went from partially succeeded to succeeded. At this point, here is what my “If Test Assemblies Found” block looks like. The Assign blocks under Standard Output and Standard error assign BuildDetail.TestStatus to Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Failed. Publish XUnit results to TFS A couple of months ago I went about integrating XUnit tests and results into TFS 2008, so I figured that those same steps would work more or less. After going down that path initially, I decided it would be cleaner to just create a new XSLT template based on the NUnit4MSBuild template that takes an XUnit results file and transforms it into a MSTest trx file directly. This cuts out an extra step in my sequence, and eliminates the need for an external dependency on nxslt3. I wrote up a separate blog post on how to Transform XUnit to MSTest. Like the information presented in this post, it’s a work in progress. After getting the results file in a compatible format I still needed to get them published to TFS. You can use an Invoke activity to MSTest (which requires Visual Studio to be installed on your build server) to publish your results. The filename argument I used was: """" & System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) & "\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe""" and the arguments I used where: "/publish:""" & BuildDetail.BuildServer.TeamProjectCollection.Uri.ToString() & """" & _ " /publishbuild:""" & BuildDetail.BuildNumber & """" & _ " /publishresultsfile:" & """" & item & "_XunitResults.trx" & """" & _ " /teamproject:""" & BuildDetail.TeamProject & """" & _ " /platform:""" & platformConfiguration.Platform & """" & _ " /flavor:""" & platformConfiguration.Configuration & """" Final Thoughts and What’s Next Well it works, but it’s what I consider to be a hack. However, I’m going to invoke YAGNI in that this is manageable given the number of projects, builds and workflow I am working with. I expect that I will have to work on this as my Greenfield project matures. I also expect that once TFS 2010 is released and more people start using it, there will be more community contributions. I would like to wrap this all up into an XUnit workflow activity, but it depends on how much extra time I have. Since this works, the next thing on my to do list is to get automated web deployments working using MS Deploy. Right click and choose Save As:
This is part one of a two part series in how to get XUnit test results into Team Foundation Server 2010. Originally I had started out using the NUnit to MSTest transform that was part of NUnit4TeamBuild, but it left me with an extra step to perform in my build script. Since the XUnit console runner has support for adding new transform options, I figured I could write my own XSLT and eliminate the extra transform step in my build script. If I decided to move ahead with my XUnit WF activity idea, I could also reuse this custom transform for that. Setup First I created two test projects, one based on MSTest, the other on XUnit. From these two test projects, I can create the source (XUnit) and destination (MSTest) files and then work on an XSLT transformation. When using the XUnit console runner against assemblies compiled for .net 4.0, you have to add the following to it’s app.config file: <startup> <requiredRuntime version="v4.0.20506" safemode="true"/> </startup> I also added a line under xunit\transforms to give me the option to use my new XSLT: <add commandline="mstest" xslfile="MSTestXml.xslt" description="output results to MSTest-style XML file"/> Transformation I started out with the XSLT fom the NUnit4TeamBuild project, and began modifying it until the the output from it (transforming the Xunit sample results file) matched the output from my MSTest results file. - Changes for TFS 2010
- Updated Namespace to http://microsoft.com/schemas/VisualStudio/TeamTest/2010
- Changed TestRunConfiguration element to TestSettings
- Updated adapterTypeName
- Removed CodeCoverage element
- Added default text for computerName attribute (required to open in visual studio)
- Removed leading \ from paths
- Changes for XUnit XML
- Create $startDateTime variable to store start time in correct format
- Update times element to use $startDateTime
- Updated template secondsToDuration to show fractions of a second correctly
- Change element references
- /test-results to /assembly
- /test-suit to /class
- //test-case to //test
- Change template getTestClassName to return className as AssemblyName.Class, AssemblyName
- Remove template getTestName
- Change totals to just use values in the XUnit assembly element. Executed = Total – Skipped, NotExecuted = Skip
What’s Missing - Username: Hard coded to TeamBuildUser
- Computername: Hard coded to TeamBuildServer
- Correct Date/Time for certain attributes, like finishing times
- Stack trace info does not link on machines other then the build machine (not sure if it’s possible to fix this)
- I do not use the Name or DisplayName properties on the XUnit Fact attribute so I didn’t test or look into working with those.
What’s Next Well, I skipped over the testing of the XSLT, which you can read about in my next post, where I modify the default build template in TFS 2010 to use XUnit instead of MS Test. I’m doing all this work for a Greenfield project, so I’m just getting things setup and working, and don’t have an extensive test library to try this out on yet. I’m also kind of excited to take a stab at writing a WF activity for Team Build 2010 to wrap this all up into one neat little package. The Template Here is the template in all it’s glory. It works for my brand new project consisting of 3 tests spread across 2 assemblies which I created just for testing my build. So your mileage may very. Remember, credit for the original template which is mainly intact goes to NUnit4TeamBuild. <?xml version="1.0" encoding="UTF-8" ?> <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes" /> <xsl:variable name="guidStub"> <xsl:call-template name="testRunGuid"> <xsl:with-param name="date" select="/assembly/@run-date"/> <xsl:with-param name="time" select="/assembly/@run-time"/> </xsl:call-template> </xsl:variable> <xsl:variable name="startDateTime"> <xsl:value-of select="concat(/assembly/@run-date, 'T', /assembly/@run-time)"/> </xsl:variable> <!--Set computer name and userName once so we can re-use. Hard coded until we can get the values passed --> <xsl:variable name="computerName"> <xsl:value-of select="'TeamBuildServer'"/> </xsl:variable> <xsl:variable name="userName"> <xsl:value-of select="'TeamBuildUser'"/> </xsl:variable> <xsl:template match="/"> <TestRun xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010"> <xsl:attribute name="id"> <xsl:value-of select="concat($guidStub,'30db1d215203')"/> </xsl:attribute> <xsl:attribute name="runUser"> <xsl:value-of select="concat($computerName,'\',$userName)"/> </xsl:attribute> <xsl:attribute name="name"> <xsl:value-of select="concat($userName,'@',$computerName,' ',$startDateTime)"/> </xsl:attribute> <TestSettings name="Local Test Run" id="c136642c-2e64-4f99-9ec3-30db1d215203"> <Description>This is a default test run configuration for a local test run.</Description> <Deployment> <xsl:attribute name="runDeploymentRoot"> <xsl:value-of select="//environment/@cwd" /> </xsl:attribute> <DeploymentItem filename="C:\temp\powerlink\Trunk\Rhino\Rhino.Mocks.dll"> <xsl:attribute name="filename"> <xsl:value-of select="/assembly/@name"/> </xsl:attribute> </DeploymentItem> </Deployment> </TestSettings> <ResultSummary> <xsl:attribute name="outcome"> <xsl:choose> <xsl:when test="/assembly/@failed > 0">Failed</xsl:when> <xsl:otherwise>Completed</xsl:otherwise> </xsl:choose> </xsl:attribute> <Counters error="0" timeout="0" aborted="0" passedButRunAborted="0" notRunnable="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0"> <xsl:attribute name="total"> <xsl:value-of select="/assembly/@total"/> </xsl:attribute> <xsl:attribute name="executed"> <xsl:value-of select="/assembly/@total - /assembly/@skipped"/> </xsl:attribute> <xsl:attribute name="notExecuted"> <xsl:value-of select="/assembly/@skipped"/> </xsl:attribute> <xsl:attribute name="passed"> <xsl:value-of select="/assembly/@passed"/> </xsl:attribute> <xsl:attribute name="failed"> <xsl:value-of select="/assembly/@failed"/> </xsl:attribute> <xsl:attribute name="inconclusive"> <xsl:value-of select="'0'"/> </xsl:attribute> </Counters> <RunInfos /> </ResultSummary> <Times> <xsl:attribute name="creation"> <xsl:value-of select="$startDateTime"/> </xsl:attribute> <xsl:attribute name="queuing"> <xsl:value-of select="$startDateTime"/> </xsl:attribute> <xsl:attribute name="start"> <xsl:value-of select="$startDateTime"/> </xsl:attribute> <xsl:attribute name="finish"> <xsl:value-of select="$startDateTime"/> </xsl:attribute> </Times> <TestDefinitions> <xsl:for-each select="//test"> <xsl:variable name="pos" select="position()" /> <UnitTest> <xsl:attribute name="name"> <xsl:value-of select="@method"/> </xsl:attribute> <xsl:attribute name="storage"> <xsl:value-of select="concat(//environment/@cwd,/assembly/@name)"/> </xsl:attribute> <xsl:attribute name="id"> <xsl:call-template name="testIdGuid"> <xsl:with-param name="value" select="$pos"/> </xsl:call-template> </xsl:attribute> <Css projectStructure="" iteration="" /> <xsl:if test="@description"> <Description><xsl:value-of select="@description" /></Description> </xsl:if> <Owners> <Owner name="" /> </Owners> <Execution> <xsl:attribute name="id"> <xsl:call-template name="executionIdGuid"> <xsl:with-param name="value" select="$pos"/> </xsl:call-template> </xsl:attribute> </Execution> <TestMethod adapterTypeName="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestAdapter, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.Adapter, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" > <xsl:attribute name="name"> <xsl:value-of select="@method"/> </xsl:attribute> <xsl:attribute name="codeBase"> <xsl:value-of select="concat(//environment/@cwd,/assembly/@name)"/> </xsl:attribute> <xsl:attribute name="className"> <xsl:variable name="testClassName"> <xsl:call-template name="getTestClassName"> <xsl:with-param name="type" select="@type"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="$testClassName" /> </xsl:attribute> </TestMethod> </UnitTest> </xsl:for-each> </TestDefinitions> <TestLists> <TestList name="Results Not in a List" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" /> <TestList name="All Loaded Results" id="19431567-8539-422a-85d7-44ee4e166bda" /> </TestLists> <TestEntries> <xsl:for-each select="//test"> <xsl:variable name="pos" select="position()" /> <TestEntry testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d"> <xsl:attribute name="testId"> <xsl:call-template name="testIdGuid"> <xsl:with-param name="value" select="$pos"/> </xsl:call-template> </xsl:attribute> <xsl:attribute name="executionId"> <xsl:call-template name="executionIdGuid"> <xsl:with-param name="value" select="$pos"/> </xsl:call-template> </xsl:attribute> </TestEntry> </xsl:for-each> </TestEntries> <Results> <xsl:for-each select="//test"> <xsl:variable name="pos" select="position()" /> <UnitTestResult testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d"> <xsl:attribute name="startTime"> <xsl:value-of select="$startDateTime"/> </xsl:attribute> <xsl:attribute name="endTime"> <xsl:value-of select="$startDateTime"/> </xsl:attribute> <xsl:attribute name="testName"> <xsl:value-of select="@method"/> </xsl:attribute> <xsl:attribute name="computerName"> <xsl:value-of select="$computerName"/> </xsl:attribute> <xsl:attribute name="duration"> <xsl:call-template name="secondsToDuration"> <xsl:with-param name="seconds" select="@time"/> </xsl:call-template> </xsl:attribute> <xsl:attribute name="testId"> <xsl:call-template name="testIdGuid"> <xsl:with-param name="value" select="$pos"/> </xsl:call-template> </xsl:attribute> <xsl:attribute name="executionId"> <xsl:call-template name="executionIdGuid"> <xsl:with-param name="value" select="$pos"/> </xsl:call-template> </xsl:attribute> <xsl:attribute name="outcome"> <xsl:choose> <xsl:when test="@result='Pass'"> <xsl:value-of select="'Passed'"/> </xsl:when> <xsl:when test="@result='Fail'"> <xsl:value-of select="'Failed'"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="'NotExecuted'"/> </xsl:otherwise> </xsl:choose> </xsl:attribute> <Output> <xsl:for-each select="./failure"> <ErrorInfo> <Message> <xsl:value-of select="./message"/> </Message> <StackTrace> <xsl:value-of select="./stack-trace"/> </StackTrace> </ErrorInfo> </xsl:for-each> </Output> </UnitTestResult> </xsl:for-each> </Results> </TestRun> </xsl:template> <xsl:template name="substring-after-last"> <xsl:param name="string" /> <xsl:param name="delimiter" /> <xsl:choose> <xsl:when test="contains($string, $delimiter)"> <xsl:call-template name="substring-after-last"> <xsl:with-param name="string" select="substring-after($string, $delimiter)" /> <xsl:with-param name="delimiter" select="$delimiter" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$string" /> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="getTestClassName"> <!--Takes type in the form of Assembly.Class and returns Assembly.ClassName, Assembly--> <xsl:param name="type" /> <xsl:value-of select="concat($type, ', ', substring-before($type, '.'))" /> </xsl:template> <xsl:template name="testIdGuid"> <xsl:param name="value" /> <xsl:variable name="id"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="$value"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="concat($guidStub,substring(concat('000000000000', $id),string-length($id) + 1, 12))"/> </xsl:template> <xsl:template name="executionIdGuid"> <xsl:param name="value" /> <xsl:variable name="id"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="$value"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="concat($guidStub,substring(concat('000000000000', $id),string-length($id) + 1, 12))"/> </xsl:template> <xsl:template name="testRunGuid"> <xsl:param name="date" /> <xsl:param name="time" /> <xsl:variable name="year"> <xsl:value-of select="substring($date,1,4)"/> </xsl:variable> <xsl:variable name="month"> <xsl:value-of select="substring($date,6,2)"/> </xsl:variable> <xsl:variable name="day"> <xsl:value-of select="substring($date,9,2)"/> </xsl:variable> <xsl:variable name="hour"> <xsl:value-of select="substring($time,1,2)"/> </xsl:variable> <xsl:variable name="minute"> <xsl:value-of select="substring($time,4,2)"/> </xsl:variable> <xsl:variable name="second"> <xsl:value-of select="substring($time,7,2)"/> </xsl:variable> <xsl:variable name="hexYear"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="$year"/> </xsl:call-template> </xsl:variable> <xsl:variable name="hexMonth"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="$month"/> </xsl:call-template> </xsl:variable> <xsl:variable name="hexDay"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="$day"/> </xsl:call-template> </xsl:variable> <xsl:variable name="hexHour"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="$hour"/> </xsl:call-template> </xsl:variable> <xsl:variable name="hexMinute"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="$minute"/> </xsl:call-template> </xsl:variable> <xsl:variable name="hexSecond"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="$second"/> </xsl:call-template> </xsl:variable> <xsl:variable name="padYear"> <xsl:value-of select="substring(concat('0000', $hexYear),string-length($hexYear) + 1, 4)"/> </xsl:variable> <xsl:variable name="padMonth"> <xsl:value-of select="substring(concat('00', $hexMonth),string-length($hexMonth) + 1, 2)"/> </xsl:variable> <xsl:variable name="padDay"> <xsl:value-of select="substring(concat('00', $hexDay),string-length($hexDay) + 1, 2)"/> </xsl:variable> <xsl:variable name="padHour"> <xsl:value-of select="substring(concat('00', $hexHour),string-length($hexHour) + 1, 2)"/> </xsl:variable> <xsl:variable name="padMinute"> <xsl:value-of select="substring(concat('00', $hexMinute),string-length($hexMinute) + 1, 2)"/> </xsl:variable> <xsl:variable name="padSecond"> <xsl:value-of select="substring(concat('00', $hexSecond),string-length($hexSecond) + 1, 2)"/> </xsl:variable> <xsl:value-of select="concat($padYear,$padMonth,$padDay,'-',$padHour,$padMinute,'-',$padSecond,'00-91c4-')"/> </xsl:template> <xsl:variable name="hex_digits" select="'0123456789ABCDEF'" /> <xsl:template name="dec_to_hex"> <xsl:param name="value" /> <xsl:if test="$value >= 16"> <xsl:call-template name="dec_to_hex"> <xsl:with-param name="value" select="floor($value div 16)" /> </xsl:call-template> </xsl:if> <xsl:value-of select="substring($hex_digits, ($value mod 16) + 1, 1)" /> </xsl:template> <xsl:template name="secondsToDuration"> <xsl:param name="seconds" /> <xsl:variable name="duration"> <xsl:choose> <xsl:when test="$seconds"> <xsl:variable name="hours" select="floor($seconds div 3600)" /> <xsl:variable name="mins" select="floor(($seconds - ($hours * 3600)) div 60)" /> <xsl:variable name="secs" select="floor($seconds - ($hours * 3600) - ($mins * 60))" /> <xsl:variable name="frac" select="substring($seconds - floor($seconds), 3, 7)" /> <xsl:value-of select="substring(concat('00', $hours), string-length($hours) + 1, 2)" /> <xsl:text>:</xsl:text> <xsl:value-of select="substring(concat('00', $mins) ,string-length($mins) + 1, 2)" /> <xsl:text>:</xsl:text> <xsl:value-of select="substring(concat('00', $secs), string-length($secs) + 1, 2)" /> <xsl:if test="$frac > 0"> <xsl:value-of select="concat('.', $frac)" /> </xsl:if> </xsl:when> <xsl:otherwise>00:00:00.0000000</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:value-of select="$duration" /> </xsl:template> </xsl:transform>
This post is part of my PDC09 Conference Notes series. These are my raw notes taken while watching the various session videos from PDC09. Refer to my original post for some conventions I tried to use. FT60 - A Lap around Visual Studio and TFS 2010 - Planning
- Excel workbook for planning Iteration (for a specific Area?)
- Calculates # of working days based on Start and End Date
- Supports holidays and other non-working days by using the Interruptions sheet
- Capacity Planning graphs
- Looks like working in Excel is a much better (almost preferred) experience in 2010.
- Reports (at least the capacity report) was updated right away, no waiting for warehouse to refresh.
- Hierarchical Work Items
- As the name implies, you can setup hierarchies with your work items (kick ass!)
- Query support for hierarchical work items, allows you to define a query for the top level item, and a second query for the child work items (I have to think that this feature was added as a direct result of MS dog fooding TFS).
- Develop in Parallel
- Branch visualization
- Branches now show up as a "special" folder in Source Control viewer
- View Hierarchy (Right click on branch) allows you to view the branches and their relationships in a graphical view. You can add a description for the branch
- Drag and Drop merges
- Change tracing
- Combine branch visualization with change sets, you can see which branches were affected by a given change set. Arrows show stuff like merge directions.
- Continuous Integration
- Gated Check-in
- Verify code via a shelved check in set before something gets actually checked in.
- Seems somewhat redundant with proper branches for development, but I guess it's another way to solve the problem. Although I think that you should be running most of the tests locally before even trying to check in.
- Architecture Diagrams
- Map actually code assemblies (projects) to a block diagram.
- Blocks are for things like Web Layer, Business Layer, Data Layer etc.
- You can setup dependencies between the blocks, like Web Layer depends on Business Layer.
- You can verify that code does not break the dependencies you have outlined in the block diagram (i.e. don't have a business component that references the web component).
- Visual Work Flow (WF) designer for builds looks a lot better then editing XML, but we all know that Microsoft rarely demos stuff that has been tested in the real world.
- Build Reports are improved, more information with direct links to the information in TFS that you need to get more details. Hopefully less digging around the build output folder.
- New SysTray app that notifies you about build events
- Project Visibility and Health
- Nice graphs in MOSS. Do they work in WSS?
- Burndown
- User Stories vs. Tasks
- Graphs build on Web Parts so you can customize the page layout. Also change parameters passed to reports.
- Manageability
- TFS Basic Install
- New Admin Console written WPF. Looks like MMC.
- Ports, URL's, etc
- One click change TFS account password
- View logs
I’ve recently decided to wrap up my previous at home project and start a new project code named WheelJack. I will be posting sometime in the future what WheelJack actually is, but for now you’ll just have to guess based on what I’m writing about. I will be using Team Foundation Server to manage the project, source code, work items, builds, etc. While we have TFS where I work, we do not utilize all of it’s functionality, and part of this new project is an attempt to better understand what TFS can bring to the table. I will have to admit that it’s a complete overkill for a single developer scenario, and I face a very real risk of loosing precious time dealing with TFS, but in the end I think it’s worth it. If it becomes to much of a burden, I can always switch over to subversion and re-task the SharePoint site with project management. Before I sit down and start writing code, I need to setup a new team project, determine my branching strategy, and get Team Build running for nightly and continuous integration builds. For this project I will be using the Scrum Template for Team System created by Conchango. Detailed instructions for installing a new template into TFS and creating a new Team project are outside the scope of this post. Provided that you have full administrative rights to the TFS box, the MSI installer does all the heavy lifting, and creating a new project is a 5 step wizard process. With my new project created I can begin adding work items to the product backlog, define sprints, and areas. I’ve also created a Wiki on the project SharePoint site to various information, including feature description, architecture, design and implementation details. The wiki is a great tool to use when working on a project because it is so easy to create content and organize content. With all of that out of the way for now, I can move onto creating my source tree, which involves the selection of a branching strategy. The branching strategy used for WheelJack is the Single Team Branching Strategy as defined in TFS Branching Guide version 2.0. Single Team Branching Strategy The single team branching strategy requires 3 branches, MAIN, DEV and RELEASE, where DEV and RELEASE are branched from MAIN. While I say 3, there will in fact be more then 3 actual branches. Whenever a new release is created, it gets it’s own branch. This strategy also allows for concurrent DEV branches. Working in DEV After each successful nightly build, code should be merged from MAIN to DEV. In this project, with only one dev branch, and only one developer, there will usually be nothing to merge. The following situations are examples of when there will actually be code in MAIN that needs to be merged to DEV. - Once we create a release branch, if a show stopping bug is discovered and fixed in a release branch, that change is merged to MAIN, and then merged from MAIN to all dev branches.
- If we have multiple dev branches, changes in one DEV branch could be merged to MAIN, and then from MAIN to all other dev branches.
Key features of working in DEV (taken from TFS Branching Guide) - Focus on wide, flat branches to enable steady code flow to MAIN and then back to peer DEV branches
- Work in DEV branches can be segregated by feature, organization, or temporary collaboration.
- Each DEV branch should be a full branch of MAIN.
- DEV branches should build and run Build Verification Tests (BVT’s) the same way as MAIN.
- Forward Integrate (FI) with each successful build of MAIN
- Reverse Integrate (RI) based on some objective team criteria (e.g. internal quality gates, end of sprint, etc.).
Working in MAIN Aside from the initial solution and build creation, no work should be done in Main. Working in a Release Branch Work in production should be limited to show stopping bugs. Changes should be merged into Main once completed. The following list was taken from the branching guide. Key features of working with Release Branches (taken from TFS Branching Guide) - Each RELEASE is a child branch of MAIN.
- Your major product releases from the RELEASE branch and then RELEASE branch access permissions are set to read only.
- Changes from the RELEASE branch RI to main. This merge is one way. Once the release branch is created MAIN may be taking changes for next version work not approved for the release branch
- Duplicate RELEASE branch plan for subsequent major releases. This means there will be one branch per major release.
Source Code Repository Directory Structure - WheelJack$
- DEV
- 1.0 (Version or Feature)
- 1.1 (Version or Feature)
- MAIN
- Source
- Project1
- Project1.Tests
- Project2
- Project2.Tests
- References
- BuildTypes
- Releases
- Release 1.0
- Release 1.1
- Release 2.0
- Release 2.1
Every branch from Main contains everything that is in the Main branch, including the Source and BuildTypes folder (BuildTypes contain the Team Build files that define a build). I started out by creating a new folder on my hard drive that corresponds to the root folder TFS (WheelJack$), and working my way down the hierarchy for Main, creating Source and BuildTypes folders. My project contains a server piece and a client piece, so starting with the server side, I created a folder named MessagingServer, and then created 4 projects under that folder. I also created a _Solutions folder under WheelJack which is where I store all of my Visual Studio Solution files (*.sln). The solution files are not checked into source control. Below you can see what my folder structure looks like in Windows Explorer, and in Solution Explorer. After cleaning up the project names, namespaces, and adding the correct project references, I was able to begin looking at setting up the automated builds. I’ll pick up here next time.
|