I am writing a plugin for TFS that performs automatic branching and merging based on an xml file stored in source. I am able to perform this on the server except I am unable to get the latest of the xml file if it was changed.
public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType, object notificationEventArgs, out int statusCode, out string statusMessage, out ExceptionPropertyCollection properties)
{
TeamFoundationVersionControlService versionControl = requestContext.GetService<TeamFoundationVersionControlService>();
string localTempFile = Path.GetTempFileName();
versionControl.DownloadFile(requestContext, serverItemPath, 0, VersionSpec.Parse("C" + versionControl.GetLatestChangeset(requestContext).ToString(), null).First(), localTempFile);
return EventNotificationStatus.ActionApproved
}
The issue is that because I want to intercept the checkin before it becomes a changeset, the download file function gets me the version of the latest checkin, not the version that was promoted. Does anyone know how to get the version being checked in?
You should try the artifacts field of the CheckinEvent class.
CheckinEvent ev = notificationEventArgs as CheckinEvent;
I would take a different route: use a CI build that monitors just that file.
It is way more robust and manageable than a server plug-in.
Related
I am attempting to check-out a single file via workspace.PendEdit with an exclusive lock LockLevel.CheckOut. The following function succeeds (no errors) but it seems to have no effect on the file in TFS (not checked-out and no lock).
public static void Lock(string filePath)
{
var workspace = GetWorkspace(filePath);
workspace.PendEdit(new[] {filePath}, RecursionType.None, null, LockLevel.CheckOut);
}
I am suspecting that this has something to do with my TFS Workspace being local. However, Visual Studio 2015 seems to have no problem establishing a lock on the file via [Source Control Explorer]->[Right Click Selected File]->[Advanced]->[Lock]. What am I doing that's different than what VS is doing? Am I missing something?
You should use RecursionType.Full not RecursionType.None.
workspace.PendEdit(new[] {filePath}, RecursionType.Full, null, LockLevel.CheckOut);
The PendEdit() method return the number of files that were checked out/locked for the filePath you specify. The RecursionType.Full will recurse to the last child of the path.
Update:
Please try to install this TFS nuget package(https://www.nuget.org/packages/Microsoft.TeamFoundationServer.ExtendedClient/) for your API project and test if this issue still exists. If it works, no matter what version of VS you use, this issue won't appear.
After much trial and error I ended up implementing an event handler for NonFatalError like this:
private static void VersionControlServer_NonFatalError(object sender, ExceptionEventArgs e)
{
if (e.Failure != null && e.Failure.Severity == SeverityType.Error)
throw new ApplicationException("An internal TFS error occurred. See failure message for details:\r\n"+e.Failure.Message);
}
Once the event handler was hooked up to the versionControlServer object via versionControlServer.NonFatalError += VersionControlServer_NonFatalError; I was able to see what was going on with my exclusive check-outs. As it turned out, TFS was failing silently with the following error:
TF400022: The item $/Fake/Server/Path/project.config cannot be locked for checkout in workspace MYWORKSPACE;Dan Lastname. Checkout locks are not supported in local workspaces.
The solution was to change the LockLevel from LockLevel.CheckOut to LockLevel.Checkin. Its a slightly different type of lock but its sufficient for my needs and that's the type of lock VS is using when you attempt to lock a file in a local workspace. So here is my original function with the tiny change in LockLevel that made all the difference.
public static void Lock(string filePath)
{
var workspace = GetWorkspace(filePath);
workspace.PendEdit(new[] {filePath}, RecursionType.None, null, LockLevel.Checkin);
}
I've encountered a problem when I try to retrieve the valid states of all features within an MSI package using the Microsoft.Deployment.WindowsInstaller.Installer class.
I want to copy the ValidStates property of each FeatureInfo within a Session. However when doing so I get a "Handle is in an invalid state." exception.
If I print each of these values out using Console.WriteLine() or step through the code in Visual Studio there is no exception.
I am at a loss as to what is preventing me from doing this.
Thanks in advance!
My Code:
var featureDictionary = new Dictionary<string, string[]>();
if (string.IsNullOrWhiteSpace(mPath))
return featureDictionary;
try
{
Installer.SetInternalUI(InstallUIOptions.Silent);
using (var session = Installer.OpenPackage(mPath, true))
{
foreach (var feature in session.Features)
{
try
{
var states = feature.ValidStates.Select((state) => state.ToString());
featureDictionary.Add(feature.Name, states.ToArray());
}
catch (InstallerException ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}
catch (InstallerException) { }
return featureDictionary;
The basic problem appears to be that you are opening the MSI as a file. Since you haven't posted its declaration, or how it is set, I'm assuming that mpath means it's a path to the file. Your OpenPackage method parameters seem to indicate this too. You're getting that error because you are trying to open the MSI file as a file during the actual install, and failing.
The way to get hold of the database for the running install is to use Session.Database.
You can't open the running MSI as a file during the install perhaps for the same reason you can't run an MSI file that you have open with Orca, a simple file sharing violation. When you step through with Visual Studio you're simply accessing the static file and getting default values and the file isn't being used for an install. The other issue is that there can only be one Session object per process (as the OpenPackage docs say) and you are attempting to get a second one while there is already a Session object associated with the handle of the install.
As a custom action it needs to be sequenced after CostFinalize.
Windows Installer conditional expressions such as !feature-state will tell you what state the feature is in, because it's usually better to avoid code where Windows Installer will just give you the answer.
So, I am trying to display all available TFS test suites in a custom build workflow parameter editor. See my previous question.
Now I can establish a connection to my TFS instance by using the .Net TFS API just as a normal client Application would. But I would have to embedd the URL to my TFS in the custom assembly, and that's something I would like to avoid.
That got me thinking: This code runs within Visual Studio, so it must somehow be possible to obtain information about the current TFS connection. After searching the web, a lot of different sites showed code about how to do this in a normal Visual Studio extension. So I put together something like this:
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (provider != null)
{
EnvDTE80.DTE2 dte;
dte = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.12.0");
MessageBox.Show("Got dte: " + dte.ActiveDocument.ToString());
TeamFoundationServerExt ext = dte.DTE.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt") as TeamFoundationServerExt;
MessageBox.Show("Got tfs: " + ext);
I am able to get the DTE object, calling its ToString() method gives me System.__ComObject, so this part appearantly works.
But when I try to get the TeamFoundationServerExt object, I always get null
Any tips why this does not work?
So, as it turns out, you do not have to use the DTE stuff at all.
You actually can get the TFS connection like this:
var builddef = (IBuildDefinition)provider.GetService(typeof(IBuildDefinition));
var tpc = builddef.BuildServer.TeamProjectCollection;
var tp = builddef.TeamProject;
After searching the googles for couple hours I found an answer to my question. I know this post Undo checkout TFS answers my question, however it doesn't answer all the questions I have. I want to achieve the same objective that the post asked about. How to only revert files that have been checked out if nothing was modified in that file? The answer to my question shouldn't be too hard to answer.
So what I'm doing is copying files from a server and overwriting them in my local workspace. I am checking out all the files being copied. However, if a file that was copied is not modified in anyway(server file and destination file are exact same), I'd like to undo the checkout of that file.
I know I'm to use the workspace.Undo() method and the gentleman said it worked for him. However he didn't show how he implemented it.
Here is the code I have with help from the link:
public static void CheckOutFromTFS(string filepath)
{
var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(filepath);
if (workspaceInfo == null)
{
return;
}
var server = new TfsTeamProjectCollection(workspaceInfo.ServerUri);
var workspace = workspaceInfo.GetWorkspace(server);
workspace.PendEdit(filepath);
}
The answer given was to use the workspace.Undo() method. Do I add this method as the last line in CheckOutFromTFS() like so?
public static void CheckOutFromTFS(string filepath)
{
var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(filepath);
if (workspaceInfo == null)
{
return;
}
var server = new TfsTeamProjectCollection(workspaceInfo.ServerUri);
var workspace = workspaceInfo.GetWorkspace(server);
workspace.PendEdit(filepath);
workspace.Undo();
}
Or is it done differently? I'm not sure if this Undo() will only revert files if there are no changes or just revert the checkout entirely and render the PendEdit() useless. Can someone help clarify this for me?
If you use a local workspace then all file that have no changes will automatically revert to not checked-out. You don't need to do anything at all. This works with VS 2012 or better with TFS 2012 or better. You'll need to convert you workspace to a local workspace first like this
So I found the answer to my question in various posts. I kinda took bits an pieces and combined them together to get my working solution. The use of the Undo() function with passing in the filepath actually does uncheckout the file regardless if it was modified or not. My workspace was also local but VS and TFS couldn't automatically revert those unmodified files for me so I took the below approach.
So what I decided to do was to just use the Team Foundation Power Tools "uu" command to undo the changes to unchanged files in the workspace. I created a batch file and entered the following command: echo y | tfpt uu . /noget /recursive. Since we will not show the shell during execution, I used the "echo y" command to automatically answer the question, "Do you wish to undo these redundant pending changes? (Y/N)". Including /noget is highly recommended since it prevents a forced 'get latest' of all your project's files which depending on the total number can take a extremely long time.
var startInfo = new System.Diagnostics.ProcessStartInfo
{
WorkingDirectory = projectRoot,
FileName = projectRoot + #"\undoUnchanged.bat",
UseShellExecute = false,
CreateNoWindow = true
};
Process process = Process.Start(startInfo);
process.WaitForExit();
process.Close();
After the script runs and the process.Close() executes you and double check if your unmodified files actually were unchecked out by hitting the refresh button on the Team Explorer window in your project. Hope someone else can find some use in this.
If I understand the question well and you actually need undo through C# code behind, I believe this shoul help you:
Undo checkout TFS
I have a project that should be get latest to specific date. I have this code:
var serverFolder = pathInTfs;
var localFolder = pathInLocalMachin;
var workingFolder = new WorkingFolder(serverFolder, localFolder);
// Create a workspace mapping
workspace.CreateMapping(workingFolder);
if (!workspace.HasReadPermission)
{
throw new SecurityException(
String.Format("{0} does not have read permission for {1}",
versionControl.AuthorizedUser, serverFolder));
}
// Get the files from the repository
workspace.Get(dateForLatest, GetOptions.Overwrite);
every thing is good but I want to be get latest only directory "pathInTfs" in "pathInLocalMachin" but when program run workspace.Get() every project be get latest.
How I can get latest one path in my project.
There are several overloads of Get which allow you to specify the set of objects you want to get. For what you're doing, I think you want Get(GetRequest, GetOptions).
The GetRequest includes an ItemSpec where you can specify a folder to get, and then indicate RecursionType.Full.