Release Management sets builds to Retain Indefinitely - tfs

We are using Microsoft's Release Management tool for automating the deployment of our solution to our various dev servers. This tool is ideal for us because it can perform more complicated deployments that span multiple servers. In this sense, it is working fine and everything is deploying correctly.
A minor issue is that after Release Management automatically deploys a build, it sets the build to "Retain Indefinitely" which is indicated by the Lock icon. Since we are doing continuous deployment, we are retaining a large number of builds and the Build Definition's retention policy is overridden. I therefore have to go in periodically and highlight all of the previous builds and unselect Retain Indefinitely.
Because we are not deploying past dev with Release Management (we unfortunately aren't allowed to), we don't need to keep all of these dev builds around.
Is there a way to change Release Management so that it doesn't set builds to Retain Indefinitely?
Update: Since this is not currently possible, if you would like this feature, please vote for it on UserVoice: https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/6537614-allow-retain-indefinitely-to-not-be-set

As of RM for TFS 2013.3, this is not possible. According to Teodora Stanev on this post, this is by design.
Feel free to post a request on the Visual Studio UserVoice site

I have a solution to this, rather a workaround.
I have customized our build definition with the following Sequence (for TFS 2013) at the beginning of the workflow. This sequence grabs all succeeded builds for the definition and clears the Retain Indefinitely.
This means only the last ran build will have the flag. It also means you can't set a build as retain indefinitely for that definition. That is OK for us in this specific situation as it is for continuous integration builds (like the poster).
Edit: Changing to include All builds (was Successful only) as a build is marked as failed and still set to retain indefinitely if the build succeeds but release fails.
<Sequence DisplayName="Cleanup: Clear 'Retain Indefinitely' from previous builds">
<Sequence.Variables>
<Variable x:TypeArguments="mtbc:IBuildDetail" Name="localBuildDetail" />
<Variable x:TypeArguments="mtbc:IBuildDetailSpec" Name="localBuildDetailSpec" />
<Variable x:TypeArguments="mtbc:IBuildQueryResult" Name="localBuildQueryResult" />
</Sequence.Variables>
<mtbwa:GetBuildDetail DisplayName="Get the Build Details" Result="[localBuildDetail]" />
<InvokeMethod DisplayName="Create the Build Detail Spec" MethodName="CreateBuildDetailSpec">
<InvokeMethod.TargetObject>
<InArgument x:TypeArguments="mtbc:IBuildServer">[localBuildDetail.BuildServer]</InArgument>
</InvokeMethod.TargetObject>
<InvokeMethod.Result>
<OutArgument x:TypeArguments="mtbc:IBuildDetailSpec">[localBuildDetailSpec]</OutArgument>
</InvokeMethod.Result>
<InArgument x:TypeArguments="x:String">[localBuildDetail.TeamProject]</InArgument>
</InvokeMethod>
<Assign DisplayName="Setting Query Order Descending">
<Assign.To>
<OutArgument x:TypeArguments="mtbc:BuildQueryOrder">[localBuildDetailSpec.QueryOrder]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="mtbc:BuildQueryOrder">FinishTimeDescending</InArgument>
</Assign.Value>
</Assign>
<Assign DisplayName="Setting Build Definition">
<Assign.To>
<OutArgument x:TypeArguments="x:String">[localBuildDetailSpec.DefinitionSpec.Name]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="x:String">[localBuildDetail.BuildDefinition.Name]</InArgument>
</Assign.Value>
</Assign>
<Assign DisplayName="Setting Query Type to All Builds">
<Assign.To>
<OutArgument x:TypeArguments="mtbc:BuildStatus">[localBuildDetailSpec.Status]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="mtbc:BuildStatus">All</InArgument>
</Assign.Value>
</Assign>
<InvokeMethod DisplayName="Getting Previous Builds" MethodName="QueryBuilds">
<InvokeMethod.TargetObject>
<InArgument x:TypeArguments="mtbc:IBuildServer">[localBuildDetail.BuildServer]</InArgument>
</InvokeMethod.TargetObject>
<InvokeMethod.Result>
<OutArgument x:TypeArguments="mtbc:IBuildQueryResult">[localBuildQueryResult]</OutArgument>
</InvokeMethod.Result>
<InArgument x:TypeArguments="mtbc:IBuildDetailSpec">[localBuildDetailSpec]</InArgument>
</InvokeMethod>
<ForEach DisplayName="Loop through all builds and undo 'Retain Indefinitely' set by Release Management."
x:TypeArguments="mtbc:IBuildDetail"
Values="[localBuildQueryResult.Builds]">
<ActivityAction x:TypeArguments="mtbc:IBuildDetail">
<ActivityAction.Argument>
<DelegateInArgument x:TypeArguments="mtbc:IBuildDetail" Name="localBuild" />
</ActivityAction.Argument>
<Sequence DisplayName="Checking Build Next Build">
<If Condition="[localBuild.KeepForever = True]" DisplayName="If the build is marked 'Retain Indefinitely'">
<If.Then>
<Sequence DisplayName="Updating Build Details">
<Assign DisplayName="Setting KeepForver to false">
<Assign.To>
<OutArgument x:TypeArguments="x:Boolean">[localBuild.KeepForever]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="x:Boolean">false</InArgument>
</Assign.Value>
</Assign>
<InvokeMethod DisplayName="Saving Build Details" MethodName="Save">
<InvokeMethod.TargetObject>
<InArgument x:TypeArguments="mtbc:IBuildDetail">[localBuild]</InArgument>
</InvokeMethod.TargetObject>
</InvokeMethod>
</Sequence>
</If.Then>
</If>
</Sequence>
</ActivityAction>
</ForEach>
</Sequence>

Release Management follows the model of Lab Management, whereby if a Build is deployed it is retained indefinately.
This allows you to alter the build quality etc as the build is verified and signed off, you may then promote / do whatever with that build without fear that it will be deleted by the retention policy.
you just need to do it as a daily / weekly / monthly admin task, choose your build from the drop down list in the build screen and then show all.
highlight all of the builds and then click the remove retention button.
if it bothers you that much you could write a script to find all of the builds and then remove the lock.
psudo:
BuildDefinition.KeepForever = False;

Related

A Gated Check in with an induced Build "Failure" : the Code Still Commits :(

Short Version:
I am setting the Status=Failed and TestStatus = Failed on a custom build template (.xaml).
When the build is setup as a Gated Check In (CheckIn), the code still commits.
Long Version:
I have some custom logic in the build workflow that is setting the below properties.
<!--the below is a result if a custom code activity I wrote returns a "true" for Code Coverage being lower than expected -->
<mtbwa:SetBuildProperties DisplayName="Set Status and TestStatus to Failed" Status="[Microsoft.TeamFoundation.Build.Client.BuildStatus.Failed]" TestStatus="[Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Failed]" sap2010:WorkflowViewState.IdRef="SetBuildProperties_7" />
My build "goes orange", but the code still gets checked in. The check-in of the code is the undesired result.
Some other "setup" screens:
Other articles I found:
TFS Gated check-in -- How to reject check-in on Partial Build Success?
Fail a build if code coverage is below a threshold in TFS2012
APPEND:
My template was from a Microsoft default one.
My custom check is defined between
If CompilationStatus = Unknown
and
If TestStatus = Unknown
At the end of the workflow there should be a task that checks in the shelfset. Make sure that is encapsulated in the proper logic to skip the checkin when the compilation status, test status or overall build status isn't what you want.
Original Gated Checkin might look like this:
<mtbwa:InvokeForReason DisplayName="Check In Gated Changes for CheckInShelveset Builds" sap2010:WorkflowViewState.IdRef="InvokeForReason_6" Reason="CheckInShelveset">
<mtbwa:CheckInGatedChanges DisplayName="Check In Gated Changes" sap2010:WorkflowViewState.IdRef="CheckInGatedChanges_1" />
</mtbwa:InvokeForReason>
With an IF condition around it:
<If Condition="[BuildDetail.TestStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Succeeded]" DisplayName="BuildDetail.TestStatus = Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus.Succeeded" sap2010:WorkflowViewState.IdRef="If_41">
<If.Then>
<mtbwa:InvokeForReason DisplayName="Check In Gated Changes for CheckInShelveset Builds" sap2010:WorkflowViewState.IdRef="InvokeForReason_6" Reason="CheckInShelveset">
<mtbwa:CheckInGatedChanges DisplayName="Check In Gated Changes" sap2010:WorkflowViewState.IdRef="CheckInGatedChanges_1" />
</mtbwa:InvokeForReason>
</If.Then>
<If.Else>
<mtbwa:WriteBuildWarning DisplayName="Write BuildDetail.TestStatus" sap2010:WorkflowViewState.IdRef="WriteBuildWarning_5" Importance="[Microsoft.TeamFoundation.Build.Client.BuildMessageImportance.High]" Message="["Gated Checkin Failed, BuildDetail.TestStatus=" & System.Enum.GetName(GetType(Microsoft.TeamFoundation.Build.Client.BuildPhaseStatus), BuildDetail.TestStatus)]" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces" />
</If.Else>
</If>
Note. The BuildStatus will be set back to "InProgress" by TFS. Piggyback on BuildDetail.TestStatus is probably the better bet.

ClickOnce myApp.application generated with wrong PublishUrl

I have added a MSBuild target to update the PublishUrl and then call the 'Publish' target, passing the new value in. This has allowed me to build multiple branches and have the corresponding ClickOnce app dropped in a branch specific share location.
<PropertyGroup>
<PublishUrl >\\someNetworkShare\</PublishUrl>
</PropertyGroup>
<Target Name="PublishClickOnce">
<PublishUrl>$(PublishUrl)$(BranchName)\</PublishUrl>
<MSBuild Projects="$(ProjectPath)" Properties="PublishUrl=$(PublishUrl); PublishDir=$(PublishUrl); Platform=AnyCPU" Targets="Publish" />
</Target>
The problem is that this just doesn't work on one of my build machines. It works perfectly fine on 2 of them, but not the 3rd.
Looking at the myApp.application file i can see that the deploymentProvider codebase points to the unchanged PublishedUrl (but it has been updated at the time of calling the 'Publish' target:
<deploymentProvider codebase="file://someNetworkShare/myApp.application" />
The above should be:
<deploymentProvider codebase="file://someNetworkShare/branchName/myApp.application" />
A few things i have tried:
Update the 'InstallUrl' to match the 'PublishUrl'
Added logging to ensure that the 'PublishUrl' has updated before calling the 'Publish' target.
Added a condition to the 'PublishUrl' to only update if it is empty
Any help would be much appreciated.
Thanks
-------------------------------- UPDATE --------------------------------
So a couple of things:
The working build machines are on W7, the failing machines are on a mixture of W7 and W8.
It turns out that you can exclude the deploymentProvider element (ProjectProperties -> Publish -> Manifest -> Exclude deployment provider URL). If excluded the system attempts to figure it out by its self at run-time (this is sufficient in most cases).
So I tested the build on a few more machines (a completely clean W7 VM, a W7 dev machine, W8.1 dev machine) and they both produced the incorrect deploymentProvider line.
Still curious to the actual root cause of the issue so I don't want to mark it as answered.

Microsoft Team Foundation Server Express 2012 - How to clean up in tbl_TestResult (old test results)

I have installed a Microsoft Team Foundation Server Express 2012.
The table tbl_TestResult is using up 7000 MB of my Database space.
I tried to find information on how to clean up this Table but found no way to do so.
When I want to check in new files into TFS I get the Error TF30042: The database is full...
Over the Visul Studio I deleted all visible Tests but still the size of tbl_TestResult just decreased very little.
Can anyone explain to me how I can cleanup all test results in a proper way?
I am using the TFS client API to delete old test runs. Here's some sample code:
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri("http://tfs2012:8080/tfs/DefaultCollection/"));
TestManagementService testManagementService = tpc.GetService<TestManagementService>();
ITestManagementTeamProject teamProject = testManagementService.GetTeamProject("MyProject");
int totalRuns = teamProject.TestRuns.Count("SELECT * FROM TestRun");
// Limit by date, so the query doesn't take too long.
string query = "SELECT * FROM TestRun WHERE CreationDate < '2013/07/01'";
int numTotalToDelete = teamProject.TestRuns.Count(query);
if (numTotalToDelete == 0) { return; }
// Only delete 500 at a time, to give SQL Server time to breathe (don't ask).
var runsToDelete = teamProject.TestRuns.Query(query, false)
.Take(500);
teamProject.TestRuns.Delete(runsToDelete);
Query syntax comes from here: http://blogs.msdn.com/b/duat_le/archive/2010/02/25/wiql-for-test.aspx
We had the same issue for our on prem TFS. This is how we did it for TFS 2015 up to TFS 2017.
Install Microsoft Visual Studio Team Foundation Server 2015 Power Tools to get access to Test Attachment Cleaner(TAC), tcmpt.exe.
It installs to c:\Program Files (x86)\Microsoft Team Foundation Server 2015 Power Tools
Sample settings for TAC is located in the installation folder, at <INSTALL_DIR>\TestAttachmentCleaner_samples_settingsfile
Create your own settings file, for example, all-100mb.xml file:
<DeletionCriteria>
<TestRun>
<Created Before="2016-01-01" />
</TestRun>
<Attachment>
<Extensions>
<Include value="wmv" />
<Include value="xesc" />
<Include value="trmx" />
</Extensions>
<!-- size in MB -->
<SizeInMB GreaterThan="100" />
</Attachment>
<LinkedBugs>
<Exclude state="Active" />
</LinkedBugs>
</DeletionCriteria>
The above config file removes test attachments of inactive test runs which are larger than 100MB with extensions: wmv, xesc, trmx.
Perform a test attempt to find out the outcome with this command: tcmpt.exe AttachmentCleanup /settingsfile:"all-100mb.xml" /mode:preview /collection:"https://tfs.contoso.com/DefaultCollection" /teamproject:"TeamProjectName" /outputfile:all-100mb.log"
This produces a very detailed log file that includes key information you needed, like:
------------ Summary ------------
Number of attachments affected: 339
Total size of attachments: 39646,7 MB
Elapsed time was 247,94 seconds
When you are satisfied with the result, do this:
Turn off transaction logs for your TFS database, if you use MSSQL server.
If you don't, you may grow your transaction logs faster than you can remove the test attachments.
Run tcmpt.exe command above without the /mode:preview parameter to delete test attachments.
When done, run these 2 stored procedures from the TFS database:
EXEC dbo.prc_DeleteUnusedContent 1
EXEC dbo.prc_DeleteUnusedFiles 1, 0, 1000
The stored procedures removes emptied rows from the database.
If the stored procedures last less than 1 second each, wait some time and re-run them again.
Then you can choose to shrink your database.
Good luck with it.
if you delete unwanted builds then you will be presented with the option to delete test results associated with the build when you delete it.
You may find that the test attachments are consuming a lot of space as well, you can use the test attachment cleaner provided with TFS power tools to remove these.
TFPT can be found here

team build target not executed

I have a tfs 2008 build that I need to add WiX compliation to.
Currently the build executes and compiles and copies all output to a drops location in the following target
<Target Name="AfterCompile"> .... </Target>
I have added another target directly below it that looks like the following
<UsingTask TaskName="HeatDirectory" AssemblyFile="$(WixTasksPath)" />
<Target Name="BuildMsi" DependsOnTargets="AfterCompile">
<Message Text="Start harvesting Website files for Wix compliation" />
<HeatDirectory
ToolPath="$(WixToolPath)"
Directory="$(DropLocation)\Latest\x86\Release\_PublishedWebsites\IFMA.MasterPlan.Web"
GenerateGuidsNow="yes"
ComponentGroupName="Web"
OutputFile="$(MSBuildProjectDirectory)\Setup\Product\Fragments\wwwfiles.wxs"
SuppressFragments="yes"
DirectoryRefId="WebRoot"
KeepEmptyDirectories="yes"
PreprocessorVariable="var.WebRoot"
SuppressRegistry="yes"
SuppressRootDirectory="yes"
SuppressCom="yes"
/>
<Message Text="Finished harvesting Website files for Wix compliation" />
</Target>
The BuildMsi target is never executed but the AfterCompile one definitly is.
The BuildMsi isn't listed in the default build targets
but I thought that since it has a dependency on AfterCompile it would be executed after it.
What am I missing here?
DependsOnTargets lists the targets that must be executed before your target can run, it does not force your target to run after the list of targets run.
See: http://msdn.microsoft.com/en-us/library/t50z2hka(v=VS.90).aspx
If you're using MSBuild 4.0, AfterTargets attribute is what you need:
AfterTargets: Optional attribute.
A semicolon-separated list of target
names. When specified, indicates that
this target should run after the
specified target or targets. This
lets the project author extend an
existing set of targets without
modifying them directly.
Alternatively you can use target injection, which basically is overriding the CompileDependsOn property in your .proj file to include your target at the end. You need to declare this property after the imports of the common target files to ensure it is the last definition of the property.
<PropertyGroup>
<CompileDependsOn>
$(CompileDependsOn);
MyCustomTarget
</CompileDependsOn>
</PropertyGroup>
See "How to extend the visual studio build process" for more details.

Manually Trigger a TFS Team Build

How would you manually trigger additional team builds from a team build? For example, when we were in CC.Net other builds would trigger if certain builds were successful. The second build could either be projects that use this component or additional, long running test libraries for the same component.
One way you could do it is you could an an AfterEndToEndIteration target to your TFSBuild.proj file that would runs the TfsBuild.exe command line to start you other builds. I'm thinking something like this (though I haven't tested it)
<Target Name="AfterEndToEndIteration">
<GetBuildProperties TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
Condition=" '$(IsDesktopBuild)' != 'true' ">
<Output TaskParameter="Status" PropertyName="Status" />
</GetBuildProperties>
<Exec Condition=" '$(Status)'=='Succeeded' "
Command="TfsBuild.exe start /server:$(TeamFoundationServerUrl) /buildDefinition:"Your Build Definition To Run"" />
</Target>
I've done the same thing Martin suggested on a number of occasions (his blog is beyond helpful, BTW). However, I ended up needing to trigger cascading builds like this (based on some other complicated rules) enough that I created a custom task to do it. Keep your build scripts nice and lean and gives you some more flexibility and encapsulation possibilities.
public override bool Execute()
{
IBuildDefinition[] buildDefinitions = BuildServer.QueryBuildDefinitions(ProjectName);
foreach (IBuildDefinition build in buildDefinitions)
{
if(build.Enabled) //I did a bunch of custom rules here
{
Log.LogMessage(String.Concat("Queuing build: ", build.Name));
BuildServer.QueueBuild(build);
}
}
return true;
}
There's some more good stuff on Aaron Hallberg's blog too:
http://blogs.msdn.com/aaronhallberg/archive/2007/04/24/team-build-object-model-queueing-a-build.aspx

Resources