MBBuild fail if string exists in any file in the solution - visual-studio-2013

Is there a way to have MSBuild fail if any of the files included in a solution have a certain string in them?
In this specific case, we are using DbUp with Ocotpus Deploy to deploy SQL scripts in the form of .sql files. With our deployment process we specifically target each database directly. Occasionally, scripts will get created that have in them USE [devDatabase]. I'd like to fail the build if this string exists in any of the .sql files that are part of the build.
Is that possible?

Here's an MsBuild solution, add this to the project file:
<Target Name="CheckSqlFiles" BeforeTargets="Build">
<ItemGroup>
<SqlFiles Include="$(MSBuildThisFileDirectory)**\*.sql" />
</ItemGroup>
<MSBuild Projects="$(MSBuildThisFile)" Targets="ErrorIfDevDataBaseUsed" Properties="SqlFileToCheck=%(SqlFiles.Identity)" />
</Target>
<Target Name="ErrorIfDevDataBaseUsed">
<ReadLinesFromFile File="$(SqlFileToCheck)">
<Output TaskParameter="Lines" ItemName="Sql" />
</ReadLinesFromFile>
<Error Text="Found devDatabase in $(SqlFileToCheck)"
Condition="$([System.String]::Copy('%(Sql.Identity)').Contains('USE [devDatabase]'))" />
</Target>
It will run before the Build target. There are many other targets so if this one is too late or too early, run msbuild with the /v:d flag and figure out before (or after, using AfterTargets=...) which target you want it to run. If it's slow, or you prefer PowerShell, you can off course apply the same principle and invoke Powershell instead in the CheckSqlFiles target using e.g. <Exec command="PowerShell checksql.ps1 $(MsBuildThisFileDirectory)"/> where checksql.ps1 is like AlexM's answer but taling the directory to run in as an argument.

A possible solution could be to have an additional Octopus step to make this check and fail a deployment if there are files alike.
Would just be a PowerShell script step :
$Files = #(Get-ChildItem -Path C:\Projects\SQL -Filter *.sql -Recurse | Select-String -Pattern "use " | group path | select name)
if ($Files.length -eq 0) {
write-host "no SQL files with 'USE'"
} else {
foreach ($file in $Files) {
write-host $file.Name
}
Exit 1
}
Given your next step has Conditions as below it should fail:

You could write a unit test that reads all the embedded SQL files and checks for that statement.
Also have a look at https://github.com/andrewabest/Conventional, it may be a base for starting similar tests.

Definitely possible to do this check with an Octopus step, as #AlexM demonstrates, but it seems like Octopus is not the best place for this kind of checks.
I mean, is there any situation where you would like to deploy the script as is?
If the answer is no, then this check belongs in your build server (TeamCity, Jenkins, etc.) and the build should fail if a script contains the string you're looking for, and you shouldn't send the artifacts to Octopus at all - which means it can't be deployed anyway.
Lots of ways to do that on the build server... With Powershell, MSBuild (as #stijn points out), etc.

Related

Using a ** wildcard in an F# project causes Visual Studio to refuse to load the project

I've got an F# project with several additional files stored in many nested folders, creating a complex folder structure. The compiler doesn't need to know about them: all I need to do is to have them copied into the output directory at the end of the build process.
I tried adding my files using the ** wildcard:
<ItemGroup>
    <FilesToCopyToOutput Include="additionalData\**\*.*"/>
</ItemGroup>
It works fine when running MSBuild manually from PowerShell. However, when I attempt to open my project in Visual Studio (v. 2015), I get the following error message:
Cannot open F# project . This project uses
wildcards in the item specification. Wildcards in F# projects are not
currently supported.
Similarly, a C# project with the wildcards works just fine. I guess this has something to do with the fact that the order of files in an F# project matters and using the wildcards causes a problem if a user wants to reorder the files.
However, I'm wondering if there's anything that can be done in my particular case: I don't care if the files are available in VS or not: I just want them to be copied to the output folder.
Using a ** wildcard in an F# project causes Visual Studio to refuse to load the project
Indeed, Visual Studio 2017 is a good choose. I have test the ** wildcard in the Visual Studio 2015 and 2017, it works fine in the Visual Studio 2017 but not in the Visual Studio 2015, got the same error as you.
Since you could not change the tech stack to use Visual Studio 2017, I would like provide a workaround for this issue, you can check if it works for you.
Since you do not care if the files are available in VS or not, you just want them to be copied to the output folder, you can use a power shell script to copy the additionalData folder to the output folder, like:
Copy-Item -Path "ThePathForAdditionalData\additionalData" -Destination "ThePathForProject\bin\Debug" -recurse -Force
Then execute this power shell script with MSBuild task after build:
<Target Name="CopyMyFiles" AfterTargets="Build">
<Message Text="Copying files..."/>
<Exec Command="C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -executionpolicy Unrestricted -command "& { .\YourCopyFiles.ps1 } "" ></Exec>
</Target>
Hope this helps.
I assume FilesToCopyToOutput is the name you gave to an item collection, and the question is how to stop the old F# compiler from thinking it should handle it?
I suspect what you want to do though is to treat an entire folder as content items and copy them to the output folder :
<Content Include="additionalData\**\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
Copy Task
If you want to copy items with an MSBuild task you can use Copy. You should be able to write :
<ItemGroup>
<MyAdditionalData Include="additionalData\**\*.*" />
</ItemGroup>
<Target Name="CopyFiles">
<Copy
SourceFiles="#(MyAdditionalData)"
DestinationFolder="$(OutputPath)"
/>
</Target>
The Include attribute allows selecting multiple files that can be used as arguments to a task. Exclude is used to exclude files. You can specify a Condition attribute too, eg to run the task only for Release or Debug configuration. $(OutputPath) is one of the MSBuild properties that can be used in paths.
How to: Exclude Files from the Build shows how you can combine all those attributes to process specific items in a directory, eg:
<JPGFile
Include="Images\**\*.jpg"
Exclude = "Images\**\Version2\*.jpg"/>
This will include all files in the Images directory except those in Version2
Using Post-build events
Most people though didn't use MSBuild until the latest simplified format.
The most common way to copy files after build is to use a post-build event from the project's property pages, eg :
xcopy $(ProjectDir)additionalData\*.* $(TargetDir) /s /e
$(ProjectDir) and $(TargetDir) are replacement macros that point to the projects' folder and output folder. The list of all macros is available in Pre-build Event/Post-build Event Command Line Dialog Box.

How to get an assembly's version in Team Build 2010 Workflow

Is there any way to get an assembly's version number like we previously used to do with Msbuild with Team Build 2010 Workflow? Here is a simple example of how we used to get an assemby's version with %(Info.Version).
<Target Name="CheckFileVersion" DependsOnTargets="AfterDrop">
<ItemGroup>
<MyAssemblies Include='$(DropLocation)\$(BuildNumber)\Release\MyApp.exe' />
</ItemGroup>
<GetAssemblyIdentity AssemblyFiles="#(MyAssemblies)">
<Output TaskParameter="Assemblies" ItemName="Info"/>
</GetAssemblyIdentity>
</Target>
I've found some methods to create custom Activities with many lines of code, but I think there must be a simpler way to do it.
GoRoS had the right idea, but it only works correctly the first time.
In order to avoid this problem you have to run this code, or similar code in a different AppDomain from the Build Agent, Powershell and an InvokeProcessActivity can solve our problem.
GetAssemblyVersionNumber.ps1:
$error.clear()
if ($args.length -ne 1)
{
Write-Error "Usage: GetAssemblyVersionNumber.ps1 <Assembly>"
exit 1
}
# Now load the assembly
$assembly = [System.Reflection.Assembly]::Loadfile($args[0])
# Get name, version and display the results
$name = $assembly.GetName()
Write-Host $name.version
And the following gets added to your build process template
InvokeProcessActivity
Name: Call Powershell Version Script
Filename: "powershell.exe"
Arguments: "-NonInteractive -NoProfile -Command " _
+ "c:\Builds\GetAssemblyVersionNumber.ps1" _
+ " '" + BuildDirectory + "\Binaries\Foobar.dll'"
Handle Standard Output (stdOutput)
AssignActivity
To: VersionInfo
Value: stdOutput
You can call out to MSBuild scripts from TFS Build Workflows using the MSBuild Workflow Activity and continue doing this the same way as always. Or you could create a custom Workflow Activity to do a similar thing. The best way will probably depend on what you intend to do with those version #'s.
You can read about how to get started creating a custom activity here: http://blogs.msdn.com/b/jimlamb/archive/2010/02/12/how-to-create-a-custom-workflow-activity-for-tfs-build-2010.aspx
Also there is the TFS Community Build Extensions which has a bunch of ready-made custom activities you can use. One of those is called AssemblyInfo which sounds promising: http://tfsbuildextensions.codeplex.com/documentation
Finally I found a quick easy way to perform the operation I wanted. Using a simple Assign activity I've written the following code:
To: VersionInfo
Value: System.Reflection.Assembly.UnsafeLoadFrom(BuildDetail.DropLocation + _
"\MyApplication.exe").GetName().Version.ToString()
After that, I can use VersionInfo variable to whatever I want. I recognize I'd prefer to avoid using reflection, but that's the easy and short way I was finding which doesn't use third party libraries or custom activities.

Run web.config transformation from command-line

Good day!
I want to have ability to build ASP.NET MVC 2 project using VS2010 Publish dialog and from command-line.
For command-line I get the following to work:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe .\SolutionFolder\MyProject.csproj /p:Configuration=Release;DeployOnBuild=True;PackageAsSingleFile=False;outdir=c:\_OutputFolder\
The only problem I have that Web.config transformation are not applied (but added to WebDeploy package). I don't use WebDeploy. Is there any way to apply Web.config transformations?
Thanks!
You can also try using the XDT Transformation Tool:
http://ctt.codeplex.com was moved to Github
https://github.com/greenfinch/ctt
I'm using this instead of messing with obscure msbuild targets.
Here is another approach, which uses msbuild to transform Web.config file:
http://sedodream.com/2010/04/26/ConfigTransformationsOutsideOfWebAppBuilds.aspx
In my tests the results were better. Basically, you create a project file to perform only a TransformXML task:
<Project ToolsVersion="4.0" DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<Target Name="Demo">
<TransformXml Source="app.config"
Transform="Transform.xml"
Destination="app.prod.config"/>
</Target>
</Project>
Save the project file and then apply the transformation, running the following command:
msbuild trans.proj /t:Demo
Where trans.proj is the name of the project file and Demo is the name of task target.
I think it's worth to mention that you can also use with PowerShell the DLL that Visual Studio is using: Microsoft.Web.XmlTransform.dll
PowerShell script, see: Web.Config transforms outside of Microsoft MSBuild?
To load the DLL instead of copying around, I do like this (so you see where to find this DLL, at least in my scenario at work we had to look-up these locations):
if (Test-Path "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll") {
Add-Type -LiteralPath "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.XmlTransform.dll"
} elseif (Test-Path "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.XmlTransform.dll") {
Add-Type -LiteralPath "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.XmlTransform.dll"
} else {
throw [System.IO.FileNotFoundException] "Microsoft.Web.XmlTransform.dll not found."
}

How do I tell MSTEST to run all test projects in a Solution?

I need to know how to tell MSTEST to run all test projects in a solution file. This needs to be done from the command line. Right now I have to pass it a specific project file, I'm trying to get it to run from a SOLUTION file.
I'm hoping this is possible, because in Visual Studio, hitting Ctrl+R, A, runs ALL tests in the currently opened solution.
The way I've interpretted the help files, you have to pass in each DLL specifically.
I want to run this from the command line from my CruiseControl.NET server, so I can write other utilities to make this happen. If there is a wierd way of getting this to happen through some OTHER method, let me know.
How do I tell MSTEST to run all test projects for a solution?
<exec>
<!--MSTEST seems to want me to specify the projects to test -->
<!--I should be able to tell it a SOLUTION to test!-->
<executable>mstest.exe</executable>
<baseDirectory>C:\projects\mysolution\</baseDirectory>
<buildArgs>/testcontainer:testproject1\bin\release\TestProject1.dll
/runconfig:localtestrun.Testrunconfig
/resultsfile:C:\Results\testproject1.results.trx</buildArgs>
<buildTimeoutSeconds>600</buildTimeoutSeconds>
</exec>
To elaborate on VladV's answer and make things a bit more concrete, following the suggested naming convention running your tests can be easily be automated with MSBuild. The following snippet from the msbuild file of my current project does exactly what you asked.
<Target Name="GetTestAssemblies">
<CreateItem
Include="$(WorkingDir)\unittest\**\bin\$(Configuration)\**\*Test*.dll"
AdditionalMetadata="TestContainerPrefix=/testcontainer:">
<Output
TaskParameter="Include"
ItemName="TestAssemblies"/>
</CreateItem>
</Target>
<!-- Unit Test -->
<Target Name="Test" DependsOnTargets="GetTestAssemblies">
<Message Text="Normal Test"/>
<Exec
WorkingDirectory="$(WorkingDir)\unittest"
Command="MsTest.exe #(TestAssemblies->'%(TestContainerPrefix)%(FullPath)',' ') /noisolation /resultsfile:$(MSTestResultsFile)"/>
<Message Text="Normal Test Done"/>
</Target>
Furthermore integrating MsBuild with CruiseControl is a piece of cake.
Edit
Here's how you can 'call' msbuild from your ccnet.config.
First if you do not already use MSBuild for your build automation add the following xml around the snippet presented earlier:
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
..... <insert snippet here> .....
</Project>
Save this in e.g. RunTests.proj next to your solution in your source tree. Now you can modify the bit of ccnet.config above to the following:
<msbuild>
<executable>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe</executable>
<workingDirectory>C:\projects\mysolution\</workingDirectory>
<baseDirectory>C:\projects\mysolution\</baseDirectory>
<projectFile>RunTests.proj</projectFile>
<targets>Test</targets>
<timeout>600</timeout>
<logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
This is an old thread, but I have been struggling with the same issue and I realized that you can really just run MSTest on every dll in the whole solution and it doesn't really cause any problems. MSTest is looking for methods in the assemblies marked with the [TestMethod] attribute, and assemblies that aren't "test" assemblies just won't have any methods decorated with that attribute. So you get a "No tests to execute." message back and no harm done.
So for example in NAnt you can do this:
<target name="default">
<foreach item="File" property="filename">
<in>
<items>
<include name="**\bin\Release\*.dll" />
</items>
</in>
<do>
<echo message="${filename}" />
<exec program="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe">
<arg value="/testcontainer: ${filename}" />
<arg value="/nologo" />
</exec>
</do>
</foreach>
</target>
and it will run all the test methods in every dll in every bin\Release folder in the solution. Those which are not test dlls will return a "No tests to execute." and those that have tests will have the tests run. The only part I haven't figured out yet is that (in NAnt) execution stops the first time a command returns a non-zero value. So if any unit tests fail it doesn't keep going to execute any tests in subsequent assemblies. That is not great, but if all the tests pass, then they will all run.
I just resolve this problem recently. Here is my proposal: Use testmetadata + testlist option of mstest
First you should create a testlist in testmetadata file(vsmdi)
the commandline should be mstest /testmetadata:....vsmdi /testlist:<name>
Then use ccnet config to run mstest
i know this thread is quite Old, but its still high on Google so i thought i might help one or two.
Anyway, since there is no satisfactory solution for this.
I've written an msbuild task for this.
details can be found here:
http://imistaken.blogspot.com/2010/08/running-all-tests-in-solution.html
You could enforce some convention on the naming and location of test projects, then you could run MSTest on, say, all *Test.dll below the location of your solution.
As far as I know, there is no way to tell a test project from a 'normal' DLL project based soleley on a solution file. So, an alternative could be to analyze the project files and/or .vsmdi files to find the test projects, but that could be rather tricky.
I don't know directly but this is where VSMDI [fx:spits in a corner] can help. In your solution add all the tests to the VSMDI. And then pass the VSMDI to mstest using /testmetadata.
However I would suggest that you follow the conventions above. And use a naming convention and dump that out from the SLN file using say a for loop in the command script
I would just write a target that calls it the way you want, then whip up a batch file that calls the target that contains all the DLL's to be tested.
Unless you're adding test projects all the time, you'll very rarely need to modify it.
Why not just have msbuild output all the test assemblies to a folder.
Try setting OutputPath,OutputDir,OutDir properties in msbuild to accomplish this.
then have mstest execute against all assemblies in that folder.

How to copy a build to test server?

Hope someone can assist me with this. Have TeamCity up and running and doing builds on various projects. I'd like to be able to copy/deploy a successful TeamCity ran build to a test server automatically.
I was thinking of using PowerShell to do this but, am open to other ideas. Can some provide me with info on how I can accomplish this.
Thanks.
I use WGet. Here are the instructions for forming the team city URL. You can do a WGet in powershell, but if you only wanted powershell for this functionality, you can just use a plain wget utility for windows.
EDIT: Here is an example from our QA deployment (names changed to protect the guilty):
"C:\Program Files (x86)\NcFTP\wget.exe" "http://teamcityserver.domain.com:8111/guestAuth/repository/download/bt6/.lastFinished/Artificat.ear"
The location of the wget isn't relevant, that is just where it happens to be. The guestAuth part of the parameter specifies the authentication type (in our case we enabled guest authorization to not have to bother with passwords - it is an internal server only anyway and protected by firewalls). The options are in the documentation I linked to.
The other interesting feature of the parameters is the bt6. That is the unique key of the build, and is different for every project. You can discover what it is by navigating the team city website to the configuration of that build - it will be there. There are also instructions for referencing the configuration by name, but we found that was too verbose to bother with.
I've been implementing this for our applications today. Using msbuild. I have found this very useful as we can add in custom steps such as modifying config files, archiving live builds and notifying people of changes.
Here is a build script you may find useful. It precompiles the application and then copies it into the deploy directory.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Deploy">
<PropertyGroup>
<WebsitePublishDirectory>Artifacts\Website</WebsitePublishDirectory>
<WebsiteDeployDirectory>\\SERVERNAME\Path\to\web\root</WebsiteDeployDirectory>
<WebsiteProject>[Project name here]</WebsiteProject>
</PropertyGroup>
<Target Name="Deploy">
<RemoveDir Directories="$(WebsitePublishDirectory)" />
<AspNetCompiler
VirtualPath="test"
PhysicalPath="$(WebsiteProject)"
TargetPath="$(WebsitePublishDirectory)"
Force="true"
Debug="false" />
<ItemGroup>
<PublishedFiles Include="$(WebsitePublishDirectory)\**" />
</ItemGroup>
<Copy SourceFiles="#(PublishedFiles)" DestinationFolder="$(WebsiteDeployDirectory)\%(RecursiveDir)" />
</Target>
</Project>
You could also install a TeamCity agent on the test server. That's actually how TeamCity was intented to be used.
I created a Post Build Script in Visual Studio like this:
c:\TeamCityBuild\pt_build.bat
exit 0
Then on the TC-server I have a .bat that looks like this:
net use r: \192.168.16.85\WebSite password /USER:domain.com\administrator
xcopy "C:\TeamCityBuild\path\WebSite*" "r:\" /R /Y /E
r: \192.168.16.85\WebSite /DELETE
if errorlevel 1 goto buildFAILED
:buildOK
echo Wehej!!!
exit 0
:buildFAILED
echo Oh NOOO!!!
exit 1
'R:' is a mapped drive to the test server.
The error handling is only needed to avoid script errors when someone builds the project on a environment without the correct folder structure.
So far everyting is working great!

Resources