We'd like to generate build notes with the following format:
1) Associated ChangeSets:
2) - ChangeSet 45241, by Joe: "Patching fix for foobar"
3) 'Foo.cs' integrated from dev v. 22 to qa v. 7
4) 'Bar.cs' integrated from dev v. 9 to qa v. 3
So far, we have a custom build step that accomplishes 1) and 2). It looks at the information produced by the 'AssociatedChangesetsAndWorkItems' TFS Build Activity. Here is the code:
protected override bool Execute(CodeActivityContext context)
{
StreamWriter sw = new StreamWriter(Path.Combine(BuildNotesPath.Get(context),"build-notes.txt"));
sw.WriteLine("Associated ChangSets:");
foreach (Changeset changeset in BuildAssociatedChangesets.Get(context))
{
sw.WriteLine(string.Format("ChangeSet {0}, by {1}: {2}", changeset.ChangesetId, changeset.Committer, changeset.Comment));
foreach (Change change in changeset.Changes)
{
foreach (MergeSource source in change.MergeSources)
sw.WriteLine(string.Format("\'t{0}': integrated from dev v. {1} to qa v. {2}", source.ServerItem, source.VersionFrom, source.VersionTo));
}
}
sw.Flush();
sw.Dispose();
return true;
}
The problem we're having is that the 'MergeSources' field is always an empty list. What do we have to do to get that field populated?
Take a look at MergeWorkItemsEventHandler.MergeWorkItems and .GetMergeHistory along with the Extensions.PendingMerges method in the TFS 2010 Merge Work Items Event Handler for an example of getting merge sources.
Jakob Enh deserves the credit for this answer. :)
Related
We use TFS 2017 CI / CD pipelines, and it works great. However, the TFS 2017 databases grow averaging around 1GB per day. One database grows from 10GB to 44GB as of 10/23/2018. The growth slowly becomes unsustainable for us. We already adjusted retention policy to minimum.
Researched and read at least 30 articles. Here are some relevant articles:
TFS tbl_Content started growing very fast after using VNext build
https://mattyrowan.com/2014/04/02/need-help-tfs-tbl_content-table-and-database-growth-out-of-control/
https://developercommunity.visualstudio.com/content/problem/63712/tfs-database-size.html
Here are what I did so far:
Reviewed again and again the retention policies, and reduced to minimum (1 day 1 copy). Adjusted 'Keep Deleted' for 10 days.
Uncheck the 'Retain Build' box in release definition
Run the scripts from three articles mentioned above, and found:
a) FileContainer, has 149176 number of files, 43GB, (34GB compressed)
b) FileContainerOwner: Build, 29GB
So the main cause of the growth is Build (and artifacts).
My question is how to shrink the database size down?
I look at the tabs 'History' and 'Deleted' under build definitions.
Some records in 'History' are locked with 'Retained by release'. I can click on records and delete. But it doesn't do anything. The records are still there.
All records in 'Deleted' are still there.
So back to my question again, how do I delete these records so that the space can be reclaimed?
Thanks.
After reset the RetainedByRelease to false and waited for at least 24 hours, the growth spurt stopped and entries in tbl_content were removed daily.
So in a summary, I also did this:
Reset the RetainedByRelease to false using TFS REST API after nuget Microsoft.VisualStudio.Services.Client
Special thanks to these two threads:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/5f649821-b1bf-4008-bba9-0c960e124abb/tfs-releasemanagement-vnext-quotthis-build-has-been-retained-by-a-releasequot-issue?forum=tfsbuild
Trying to get list of TFS users trough client library
The full source code to help fellow developers:
using Microsoft.TeamFoundation.Build.WebApi;
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using System;
namespace TfsRestAPIs
{
public class RestAPI
{
public static void UpdateRetainedByRelaseToFalse()
{
Uri tfsURI = new Uri("http://TFS2017:8080/tfs/YourProjectCollection");
VssCredentials creds = new VssClientCredentials();
creds.Storage = new VssClientCredentialStorage();
VssConnection connection = new VssConnection(tfsURI, creds);
var projectClient = connection.GetClient<ProjectHttpClient>();
var projects = projectClient.GetProjects().Result;
var buildClient = connection.GetClient<BuildHttpClient>();
foreach (var project in projects)
{
Log(project.Name);
if (project.Name == "YourProjectName")
{
var builds = buildClient.GetBuildsAsync(project.Id).Result;
foreach (Build build in builds)
{
if (build.BuildNumber.StartsWith("YourSearchCondition"))
try
{
if (build.RetainedByRelease.Value)
{
Log(build.BuildNumber + "'s RetainedByRelease=true");
build.RetainedByRelease = false;
var res = buildClient.UpdateBuildAsync(build, build.Id).Result;
Log(" --> RetainedByRelease is set to " + res.RetainedByRelease.Value);
}
}
catch (Exception e)
{
Log(build.BuildNumber + ":" + e.Message);
}
}
}
}
}
private static void Log(string msg)
{
Console.WriteLine(msg);
}
}
}
Can anyone help me to understand the below query,Actually in our tfs we have huge number of build definition those are not in use so far hence i want to delete old build definition by seraching all the builds those are older more than one year.
how to search build definitions in TFS2013 those are no longer in use??
Please help.
Thanks,
You can query out the definitions those are older more than one year by this query in SQL Server.
SELECT [DefinitionId],[DefinitionVersion],[DefinitionName] FROM [Tfs_CollectionLC].[Build].[tbl_Definition]
WHERE [CreatedOn] < '2016-06-12 00:00:00.000'
But for the definitions are no longer in use, you have to get the last build for each definition and check whether the build FinishTime is before a year ago (Where [FinishTime] < '2016-06-12 00:00:00.000' ).
You can use TFS API/Client API to get the build details which including the build FinishTime, then filter them accordingly.You can reference below code which enumerates each team project and gets the latest build status for each of the definitions:
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri("http://tfs:8080"));
var vcs = tfs.GetService<VersionControlServer>();
var teamProjects = vcs.GetAllTeamProjects(true);
IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));
foreach (TeamProject proj in teamProjects)
{
var defs = buildServer.QueryBuildDefinitions(proj.Name);
System.Console.WriteLine(string.Format("Team Project: {0}", proj.Name));
foreach(IBuildDefinition def in defs)
{
IBuildDetailSpec spec = buildServer.CreateBuildDetailSpec(proj.Name, def.Name);
spec.MaxBuildsPerDefinition = 1;
spec.QueryOrder = BuildQueryOrder.FinishTimeDescending;
var builds = buildServer.QueryBuilds(spec);
if (builds.Builds.Length > 0)
{
var buildDetail = builds.Builds[0];
System.Console.WriteLine(string.Format(" {0} - {1} - {2}", def.Name, buildDetail.Status.ToString(), buildDetail.FinishTime));
}
}
System.Console.WriteLine();
}
Reference this thread:TFS API - How to query builds independent of which build definition they belong to
Update: Based on #Dhurva's comments below:
We can also use the utility from github TFS Manager that finds all build definition from team project:
https://github.com/jelledruyts/TfsTeamProjectManager
I am currently setting up a Team Foundation Server 2010 and I found a very strange behavior when performing a build:
The situation explained:
We have 2 Branches
Development
Main
All developers check in code into the Development branch only. Once per day, the build manager merges some changesets over to the Main branch. On the Development brach, a continuous build at each check in is running. On the Main branch, once per day (in the night) a build is triggered.
Now suppose that the changesets 1-100 are being merged into the Main brach at 5pm, giving changeset 101 as the merge operation. Some developers check in changesets 102-106 after 5 o'clock into the Development branch. Now at 11pm the daily build is automatically triggered and runs on the Main branch. The last changeset of the Main branch is changeset 101. However, the Build details shows changeset 106:
I could imagine that this behavior is intended, because if you check out changeset 106 on the Main branch, you will in fact get the content of changeset 101. But it would be much more readable if this Build summary showed the correct number.
Question 1: Is there a way of manipulating the ouput of the SourceGetVersion information? Maybe through the Build Process Template?
The second scenario, where the TFS behaves strange is even worse:
When queuing a new build, there is the option of entering the "Get Version" Parameter, as shown in the following picture:
If I now click on "queue", the build is triggered and AGAIN the build detail outputs the changeset 106 although I specifically set it to get changeset 76.
Question 2: Is this a bug? Is there a hotfix or something to fix this? Or is there any option flag that has to be set?
I hope someone knows more about this. I don't really believe that this is a bug, because it is such a vital functionality that other people must have encountered it before.
Thanks for any help!!
Christian
EDIT 1
The folder structure of the Team Project is:
$ProjectName
BuildProcessTemplates
Documentation
SourceCode
Development <-- this is a branch
3rdParty
Source
Main <-- this is a branch
3rdParty
Source
The build only pulls the Main branch and everything below it.
EDIT 2
Here is a picture of the Workspace tab in the build definition:
Finally I found out what is going on:
Basically The changeset that can be seen in my picture 1 is always the latest changeset of the entire Team Project Collection. It is the property "SourceGetVersion" on the object "BuildDetails" of type "IBuildDetails".
I think this is a bug which can be worked around:
If you change the BuildDetails.SourceGetVersion (which is a string) to some other value, then the build summary will show the updated string. Furthermore, it is then saved correctly to the collection database.
What I have done in order to add the correct changeset number is I have created a custom build activity that takes the branch which should be build as input parameter. It outputs the correct changeset. The activity finds out the correct changeset by connecting to the TFS and downloading the History. Then it looks at all the items in the history and outputs the largest changeset number. Here is the code of that activity:
using System.Activities;
using System.Collections;
using System.Net;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.Build.Client;
namespace SourceGetVersionActivity
{
[BuildActivity(HostEnvironmentOption.All)]
public sealed class SourceGetVersionActivity : CodeActivity<string>
{
// Define an activity input argument of type string
public InArgument<string> Branch { get; set; }
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override string Execute(CodeActivityContext context)
{
// Obtain the runtime value of the Text input argument
string branch = context.GetValue(this.Branch);
ICredentials account = new NetworkCredential("Useranme", "password", "domain");
// connect / authenticate with tfs
TeamFoundationServer tfs = new TeamFoundationServer("http://tfs:8080/tfs/CollectionName", account);
tfs.Authenticate();
// get the version control service
VersionControlServer versionControl = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
IEnumerable changesets = versionControl.QueryHistory(branch, VersionSpec.Latest, 0, RecursionType.Full,
null, null, null, int.MaxValue, false, false, false, false);
int maxVersion = 0;
foreach (Changeset c in changesets)
{
if (c.ChangesetId > maxVersion)
{
maxVersion = c.ChangesetId;
}
}
return string.Concat('C', maxVersion.ToString());
}
}
}
I call this activity as soon as possible (after the GetBuild activity).
Basically in the BuildProcessTemplate I have added an Argument (string) "Branch" which needs to be filled with a string that points to the top folder that is being build. The custom activity takes that as input and outputs a string which is the correct changeset id. The BuildDetail.SourceGetVersion property will then be overriden by the correct changeset id.
I find it really strange that no-one else seems to have encountered this problem. I could not find any person on the internet with the same problem. Anyway, I hope this answer helps someone else in the future as well.
EDIT - Writing the above code directly in Workflow Foundation:
To get the correct changeset using more compact code and avoiding custom activites, it is also possible to use Workflow Foundation directly. Below is the "code" (doing exactly what is done in above C# code):
(1) The GetTeamProjectCollection activity gets the current collection. I am saving it inside the TeamProjectCollection variable (see bottom of the picture). Important: The variable needs to be defined inside this sequence, if you define it in outer scope, an error will occur: "Unable to serialize type 'Microsoft.TeamFoundation.Client.TfsTeamProjectCollection'. Verify that the type is public and either has a default constructor or an instance descriptor."
(2) Foreach "changeset" in "TeamProjectCollection.GetService(Of VersionControlServer).QueryHistory(Branch, VersionSpec.Latest, 0, RecursionType.Full, Nothing, Nothing, Nothing, Integer.MaxValue, False, False, False).Cast(Of Changeset)()"
The TypeArgument of the Foreach loop is "Microsoft.TeamFoundation.VersionControl.Client.Changeset".
This expression gets the version control object from the collection, calls it "QueryHistory" method which returns an IEnumerable with all changesets.
(3) So we are iterating over all changesets and looking at the ChangesetId. Then saving the maximum ChangesetId to the variable "maxId".
(4) At the end, BuildDetails.SourceGetVersion = "C" + maxId.ToString(). The "C" indicates, that the version is a changeset.
I hope someone finds this piece of "Code" useful!
Christian
As the title suggests, I am looking for a command-line utility (like tf.exe) that will show me the work items that are associated with a list of changesets. Please no GUI stuff, and especially no command-line utilities that pop up GUI stuff.
I might be persuaded to use a PowerShell cmdlet, but only under duress. Preferably a good old-fashioned .exe.
With the following snippet you obtain a list of work items that are associated with Changeset '1111'.Depending on how you retrieve your Changeset(s), I suppose this can be adapted accordingly to fit your needs:
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
namespace GetWorkItems
{
class Program
{
static void Main(string[] args)
{
TfsTeamProjectCollection tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://myserver:8080/collection"));
VersionControlServer vcs = tpc.GetService(typeof(VersionControlServer)) as VersionControlServer;
Changeset myChangeset = vcs.GetChangeset(1111);
WorkItem[] workItems = myChangeset.WorkItems;
foreach (var workItem in workItems)
{
Console.WriteLine(workItem.Id);
}
}
}
}
You can do this with the tf program.
tf changeset /collection:[url] [changeset#] /noprompt
The /noprompt flag prevents the gui from popping up, but you'll have to write something to parse the output. Here's what the output looks like:
Changeset: 58
User: Alex
Date: Saturday, December 10, 2011 00:11:41
Comment:
Fixed bug # 1
Items:
edit $/netlib/Main/src/NetLib/Samples/Program.cs
Work Items:
ID Type State Assigned To Title
-- ---- -------- ----------- -----------------------
1 Bug Resolved Alex Bug1
Check-in Notes:
Code Reviewer:
Performance Reviewer:
Security Reviewer:
I'm working on customizing our build activity. I'd like to have your help for an issue.
Following is our version control hierarchy.
Main
|- Dev
|- QA
we are working on Dev branch and while taking the build we need to merge Dev branch to Main then to QA.
Main is the root branch as you might know.
In our build template, I've added two custom activities to merge one from Dev to Main and another one to merge from Main to QA. Following is the code for the custom activity.
protected override string Execute(CodeActivityContext context)
{
string lstrStatus = string.Empty;
string lstrSourceBranchPath = context.GetValue(this.SourceBranchPath);
string lstrTargetBranchPath = context.GetValue(this.TargetBranchPath);
// Obtain the runtime value of the input arguments
Workspace workspace = context.GetValue(this.Workspace);
GetStatus status = workspace.Merge(lstrSourceBranchPath,
lstrTargetBranchPath,
null,
null,
LockLevel.None,
RecursionType.Full,
MergeOptions.None);
// resolve the conflicts, if any
if (status.NumConflicts > 0)
{
Conflict[] conflicts = workspace.QueryConflicts(new string[]
{ lstrTargetBranchPath }, true);
foreach (Conflict conflict in conflicts)
{
conflict.Resolution = Resolution.AcceptTheirs;
workspace.ResolveConflict(conflict);
}
}
// checkin the changes
PendingChange[] pendingChanges = workspace.GetPendingChanges();
if (pendingChanges != null && pendingChanges.Length > 0)
{
workspace.CheckIn(pendingChanges, "Merged by MERGE BRANCHES activity");
}
return lstrStatus;
}
Problem is, merging happens perfectly in the server. But, it's not getting reflected in the local folder. I tried to add SyncWorkspace activity after each Merge custom activity. Still not working.
My guess was that a SyncWorkspace should be the only thing to do.
You could try doing a RevertWorkspace before that.
EDIT
After you now stated that even this wouldn't work, I would generate a bug against MS at least to get an official answer.
In the meanwhile you can try with the following method, which I absolutely see as an overkill: Once you have checked in, redo all the steps within sequence Initialize Workspace.
If even that doesn't work I'd consider two different builds, one that does your merge & one that does the actual build. You can then organize a scheme where your first build, once it's done, triggers the second one. Here is a good resource for that.