How can I process a path recursively so that once the processing completes there are no empty directories under the path.
For example, say we have C:\Dir1\Dir2\Dir3 and there are no files in any of these directories. The outcome should be the removal of all three directories.
I would like to accomplish this without the use of custom tasks.
Something like this should work, didn't check the performance of counting thousands of files though just to get array length...
<Project DefaultTargets="Foo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foo">
<ItemGroup>
<Directories Include="$([System.IO.Directory]::GetDirectories('D:\foo', '*', System.IO.SearchOption.AllDirectories))" />
<Directories>
<Files>$([System.IO.Directory]::GetFiles("%(Directories.Identity)", "*", System.IO.SearchOption.AllDirectories).get_Length())</Files>
</Directories>
</ItemGroup>
<RemoveDir Directories="#(Directories)" Condition="%(Files)=='0'" />
</Target>
</Project>
Using an Exec Task running PowerShell:
MSBuild
<Project DefaultTargets="DefaultTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CleanPath>C:\MyDirAboveDir1</CleanPath>
</PropertyGroup>
<Target Name="DefaultTarget">
<Exec Command="PowerShell .\RemoveDir.ps1 '$(CleanPath)'" />
</Target>
</Project>
PowerShell RemoveDir.ps1
Param ([string]$folderpath = $(throw "provide folderpath"))
$folders = Get-ChildItem $folderpath -recurse -force | ? {$_.PSIsContainer}
if ($folders -ne $null)
{
[array]::Reverse($folders)
foreach($folder in $folders)
{
Write-Host "Examining contents of $($folder.fullname)"
$childitems = Get-Childitem $folder.fullname -recurse -force | ? { $_.PSIsContainer -eq $false }
if($childitems -eq $null)
{
"Remove folder: " + $folder.FullName
Remove-Item $folder.FullName -Recurse -Force
}
else
{
Write-host "Files found in $folder, skipping delete"
}
$childitems = $null
}
}
else
{
Write-Host "no sub folders found"
}
Courtesy to Guy Ellis Rocks: Powershell script to remove empty directories
Related
I am trying to override the AssemblyName property defined in .NET projects in my Azure Pipeline. (The purpose is to standardize assembly names for NuGet packages.)
It works when I create a Directory.Build.props file right before building with MSBuild.
The problem is that this only works if the AssemblyName has not been defined in the .csproj file. Otherwise the .csproj property takes precedence.
According to the docs, I should be able to use Directory.Build.targets to override .csproj properties:
https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2022#choose-between-adding-properties-to-a-props-or-targets-file
If you need to override properties, do it in a .targets file, after
all user-project customizations have had a chance to take effect.
This is what the Directory.Build.targets file contains:
<Project>
<Target Name="IncludeProjectMatchMessage" Condition="$(MSBuildProjectName) == ''${{ parameters.projectName }}''" BeforeTargets="Build">
<Message Text="The custom Directory.Build.targets properties will be applied to this project $(MSBuildProjectName), AssemblyName = $(packageName)" Importance="high" />
</Target>
<PropertyGroup Condition="$(MSBuildProjectName) == ''${{ parameters.projectName }}''">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Company>MyCompany</Company>
<AssemblyName>$(packageName)</AssemblyName>
</PropertyGroup>
</Project>
I have also tried this, with the same result:
<Project>
<Target Name="IncludeProjectMatchMessage" Condition="$(MSBuildProjectName) == ''${{ parameters.projectName }}''" BeforeTargets="Build">
<Message Text="The custom Directory.Build.targets properties will be applied to this project $(MSBuildProjectName), AssemblyName = $(packageName)" Importance="high" />
<PropertyGroup Condition="$(MSBuildProjectName) == ''${{ parameters.projectName }}''">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Company>MyCompany</Company>
<AssemblyName>$(packageName)</AssemblyName>
</PropertyGroup>
</Target>
</Project>
The text message is displayed during the MSBuild, but the properties are not applied.
Why is that, and how do I fix it?
I could not get .targets to work, nor could I find any documentation on the properties that it is possible to override using this method.
Instead, I added a PS script that modifies the .csproj file directly:
- task: PowerShell#2
displayName: Modify .csproj file
inputs:
targetType: 'inline'
script: |
# This script will overwrite csproj properties that are already defined.
# If they are not defined, no changes are made - values are not added.
# The case of missing properties is handled by adding a .props file in the next step.
# Note that changing the assembly name may break the build when the library contains xaml files.
# Read more below.
$path = "$(projectFolder)\${{ parameters.projectName }}.csproj"
Write-Host "Reading project file: " $path
[xml]$xml =(gc $path)
$projectAssemblyName = $xml.Project.PropertyGroup.AssemblyName
Write-Host "AssemblyName: " $projectAssemblyName
if ($projectAssemblyName -like "$(packageName)")
{
Write-Host "AssemblyName corresponds to package name."
}
else
{
Write-Host $projectAssemblyName "is different from package name:" $(packageName)
Write-Host "Overwriting AssemblyName with " $(packageName)
$xml.Project.PropertyGroup |? AssemblyName |% {$_.AssemblyName="$(packageName)"}
}
$xml.Project.PropertyGroup |? Company |% {$_.Company="MyCompany"}
$xml.Project.PropertyGroup |? GenerateDocumentationFile |% {$_.GenerateDocumentationFile="True"}
$xml.Save($path)
$fileContent = Get-Content $path
Write-Host "Success. File contents: " $fileContent
Context
I have a task integration testing and code coverage which I execute in my jenkins pipeline.
The tools used is dotcover and Nunit.
Nunit is executed throught dotcover during the integration test build when the configuration is Integration.
Problem
When I execute the configuration Integration in visual studio with some tests in error, then the build failed, everything are ok, but when the same configuration are executed with msbuild, it doesn't return any error code then jenkins pipelin doesn't fail.
The situation put us in delicate way because we can't trust our build pipeline anymore.
I looking for a solution on web for some days and I still on the same point, it's why I asking for your help here.
Thank you for helping.
Files
jenkinsfile
node('BUILD_PROJECT') {
stage ('Checkout')
{
checkout scm
}
stage ('Build')
{
bat '"C:/Program Files (x86)/NuGet/nuget.exe" restore -NonInteractive MySolution.sln'
bat "\"C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin/MSBuild.exe\" /p:Configuration=Release;AssemblyVersion=0.1.0.${env.BUILD_NUMBER} /maxcpucount:8 MySolution.sln"
}
stage ('Integration')
{
bat "\"C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin/MSBuild.exe\" /p:Configuration=Integration /maxcpucount:8 MySolution.sln"
}
stage ('Publish Coverage')
{
publishHTML target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: 'Solution/IntegrationProject/bin/Integration/TestResult',
reportFiles: 'ProjectCoverageReport.html',
reportName: 'Project Coverage Report'
]
}
stage ('Setup')
{
bat "\"C:/Program Files (x86)/Microsoft Visual Studio/2017/BuildTools/MSBuild/15.0/Bin/MSBuild.exe\" /p:Configuration=Setup;Platform=x86;AssemblyVersion=0.1.0.${env.BUILD_NUMBER} /maxcpucount:8 MySolution.sln"
}
stage ('Archive')
{
archiveArtifacts artifacts: 'Solution/SetupProject/bin/x86/Setup/MySetup.exe'
}
}
In IntegrationProject.csproj
<Target Name="CoverageReport" AfterTargets="CopySqlFiles" Condition="$(Configuration) == Integration">
<Exec Command="..\packages\JetBrains.dotCover.CommandLineTools.2018.1.3\tools\dotCover.exe analyse /TargetExecutable=..\packages\NUnit.ConsoleRunner.3.8.0\tools\nunit3-console.exe /ReturnTargetExitCode /TargetArguments="$(TargetPath)" /Filters=-:nunit.framework;-:IntegrationProjectTest;-:type=MyNamespace.View.*;-:type=*Test /TargetWorkingDir=$(TargetDir) /Output=$(TargetDir)\TestResult\MyCoverageReport.html /ReportType=HTML" />
</Target>
You should be able to make use of the dotcover parameter ReturnTargetExitCode to get the return code from nunit.
<Target Name="CoverageReport" AfterTargets="CopySqlFiles" Condition="$(Configuration) == Integration">
<Exec Command="..\packages\JetBrains.dotCover.CommandLineTools.2018.1.3\tools\dotCover.exe analyse ^
/TargetExecutable=..\packages\NUnit.ConsoleRunner.3.8.0\tools\nunit3-console.exe ^
/ReturnTargetExitCode ^
/TargetArguments="$(TargetPath)" ^
/Filters=-:nunit.framework;-:IntegrationProjectTest;-:type=MyNamespace.View.*;-:type=*Test ^
/TargetWorkingDir=$(TargetDir) ^
/Output=$(TargetDir)\TestResult\MyCoverageReport.html ^
/ReportType=HTML
/ReturnTargetExitCode">
<Output TaskParameter="ExitCode" PropertyName="DotCoverExitCode" />
</Exec>
<Message Text="Unit Tests Failed!" Condition="$(DotCoverExitCode) != '0'"/>
</Target>
I am trying to run simple powershell script prom a job(sql server agent).
script:
[string]$SrcFolder = "G:\MSSQL\Test";
[string]$TrgFolder = "\\xx.xx.xx.xxx\d$\sql\logshipping" ;
if (-not(Get-Module -Name SQLPS))
{
if (Get-Module -ListAvailable -Name SQLPS) {
Push-Location
Import-Module -Name SQLPS -DisableNameChecking
Pop-Location
};
};
if ($SrcFolder -eq $null -or $TrgFolder -eq $null )
{
Write-Host "The source Folder = $SrcFolder ,OR target folder = $TrgFolder is not valid/Null";
};
$prafix = "[A-Za-z]+_[0-9]+_[0-9].trn" ;
Set-Location -Path C:\ ;
# Copy to Destination
foreach ($file in gci -Path $SrcFolder | Where-Object{ $_.Mode -eq '-a---' -and $_.Extension -eq '.trn' -and $_.Name -match $prafix})
{
write-host "Starting Copy File: $($file.FullName) ." ;
Copy-Item -Path $file.FullName -Destination $TrgFolder -Force -ErrorAction Stop ;
if (Test-Path -LiteralPath "$TrgFolder\$($file.Name)")
{
write-host "End Copy File: $($file.FullName) ." ;
Move-Item -Path $file.FullName -Destination "$SrcFolder\Moved" -Force ;
}
else
{
Write-Host "The Copy File: $TrgFolder\$($file.BaseName) . Failed "
};
}
The script doing: Copy .bak files to remote server. then check if the bak file exists in the remote server, and if it's exists his move the bak file to a local folder .
The job is failing message:
Date 9/1/2016 6:29:31 PM Log Job History (LS_Manual-Copy)
Step ID 3 Server SQL2012ENT-IR-3 Job Name LS_Manual-Copy Step
Name Delete Old Logs Duration 00:00:00 Sql Severity 0 Sql Message
ID 0 Operator Emailed Operator Net sent Operator Paged Retries
Attempted 0
Message Unable to start execution of step 3 (reason: line(23): Syntax
error). The step failed.
the sql server agent is a member in the Administrator group .
please help .
10X
We try to get our DB updated as part of the Visual Studio Online build as dedicated build step, but the PowerShell script that we locally using fails to work on the hosted build controller with following error.
2015-07-28T20:35:26.8546681Z ##[error]import-module : The specified module 'sqlps' was not loaded because no valid module file was found in any module
2015-07-28T20:35:26.8546681Z ##[error]directory.
Is there any way to use "Invoke-SqlCmd" in VSO hosted build controller?
Got it working with those lines!
Add-PSSnapin SqlServerCmdletSnapin100 -ErrorAction SilentlyContinue
Add-PSSnapin SqlServerProviderSnapin100 -ErrorAction SilentlyContinue
Here how I do it:
$moduleName = "SqlServer"
if (Get-Module $moduleName -ListAvailable) {
Write-Host "Module '$moduleName' is available"
} else {
Write-Host "Module '$moduleName' does not exist"
try {
Install-Module -Name $moduleName -Force -AllowClobber
Write-Host "Module '$moduleName' successfully installed"
} catch {
Throw "Installation of module '$moduleName' failed: $($_.Exception)"
}
}
I have 3 projects in the solution,
A WPFApplication and 2 ClassLibrary projects
When i build the Solution i get error below..
properties {
$base_dir = resolve-path .
$build_dir = "$base_dir\build"
$buildartifacts_dir = "$build_dir\BuildArtifacts"
$sln_file = "$base_dir\Hello.sln"
}
task default -depends Compile
task Clean {
Write-Host "Cleaning solution" -ForegroundColor Green
remove-item -force -recurse $buildartifacts_dir -ErrorAction
SilentlyContinue
}
task Init -depends Clean {
Write-Host "Creating BuildArtifacts directory" -ForegroundColor Green
new-item $buildartifacts_dir -itemType directory
}
task Compile -depend Init {
Write-Host "Compiling ---" $sln_file -ForegroundColor Green
Exec { msbuild $sln_file "/p:OutDir=$build_artifacts_dir"
/p:Configuration=Release /v:quiet }
}
i get the following error's -- what am i doing wrong?
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(2868,9):
error MSB3023: No destination specified for Copy. Please supply either "DestinationFiles" or "DestinationFolder". [D:\Nusrofe\GrokPSake2\ClassLibrary1\ClassLibrary1.csproj]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3471,9):
error MSB4044: The "FindUnderPath" task was not given
a value for the required parameter "Path". [D:\Nusrofe\GrokPSake2\ClassLibrary1\ClassLibrary1.csproj]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(2868,9):
error MSB3023: No destination specified for Copy. Ple
ase supply either "DestinationFiles" or "DestinationFolder". [D:\Nusrofe\GrokPSake2\ClassLibrary2\ClassLibrary2.csproj]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3471,9):
error MSB4044: The "FindUnderPath" task was not given
a value for the required parameter "Path". [D:\Nusrofe\GrokPSake2\ClassLibrary2\ClassLibrary2.csproj]
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(3471,9):
error MSB4044: The "FindUnderPath" task was not given
a value for the required parameter "Path". [D:\Nusrofe\GrokPSake2\WpfApp\WpfApp.csproj]
build2.ps1:Error executing command: msbuild $sln_file
"/p:OutDir=$build_artifacts_dir" /p:Configuration=Release /v:quiet
Thanks --
Corku
Looks like in your Compile task you have a stray underscore in your $buildartifacts_dir variable. MSBuild probably doesn't know what to do because essentially you are passing it an empty location for the OutDir.