VSTS – Automated Code Check-in/Out via C# Code

Hi,

This is my new article on the VSTS (Visual Studio Team Services) automation.

VSTS is a popular and a very important tool in any organisation where by the developer can version their code, manage work, documentation relating the project and much more.

Recently i wrote an article SQL – Backup Database Schema and SQL Jobs via Powershell, where i illustrated to take a scripts only back up of database and jobs and version them every day.

Then to take it a step further I planned, why not automate check-in this scripts every night to the VSTS so every day we can record the change set occurring in the database (like procedures, views, functions).

And this forms the context of this post, and rather me doing more of talking let the code do the talking and then I would summarise the steps:

(TfsFileUploader.cs)

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace TfsFileUploader
{
    class TfsOperation
    {
        private string _localTempPath;
        private string _tfsRepoPath;
        private string _wkSpcName;

        private string _tfsUserToken;
        private string _tfsUserName;
        private Uri _tfsUri;

        private TfsTeamProjectCollection _tfs;
        private VersionControlServer _versionControl;
        private Workspace _workspace;


        public TfsOperation(string localTempPath , string tfsRepoPath, string wkSpcName)
        {
            _localTempPath = localTempPath;
            _tfsRepoPath = tfsRepoPath;
            _wkSpcName = wkSpcName;

            _tfsUserToken = ConfigurationManager.AppSettings["TFS_USER_TOKEN"];
            _tfsUserName = ConfigurationManager.AppSettings["TFS_USERNAME"];
            _tfsUri = new Uri(ConfigurationManager.AppSettings["TFS_URL"]);
        }


        public void RecreateTempFolderAndGetRepo()
        {
            bool folderExists = Directory.Exists(_localTempPath);
            if (!folderExists)
                Directory.CreateDirectory(_localTempPath);

            NetworkCredential networkCredential = new NetworkCredential(_tfsUserName, _tfsUserToken);
            VssBasicCredential basicCredential = new VssBasicCredential(networkCredential);

            VssCredentials tfsCredentials = new VssCredentials(basicCredential);

            _tfs = new TfsTeamProjectCollection(_tfsUri, tfsCredentials);
            _tfs.Authenticate();
            // Get a reference to Version Control.              
            _versionControl = _tfs.GetService<VersionControlServer>();

            _workspace = _versionControl.TryGetWorkspace(_localTempPath);
            if (_workspace != null)
            {
                _workspace.Delete();
            }
            _workspace = _versionControl.CreateWorkspace(_wkSpcName, _versionControl.AuthorizedUser);

            // Create a mapping using the Team Project supplied on the command line.
            _workspace.Map(_tfsRepoPath, _localTempPath);
            Console.WriteLine("Completed: workspace.Map();");

            // Get the files from the repository.
            _workspace.Get();

            Console.WriteLine("Completed: workspace.Get();");

        }

        public void CommitChangesAndCleanup()
        {
            string tmpFolderName = Path.GetFileName(Path.GetDirectoryName(_localTempPath));
            _workspace.PendAdd(_localTempPath, true);
            _workspace.PendEdit(_localTempPath, RecursionType.Full);
            _workspace.PendDelete(_localTempPath, RecursionType.Full);

            List<PendingChange> pc = new List<PendingChange>(_workspace.GetPendingChanges());
            PendingChange[] pcArr = pc.Where(x => x.ChangeType != ChangeType.Delete && x.ItemType != ItemType.Folder && x.FileName != tmpFolderName).ToArray();

            if (pcArr.Length > 0)
                _workspace.CheckIn(pcArr, "SQL Automated Checkin (" + _wkSpcName + ") " + DateTime.Today.ToShortDateString());

            Console.WriteLine("Completed: Checkin");

            foreach (var pchg in pc)
            {
                Console.WriteLine(pchg.ChangeType.ToString() + "::" + pchg.FileName);
            }

            _workspace.Delete();
            
            bool folderExists = Directory.Exists(_localTempPath);
            if (folderExists)
            {
                var fList = Directory.GetFiles(_localTempPath);
                foreach (var f in fList)
                {
                    File.Delete(f);
                }
                Directory.Delete(_localTempPath);
            }
        }

        /// <summary>
        /// This procedure is used to clear out the orphaned workspace still present due to failure of the program execution
        /// </summary>
        public void ClearOrphanWorkSpace()
        {
            NetworkCredential networkCredential = new NetworkCredential(_tfsUserName, _tfsUserToken);
            VssBasicCredential basicCredential = new VssBasicCredential(networkCredential);

            VssCredentials tfsCredentials = new VssCredentials(basicCredential);
            //tfsCredentials.AllowInteractive = false;

            _tfs = new TfsTeamProjectCollection(_tfsUri, tfsCredentials);
            _tfs.Authenticate();
            // Get a reference to Version Control.              
            _versionControl = _tfs.GetService<VersionControlServer>();

            _workspace = _versionControl.TryGetWorkspace(_localTempPath);
            if (_workspace != null)
            {
                _workspace.Delete();
            }
        }

    }
}

And then to call upon the above class functionality:
(Program.cs)

                    TfsOperation tfsSqlJobOperation = new TfsOperation(tmpSqlJobScriptFolderPath, tfsSqlJobScriptRepoPath, wkSpcSqlJobScriptName);

                    tfsSqlJobOperation.RecreateTempFolderAndGetRepo();

                    CopySqlJobFileFromSourceToRepo(srcJobSqlScriptsFolder, tmpSqlJobScriptFolderPath);

                    tfsSqlJobOperation.CommitChangesAndCleanup();

So the above code performs the following steps:

  1. The code create a temp folder and performs a GET operation of code base from TFS
  2. Then the nightly created files are copied over the temp TFS folder to bring in the changes in the scripts from the day
  3. Then the script performs a commit and cleanup operation to push changes to TFS

In the above code important thing to consider is, that the user account under which this code runs needs to generate a user token on the VSTS portal and provide that to the code to perform the authentication.

Authenticate access with personal access tokens for VSTS and TFS

And in the project to perform the TFS operation you would require the following nuget package refrence:

Microsoft.TeamFoundationServer.ExtendedClient (Nuget)

Hope it helps.

Leave a Reply

Your email address will not be published.

*