Why Msbuild task failed to deploy database, but Exec work fine - deployment

I'm trying to deploy database project ( dbproj format, not new SSDT sqlproj ) inside automated build server processing. I found the following:
When I'm calling deploy with Exec task in my Msbuild script - everything working fine:
<Exec Command="$(MSBuildPath)\MSBuild.exe $(SourceFilesPath)\$(DeployDatabaseProjectName)\$(DeployDatabaseProjectName).dbproj
/t:Deploy
/p:OutputPath=$(BaseOutput)\$(DeployDatabaseProjectName)\
/p:TargetDatabase=$(DeployDatabaseName)
/p:TargetConnectionString=$(DeployDatabaseConnectionString)" />
But when I try to repeat this with Msbuild task - it behaves differently:
<MSBuild Projects="$(SourceFilesPath)\$(DeployDatabaseProjectName)\$(DeployDatabaseProjectName).dbproj"
Targets="Deploy"
Properties="Configuration=$(BuildConfiguration);
TargetDatabase=$(DeployDatabaseName);
TargetConnectionString="$(DeployDatabaseConnectionString)";
OutputPath=$(BaseOutput)\$(DeployDatabaseProjectName)\;
" />
Msbuild task broke on semicolons in DeployDatabaseConnectionString:
<DeployDatabaseConnectionString>Data Source=$(DeployDatabaseServer);Integrated Security=True;Pooling=False</DeployDatabaseConnectionString>
It will report something like this:
The name "Integrated Security" contains an invalid character " ".
But if I replace semicolons with percent encoding value - %3B - it will broke inside SqlDeployTask:
error MSB4018: The "SqlDeployTask" task failed unexpectedly.
What is the proper way to pass TargetConnectionString to Deploy target of SqlProject ?
PS: I Could live with exec task fine, but make a call to msbuild.exe inside msbuild script just hurts my inner perfectionist man.

I found the proper way - new Msbuild allow to define AdditionalProperties metadata on item. So with this feature everything work fine and have no problems with escaping\encoding
<ItemGroup>
<DbProjectToBuild Include="$(SourceFilesPath)\$(DeployDatabaseProjectName)\$(DeployDatabaseProjectName).dbproj">
<AdditionalProperties>Configuration=$(BuildConfiguration)</AdditionalProperties>
<AdditionalProperties>OutputPath=$(BaseOutput)\$(DeployDatabaseProjectName)\</AdditionalProperties>
<AdditionalProperties>TargetDatabase=$(DeployDatabaseName)</AdditionalProperties>
<AdditionalProperties>TargetConnectionString="Data Source=$(DeployDatabaseServer);Integrated Security=True;Pooling=False"</AdditionalProperties>
</DbProjectToBuild>
</ItemGroup>
<MSBuild Projects="%(DbProjectToBuild.Identity)" Targets="Build;Deploy" />

Related

How do I have a meaningful error message from powershell script running in TeamCity build?

I have a TeamCity build and one of the steps is an MSBuild invokation of a .proj file:
Runner type: MSBuild
Build file path: TestProject.proj
Targets: Test
inside the .proj I have targets:
<Target Name="DeployTestService">
<Message Text="Deploying test service" />
<Exec Command="powershell -Command "& { myUsefulPsScript.ps }"" />
</Target>
<Target Name="Test">
// other stuff, then
<CallTarget Targets="DeployTestService" />
</Target>
and it works good most of the time. Yet if the powershell script fails (an unhandled exception is thrown and I can see its text in the full log) and exits with non-zero code I see the following in Teamcity build results:
Tests passed: (some number); exit code 1
and the build tree just says:
Build failure condition (1)
Process exited with code 1
[Time]Process exited with code 1
and until I get the full log I don't really know which step failed and how exactly. It's not about fixing the initial problem it's about locating the faulty step faster.
Is there a way to make Teamcity say something like "runner in step N failed with this error message [message which I see in the full log]"?
You need to use the Teamcity output format. For example, every time a new test starts, print:
##teamcity[testStarted name='foo|'s test']
This way Teamcity will know what was the last step or the last test before the app crashed and it will be able to format the output accordingly.

Deploy Database Project with MSBuild with CommandLine Parameters fails

I am failing to deploy Database project using command line parameters with msbuild. I would like to:
use publish xml profile
define connection string as command line parameter
but this seems to be impossible for no reason. There is for sure some simple catch I am missing. I tried following variants:
msbuild /t:SqlDeploy /p:SqlPublishProfilePath="test.publish.xml" /p:TargetConnectionString="Data Source=..." Database.sqlproj
deploys database
values in test.pubhlish.xml are ignored
msbuild /t:Build /t:Deploy /p:SqlPublishProfilePath="test.publish.xml" /p:TargetConnectionString="Data Source=..." Database.sqlproj
deploys database
values in test.pubhlish.xml are ignored
msbuild /t:Build /t:Publish /p:SqlPublishProfilePath="test.publish.xml" /p:TargetConnectionString="Data Source=..." Database.sqlproj
fails with error: Deploy Error : The connection string is not valid
values in test.publish.xml file are used (when test.publish.xml contains TargetConnectionString, database is deployed correctly)
In all 3 cases I am using exactly the same ConnectionString, so it is not a typo or error in the connection string. I need to use the 3rd variant, but this just fails.. What am I missing?

Is there an MSBuild macro for build date/time?

Suppose I have a build script with a Target section like the following:
<Target Name="AssemblyVersionMAIN" Inputs="#(AssemblyVersionFiles)" Outputs="UpdatedAssemblyVersionFiles">
<Attrib Files="%(AssemblyVersionFiles.FullPath)" Normal="true"/>
<AssemblyInfo
CodeLanguage="CS"
OutputFile="%(AssemblyVersionFiles.FullPath)"
AssemblyProduct="$(ProductName)"
AssemblyTitle="$(ProductName)"
AssemblyCompany="$(CompanyName)"
AssemblyCopyright="© $(CompanyName) 2014" <!-- THIS LINE -->
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyInformationalVersion="$(Major).$(Minor).$(Build).$(Revision)">
<Output TaskParameter="OutputFile" ItemName="UpdatedAssemblyVersionFiles"/>
</AssemblyInfo>
</Target>
At the moment, the year is static and has to be changed manually. Is there a simple way of replacing "2014" with something like $(Year)? I've checked the MSBuild reference but nothing jumps out at me.
Since you are using MsBuild 4 you could also use a property function for this, like e.g.:
<PropertyGroup>
<CurrentDate>$([System.DateTime]::Now.ToString(yyyy.MM.dd))</CurrentDate>
</PropertyGroup>
Just format the recieved date as you need it. There are also plenty of other functions available, see also http://msdn.microsoft.com/en-us/library/dd633440(v=vs.100).aspx.
Since I'm using MSBuild Community Tasks and MSBuild 4, I can substitute the following:
AssemblyCopyright="© $(CompanyName) $([System.DateTime]::Now.ToString(`yyyy`))"
which seems to work.

How to write AssemblyVersion to file using MSBuild?

FinalEdit: Despite relative directories not working in the first post, it worked if I simply removed the $(MsBuildThisFileDirectory) from the Exec line.
Edit2: I added the new targets to the DefaultTargets. Which now runs them by default. However, timing was now off with the postbuild command. I added <Exec Command="call $(MsBuildThisFileDirectory)documentation\tools\GenerateDocumentation.bat" IgnoreExitCode="false" /> to the target, but it gives an error that C:\Users\my is not a valid batch file because of the space which is actually C:\Users\my program\documentation\tools\GenerateDocumentation.bat. Putting quotes around the path gives me error MSB4025 that Name cannot begin with $.
Edit: I have tried stijn's code and it works when I explicitly run it from the command line using /t:RetrieveIdentities, but for some reason it doesn't seem to run otherwise.
I have been using Doxygen to generate documentation for my source code, however, I would like to be able to do it automatically. I wrote a simple .bat script to run Doxygen with my desired config file and compile the output into a .chm help file, but I have been unable to change the revision number automatically in Doxygen.
I was attempting to simply update the config file by adding a new line to the config file with the new revision number using MSBuild, but I have been unable to get anything to print or even create a new file when none is present.
The code I have so far I have gotten from other similar questions, but I cannot seem to get it to work.
<ItemGroup>
<MyTextFile Include="\documentation\DoxygenConfigFile.doxyconfig"/>
<MyItems Include="PROJECT_NUMBER = %(MyAssemblyIdentitiesAssemblyInfo.Version)"/>
</ItemGroup>
<Target Name="RetrieveIdentities">
<GetAssemblyIdentity AssemblyFiles="bin\foo.exe">
<Output TaskParameter="Assemblies" ItemName="MyAssemblyIdentities"/>
</GetAssemblyIdentity>
<WriteLinesToFile File="#(MyTextFile)" Lines="#(MyItems)" Overwrite="false" Encoding="UTF8" />
</Target>
Encoding is wrong, it should be UTF-8
When working with items/properties, the % and # and $ must come right before the (, no spacing in between: %(MyAssemblyIdentitiesAssemblyInfo.Version)
MyAssemblyIdentitiesAssemblyInfo does not exist, you probably meant MyAssemblyIdentities
Look up how msbuild evaluates properties and items. Basically what it will do in your script is evaluate MyItems, but at that time MyAssemblyIdentities does not yet exist so is empty, and only afterwards the GetAssemblyIdentity gets executed. Fix this by enforcing correct evaluation order: put your items inside the target and make it depend on another target that creates MyAssemblyIdentities before evaluating your items.
To summarize:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GetAssemblyIdentities">
<GetAssemblyIdentity AssemblyFiles="bin\foo.exe">
<Output TaskParameter="Assemblies" ItemName="MyAssemblyIdentities"/>
</GetAssemblyIdentity>
</Target>
<Target Name="RetrieveIdentities" DependsOnTargets="GetAssemblyIdentities">
<ItemGroup>
<MyTextFile Include="\documentation\DoxygenConfigFile.doxyconfig"/>
<MyItems Include="PROJECT_NUMBER = %(MyAssemblyIdentities.Version)"/>
</ItemGroup>
<WriteLinesToFile File="#(MyTextFile)" Lines="#(MyItems)"
Overwrite="false" Encoding="UTF-8" />
</Target>
</Project>
Note this will only work if you invoke msbuild in the directory where the script is, else the paths (documentation/foo) will be wrong. That could be fixed by using eg $(MsBuildThisFileDirectory)\bin\foo.exe)

Deploy to remote server using scp in NANT script

I am trying to copy a file to a remote server using scp task in Nant.Contrib .
I have used the following code to do that:
<target name= "QADeploy"description="gthtyb" >
<loadtasks assembly="C:\nantcontrib-0.85\bin\NAnt.Contrib.Tasks.dll" />
<echo message="htyh"/>
<scp file="D:\SourceTest\redist.txt" server="\\10.4.30.19" user="xxx:uuuu">
</scp>
</target>
But I am getting an error: scp failed to start. The system cannot find the file specified.
The code is as follows:
Then I have downloaded pscp.exe and modified the code as below:
<target name= "QADeploy"
description="gthtyb" >
<loadtasks assembly="C:\nantcontrib-0.85\bin\NAnt.Contrib.Tasks.dll" />
<echo message="htyh"/>
<scp file="D:\SourceTest\redist.txt" server="\\10.4.30.19" user="xxx:uuuu" program="C:\pscp\pscp.exe">
</scp>
Now I am getting the following error:
[scp] ssh_init:host does not exist
External Program Failed:C:\pscp\pscp.exe
can u please help whats the best way to copy a file to a remote server using Nant. I am using this code to deploy files to a remote server.
Thanks
You don't have to put two backslashes behind the IP of your server.
<scp file="D:\SourceTest\redist.txt" server="10.4.30.19" user="xxx:uuuu" program="C:\pscp\pscp.exe">
Also note that without the "path" parameter, the default destination folder is "~".
Update: it is the username that is crashing the pscp.exe program. Remove the ":" from your username or try with a different one.
it seems like there is some weirdness on how pscp parses paths in windows. The following should fix ssh_init:host does not exist problem:
-upload
pscp some.file user#[remote-host-or-ip]:/some/path/
-download
pscp user#[remote-host-or-ip]:/some/path/some.file some.file