I’ve been working with VSTS for quite some time now and wanted to share some of the sample code I’ve written to work with VSTS data. As I work with many teams, there have been requests such as getting specific metadata during and/or after build. Examples would be people wanting to get specific data from associated work-items during builds or collection level licensing information for your users. Here are I’ll tap in to specific areas:
VSTS Builds, VSTS Work-Items, VSTS GIT (Commits), VSTS User License Information
You’ll need to the following Nuget Packages:
- Microsoft.TeamFoundationServer.ExtendedClient
- Microsoft.TeamFoundationServer.Client
- Microsoft.VisualStudio.Services.Client
- Microsoft.VisualStudio.Services.InteractiveClient
Sample Code to retrieve all Builds from a given VSTS Team Project Build Definition containing associated commits and work-items:
using System; using System.Configuration; using System.Linq; using System.Text; using AAG.Test.Core.Logger; using Microsoft.TeamFoundation.Build.WebApi; using Microsoft.TeamFoundation.SourceControl.WebApi; using Microsoft.VisualStudio.Services.Client; using VSTSApi.Entities; using Microsoft.VisualStudio.Services.Common; using Microsoft.TeamFoundation.WorkItemTracking.WebApi; namespace VSTSApi { class Program { private static BuildOutputModel _buildoutputmodel; private static string VssAccountUrl { get; set; } = ConfigurationManager.AppSettings["VssAccountUrl"]; static void Main(string[] args) { try { StringBuilder outputStringBuilder = new StringBuilder(); var buildoutputmodel = SetBuildOutputModel(args); var creds = new VssClientCredentials(false); creds.PromptType = CredentialPromptType.PromptIfNeeded; var vssConnection = new VssConnection(new Uri(buildoutputmodel.VSOUrl + "/defaultcollection"), new VssBasicCredential(buildoutputmodel.UserName, buildoutputmodel.Password)); var buildserver = vssConnection.GetClient<BuildHttpClient>(); var workitems = vssConnection.GetClient<WorkItemTrackingHttpClient>(); var commititems = vssConnection.GetClient<GitHttpClient>(); var builds = buildserver.GetBuildsAsync(_buildoutputmodel.TeamProjectName).Result; var targetbuilds = builds.Where(definition => definition.Definition.Name.Contains(buildoutputmodel.BuilDefinitionName)); foreach (var build in targetbuilds) { outputStringBuilder.AppendLine($"Name: {build.Definition.Name} : BuildID: {build.Id}"); var associatedcommits = buildserver.GetBuildCommitsAsync(build.Definition.Project.Name, build.Id).Result; if (associatedcommits.Any()) outputStringBuilder.AppendLine($"All Commits Made for this Build: {Environment.NewLine} ========= {Environment.NewLine} "); associatedcommits.ForEach(commit => { var user = commititems.GetCommitAsync(buildoutputmodel.TeamProjectName, commit.Id, buildoutputmodel.GitRepo).Result.Author; outputStringBuilder.AppendLine($"ID: {commit.Id} Committed By: {user.Name} E-mail: {user.Email} {Environment.NewLine} Description: {commit.Message} {Environment.NewLine}"); }); var commits = associatedcommits.Select(change => change.Id); var associatedworkitems = buildserver.GetBuildWorkItemsRefsAsync(commits, build.Definition.Project.Name, build.Id).Result; if (associatedworkitems.Any()) outputStringBuilder.AppendLine($"All Associated Workitems for this Build: {Environment.NewLine} ========= {Environment.NewLine} "); foreach (var wi in associatedworkitems) { outputStringBuilder.AppendLine($"ID : {wi.Id} : URL: {wi.Url}"); var workitem = workitems.GetWorkItemAsync(int.Parse(wi.Id)).Result; outputStringBuilder.AppendLine($"Title : {workitem.Fields["Title"]} : Description: {workitem.Fields["Description"]}"); } outputStringBuilder.AppendLine($"{Environment.NewLine} ========= {Environment.NewLine} "); } //} DumpData(outputStringBuilder.ToString(), Console.WriteLine); DumpData(outputStringBuilder.ToString(), print => AsLogger.Info(print)); Console.WriteLine("Press Any Key to Continue..."); Console.ReadKey(); } catch (Exception exception) { throw new Exception($"Error with Application: {exception.Message}", exception.InnerException); } } static void DumpData(string stringoutput, Action<string> print) { print(stringoutput); } static BuildOutputModel SetBuildOutputModel(string[] args) { _buildoutputmodel = new BuildOutputModel { UserName = ConfigurationManager.AppSettings["username"], Password = ConfigurationManager.AppSettings["password"], VSOUrl = ConfigurationManager.AppSettings["vsourl"], TeamProjectName = ConfigurationManager.AppSettings["teamproject"], BuilDefinitionName = ConfigurationManager.AppSettings["builddefinition"], GitRepo = ConfigurationManager.AppSettings["gitrepo"] }; return _buildoutputmodel; } } }
Entity
namespace VSTSApi.Entities { public class BuildOutputModel { public string UserName { get; set; } public string Password { get; set; } public string BuilDefinitionName { get; set; } public string TeamProjectName { get; set; } public string VSOUrl { get; set; } public string GitRepo { get; set; } } }
Sample Code for Getting User Information/Licenses:
using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using VSTSAccountAdmin.Model; using Microsoft.VisualStudio.Services.Client; using Microsoft.VisualStudio.Services.Common; using Microsoft.VisualStudio.Services.Identity.Client; using Microsoft.VisualStudio.Services.Licensing; using Microsoft.VisualStudio.Services.Licensing.Client; namespace VSTSAccountAdmin { public class Program { private static string VssAccountUrl { get; set; } = ConfigurationManager.AppSettings["VssAccountUrl"]; private static string VssAccountName { get; set; } private static License VssLicense { get; set; } private static List<VSOUserInfo> _vsousers; public static void Main(string[] args) { try { _vsousers = new List<VSOUserInfo>(); // Create a connection to the specified account. // If you change the false to true, your credentials will be saved. var creds = new VssClientCredentials(false); creds.PromptType = CredentialPromptType.PromptIfNeeded; var vssConnection = new VssConnection(new Uri(VssAccountUrl), creds); // We need the clients for tw4o services: Licensing and Identity var licensingClient = vssConnection.GetClient<LicensingHttpClient>(); var identityClient = vssConnection.GetClient<IdentityHttpClient>(); var entitlements = licensingClient.GetAccountEntitlementsAsync().Result; IEnumerable<AccountEntitlement> accountEntitlements = entitlements as IList<AccountEntitlement> ?? entitlements.ToList(); var userIds = accountEntitlements.Select(entitlement => entitlement.UserId).ToList(); var users = identityClient.ReadIdentitiesAsync(userIds).Result.ToDictionary(item => item.Id); foreach (var entitlement in accountEntitlements) { var user = users[entitlement.UserId]; _vsousers.Add(new VSOUserInfo() { DisplayName = user.DisplayName, LastAccessDate = entitlement.LastAccessedDate, License = entitlement.License.ToString().ToLowerInvariant(), UserID = entitlement.UserId }); var stringoutput = $"{Environment.NewLine}Name: {user.DisplayName}, UserId: {entitlement.UserId}, License: {entitlement.License}."; Console.WriteLine(stringoutput); } } catch (Exception ex) { throw new ArgumentException(ex.Message, ex.InnerException); } } } }
Entity:
namespace VSTSAccountAdmin.Model { public class VSOUserInfo { public string DisplayName { get; set; } public Guid UserID { get; set; } public string License { get; set; } public DateTimeOffset LastAccessDate { get; set; } } }
How can I get drop path programmatically? (I need it to download build after it’s finished)
For XAML builds it was DropLocation property, I can’t find it in case of vNext builds…
LikeLike
I’ll upload my sample code in GitHub and will reply back once done. Ty!
LikeLike