Archive

Archive for the ‘TFS’ Category

PowerShell Log Levels Included In TFS 2017 and VSTS Build and Release Logs

October 26th, 2017 No comments

We use quite a few custom PowerShell scripts in some of our builds and releases. This led me to ask the question, which PowerShell log levels actually get written to the TFS Build and Release logs? So I did a quick test on both our on-premise TFS 2017 Update 2 installation and my personal VSTS account, and they yielded the same results.

The PowerShell Script Used To Test

I created a blank build definition and release definition, and the only thing I added to them was a PowerShell task with the following inline script:

Write-Host "Host"
Write-Output "Output"
Write-Debug "Debug"
Write-Debug "Debug Forced" -Debug
Write-Information "Information"
$InformationPreference = 'Continue'
Write-Information "Information Forced"
Write-Verbose "Verbose"
Write-Verbose "Verbose Forced" -Verbose
Write-Warning "Warning"
Write-Error "Error"
throw "Throw"

The Results

Both the build and the release logs yielded the same results. The real-time output shown in the Console resulted in:

Host
Output
DEBUG: Debug Forced
Write-Debug : Windows PowerShell is in NonInteractive mode. Read and Prompt functionality is not available.
Information Forced
VERBOSE: Verbose Forced
WARNING: Warning
C:\Builds\_work\_temp\c0558237-7d53-43ca-97bf-90ed03b8247f.ps1 : Error

The more detailed information written to the log files was:

2017-10-26T06:31:09.5118196Z Host
2017-10-26T06:31:09.5138198Z Output
2017-10-26T06:31:09.5228183Z DEBUG: Debug Forced
2017-10-26T06:31:09.6678192Z ##[error]Write-Debug : Windows PowerShell is in NonInteractive mode. Read and Prompt functionality is not available.
At D:\a\_temp\3b089729-e7c5-484d-aa58-256b33f12e01.ps1:4 char:1
+ Write-Debug "Debug Forced" -Debug
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Debug], PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.WriteDebugCommand
 

2017-10-26T06:31:09.6698195Z Information Forced
2017-10-26T06:31:09.6698195Z VERBOSE: Verbose Forced
2017-10-26T06:31:09.6698195Z WARNING: Warning
2017-10-26T06:31:09.7258181Z ##[error]D:\a\_temp\3b089729-e7c5-484d-aa58-256b33f12e01.ps1 : Error
At line:1 char:1
+ . 'd:\a\_temp\3b089729-e7c5-484d-aa58-256b33f12e01.ps1'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,3b089729-e7c5-484d-aa58-256b33f12e01.p 
   s1
 
Throw
At D:\a\_temp\3b089729-e7c5-484d-aa58-256b33f12e01.ps1:12 char:1
+ throw "Throw"
+ ~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Throw:String) [], RuntimeException
    + FullyQualifiedErrorId : Throw

It’s worth pointing out that even when toggling the system.debug variable to true, regular Debug statements were not written; only the Forced Debug statement was. Also, notice that even though the forced Debug statement was written to the log, it resulted in an error since the script is running in non-interactive mode, so you should probably avoid forcing Debug statements.

Summary

So there you have it. By default only the following statements are written to the log files:

  • Write-Host
  • Write-Output
  • Write-Warning
  • Write-Error

and if you force them, you can also have the following written to the log files as well:

  • Write-Debug
  • Write-Information
  • Write-Verbose

I hope you find this information useful. Happy coding!

Template Solution For Deploying TFS Checkin Policies To Multiple Versions Of Visual Studio And Having Them Automatically Work From “TF.exe Checkin” Too

March 24th, 2014 No comments

Get the source code

Let’s get right to it by giving you the source code.  You can get it from the MSDN samples here.

 

Explanation of source code and adding new checkin policies

If you open the Visual Studio (VS) solution the first thing you will likely notice is that there are 5 projects.  CheckinPolicies.VS2012 simply references all of the files in CheckinPolicies.VS2013 as links (i.e. shortcut files); this is because we need to compile the CheckinPolicies.VS2012 project using TFS 2012 assemblies, and the CheckinPolicies.VS2013 project using TFS2013 assemblies, but want both projects to have all of the same checkin policies.  So the projects contain all of the same files; just a few of their references are different.  A copy of the references that are different between the two projects are stored in the project’s “Dependencies” folder; these are the Team Foundation assemblies that are specific to VS 2012 and 2013.  Having these assemblies stored in the solution allows us to still build the VS 2012 checkin policies, even if you (or a colleague) only has VS 2013 installed.

Update: To avoid having multiple CheckinPolicy.VS* projects, we could use the msbuild targets technique that P. Kelly shows on his blog. However, I believe we would still need multiple deployment projects, as described below, in order to have the checkin policies work outside of Visual Studio.

The other projects are CheckinPolicyDeployment.VS2012 and CheckinPolicyDeployment.VS2013 (both of which are VSPackage projects), and CheckinPolicyDeploymentShared.  The CheckinPolicyDeployment.VS2012/VS2013 projects will generate the VSIX files that are used to distribute the checkin policies, and CheckinPolicyDeploymentShared contains files/code that are common to both of the projects (the projects reference the files by linking to them).

Basically everything is ready to go.  Just start adding new checkin policy classes to the CheckinPolicy.VS2013 project, and then also add them to the CheckinPolicy.VS2012 project as a link.  You can add a file as a link in 2 different ways in the Solution Explorer:

  1. Right-click on the CheckinPolicies.VS2012 project and choose Add -> Existing Item…, and then navigate to the new class file that you added to the CheckinPolicy.VS2013 project.  Instead of clicking the Add button though, click the little down arrow on the side of the Add button and then choose Add As Link.
  2. Drag and drop the file from the CheckinPolicy.VS2013 project to the CheckinPolicy.VS2012 project, but while releasing the left mouse button to drop the file, hold down the Alt key; this will change the operation from adding a copy of the file to that project, to adding a shortcut file that links back to the original file.
    There is a DummyCheckinPolicy.cs file in the CheckinPolicies.VS2013 project that shows you an example of how to create a new checkin policy.  Basically you just need to create a new public, serializable class that extends the CheckinPolicyBase class.  The actual logic for your checkin policy to perform goes in the Evaluate() function. If there is a policy violation in the code that is trying to be checked in, just add a new PolicyFailure instance to the failures list with the message that you want the user to see.

      Building a new version of your checkin policies

      Once you are ready to deploy your policies, you will want to update the version number in the source.extension.vsixmanifest file in both the CheckinPolicyDeployment.VS2012 and CheckinPolicyDeployment.VS2013 projects.  Since these projects will both contain the same policies, I recommend giving them the same version number as well.  Once you have updated the version number, build the solution in Release mode.  From there you will find the new VSIX files at "CheckinPolicyDeployment.VS2012\bin\Release\TFS Checkin Policies VS2012.vsix" and "CheckinPolicyDeployment.VS2013\bin\Release\TFS Checkin Policies VS2013.vsix".  You can then distribute them to your team; I recommend setting up an internal VS Extension Gallery, but the poor-man’s solution is to just email the vsix file out to everyone on your team.

      Having the policies automatically work outside of Visual Studio

      This is already hooked up and working in the template solution, so nothing needs to be changed there, but I will explain how it works here.  A while back I blogged about how to get your Team Foundation Server (TFS) checkin polices to still work when checking code in from the command line via the “tf checkin” command; by default when installing your checkin policies via a VSIX package (the MS recommended approach) you can only get them to work in Visual Studio.  I hated that I would need to manually run the script I provided each time the checkin policies were updated, so I posted a question on Stack Overflow about how to run a script automatically after the VSIX package installs the extension.  So it turns out that you can’t do that, but what you can do is use a VSPackage instead, which still uses VSIX to deploy the extension, but then also allows us to hook into Visual Studio events to run our script when VS starts up or exits.

      Here is the VSPackage class code to hook up the events and call our UpdateCheckinPoliciesInRegistry() function:

      /// <summary>
      /// This is the class that implements the package exposed by this assembly.
      ///
      /// The minimum requirement for a class to be considered a valid package for Visual Studio
      /// is to implement the IVsPackage interface and register itself with the shell.
      /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
      /// to do it: it derives from the Package class that provides the implementation of the 
      /// IVsPackage interface and uses the registration attributes defined in the framework to 
      /// register itself and its components with the shell.
      /// </summary>
      // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
      // a package.
      [PackageRegistration(UseManagedResourcesOnly = true)]
      // This attribute is used to register the information needed to show this package
      // in the Help/About dialog of Visual Studio.
      [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
      // Auto Load our assembly even when no solution is open (by using the Microsoft.VisualStudio.VSConstants.UICONTEXT_NoSolution guid).
      [ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]
      public abstract class CheckinPolicyDeploymentPackage : Package
      {
      	private EnvDTE.DTEEvents _dteEvents;
      
      	/// <summary>
      	/// Initialization of the package; this method is called right after the package is sited, so this is the place
      	/// where you can put all the initialization code that rely on services provided by VisualStudio.
      	/// </summary>
      	protected override void Initialize()
      	{
      		base.Initialize();
      
      		var dte = (DTE2)GetService(typeof(SDTE));
      		_dteEvents = dte.Events.DTEEvents;
      		_dteEvents.OnBeginShutdown += OnBeginShutdown;
      
      		UpdateCheckinPoliciesInRegistry();
      	}
      
      	private void OnBeginShutdown()
      	{
      		_dteEvents.OnBeginShutdown -= OnBeginShutdown;
      		_dteEvents = null;
      
      		UpdateCheckinPoliciesInRegistry();
      	}
      
      	private void UpdateCheckinPoliciesInRegistry()
      	{
      		var dte = (DTE2)GetService(typeof(SDTE));
      		string visualStudioVersionNumber = dte.Version;
      		string customCheckinPolicyEntryName = "CheckinPolicies";
      
      		// Create the paths to the registry keys that contains the values to inspect.
      		string desiredRegistryKeyPath = string.Format("HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\{0}_Config\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      		string currentRegistryKeyPath = string.Empty;
      		if (Environment.Is64BitOperatingSystem)
      			currentRegistryKeyPath = string.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\{0}\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      		else
      			currentRegistryKeyPath = string.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\{0}\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      
      		// Get the value that the registry should have, and the value that it currently has.
      		var desiredRegistryValue = Registry.GetValue(desiredRegistryKeyPath, customCheckinPolicyEntryName, null);
      		var currentRegistryValue = Registry.GetValue(currentRegistryKeyPath, customCheckinPolicyEntryName, null);
      
      		// If the registry value is already up to date, just exit without updating the registry.
      		if (desiredRegistryValue == null || desiredRegistryValue.Equals(currentRegistryValue))
      			return;
      
      		// Get the path to the PowerShell script to run.
      		string powerShellScriptFilePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(CheckinPolicyDeploymentPackage)).Location),
      			"FilesFromShared", "UpdateCheckinPolicyInRegistry.ps1");
      
      		// Start a new process to execute the batch file script, which calls the PowerShell script to do the actual work.
      		var process = new Process
      		{
      			StartInfo =
      			{
      				FileName = "PowerShell",
      				Arguments = string.Format("-NoProfile -ExecutionPolicy Bypass -File \"{0}\" -VisualStudioVersion \"{1}\" -CustomCheckinPolicyEntryName \"{2}\"", powerShellScriptFilePath, visualStudioVersionNumber, customCheckinPolicyEntryName),
      
      				// Hide the PowerShell window while we run the script.
      				CreateNoWindow = true,
      				UseShellExecute = false
      			}
      		};
      		process.Start();
      	}
      }
      

      All of the attributes on the class are put there by default, except for the “[ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]” one; this attribute is the one that actually allows the Initialize() function to get called when Visual Studio starts.  You can see in the Initialize method that we hook up an event so that the UpdateCheckinPoliciesInRegistry() function gets called when VS is closed, and we also call that function from Initialize(), which is called when VS starts up.

      You might have noticed that this class is abstract.  This is because the VS 2012 and VS 2013 classed need to have a unique ID attribute, so the actual VSPackage class just inherits from this one.  Here is what the VS 2013 one looks like:

      [Guid(GuidList.guidCheckinPolicyDeployment_VS2013PkgString)]
      public sealed class CheckinPolicyDeployment_VS2013Package : CheckinPolicyDeploymentShared.CheckinPolicyDeploymentPackage
      { }
      

      The UpdateCheckinPoliciesInRegistry() function checks to see if the appropriate registry key has been updated to allow the checkin policies to run from the “tf checkin” command prompt command.  If they have, then it simply exits, otherwise it calls a PowerShell script to set the keys for us.  A PowerShell script is used because modifying the registry requires admin permissions, and we can easily run a new PowerShell process as admin (assuming the logged in user is an admin on their local machine, which is the case for everyone in our company).

      The one variable to note here is the customCheckinPolicyEntryName. This corresponds to the registry key name that I’ve specified in the RegistryKeyToAdd.pkgdef file, so if you change it be sure to change it in both places.  This is what the RegistryKeyToAdd.pkgdef file contains:

      // We use "\..\" in the value because the projects that include this file place it in a "FilesFromShared" folder, and we want it to look for the dll in the root directory.
      [$RootKey$\TeamFoundation\SourceControl\Checkin Policies]
      "CheckinPolicies"="$PackageFolder$\..\CheckinPolicies.dll"
      

      And here are the contents of the UpdateCheckinPolicyInRegistry.ps1 PowerShell file.  This is basically just a refactored version of the script I posted on my old blog post:

      # This script copies the required registry value so that the checkin policies will work when doing a TFS checkin from the command line.
      param
      (
      	[parameter(Mandatory=$true,HelpMessage="The version of Visual Studio to update in the registry (i.e. '11.0' for VS 2012, '12.0' for VS 2013)")]
      	[string]$VisualStudioVersion,
      
      	[parameter(HelpMessage="The name of the Custom Checkin Policy Entry in the Registry Key.")]
      	[string]$CustomCheckinPolicyEntryName = 'CheckinPolicies'
      )
      
      # Turn on Strict Mode to help catch syntax-related errors.
      # 	This must come after a script's/function's param section.
      # 	Forces a function to be the first non-comment code to appear in a PowerShell Module.
      Set-StrictMode -Version Latest
      
      $ScriptBlock = {
      	function UpdateCheckinPolicyInRegistry([parameter(Mandatory=$true)][string]$VisualStudioVersion, [string]$CustomCheckinPolicyEntryName)
      	{
      		$status = 'Updating registry to allow checkin policies to work outside of Visual Studio version ' + $VisualStudioVersion + '.'
      		Write-Output $status
      
      		# Get the Registry Key Entry that holds the path to the Custom Checkin Policy Assembly.
      		$HKCUKey = 'HKCU:\Software\Microsoft\VisualStudio\' + $VisualStudioVersion + '_Config\TeamFoundation\SourceControl\Checkin Policies'
      		$CustomCheckinPolicyRegistryEntry = Get-ItemProperty -Path $HKCUKey -Name $CustomCheckinPolicyEntryName
      		$CustomCheckinPolicyEntryValue = $CustomCheckinPolicyRegistryEntry.($CustomCheckinPolicyEntryName)
      
      		# Create a new Registry Key Entry for the iQ Checkin Policy Assembly so they will work from the command line (as well as from Visual Studio).
      		if ([Environment]::Is64BitOperatingSystem)
      		{ $HKLMKey = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\' + $VisualStudioVersion + '\TeamFoundation\SourceControl\Checkin Policies' }
      		else
      		{ $HKLMKey = 'HKLM:\SOFTWARE\Microsoft\VisualStudio\' + $VisualStudioVersion + '\TeamFoundation\SourceControl\Checkin Policies' }
      		Set-ItemProperty -Path $HKLMKey -Name $CustomCheckinPolicyEntryName -Value $CustomCheckinPolicyEntryValue
      	}
      }
      
      # Run the script block as admin so it has permissions to modify the registry.
      Start-Process -FilePath PowerShell -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command &amp; {$ScriptBlock UpdateCheckinPolicyInRegistry -VisualStudioVersion ""$VisualStudioVersion"" -CustomCheckinPolicyEntryName ""$CustomCheckinPolicyEntryName""}"
      

      While I could have just used a much smaller PowerShell script that simply set a given registry key to a given value, I chose to have some code duplication between the C# code and this script so that this script can still be used as a stand-alone script if needed.

      The slight downside to using a VSPackage is that this script still won’t get called until the user closes or opens a new instance of Visual Studio, so the checkin policies won’t work immediately from the “tf checkin” command after updating the checkin policies extension, but this still beats having to remember to manually run the script.

       

      Conclusion

      So I’ve given you a template solution that you can use without any modification to start creating your VS 2012 and VS 2013 compatible checkin policies; Just add new class files to the CheckinPolicies.VS2013 project, and then add them to the CheckinPolicies.VS2012 project as well as links.  By using links it allows you to only have to modify checkin policy files once, and have the changes go to both the 2012 and 2013 VSIX packages.  Hopefully this template solution helps you to get your TFS checkin policies up and running faster.

      Happy Coding!

      “Agent lost communication with Team Foundation Server” TFS Build Server Error

      March 12th, 2014 1 comment

      We had recently started getting lots of error messages similar to the following on our TFS Build Servers:

      Exception Message: The build failed because the build server that hosts build agent TFS-BuildController001 - Agent4 lost communication with Team Foundation Server. (type FaultException`1) 
      

      This error message would appear randomly; some builds would pass, others would fail, and when they did fail with this error message it was often at different parts in the build process.

      After a bit of digging I found this post and this one, which discussed different error messages around their build process failing with some sort of error around the build controller losing connection to the TFS server.  They talked about different fixes relating to DNS issues and load balancing, so we had our network team update our DNS records and flush the cache, but were still getting the same errors.

      We have several build controllers, and I noticed that the problem was only happening on two of the three, so our network team updated the hosts file on the two with the problem to match the entries in the one that was working fine, and boom, everything started working properly again 🙂

      So the problem was that the hosts file on those two build controller machines somehow got changed.

      The hosts file can typically be found at "C:\Windows\System32\Drivers\etc\hosts", and here is an example of what we now have in our hosts file for entries (just the two entries):

      12.345.67.89	TFS-Server.OurDomain.local
      12.345.67.89	TFS-Server
      

      If you too are running into this TFS Build Server error I hope this helps.

      Launch Visual Studio Checkin Window With A Keystroke

      September 27th, 2013 No comments

      A few weeks ago I blogged about how you can get custom TFS checkin policies to work when committing from the command line. In that post, I had a quick aside about how you can launch the checkin window (i.e. pending changes) with a quick keystroke using AutoHotkey.  I realize that many people don’t use AutoHotkey (although you really should; it can save you a lot of time), so I thought I would show how you can accomplish this task without AutoHotkey.  It’s quite simple really, and it allows you to launch the VS Checkin window from anywhere, even if you don’t have Visual Studio open.

       

      Steps To Launch VS Checkin Window From The Visual Studio Command Prompt

      1. Open the Visual Studio Command Prompt.  To do this, just hit the windows key and type Developer Command Prompt For VS2012 if using VS 2012, or Visual Studio Command Prompt (2010) if using VS 2010.
      2. In the VS Command Prompt, change to a directory that is in your TFS workspace mapping. e.g. cd C:\Dev\TFS
      3. Type tf checkin and hit enter.

        Steps To Launch VS Checkin Window With A Shortcut Key

        1. Right click on your desktop and choose New –> Shortcut to create a new shortcut file.
          CreateShortcutOnDesktop
        2. Have the shortcut point to the TF executable. This can be found at "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\TF.exe".
          PathToTfExeForShortcut

        3. Enter the name for your shortcut file, such as VS Checkin.
          NameShortcut

        4. Now that the shortcut has been created on your desktop, right-click on it and go the the properties (or use alt+enter). You will want to:
        • add checkin to the very end of the Target,
        • change the Start In directory to a directory you have mapped in your TFS workspace,
        • and assign it a Shortcut Key. 
          CheckinShortcutPropertiesToChange

         

        Results

        That’s it.  Go ahead and try your shortcut key.  I’ll wait.  You should see something like this:

        VsCheckinWindow

        Notice that it pops both a command prompt window and the actual checkin window.  If you don’t have any pending changes, then the command prompt window will simply open and close.

         

        More Information and Caveats

        Old Style Checkin Window

        You probably noticed in the screenshot above that even though I’m using Visual Studio 2012, it still pops the old VS 2010 style of checkin window.  I actually prefer this popped out window to the VS 2012 pending changes pane, and I know a lot of people agree.

         

        Getting The Shortcut Off Your Desktop

        Above I had you create the shortcut on your desktop, but you might not want to have it clutter up your desktop. Unfortunately if you move it to some other folder you will find that the shortcut key no longer works.  For some reason I have found that for the shortcut key to work the shortcut file must either be on your desktop, or in the Start Menu. If you are using Windows 7 you can simply drag and drop the shortcut into the Programs section of the Start Menu.  For us Windows 8.0 folks the Start Menu is gone, so just manually move the shortcut to “C:\ProgramData\Microsoft\Windows\Start Menu\Programs”.

        You may find that after moving the shortcut file the shortcut key no longer works. You just have to go back into the Shortcut file properties, assign it a new Shortcut Key, hit Apply, and then assign the original Shortcut Key back.

         

        Shelving Changes In TFS Using The Old Style Shelve Window

        If you are using TFS and want to shelve your changes instead of checking them in, you can access the old Shelve Pending Changes window in the same way.  In step 4 above, instead of adding checkin as the TF.exe argument, add shelve.  To launch it from the Visual Studio Command Prompt, type tf shelve instead of tf checkin.

         

        Getting Custom TFS Checkin Policies To Work

        As you may have guessed from the very start of the article, custom TFS checkin policies don’t run in this checkin window by default; they will throw errors.  Fortunately for you I have already created a registry file that you can run after each checkin policy update that you do which will rectify this problem.

         

        I hope this helps you be more productive.  Happy coding!

        Getting Custom TFS Checkin Policies To Work When Committing From The Command Line (i.e. tf checkin)

        September 6th, 2013 1 comment

        Update – I show how to have your checkin policies automatically update the registry keys shown in this blog post on this newer blog post. If you are not the person creating the checkin policies though, then you will still need to use the technique shown in this post.

        I frequently check code into TFS from the command line, instead of from Visual Studio (VS), for a number of reasons:

        1. I prefer the VS 2010 style of checkin window over the VS 2012 one, and the 2010 style window is still displayed when checking in from the command line.
        2. I use AutoHotkey to pop the checkin window via a keyboard shortcut, so I don’t need to have VS open to check files in (or navigate to the pending changes window within VS).
          – Aside: Just add this one line to your AutoHotkey script for this functionality. This sets the hotkey to Ctrl+Windows+C to pop the checkin window, but feel free to change it to something else.
          ^#C UP::Run, tf checkin
          
        3. Other programs, such as Git-Tf and the Windows Explorer shell extension, call the TFS checkin window via the command line, so you don’t have the option to use the VS checkin pending changes window.

                The Problem

              The problem is that if you are using a VSIX package to deploy your custom checkin policies, the custom checkin policies will only work when checking code in via the VS GUI, and not when doing it via the command line.  If you try and do it via the command line, the checkin window spits an “Internal error” for each custom checkin policy that you have, so your policies don’t run and you have to override them.

              InternalErrorInCheckinPolicies
              P. Kelly mentions this problem on his blog post, and has some other great information around custom checkin policies in TFS.
              The old TFS 2010 Power Tools had a feature for automatically distributing the checkin policies to your team, but unfortunately this feature was removed from the TFS 2012 Power Tools.  Instead, the Microsoft recommended way to distribute your custom checkin policies is now through a VSIX package, which is nice because it can use the Extension And Updates functionality built into VS and automatically notify users of updates (without requiring users to install the TFS Power Tools).  The problem is that VSIX packages are sandboxed and are not able to update the necessary registry key to make custom checkin policies work from the command line.  I originally posted this question on the MSDN forums, then I logged a bug about this on the Connect site, but MS closed it as “By Design” Sad smile. Maybe if it gets enough up-votes though they will re-open it (so please go up-vote it).

             

            The Workaround

            The good news though is that there is a work around.  You simply need to copy your custom checkin policy entry from the key:

            "HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0_Config\TeamFoundation\SourceControl\Checkin Policies"

            to:

            "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\11.0\TeamFoundation\SourceControl\Checkin Policies" (omit the Wow6432Node on 32-bit Windows).

             

            Not Perfect, but Better

            The bad news is that every developer (who uses the command line checkin window) will need to copy this registry value on their local machine.  Furthermore, they will need to do it every time they update their checkin policies to a new version.

            While this sucks, I’ve made it a bit better by creating a little powershell script to automate this task for you; here it is:

            # This script copies the required registry value so that the checkin policies will work when doing a TFS checkin from the command line.
            
            # Turn on Strict Mode to help catch syntax-related errors.
            # 	This must come after a script's/function's param section.
            # 	Forces a function to be the first non-comment code to appear in a PowerShell Module.
            Set-StrictMode -Version Latest
            
            $ScriptBlock = {
                # The name of the Custom Checkin Policy Entry in the Registry Key.
                $CustomCheckinPolicyEntryName = 'YourCustomCheckinPolicyEntryNameGoesHere'
            
                # Get the Registry Key Entry that holds the path to the Custom Checkin Policy Assembly.
                $CustomCheckinPolicyRegistryEntry = Get-ItemProperty -Path 'HKCU:\Software\Microsoft\VisualStudio\11.0_Config\TeamFoundation\SourceControl\Checkin Policies' -Name $CustomCheckinPolicyEntryName
                $CustomCheckinPolicyEntryValue = $CustomCheckinPolicyRegistryEntry.($CustomCheckinPolicyEntryName)
            
                # Create a new Registry Key Entry for the iQ Checkin Policy Assembly so they will work from the command line (as well as from Visual Studio).
                if ([Environment]::Is64BitOperatingSystem)
                { $HKLMKey = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\11.0\TeamFoundation\SourceControl\Checkin Policies' }
                else
                { $HKLMKey = 'HKLM:\SOFTWARE\Microsoft\VisualStudio\11.0\TeamFoundation\SourceControl\Checkin Policies' }
                Set-ItemProperty -Path $HKLMKey -Name $CustomCheckinPolicyEntryName -Value $CustomCheckinPolicyEntryValue
            }
            
            # Run the script block as admin so it has permissions to modify the registry.
            Start-Process -FilePath PowerShell -Verb RunAs -ArgumentList "-Command $ScriptBlock"
            

            Note that you will need to update the script to change YourCustomCheckinPolicyEntryNameGoesHere to your specific entry’s name.  Also, the “[Environment]::Is64BitOperatingSystem” check requires PowerShell V3; if you have lower than PS V3 there are other ways to check if it is a 64-bit machine or not.

            If you have developers that aren’t familiar with how to run a PowerShell script, then you can include the following batch script (.cmd/.bat file extension) in the same directory as the PowerShell script, and they can run this instead by simply double-clicking it to call the PowerShell script:

            SET ThisScriptsDirectory=%~dp0
            SET PowerShellScriptPath=%ThisScriptsDirectory%UpdateCheckinPolicyInRegistry.ps1
            
            :: Run the powershell script to copy the registry key into other areas of the registry so that the custom checkin policies will work when checking in from the command line.
            PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'"
            

            Note that this batch script assumes you named the PowerShell script “UpdateCheckinPolicyInRegistry.ps1”, so if you use a different file name be sure to update it here too.

            Your developers will still need to run this script every time after they update their checkin policies, but it’s easier and less error prone than manually editing the registry.  If they want to take it a step further they could even setup a Scheduled Task to run the script once a day or something, or even implement it as a Group Policy so it automatically happens for everyone, depending on how often your company updates their checkin policies and how many developers you have.

            Ideally I would like to simply be able to run this script during/after the VSIX installer.  I have posted a question on Stack Overflow to see if this is possible, but from everything I’ve read so far it doesn’t look like it; maybe in the next generation of VSIX though.  If you have any other ideas on how to automate this, I would love to hear them.

            Happy coding!

            Path Too Long for Team Foundation Database Project Build

            February 5th, 2012 1 comment

            Arrggghhhh TFS and builds!  Such a love-hate relationship!  So we have our TFS builds setup to both compile our C# projects as well as compile and deploy our Team Foundation (TF) Database (DB) projects.  One day I started getting the following file path too long error message on our build server:

            $/RQ4TeamProject/Prototypes/BuildProcessTests/RQ4.Database.sln – 1 error(s), 69 warning(s), View Log File
            C:Program Files (x86)MSBuildMicrosoftVisualStudiov10.0TeamDataMicrosoft.Data.Schema.TSqlTasks.targets (80): The "SqlSetupDeployTask" task failed unexpectedly. Microsoft.Data.Schema.Build.BuildFailedException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters. —> System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.    at System.IO.PathHelper.Append(Char value)    at System.IO.Path.NormalizePath(String path, Boolean fullCheck, Int32 maxPathLength)    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)    at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize)    at System.IO.StreamReader..ctor(String path, Boolean detectEncodingFromByteOrderMarks)    at Microsoft.Data.Schema.Sql.Build.SqlPrePostDeploymentModifier.GenerateMergedSqlCmdFiles(DeploymentContributorConfigurationSetup setup, DeploymentContributorConfigurationFile configFile)    at Microsoft.Data.Schema.Sql.Build.SqlPrePostDeploymentModifier.OnEstablishDeploymentConfiguration(DeploymentContributorConfigurationSetup setup)    at Microsoft.Data.Schema.Build.DeploymentContributor.EstablishDeploymentConfiguration(DeploymentContributorConfigurationSetup setup)    — End of inner exception stack trace —    at Microsoft.Data.Schema.Build.DeploymentContributor.EstablishDeploymentConfiguration(DeploymentContributorConfigurationSetup setup)    at Microsoft.Data.Schema.Build.DeploymentProjectBuilder.VerifyConfiguration()    at Microsoft.Data.Schema.Tasks.DBSetupDeployTask.BuildDeploymentProject(ErrorManager errors, ExtensionManager em)    at Microsoft.Data.Schema.Tasks.DBSetupDeployTask.Execute()    at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()    at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask, Boolean& taskResult)

            Naturally I said, "Ok, our TF DB project isn’t compiling because a path is too long. Somebody must have checked in a stored procedure with a really long name".  After viewing the history of the branch I was trying to build however, I didn’t see anything that stuck out.  So for fun I thought I would shorten the Build Definition name’s length and build again.  Viola, like most path issues with TFS this fixed the issue (this is because the build definition name is often used in the path that TFS moves/builds files to).  However, we have many queries setup that match the specific Build Definition name (since it’s used in the "Integrated in Build" work item value), so shortening it wasn’t a long term solution.  As an added frustration bonus, I discovered our build definition name was only 1 character too long!

            The first thing I did was make a Path Length Checker program so I could see how long the file paths (files and directories) really were on the build server.  Oddly enough, the longest paths were 40 characters short of the maximum limit described by the error message.

            So I took a look at our database folder structure and saw that it really was wasting a lot of characters.  This is what the path to one of our stored procedure folders looks like: "..DatabaseSchema ObjectsSchemasdboProgrammabilityStored ProceduresProcs1".  I figured that I would just rename some of these folders in Visual Studio and that should be good……..OMG never try this while connected to TFS!  I got a popup warning for every single file under the directory I was renaming (thousands of them), with something along the lines of "Cannot access file X, or it is locked…..blah blah. Please press OK".  So after holding down the enter key for a over an hour to get past all these prompts it finally finished.  When I reviewed the changes to check in, I saw that many duplicate folders had been created, and there were miscellaneous files all over the place; some got moved, some never; what a mess.  So I went ahead and reverted my changes.

            So I thought, "Ok, let’s try this again, but first going offline so as not to connect to TFS".  So I disabled my internet connection and opened the database solution (this is the only way that I know of to work "offline" in TFS 🙁 ).  I then tried to change the high level folder "Schema Objects" to just "Schema".  Nope, Visual Studio complained that the folder was locked and couldn’t be changed.  I thought to myself, "TFS makes all non-checked out files read-only, and I’m offline so it can’t check them out.  That must be the problem".  So I opened up explorer and made all of the files and folders writable and tried again. Nope, no deal; same error message.

            So I thought, "Alright, let’s try doing a low level directory instead".  It seems that VS would only let me rename a directory that didn’t contain other directories.  So I renamed the "Procs1" folder to just "1".  I no longer got the warning prompt for every file, but it was still pretty slow and I could watch VS process every file in the Solution Explorer window.  After about 10 minutes it finally finished.  So I checked in my changes and tried building again.  Nope, same error message as before about the path being too long.

            So I said screw this.  I opened up the TFS Source Control Explorer and renamed the folder from there.  It worked just fine.  I then had to open up the Database.dbproj file in a text editor and do a find and replace to replace "Schema Objects" with "Schema".  This worked for refactoring the folder structure quickly, but I was still getting the "path too long" error message on the build server. Arrrrgg!

            So I went back to the build, set the verbosity to “diagnostic” and launched another build (which failed again with the path too long error).  Looking through the error message I noticed that it did complete building the DB schema, and went on to failing on building the Pre/Post deployment scripts.  Looking back to my original error message and reading it more carefully I noticed this line, “Microsoft.Data.Schema.Sql.Build.SqlPrePostDeploymentModifier.GenerateMergedSqlCmdFiles”.  So now I was pretty sure the problem was in the pre and post deployment scripts.

            Now, we have a very custom process for our database scripts, and part of this process involves using SQLCMD mode to include other script files into our pre and post deployment files when they are generated; it basically makes it look like the referenced script’s contents were in the pre/post deployment script the entire time.  This is necessary for us so that developers don’t have to look through pre and post deployment scripts that are tens of thousands of lines long.  It turns out that while none of these referenced script files themselves had a path that was over the limit, somehow during the generation of the pre/post deployment scripts it was making the path even longer.  I looked through our referenced scripts and saw a few particularly long ones.  So I refactored them to shorten the file names, and presto the build worked!  Hooray!

            I’m guessing that the reason the build wouldn’t give me an actual filename when it encountered the error is because SQLCMD mode was dynamically referencing those scripts at build time, so to the build it just looked like the pre and post deployment scripts were each thousands of lines long, when in fact they are only maybe 50 lines long, but they "include" other files, and those file references must be used at build time.

            So the morals of this story are:

            1. If VS is blowing chunks when you try to rename a folder (especially when connected to TFS), don’t do it through VS.  Instead modify the folder structure outside of VS and then manually edit the .csproj/.dbproj/.vbproj files to mirror the changes.

            2. Whenever you are stumped on a build error, go back and THOROUGHLY read the ENTIRE error message.

            3. Be careful when using compile-time language features to reference/include external files.

            Using TFS Programmatically

            August 17th, 2011 No comments

            I recently discovered this post which shows how you can programmatically update queries in TFS, which is great for when you make a WIT (Work Item Template) change that will affect tons of queries.

            That post then led me to this one which is great as well.  In fact, Tarun Arora has tons of great posts relating to TFS. Rock on!

            Categories: TFS Tags: , ,

            TFS Power Tools Features And How To Run Them From Visual Studio

            July 11th, 2011 No comments

            There are a few great features in the TFS Power Tools that I wasn’t aware of, such as TreeDiff which let’s you compare folders (or even entire branches) to see the differences between them,  and Undo Unchanged which undoes files that are checked out, but don’t have any changes made to them, so that they don’t appear in the Pending Changes window.  This blog explains some of the features, and this one shows how to add the console-app-only features as External Tools in Visual Studio to easily run them from Visual Studio. Very cool!

            Automatically deploying TFS Checkin Policies to the entire team

            May 24th, 2011 No comments

            I stumbled across this great post showing how you can use the TFS Power Tools to automatically deploy your custom TFS check-in policies to everyone on your team in Visual Studio 2010.  It’s so awesome I had to share.

            TFS GoToWorkItem VS command and keyboard shortcut

            April 29th, 2011 No comments

            The button to jump directly to a work item by specifying its ID looks to be on the Work Item Tracking toolbar by default in VS / TFS 2010.  This button is not on the toolbar by default in VS / TFS 2008 though.  To add it yourself just go to Tools => Customize, then choose the category Team and the command Go To Work Item…, and you can drag the command into one of your existing toolbars.

            If you want to setup a keyboard shortcut for the command, just go to Tools => Options => Environment => Keyboard, and the command is called Team.GotoWorkItem.  I map it to Ctrl+Shift+/ since Ctrl+/ is the C# keyboard shortcut to search in a file.

            Solution won’t build on TFS Build Server

            April 17th, 2011 1 comment

            So if you are able to build (compile) the solution on your local machine, but it won’t build on the TFS build server and you are getting an error message similar to:

            1 error(s), 1 warning(s)
            $/TeamProject/Dev/RQ4/FBs/4.1.4_eSec/RQ4.Client.sln – 1 error(s), 1 warning(s), View Log File
            IntegrationfrmGetIntegrationAuthorization.xaml.cs (1138): The type or namespace name ‘BillingFields’ could not be found (are you missing a using directive or an assembly reference?)
            C:WindowsMicrosoft.NETFramework64v4.0.30319Microsoft.Common.targets (1360): Could not resolve this reference. Could not locate the assembly "IQ.Core.Resources, Version=4.1.2.0, Culture=neutral, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.

            Then the problem is likely one of the following:

             

            1 – The project/dll being reference is set to "Specific Version", so open the reference’s properties and change it to "Any Version".

             

            2 – The project is not set to be built under the specific configuration.  Right click on the solution in the Solution Explorer, and choose Configuration Manager.  All projects should be set to be build (i.e. have a checkmark), and all of their platforms should be set to the same value.  Change the Active Solution Platform to the platform you use and ensure that all projects are still set to always build.

             

            3 – The path to the refenced project/dll is too long.  Windows/.NET has a limitation where the reference path cannot be more than 260 characters, and the directory it’s in cannot be more than 248 characters.  So the work around for this is usually to rename your build definition to something shorter, or if you just added a new project/namespace to the project, to shorten its name so the path stays under the limit.

            Categories: Build, TFS Tags: , , , ,