I have come up with following code to identify changed files between the latest and the previous commit. This logic shows the name of the changed file. However it doesn't log the name of the "Added" file. I'm using GitLab repo to test this. Please advice how to resolve this.
static void Main(string[] args)
{
using (var repo = new Repository(#"C:\xxxx\source\repos\SampleApp"))
{
//Get the brach
var branch = repo.Branches.Where(b => !b.IsRemote && b.IsCurrentRepositoryHead).FirstOrDefault();
//Get the latest commit
var latestCommit = branch.Commits.ElementAt(0);
Console.WriteLine(string.Format("Latest Commit: {0}-{1}", latestCommit.MessageShort, latestCommit.Committer.When));
//Get the previous comit
var previousCommit = branch.Commits.ElementAt(1);
Console.WriteLine(string.Format("Previous Commit: {0}-{1}", previousCommit.MessageShort, previousCommit.Committer.When));
//Get the change set
var changeSet = repo.Diff.Compare<TreeChanges>(latestCommit.Tree, previousCommit.Tree);
var modifiedFiles = changeSet.Modified;
var addedFiles = changeSet.Added;
//Print names of modified files
foreach (var m in modifiedFiles)
{
Console.WriteLine("Modified: " + m.Path);
}
//Print names of added files
foreach (var m in addedFiles)
{
Console.WriteLine("Added: " + m.Path);
}
Console.Read();
}
}
}
Thanks in advance.
Update:
The issue was with the below line where it gets the change set. I used old tree and new tree parameters other way. I have swapped those and now it works fine.
//Get the change set
var changeSet = repo.Diff.Compare<TreeChanges>(previousCommit.Tree, latestCommit.Tree);
Below is the fully functiong code.
static void Main(string[] args)
{
using (var repo = new Repository(#"C:\xxxx\source\repos\SampleApp"))
{
//Get the brach
var branch = repo.Branches.Where(b => !b.IsRemote && b.IsCurrentRepositoryHead).FirstOrDefault();
//Get the latest commit
var latestCommit = branch.Commits.ElementAt(0);
Console.WriteLine(string.Format("Latest Commit: {0}-{1}", latestCommit.MessageShort, latestCommit.Committer.When));
//Get the previous comit
var previousCommit = branch.Commits.ElementAt(1);
Console.WriteLine(string.Format("Previous Commit: {0}-{1}", previousCommit.MessageShort, previousCommit.Committer.When));
//Get the change set
var changeSet = repo.Diff.Compare<TreeChanges>(previousCommit.Tree, latestCommit.Tree);
var modifiedFiles = changeSet.Modified;
var addedFiles = changeSet.Added;
//Print names of modified files
foreach (var m in modifiedFiles)
{
Console.WriteLine("Modified: " + m.Path);
}
//Print names of added files
foreach (var m in addedFiles)
{
Console.WriteLine("Added: " + m.Path);
}
Console.Read();
}
}
Related
I am trying to get on an AzureDevOps project the number of Warning for a build task. But I still only get the first 10 warnings. How do you do it all? I especially need the number of warning, not necessarily the detail of the warning
var credential = new VssBasicCredential(string.Empty, myPat);
var connection = new VssConnection(new Uri(myCollection), credential);
var buildClient = connection.GetClient<BuildHttpClient>();
var timeline = buildClient.GetBuildTimelineAsync(myProject, myBuildId).Result;
var vsTask= timeline.Records.FirstOrDefault(p => p?.Task?.Name == "VSBuild");
// always 10 utmost : warning and issues !!
var warning = vsTask.WarningCount;
var issues = vsTask.Issues;
This is really strange, but it looks that you cannot go above what is shown here
There is workaround (but it is a bit ugly). You can load log page and count warnings parsing the page.
static void Main(string[] args)
{
var credential = new VssBasicCredential(string.Empty, "PAT");
var connection = new VssConnection(new Uri("https://dev.azure.com/your-organization/"), credential);
var buildClient = connection.GetClient<BuildHttpClient>();
var timeline = buildClient.GetBuildTimelineAsync("yoyr project", 377).Result;
var vsTask = timeline.Records.FirstOrDefault(p => p?.Task?.Name == "VSBuild");
// always 10 utmost : warning and issues !!
var warning = vsTask.WarningCount;
var issues = vsTask.Issues;
Console.WriteLine(warning);
HttpClient client = new HttpClient();
var response = client.GetAsync(vsTask.Log.Url);
var pageContents = response.Result.Content.ReadAsStringAsync().Result;
var realNumberOfWarnings = AllIndexesOf(pageContents, "##[warning]");
Console.WriteLine(realNumberOfWarnings.Count);
Console.ReadLine();
}
public static List<int> AllIndexesOf(string str, string value)
{
if (String.IsNullOrEmpty(value))
throw new ArgumentException("the string to find may not be empty", "value");
List<int> indexes = new List<int>();
for (int index = 0; ; index += value.Length)
{
index = str.IndexOf(value, index);
if (index == -1)
return indexes;
indexes.Add(index);
}
}
I hope it will help.
Please check what Tingluo Huang [MSFT] wrote here:
We should only count and store 10 errors/warning (i guess we having
some counting bug, so we count to 11), those issues get stored in our
backend DB for driving the build summary page render. We don't want to
store too many errors/warning since most of the time the first few
errors/warnings are key to the problem, and the rest are just noise.
I'm wanting to 'Create copy of work item' which is available via the UI, ideally via the API.
I know how to create a new work item, but the feature in the UI to connect all current parent links / related links, and all other details is quite useful.
Creating via this API is here: https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work%20items/create?view=azure-devops-rest-5.1
Any help would be greatly appreciated.
We cannot just copy a work item because it contains system fields that we should skip. Additionally your process may have some rules that may block some fields on the creation step. Here is the small example to clone a work item through REST API with https://www.nuget.org/packages/Microsoft.TeamFoundationServer.Client:
class Program
{
static string[] systemFields = { "System.IterationId", "System.ExternalLinkCount", "System.HyperLinkCount", "System.AttachedFileCount", "System.NodeName",
"System.RevisedDate", "System.ChangedDate", "System.Id", "System.AreaId", "System.AuthorizedAs", "System.State", "System.AuthorizedDate", "System.Watermark",
"System.Rev", "System.ChangedBy", "System.Reason", "System.WorkItemType", "System.CreatedDate", "System.CreatedBy", "System.History", "System.RelatedLinkCount",
"System.BoardColumn", "System.BoardColumnDone", "System.BoardLane", "System.CommentCount", "System.TeamProject"}; //system fields to skip
static string[] customFields = { "Microsoft.VSTS.Common.ActivatedDate", "Microsoft.VSTS.Common.ActivatedBy", "Microsoft.VSTS.Common.ResolvedDate",
"Microsoft.VSTS.Common.ResolvedBy", "Microsoft.VSTS.Common.ResolvedReason", "Microsoft.VSTS.Common.ClosedDate", "Microsoft.VSTS.Common.ClosedBy",
"Microsoft.VSTS.Common.StateChangeDate"}; //unneeded fields to skip
const string ChildRefStr = "System.LinkTypes.Hierarchy-Forward"; //should be only one parent
static void Main(string[] args)
{
string pat = "<pat>"; //https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate
string orgUrl = "https://dev.azure.com/<org>";
string newProjectName = "";
int wiIdToClone = 0;
VssConnection connection = new VssConnection(new Uri(orgUrl), new VssBasicCredential(string.Empty, pat));
var witClient = connection.GetClient<WorkItemTrackingHttpClient>();
CloneWorkItem(witClient, wiIdToClone, newProjectName, true);
}
private static void CloneWorkItem(WorkItemTrackingHttpClient witClient, int wiIdToClone, string NewTeamProject = "", bool CopyLink = false)
{
WorkItem wiToClone = (CopyLink) ? witClient.GetWorkItemAsync(wiIdToClone, expand: WorkItemExpand.Relations).Result
: witClient.GetWorkItemAsync(wiIdToClone).Result;
string teamProjectName = (NewTeamProject != "") ? NewTeamProject : wiToClone.Fields["System.TeamProject"].ToString();
string wiType = wiToClone.Fields["System.WorkItemType"].ToString();
JsonPatchDocument patchDocument = new JsonPatchDocument();
foreach (var key in wiToClone.Fields.Keys) //copy fields
if (!systemFields.Contains(key) && !customFields.Contains(key))
if (NewTeamProject == "" ||
(NewTeamProject != "" && key != "System.AreaPath" && key != "System.IterationPath")) //do not copy area and iteration into another project
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/" + key,
Value = wiToClone.Fields[key]
});
if (CopyLink) //copy links
foreach (var link in wiToClone.Relations)
{
if (link.Rel != ChildRefStr)
{
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/relations/-",
Value = new
{
rel = link.Rel,
url = link.Url
}
});
}
}
WorkItem clonedWi = witClient.CreateWorkItemAsync(patchDocument, teamProjectName, wiType).Result;
Console.WriteLine("New work item: " + clonedWi.Id);
}
}
Link to full project: https://github.com/ashamrai/AzureDevOpsExtensions/tree/master/CustomNetTasks/CloneWorkItem
Dears
Please help me to work with VssConnection from Microsoft.VisualStudio.Services.Client.15.134.0-preview package
I need to get pending changes for workspace, query it for conflict and commit
This is how i do it with TfsTeamProjectCollection and
var vssCred = new VssClientCredentials();
using (TfsTeamProjectCollection collection = new TfsTeamProjectCollection(uri, vssCred))
{
collection.Authenticate();
var scs = collection.GetService<VersionControlServer>();
var scsProject = scs.GetTeamProject(teamProjectName);
var workspace = scsProject.VersionControlServer.GetWorkspace(localPath);
var pending = scs.QueryPendingSets(new string[] { "$/" }, RecursionType.Full, workspace.Name, loginName);
if (pending.Any())
{
var pendingChanges = new[] { pending.First().PendingChanges.First() };
var validation = workspace.EvaluateCheckin2(CheckinEvaluationOptions.Conflicts, pendingChanges, "", null, null);
var conflicts = validation.Conflicts;
if (conflicts != null && conflicts.Any())
{
var message = string.Join("\r\n", conflicts.Select(_ => string.Format("{0} {1}", _.Message, _.ServerItem)));
throw new ArgumentException(string.Format("conflict was found\r\n{0}", message));
}
var res = workspace.CheckIn(pendingChanges, "test checkin");
TestContext.WriteLine("checked in {0}", res);
}
}
However there are vsts integration samples that uses VssConnection object
How can I get the same VersionControlServer from VssConnection instance?
I've tried to find Microsoft.TeamFoundation.VersionControl.Client.WebAPi (like Microsoft.TeamFoundation.WorkItemTracking.WebApi) but failed.
var vssCred = new VssClientCredentials();
using (VssConnection connection = new VssConnection(uri, vssCred))
{
var prj = connection.GetClient<ProjectHttpClient>();
var p = prj.GetProject(teamProjectName).Result;
//i'd like to get prj.VersionControl here
//or something like var scs = connection.GetService<VersionControlServer>();
}
Is it possible to get versionControlServer from VssConnection? Should I continue to use TfsTeamProjectCollection to do this task?
You could use TfsTeamProjectCollection as before, as there is no workspace method in VssConnection:
TfvcHttpClient tfvcClient = connection.GetClient<TfvcHttpClient>();
List <TfvcItem> tfvcItems = tfvcClient.GetItemsAsync("$/", VersionControlRecursionType.OneLevel).Result;
More examples, you can refer to the link below:
https://learn.microsoft.com/en-us/vsts/integrate/get-started/client-libraries/samples?view=vsts
This is what I see in the test results in the Test Manager: some attachments (1) are on the test steps (in this case - on a shared step). Some (2) are on a result level. In the report I'm working on, I can extract information about attachments (2), but fail to get information about (1). Any suggestions?
In the debugger I can see, that the steps are iterated, I can see, i.e. "Verify the stuff" string etc, but the step has 0 attachments.
For reference, this is most of the code I am using to extract test case results:
foreach (ITestCaseResult testCaseResult in resutlsToDisplay)
{
project.TestCases.Find(test.Id);
var testRun = testCaseResult.GetTestRun();
var testPlanEntry = project.TestPlans.Find(testRun.TestPlanId);
var artifacts = testCaseResult.QueryAssociatedWorkItemArtifacts();
if (testPlanEntry.Name != testPlan)
continue;
var testResult = new TestResult // CUSTOM CLASS
{
TestConfiguration = testCaseResult.TestConfigurationName,
TestRun = testRun.Id,
DateCreated = testRun.DateCreated,
DateStarted = testRun.DateStarted,
DateCompleted = testRun.DateCompleted,
Result = testCaseResult.Outcome.ToString(),
TestResultComment = testRun.Comment,
RunBy = testCaseResult.RunBy == null ? "" : testCaseResult.RunBy.DisplayName,
Build = testRun.BuildNumber ?? ""
};
foreach (var iteration in testCaseResult.Iterations)
{
var iterationResult = testResult.Clone();
iterationResult.ResultAttachmentHtml = getAttachments(iteration.Attachments, testCase.TestCaseId.ToString(), string.Empty, iteration.IterationId.ToString()); // HERE I GET THE ATTACHMENTS OF TYPE 2
iterationResult.Iteration = iteration.IterationId;
iterationResult.IterationComment = iteration.Comment;
foreach (var step in steps)
{
var stepCopy = step.Clone();
iterationResult.Steps.Add(stepCopy);
var actionResult = iteration.FindActionResult(stepCopy.TestStep);
if (actionResult == null)
continue;
stepCopy.Result.Comment = actionResult.ErrorMessage;
stepCopy.Result.Result = actionResult.Outcome.ToString();
stepCopy.Result.AttachmentHtml = getAttachments(actionResult.Attachments, testCase.TestCaseId.ToString(), step.Number, iteration.IterationId.ToString()); // HERE I DO NOT GET ATTACHMENTS OF TYPE 1 - WHY?
}
config.TestCaseResult.TestResults.Add(iterationResult);
}
} //end foreach testCaseResult in resutlsToDisplay
I think I figured it out eventually.
The ITestCaseResult contains ITestIterationResultCollection Iterations.
The ITestIterationResult contains TestActionResultCollection of ITestActionResult.
ITestActionResult can be either ITestStepResult or ISharedStepResult.
If it is ITestStepResult, then it has Attachments collection right there.
If it is ISharedStepResult, however, then it has its own TestActionResultCollection. So this is the one that I have to iterate to find if each member has Attachments.
The code to iterate over steps, including shared steps, looks like this:
foreach (ITestIterationResult iteration in testCaseResult.Iterations)
{
var iterationResult = testResult.Clone();
iterationResult.ResultAttachmentHtml = getAttachments(iteration.Attachments,
testCase.TestCaseId.ToString(),
string.Empty, iteration.IterationId.ToString());
iterationResult.Iteration = iteration.IterationId;
iterationResult.IterationComment = iteration.Comment;
foreach (ITestActionResult action in iteration.Actions)
{
if (action is ITestStepResult)
{
GetStepResults(steps, action, iterationResult, iteration, getAttachments, testCase, false);
}
else if (action is ISharedStepResult)
{
ISharedStepResult result = action as ISharedStepResult;
foreach (var sharedAction in result.Actions)
{
if (sharedAction is ITestStepResult)
{
GetStepResults(steps, sharedAction, iterationResult, iteration, getAttachments, testCase, true);
}
}
}
}
We are using TFS 2015. I'm having a problem in retrieving non XAML build information from my C# console App using the TFS API. Actually, for one project we have setup for XAML Build definations and for another project, it is non-XAML one. I am able to connect the TFS projects and able to get the XAML defination lists. However, while trying to fetch the Non-XAML definations or builds, it is always an empty array.My code is below,
static void Main(string[] args)
{
try
{
var tfsUri = (args.Length < 1) ? new Uri(ConfigurationManager.AppSettings["ServerUri"]) : new Uri(args[0]);
var userCreds = new NetworkCredential(
ConfigurationManager.AppSettings["Tfs.User"], ConfigurationManager.AppSettings["Tfs.Password"], ConfigurationManager.AppSettings["Tfs.Domain"]);
// var tfsServer = TfsConfigurationServerFactory.GetConfigurationServer(tfsUri, userCreds);
var tfsServer = new TfsConfigurationServer(tfsUri, userCreds);
tfsServer.EnsureAuthenticated();
//// Get the catalog of team project collections
var collectionNodes = tfsServer.CatalogNode.QueryChildren(new[] { CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None);
//// List the team project collections
foreach (var collectionNode in collectionNodes)
{
//// Use the InstanceId property to get the team project collection
var collectionId = new Guid(collectionNode.Resource.Properties["InstanceId"]);
var teamProjectCollection = tfsServer.GetTeamProjectCollection(collectionId);
//// Get a catalog of team projects for the collection
var projectNodes = collectionNode.QueryChildren(new[] { CatalogResourceTypes.TeamProject }, false, CatalogQueryOptions.None);
foreach (var projectNode in projectNodes)
{
/*
Console.WriteLine("Collection: " + teamProjectCollection.Name); // Print the name of the team project collection
Console.WriteLine(" Team Project: " + projectNode.Resource.DisplayName); // List the team projects in the collection
Console.WriteLine(" Team Project Id: " + projectNode.Resource.Identifier);
*/
//// Get a catalog of team builds for the collection
var buildDefinitions = new BuildDefinition();
// var buildDetailList = buildDefinitions.GetBuildDefinitionListFromProject(teamProjectCollection, projectNode.Resource.DisplayName);
buildDefinitions.GetBuildDetailsFromProject(teamProjectCollection, projectNode.Resource.DisplayName, projectNode.Resource.Identifier);
}
}
}
catch (Exception ex)
{
ErrorLogger.LogError(ex);
}
}
public class BuildDefinition
{
public void GetBuildDetailsFromProject(TfsTeamProjectCollection tfsProjectCollection, string projectName, Guid projectId)
{
var buildService = tfsProjectCollection.GetService<IBuildServer>(); // (IBuildServer)tfs.GetService(typeof(IBuildServer));
var buildDefinitionsList = GetAllBuildDefinitionsFromTheTeamProject(buildService, projectName);
foreach (var buildDefinition in buildDefinitionsList)
{
var bdentities = new BuildDefinitionEntities
{
ProjectId = projectId,
ProjectName = buildDefinition.TeamProject,
BuildTypeId = Convert.ToInt32(buildDefinition.Id),
BuildTypeName = buildDefinition.Name,
ProjectDetailPath = string.Format("{0} > {1}", buildDefinition.TeamProject, buildDefinition.Name)
};
var buildDetailSpec = buildService.CreateBuildDetailSpec(buildDefinition);
buildDetailSpec.InformationTypes = null; // for speed improvement
buildDetailSpec.MinFinishTime = DateTime.Now.AddDays(-21); // to get only builds of last 3 weeks
buildDetailSpec.MaxBuildsPerDefinition = 1; // get only one build per build definintion
buildDetailSpec.QueryDeletedOption = QueryDeletedOption.ExcludeDeleted; // get only active builds
buildDetailSpec.QueryOrder = BuildQueryOrder.FinishTimeDescending; // get the latest build only
buildDetailSpec.QueryOptions = QueryOptions.All;
var buildDetailList = buildService.QueryBuilds(buildDetailSpec).Builds;
//// List the team builds for the collection
foreach (var buildDetail in buildDetailList)
{
bdentities.BuildId = buildDetail.RequestIds[0];
bdentities.BuildName = buildDetail.BuildNumber;
bdentities.ArtefactsPath = buildDetail.DropLocation ?? "No Artefacts";
bdentities.BuildCompleted = Convert.ToDateTime(buildDetail.FinishTime) > Convert.ToDateTime(buildDetail.StartTime)
? Convert.ToDateTime(buildDetail.FinishTime)
: Convert.ToDateTime(buildDetail.StartTime);
bdentities.BuildStatus = buildDetail.Status.ToString();
bdentities.SourceGetVersion = buildDetail.SourceGetVersion ?? string.Empty;
if (!string.IsNullOrEmpty(buildDetail.Quality))
{
bdentities.BuildQuality = buildDetail.Quality;
bdentities.BuildQualityChangedDate = Convert.ToDateTime(buildDetail.LastChangedOn);
}
BuildDefinitionDbOperations.ManageTfsBuildDefinitions(bdentities);
}
}
}
private static IBuildDefinition[] GetAllBuildDefinitionsFromTheTeamProject(IBuildServer buildServer, string projectName)
{
var buildDefinitionSpec = buildServer.CreateBuildDefinitionSpec(projectName);
buildDefinitionSpec.TriggerType = DefinitionTriggerType.All;
buildDefinitionSpec.Options = QueryOptions.Definitions;
return buildServer.QueryBuildDefinitions(buildDefinitionSpec).Definitions;
}
}
Actually, I am new to this Tfs system. Would you please guide me where I am wrong?
For the vNext/non-XAML build system you have to use the TFS REST API; you can find the details here