We are using C# projects with TFS as source control and for the CI builds.
I keep finding that other developers are referencing assemblies from /Bin directories incorrectly when they should be using our /Libs folder (where we keep 3rd party assemblies)
What can I do as part of the solution build or CI build (we do also use powershell) to check and fail the build if anyone does this?
Add a custom activity to your build process template. The activity's pseudo code should look like:
Execute before the compilation phase.
Loop all new changesets containing file extensions ending with *proj.
For all *proj files, search their contents for HintPath elements containing \Bin.
If results > 0, exit build with error, listing the policy failing projects.
To complete the solution also consider enforcing a custom check-in policy for the VS clients.
Since you are using PowerShell you might as well use it for this problem; the principle is straightforward: parse all csproj files and check if the HintPath doe not contain your Bin directory. In PowerShell that is something like (I've only just begun learning PS so there might be shorter ways):
# define path to bindir (or part of it) and main source dir
$binDir = path\to\Bin
$sourceDir = path\to\sourcefiles
# fix dots and slashes (or anything that cannot be used in regex
$binDirReg = $binDir -replace '\\', '\\' -replace '\.', '\.'
# parse
$res = Get-ChildItem -Recurse -Include *.csproj -Path $sourceDir |
Select-String "HintPath>.*$binDirReg.*<"
if( $res )
{
"ERROR somebody used Bin dir instead of Lib"
}
Related
I am using AWS Pipeline to deploy a .NET Framework application but I am stuck while trying to create the zip file for Code Deploy.
The source and build steps are passing but when trying to create the package for Code Deploy, something odd is happening.
Here is my folder structure:
Project
-Source
-Base
-CoreAPI
-Api
- bin
- scripts
- appspec.yml
- ...
-Database
- ...
-Nuget
- ...
After the build, I want to create a zip file with all files and folders within "Project\Source\Base\CoreAPI\Api"
Here is a part of the buildspec.yml which relates to artifacts:
artifacts:
files:
- '**/*'
name: "api-build-artifact"
base-directory: .\Source\Api\CoreAPI\Api
As a result code build creates a zip file with all the files and folders within "Source", basically it discards the "base-directory".
I tried some variations like these:
artifacts:
files:
- '.\Source\Base\CoreAPI\Api\**\*'
But then I get a zip with the folder structure Source\Base\CoreAPI\Api + all files and folders and obviously, Code Deploy fails because the "appspec.yml" file is not in the root folder.
Any idea what might be wrong here?
Your indentation under artifacts is wrong. Try this:
artifacts:
files:
- '**/*'
name: "api-build-artifact"
base-directory: .\Source\Api\CoreAPI\Api
discard-paths: yes
Ref:
https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html
In VS 2015, we used to be able to specify a local path in global.json like so:
{
“projects”: [ “src”, “test”, “C:\\path\\to\\other\\projects” ]
}
It would then add all the projects from that path to the current solution, allowing us to easily reference them from existing projects.
Now that VS 2017 has changed its' model to using csproj, and getting rid of project.json and global.json in the process, does anybody know of a way to this?
The best I've gotten is to manually include the other projects, one by one, into the solution. Then, in all the existing projects that need to reference it, I would have to edit their csproj to include them. This is really cumbersome compared to the previous way of simply including a filepath in one location.
Thanks for any help with this.
Alright guys, it's May and we still don't have an official solution from Microsoft. I got something working using Powershell and the new .NET core CLI. There's already commands built into dotnet.exe to add/remove solutions from a project, so here's what I came up with.
Includes.json
{
"Includes": [
"C:\\projects\\SomeProjectA\\src",
"C:\\git\\SomeProjectB\\src"
]
}
Add-Includes.ps1
echo "Beginning import of projects in Includes.json"
$JsonIncludes = (Get-Content -Raw -Path "Includes.json") | ConvertFrom-Json
$IncludePaths = $JsonIncludes.Includes;
foreach ($IncludePath in $IncludePaths) {
$ProjectFiles = Get-ChildItem ($IncludePath + "\*") `
-Include *.csproj `
-Recurse `
| % {$_.FullName }
foreach ($ProjectFile in $ProjectFiles) {
dotnet sln add $ProjectFile
}
}
Remove-Includes.ps1
echo "Beginning removal of projects in Includes.json"
$JsonIncludes = (Get-Content -Raw -Path "Includes.json") | ConvertFrom-Json
$IncludePaths = $JsonIncludes.Includes;
foreach ($IncludePath in $IncludePaths) {
$ProjectFiles = Get-ChildItem ($IncludePath + "\*") `
-Include *.csproj `
-Recurse `
| % {$_.FullName }
foreach ($ProjectFile in $ProjectFiles) {
dotnet sln remove $ProjectFile
}
}
It's a couple extra steps compared to using the old Global.json file, but it does what we need. To make it really convenient, add a solution folder and include the Includes.json so you can easily modify it from within Visual Studio.
Some notes:
The Add/Remove scripts are almost exactly the same, the only difference is the dotnet sln add/remove command. This can probably be cleaned up into one interactive script.
You could also change things so that instead of having a separate add/remove script, you simply read the Includes.json and compare it to what projects are currently in the solution by parsing the .sln file.
Just food for thought. Here's the repo if you want to clone/download: https://github.com/rushfive/VS2017-Includes
I'm in the process of writing a Powershell script that retrieves custom metadata from all the exe/dll projects in a particular C# solution.
My efforts have centered around using the Microsoft.MSBuild assembly (Construction and Evaluation namespaces) to parse the solution file and iterate over the projects to retrieve the metadata. This all works well.
The next desire is to only concentrate on those projects that are part of the solution Active Build/Platform configuration - those with the Build bit checked in Visual Studio Configuration manager.
While I am able to collect the list of project build configuration properties, I am struggling to find the how the solution file stores the Active Solution Build and Active Platform configuration selected values.
In addition, I have also reviewed the source code for the MSBuild library and it appears to use an expression match between the project and solution configurations using the string for example: .Debug - Build|Any CPU.ActiveCfg = Debug|Any CPU.
Can anyone shed any light as to how I should determine the Active Build/Platform configuration for the solution or projects?
Feedback from #stign got my mind moving in the right direction. A bit verbose, but it does what I need
script pseudo-code is as follows:
Add-Type -Path "C:\Program Files (x86)\Reference Assemblies\Microsoft\MSBuild\v14.0\Microsoft.Build.dll"
$slnFile = [Microsoft.Build.Construction.SolutionFile]::Parse("<path_to_solution>")
$slnFile.ProjectsInOrder | Where-Object { $_.ProjectType -eq "KnownToBeMSBuildFormat" } | % {
$outValue = $null
$found = $_.ProjectConfigurations.TryGetValue("Debug|Any CPU", [ref]$outValue)
if($found)
{
if($outValue.IncludeInBuild) # This bit is set by the MS code when parsing the project configurations with ...Build.0
{
#Do stuff here
}
}
} #End collection iterate
We are using TFS 2013 to manage our source code for a Windows Store app. Unfortunately, it looks like we have to change the version number manually. This is tedious and easy to forget. If we use the wrong build twice, we have to take extra steps to deploy. Is there a way to set version numbers when the Build server builds?
You need to execute a PowerShell pre-build on the build server to update the build number. There is an example script as part of the TFS Community Build Tools.
https://curah.microsoft.com/8047/run-scripts-in-your-team-foundation-build-process
Look for ApplyVersionToAssemblies.ps1. You will have to customise to update any manafest files you need...
I found the solution. This will modify the version number in the appmanifest. I'm not sure of the source, if anyone would post the source, I will add this to this comment.
This is a powershell script that I added to the project source control and set it to run pre-build. This worked like a charm for each build for me. This is exactly what I wanted it to do.
# get the build number, we assume the format is Myproject.Main.CI_1.0.0.18290
# where the version is set using the TFSVersion custom build activity (see other posts)
$buildnum = $env:TF_BUILD_BUILDNUMBER.Split('_')[1]
# get the manifest file paths
$files = Get-ChildItem -Path $env:TF_BUILD_BUILDDIRECTORY -Filter "Package.appxmanifest" -Recurse
foreach ($filepath in $files)
{
Write-Host "Updating the Store App Package '$filepath' to version ' $buildnum '"
# update the identity value
$XMLfile=NEW-OBJECT XML
$XMLfile.Load($filepath.Fullname)
$XMLFile.Package.Identity.Version=$buildnum
# set the file as read write
Set-ItemProperty $filepath.Fullname -name IsReadOnly -value $false
$XMLFile.save($filepath.Fullname)
}
I have a console application that is available via nuget or on it's own. It gets installed into the tools directory for the NuGet package. The application requires 3 pieces of 'configuration' information.
a database connection string
a folder path
one more configuration option (string)
Currently, I store these configuration values in a text file right next to the exe in a file called settings.js, serialized as json.
When the application first runs, if the file is not present, it creates one with default values.
I keep the settings.js file in this location so the file will get checked into source control.
My question is about maintaining the settings file across versions.
If you Update-Package via nuget, everything works great, except the new version doesn't have any settings i had configured, because there is a new folder created for the new version.
I have written a powershell script to run in init.ps1 to pull the settings from the last version of the package, and seems to work. However this feels kinda dirty and I am wondering if there is a better way to solve this problem when using nuget to deliver my application.
param($installPath, $toolsPath, $package)
Set-Alias hump (Join-Path $toolsPath hump.exe)
$sorted_list = new-object system.collections.SortedList
$parent_path = Join-Path $installPath ".."
foreach($f in Get-ChildItem $parent_path -Filter Humpback* | Foreach {$_.FullName}){
$sorted_list.Add($f,$f)
}
if($sorted_list.Count -gt 1){
$old_path = $sorted_list.Values[$sorted_list.Count - 2]
$new_path = Join-Path $installPath "tools"
$current_settings = Join-Path $new_path "settings.js"
$has_current_settings = Test-Path $current_settings
if($has_current_settings -eq $false){
$old_settings = Join-Path $old_path "tools\settings.js"
Copy-Item $old_settings $new_path
}
}
Also, init.ps1 doesn't appear to run when installing a package using the command line tool (nuget.exe). Is this expected behavior?
Can you access System.Environment.GetFolderPath? I'd just create a folder under ApplicationData special folder, and store the settings there.