Automating Microsoft Team Foundation (TFS) check-ins
Have you ever wanted to automate code check-ins to TSF e.g. at the end of the day or at the custom interval you define? That’s easy to do using TFS remote API provided by Microsoft.
In our exercise we are going to use following packages to make the solution working:
<packages> <package id="Microsoft.AspNet.WebApi.Client" version="5.2.2" targetFramework="net452" /> <package id="Microsoft.AspNet.WebApi.Core" version="5.2.2" targetFramework="net452" /> <package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="2.16.204221202" targetFramework="net452" /> <package id="Microsoft.TeamFoundationServer.Client" version="14.95.3" targetFramework="net452" /> <package id="Microsoft.TeamFoundationServer.ExtendedClient" version="14.95.3" targetFramework="net452" /> <package id="Microsoft.VisualStudio.Services.Client" version="14.95.3" targetFramework="net452" /> <package id="Microsoft.VisualStudio.Services.InteractiveClient" version="14.95.3" targetFramework="net452" /> <package id="Microsoft.WindowsAzure.ConfigurationManager" version="1.7.0.0" targetFramework="net452" /> <package id="Newtonsoft.Json" version="6.0.8" targetFramework="net452" /> <package id="System.IdentityModel.Tokens.Jwt" version="4.0.0" targetFramework="net452" /> <package id="WindowsAzure.ServiceBus" version="3.3.1" targetFramework="net452" /> </packages>
As a authorization mechanism, we will use token which can be generated on your Visual Studio Online account.
Tokens are more secure and flexible than using standard user/pass credentials as we can safely share it and allow it to expire after a predefined period of time.
so, in our app.config we will have following settings:
<add key="TfsURL" value="https://your_name.visualstudio.com/DefaultCollection/"/> <add key="workspaceName" value="AUTOMATED_CHECKINS"/> <add key="tfsServerFolderPath" value="$/remote_folder"/> <add key="localWorkingPath" value="D:\MyCodefolder"/> <add key="tfsUser" value="not-used"/> <add key="tfsPass" value="-your token--"/> <add key="tfsDomain" value=""/>
Our main function will look as following (see inline comments):
static void TFSCheckIn(string comment = "") { var TfsURL = new Uri(ConfigurationManager.AppSettings["TfsURL"]); //use it for standard user/pass authentication //var credential = new NetworkCredential(ConfigurationManager.AppSettings["tfsUser"], ConfigurationManager.AppSettings["tfsPass"], ConfigurationManager.AppSettings["tfsDomain"]); //var collection = new TfsTeamProjectCollection(TfsURL, credential); //collection.EnsureAuthenticated(); //token based authentication var simpleWebToken = new SimpleWebToken(ConfigurationManager.AppSettings["tfsPass"]); var networkCredential = new NetworkCredential(ConfigurationManager.AppSettings["tfsUser"], ConfigurationManager.AppSettings["tfsPass"]); var basicCredential = new BasicAuthCredential(networkCredential); var tfsCredentials = new TfsClientCredentials(basicCredential); tfsCredentials.AllowInteractive = false; var collection = new TfsTeamProjectCollection(TfsURL, tfsCredentials); collection.EnsureAuthenticated(); var versionControl = (VersionControlServer)collection.GetService(typeof(VersionControlServer)); Workspace WS = null; try { //Get the current workspace WS = versionControl.GetWorkspace(ConfigurationManager.AppSettings["workspaceName"], versionControl.AuthorizedUser); } catch (Exception) { } //create workspace if not yet created if (WS == null) { WS = versionControl.CreateWorkspace(ConfigurationManager.AppSettings["workspaceName"], versionControl.AuthorizedUser); } //map local folder if not already mapped if (!WS.IsLocalPathMapped(ConfigurationManager.AppSettings["localWorkingPath"])) { //Mapping TFS Server and code generated WS.Map(ConfigurationManager.AppSettings["tfsServerFolderPath"], ConfigurationManager.AppSettings["localWorkingPath"]); } //download remote changes (check-out) WS.Get(); //auto-resolve conflicts Conflict[] conflicts = WS.QueryConflicts(new string[] { ConfigurationManager.AppSettings["tfsServerFolderPath"] }, true); foreach (Conflict conflict in conflicts) { if (WS.MergeContent(conflict, false)) { conflict.Resolution = Resolution.AcceptTheirs; WS.ResolveConflict(conflict); } Console.WriteLine("conflict: " + conflict.FileName); } //Add all files just created to pending change int NumberOfChange = WS.PendAdd(ConfigurationManager.AppSettings["localWorkingPath"], true); //Get the list of pending changes PendingChange[] pendings = WS.GetPendingChanges(ConfigurationManager.AppSettings["tfsServerFolderPath"], RecursionType.Full); if (pendings.Any()) { //Auto check in code to Server WS.CheckIn(pendings, "Auto check-in code (#changes: " + pendings.Count() + "). " + comment); } }
Need custom solution tailored to your business needs? Contact us today!