TFS: How to validate if every file is checked-in? - tfs

We are working on several ASP.NET MVC C# projects within Visual Studio 2015 and Team Foundation Server 2013. Sometimes the NuGet upgrade process is a mess and some of the replaced files (mostly *.png, *.gif, *.ttf) have not been checked-in properly.
What we have figured out so far: the check-in process gets into trouble, if directories should be removed and created in one step. You have to check-in twice. The problem is, if you don’t know about this and one of our developers retrieves the latest source, there are missing files. Visual Studio indicates that with a warning icon in Solution Explorer.
My question is: Is it possible to validate, if every file is present during gated-checkin or nightly-build on TFS which is linked in the csproj file? At least there should be a warning during build.
Hint: This is only a problem with files, which are not compiled (*.cs files) or do not have the setting “copy during build into output directory”. This happens e.g. with JS-files, which are bundled.
Final solution:
Write-Host "Check availability for all referenced files in all projects ..."
function Check-Files($directory, $files){
if (!$directory.EndsWith("/")) { $directory = "$($directory)/" }
ForEach($file in $files){
if($file){
Write-Host " Referenced file $($directory)$file"
if(-not (Test-Path "$($directory)$($file)")){
throw [System.IO.FileNotFoundException] "$($directory)$($file) not found."
}
}
}
}
function CheckProjectFile($csprojFile){
[xml]$projectContent = Get-Content $csprojFile
Write-Host "Checking project: $($csprojFile) ..."
$directory = Split-Path $csprojFile
ForEach($itemGroup in $projectContent.Project.ItemGroup){
Check-Files -files $itemGroup.Reference.HintPath -dir $directory
Check-Files -files $itemGroup.Compile.Include -dir $directory
Check-Files -files $itemGroup.None.Include -dir $directory
Check-Files -files $itemGroup.Content.Include -dir $directory
Check-Files -files $itemGroup.TypeScriptCompile.Include -dir $directory
Check-Files -files $itemGroup.ProjectReference.Include -dir $directory
}
}
$csprojFiles = Get-ChildItem -Path ./ -Recurse *.csproj | Select-Object -Property FullName
ForEach($file in $csprojFiles){
CheckProjectFile($file.FullName)
}
I added the script file to my Team Project on TFS, changed my build definition, added the script directory to my "Source Settings" and included the script into "Pre-build script path". Done!

I created a small piece of powershell that you can execute as a step before building the solution.
function Check-Files($files){
ForEach($file in $files){
if($file){
Write-Host "looking for $file"
if(-not (Test-Path $file)){
throw [System.IO.FileNotFoundException] "$file not found."
}
}
}
}
[xml]$projectContent = Get-Content ./your.csproj
ForEach($itemGroup in $projectContent.Project.ItemGroup){
Check-Files -files $itemGroup.Reference.HintPath
Check-Files -files $itemGroup.Compile.Include
Check-Files -files $itemGroup.None.Include
Check-Files -files $itemGroup.Content.Include
Check-Files -files $itemGroup.TypeScriptCompile.Include
Check-Files -files $itemGroup.ProjectReference.Include
}
Hope this helps you.
Kind regards
Jan

First of all, make sure you check in the whole solution/project every time. In this way, all the edit files in this solution/ project will list in the Included pending changes. If you only check in a single file, then other edits will list in Excluded changes and won't be checked in.
Also you can use check-in policy to prevent check-in without a review. Here's an existing check-in policy that requires code review before check-in:
https://visualstudiogallery.msdn.microsoft.com/c476b708-77a8-4065-b9d0-919ab688f078

Related

Build agent no DotNetFramework MSBuild and VSTest capability in Team Foundation Server 2017

Since a while I discovered some of my buildagent doesn't have the necessary capabilities anymore to build a .NET Framwork web application. When I installed these build agents a few months ago it worked perfect. For some misterious reasons these capabilities went away. Since a week we have VS2017 installed on that machine. A reboot of the machine where the build agent is running doesn't help. Even we rebooted the Team Foundation Server machine. Can any help me with this problem?
Please try below items to let the agent to identify the capabilities:
Restart the Agent service to identify the capabilities
Add User Capabilities manually:
Settings >> AgentPools >> Select the pool >> select the specific Agent >> Capabilities >> Add capability under USER CAPABILITIES
Register capabilities to the machine following below steps, thus all the agents will automatically pick up the capabilities.
Control Panel >> System >> Advanced System Settings >> Environment Variables >> New System Variable >> Enter the variable name and value listed below >> OK >>
After doing so, restart the agents services will have each of them automatically pick up new capabilities.
Below capabilities for your reference (based on your environment and installed version):
DotNetFramework C:\Windows\Microsoft.NET\Framework64\v4.0.30319
DotNetFramework_4.6.1 C:\Windows\Microsoft.NET\Framework\v4.0.30319
DotNetFramework_4.6.1_x64 C:\Windows\Microsoft.NET\Framework64\v4.0.30319
MSBuild C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\
MSBuild_15.0 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\
MSBuild_15.0_x64 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\amd64\
VSTest C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TestWindow
VSTest_15.0 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TestWindow
VisualStudio C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\
VisualStudio_15.0 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\
VisualStudio_IDE_15.0 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\
I followed your step regarding adding the settings as user capalbility. The build agents pick up the build task now. But when the solution was build an error is returned from the build task. Exit code -1073741502 returned from process: file name 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe', arguments '-NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command ". ([scriptblock]::Create('if (!$PSHOME) { $null = Get-Item -LiteralPath ''variable:PSHOME'' } else { Import-Module -Name ([System.IO.Path]::Combine($PSHOME, ''Modules\Microsoft.PowerShell.Management\Microsoft.PowerShell.Management.psd1'')) ; Import-Module -Name ([System.IO.Path]::Combine($PSHOME, ''Modules\Microsoft.PowerShell.Utility\Microsoft.PowerShell.Utility.psd1'')) }')) 2>&1 | ForEach-Object { Write-Verbose $_.Exception.Message -Verbose } ; Import-Module -Name 'C:\AgentV2_work_tasks\VSBuild_71a9a2d3-a98a-4caa-96ab-affca411ecda\1.119.0\ps_modules\VstsTaskSdk\VstsTaskSdk.psd1' -ArgumentList #{ NonInteractive = $true } -ErrorAction Stop ; $VerbosePreference = 'SilentlyContinue' ; $DebugPreference = 'SilentlyContinue' ; Invoke-VstsTaskScript -ScriptBlock ([scriptblock]::Create('. ''C:\AgentV2_work_tasks\VSBuild_71a9a2d3-a98a-4caa-96ab-affca411ecda\1.119.0\VSBuild.ps1'''))"'.

Open .sln file from cmder?

Is it possible to launch devenv.exe and open a .sln file from cmder? After I clone a git repo from the command line, I'd like to quickly open the solution in Visual Studio without having to leave cmder.
Danny is correct, you simply type xxx.sln.
I'm a bit lazy and developed the script below to be able to type vs and it will find and open the first sln file it finds in the current directory.
Here are some example commands:
vs - Will open the first sln file it finds, if it doesn't find one it will just open Visual Studio without a solution. I default to opening with Visual Studio 2017. Just change the default case in the Get-VisualStudioCommand function if you'd rather default to a different version.
vs 15 - I have a bunch of versions of Visual Studio on my machine, so this will open similarly to vs, but will open with Visual Studio 2015.
vs 13 'helloWorld.sln' - this will open a specific sln file, i.e., helloWorld.sln. One of the projects I worked on recently had multiple solutions in the same directory.
vs -WhatIf -Verbose - this will show you what the script would do if it ran and will print out the Write-Verbose messages. Handy for debugging. I show and example of this in the image below.
For this solution, I'm assuming that you're using a PowerShell console. If so, you can define a .ps1 file that will run before the console opens via the -File option. It's like defining a different PowerShell profile with different commands per console window (very powerful).
See the image below where I show going to settings and modify the PowerShell:PowerShell task to execute PowerShell -NoExit -NoLogo -File C:\src\ps\Cmder\general_setup.ps1 -new_console:d:"C:\src"
Here is the image (note, although I use an alias for ls that outputs in PowerShell like a Linux ls command...this is a PowerShell console):
If you add the following script to the C:\src\ps\Cmder\general_setup.ps1 file (or wherever you want to put it)...you can then type in vs and it will execute. Notice in the Get-VisualStudioCommand function, I've put all the file paths...not the most elegant looking code, but if your file paths to devenv.exe are different than mine, just change them there.
Note, you could also get fancy and add a pass-through to the -ArgumentList or flag for the /SafeMode command to be able to open Visual Studio in safe mode when needed, I've just created these as a quick and dirty shortcut.
function Get-VisualStudioCommand
{
[CmdletBinding()]
param ( [AllowNull()][String] $vsVersion )
$vs10 = """${env:ProgramFiles(x86)}\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"""
$vs13 = """${env:ProgramFiles(x86)}\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe"""
$vs15 = """${env:ProgramFiles(x86)}\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe"""
$vs17 = """${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe"""
switch ($vsVersion)
{
'10' {$vs10}
'13' {$vs13}
'15' {$vs15}
'17' {$vs17}
default {$vs17}
}
}
function Get-SolutionName
{
[CmdletBinding()]
param ( [AllowNull()][String] $Name )
if (!$Name)
{
$Name = Get-ChildItem -Filter *.sln
}
if ($Name)
{
$Name = ('"{0}"' -f $Name)
}
return $Name
}
function Start-VisualStudioProcess
{
[CmdletBinding(SupportsShouldProcess=$true)]
param([String]$Version, [String]$Sln)
$VsCommand = Get-VisualStudioCommand -vsVersion $Version
$Sln = Get-SolutionName -Name $Sln
Write-Verbose -Message ('Starting: command={0} solutionName={1}' -f $VsCommand, $Sln)
if ($PSCmdlet.ShouldProcess($VsCommand, 'Start-Process'))
{
if ($Sln)
{
Start-Process -FilePath $vsCommand -ArgumentList $sln
}
else
{
Start-Process -FilePath $vsCommand
}
}
}
Set-Alias -Name vs -Value Start-VisualStudioProcess
Let me know if you have any questions. I hope it helps.
Run this command in your prompt(cmd, cmder, etc.)
reg add "HKCU\Software\Microsoft\Command Processor" /v Autorun /d "doskey vs=for /f \"delims=\" %a IN ('dir /b *.sln') do start %a" /f
Write "vs" for open the first *.sln in folder of Solution and be happy!

TFS 2015 build vNext update file version with commit id

Do you know how inject commit id into file version, so every assembly would heve version like 2.0.6565.0 where 6565 is related to C6565commit ID in TFS ?
It looks some power shell script is needed.
If your question is similar to your another post TFS 2015. the $(var.SourceLocation) variable is not available at gated-check in, that want to get the changeset id that hasn't checked in during gated check-in, then it's impossible in a single build.
If you don't use gated check in, then you can use $Env:BUILD_SOURCEVERSION in a powershell script to set the AssemblyVersion. Here is already a script at the website below, you can refer to it:
https://github.com/wulfland/ScriptRepository/blob/master/TFSBuild/TFSBuild/AssemblyVersion/Set-AssemblyVersion/Set-AssemblyVersion.ps1
Finally I created my own PS script based on this post.
The idea update version in all files with assembly info
$CommitId = ([string]$env:BUILD_SOURCEVERSION) -replace "[^0-9]+", ""
$AllVersionFiles = Get-ChildItem $SourceDir AssemblyInfo.cs -recurse
$regexToFindVersion = "Version\(""([0-9]+)\.([0-9]+).+"""
foreach ($file in $AllVersionFiles)
{
Write-Host "Processing " $file.FullName
(Get-Content $file.FullName) |
%{$_ -replace $regexToFindVersion, ('Version("$1.$2.0.' + $CommitId + '"') } |
Set-Content $file.FullName -Force
}
Full script can be found here.
The script must be placed before building project:

Programatically delete [permenantly] a TFS work item

Whilst I'm aware that there is a command line tool to permenantly delete a TFS work item. (e.g. How to delete Work Item from Team Foundation Server)
Has anyone been able to achieve the same action programatically using the TFS 2010 API DLLs?
Shai Raiten has blogged about this here, where he makes use of DestroyWorkItems(ids).
It is advisable that you proceed with extra caution in your implementation, since this can severely mess your installation. One could argue that constructing such a tool deviates from best practices.
You can also use PowerShell to bulk delete work items:
Copy and paste below script in a PowerShell file (with .ps1
extension), update the variable values mentioned in the list #4 below
and run the command from a machine where witadmin tool is installed
(Generally available after visual studio installation). Open
PowerShell command window and execute the script.
Note: Account running below script should have team foundation administrator or collection administrator access.
########TFS Work Items Bulk Destroy Automation Script##########
#Notes:
#1) This script requires to setup file/folder path, validate the file/folders path before running the script
#2) start the powershell window as Administrator and run the script
#3) This script requires share and admin access on the destination server, make sure your account or the account under which script is
# executing is member of admin group on the destination server
#4) Update following:
# 4.1: $CollectionURL
# 4.2: $WitAdmin tool location
# For VS 2015, Default location is C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE
# For VS 2013, Default location is C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE
# 4.3: $WI_List
# 4.4: $logfile
####################
$CollectionURL = "http://tfs:8080/tfs/CollectionName"
$WitAdminLocation = "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE"
$WI_List = Get-Content "C:\WI_List.txt"
$logfile="C:\log.txt"
$ExecutionStartTime = Get-Date
$WICount = 0
"## Starting WI Destroy # $ExecutionStartTime ##"| Out-File $logfile -Append
"Collection URL: $CollectionURL" | Out-File $logfile -Append
foreach ($WIID in $WI_List)
{
CD $WitAdminLocation
.\witadmin destroywi /collection:$CollectionURL /id:$WIID /noprompt
"WI ID: $WIID Destroyed" | Out-File $logfile -Append
$WICount = $WICount + 1
Write-Host "$WICount Work Items Deleted"
}
$ExecutionEndTime = Get-Date
"## WI Destroy Command Completed # $ExecutionEndTime ##"| Out-File $logfile -Append
$TotalExecutionTime = $ExecutionEndTime - $ExecutionStartTime
"Total Work Items Deleted: $WICount" | Out-File $logfile -Append
" Total Execution Time: $TotalExecutionTime" | Out-File $logfile -Append
##End of script##

How can I use PowerShell to update build qualities on previous TFS Builds?

We are using TFSDeployer to listen to build quality changes and deploy to our staging environment when it transitions to "Staging".
I'd like to have it go ahead and update all of the other builds that have a currently build quality of "Staging" to be "Rejected".
This appears to be something that needs to happen inside the PowerShell script which looks like:
$droplocation = $TfsDeployerBuildData.DropLocation
ECHO $droplocation
$websourcepath = $droplocation + "\Release\_PublishedWebsites\CS.Public.WebApplication\"
$webdestinationpath = "\\vmwebstg\WebRoot\CreditSolutions\"
new-item -force -path $webdestinationpath -itemtype "directory"
get-childitem $webdestinationpath | remove-item -force -recurse
get-childitem $websourcepath | copy-item -force -recurse -destination $webdestinationpath
$configFile = $webdestinationpath + "web.development.config"
remove-item $configFile -force
$configFile = $webdestinationpath + "web.staging.config"
$configFileDest = $webdestinationpath + "web.config"
move-item $configFile $configFileDest -force
So, how can I do this?
First add the Get-tfs function to your script:
function get-tfs (
[string] $serverName = $(Throw 'serverName is required')
)
{
# load the required dll
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")
$propertiesToAdd = (
('VCS', 'Microsoft.TeamFoundation.VersionControl.Client', 'Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer'),
('WIT', 'Microsoft.TeamFoundation.WorkItemTracking.Client', 'Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore'),
('BS', 'Microsoft.TeamFoundation.Build.Common', 'Microsoft.TeamFoundation.Build.Proxy.BuildStore'),
('CSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.ICommonStructureService'),
('GSS', 'Microsoft.TeamFoundation', 'Microsoft.TeamFoundation.Server.IGroupSecurityService')
)
# fetch the TFS instance, but add some useful properties to make life easier
# Make sure to "promote" it to a psobject now to make later modification easier
[psobject] $tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($serverName)
foreach ($entry in $propertiesToAdd) {
$scriptBlock = '
[System.Reflection.Assembly]::LoadWithPartialName("{0}") > $null
$this.GetService([{1}])
' -f $entry[1],$entry[2]
$tfs | add-member scriptproperty $entry[0] $ExecutionContext.InvokeCommand.NewScriptBlock($scriptBlock)
}
return $tfs
}
Next, instantiate the TFS object
$tfs = get-tfs http://YourTfsServer:8080
Then find the builds with a build quality of "Staging"
$builds = $tfs.BS.GetListOfBuilds("Test Project", "TestBuild") |
where {$_.BuildQuality -eq "Staging"}
Finally, update the quality of these builds
foreach ($build in $builds) { $tfs.BS.UpdateBuildQuality($build.BuildUri, "Rejected") }
(I haven't run this script yet, but you should be able to get it going without troubles)
More info on my blog: Using the Team Foundation Object Model with PowerShell
One last advice, if you update the Build quality from within the script that is running from TfsDeployer, you could end up with 2 script running at the same time if you have a mapping for the Staging --> Rejected transition!
This is not the full answer as I don't have much knowledge of TFSDeployer or indeed PowerScript. However the .NET API for Team Build is able to do this. You want to get hold of the IBuildDetail for the build. The easiest way to get this is if you have the BuildUri (which it sounds like you might) in which case a call to IBuildServer.GetBuild should get you the build you are interested in.
IBuildServer also has the QueryBuilds methods which you would be able to call to find the builds that are of interest to you, you would then set the Quality property on the IBuildDetails that you wanted to change, remembering to call the Save() method on each one.
Hope that gives you a start - sorry it isn't a more complete answer.

Resources