MSBuild 2010 - how to publish web app to a specific location (nant)? - visual-studio-2010

I'm trying to get MSBuild 2010 to publish a web app to a specific location.
I can get it to publish the deployment package to a particular path, but the deployment package then adds its own path that changes.
For example: if I tell it to publish to C:\dev\build\Output\Debug then the actual web files end up at C:\dev\build\Output\Debug\Archive\Content\C_C\code\app\Source\ControllersViews\obj\Debug\Package\PackageTmp
And the C_C part of the path changes (not sure how it chooses this part of the path).
This means I can't just script a copy from the publish location.
I'm using this nant/msbuild command at the moment:
<target name="compile" description="Compiles">
<msbuild project="${name}.sln">
<property name="Platform" value="Any CPU"/>
<property name="Configuration" value="Debug"/>
<property name="DeployOnBuild" value="true"/>
<property name="DeployTarget" value="Package"/>
<property name="PackageLocation" value="C:\dev\build\Output\Debug\"/>
<property name="AutoParameterizationWebConfigConnectionStrings" value="false"/>
<property name="PackageAsSingleFile" value="false"/>
</msbuild>
Any ideas on how to get it to send the web files directly to a specific location?

msbuild /t:Build;PipelinePreDeployCopyAllFilesToOneFolder /p:Configuration=Release;_PackageTempDir=C:\temp\somelocation;AutoParameterizationWebConfigConnectionStrings=false MyProject.csproj
Corresponding NAnt script:
<msbuild project="MyProject.csproj" target="PipelinePreDeployCopyAllFilesToOneFolder">
<property name="Configuration" value="Release" />
<property name="_PackageTempDir" value="C:\temp\somelocation" />
<property name="AutoParameterizationWebConfigConnectionStrings" value="false" />
</msbuild>
References
Simulating "Publish to folder" Functionality in Visual Studio 2010
Team Build + Web Deployment + Web Deploy + VS 2010 = Goodness
See also Team Build: Publish locally using MSDeploy

If you are using a VS2010 web application (as opposed to a web site project), consider setting up the Package/Publish Web settings in your project and building the 'Project' target in your nant script.
Lots of juicy msdeploy goodness and background here: http://www.hanselman.com/blog/WebDeploymentMadeAwesomeIfYoureUsingXCopyYoureDoingItWrong.aspx
In my nant scripts, I run the following msbuild commands:
<if test="${property::exists('basename')}">
<exec program="${msbuild.location}" workingdir="${project::get-base-directory()}">
<arg value="/p:Configuration=${configuration}" />
<arg value="/logger:ThoughtWorks.CruiseControl.MsBuild.XmlLogger,${msbuild.logger.dll}" if="${nunit.formatter.type == 'Xml'}"/>
<arg value="/noconsolelogger" if="${nunit.formatter.type == 'Xml'}"/>
<arg value="${basename}.sln"/>
</exec>
</if>
...
<if test="${property::exists('basename')}">
<exec program="${msbuild.location}" workingdir="${project::get-base-directory()}\${basename}">
<arg value="/p:Configuration=${configuration}" />
<arg value="/t:Package" />
<arg value="/logger:ThoughtWorks.CruiseControl.MsBuild.XmlLogger,${msbuild.logger.dll}" if="${nunit.formatter.type == 'Xml'}"/>
<arg value="/noconsolelogger" if="${nunit.formatter.type == 'Xml'}"/>
<arg value="${basename}.csproj"/>
</exec>
</if>
My basename nant variable gives the name of both the VS solution file (.sln) and the project file (.csproj) for the web application. I happen to prefer the zip-file deployment as shown in my project settings:
There is one additional quirk. If you install MSDeploy version 2.0 on the target machine, the .deploy.cmd file must be edited to change the MSDeploy version number as follows:
Change
for /F "usebackq tokens=2*" %%i in (`reg query "HKLM\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\1" /v InstallPath`) do (
To
for /F "usebackq tokens=2*" %%i in (`reg query "HKLM\SOFTWARE\Microsoft\IIS Extensions\MSDeploy\2" /v InstallPath`) do (

I think you're using the wrong property. Try the OutDir property instead.
<arg value="/property:OutDir=C:\dev\build\Output\Debug\" />
Personally, I call MsBuild.exe directly instead of using the msbuild tag:
<exec program="${MSBuildPath}">
<arg line='"${ProjectFile}"' />
<arg value="/target:_CopyWebApplication" />
<arg value="/property:OutDir=${LocalDeployPath}\" />
<arg value="/property:WebProjectOutputDir=${LocalDeployPath}" />
<arg value="/property:Configuration=${SolutionConfiguration}" />
<arg value="/verbosity:normal" />
<arg value="/nologo" />
</exec>
MSBuildPath - The path to MsBuild.exe (allows you to target any framework version you want)
ProjectFile - The relative path to your project file
LocalDeployPath - The local folder where everthing will be outputed. Your copy script will use also use this as the source directory.
SolutionConfiguration - Release, Debug

Related

use post build events with Fody.Costura installed

Once I added Fody.Costura to my project, my post build event that was copying the resulting assembly into a different location started failing with access denied message. That makes sense since Costura uses MSBuild to embed the assemblies. Is there a way to force my post builds to execute after Costura is finished? Example of a post build command:
copy /Y "$(TargetPath)" "%ALLUSERSPROFILE%\Autodesk\Revit\Addins\2019\HOK-Addin.bundle\Contents"
Basically the solution to my own question is the following.
<Target Name="CopyFiles" AfterTargets="AfterBuild;NonWinFodyTarget">
<Message Text="Signing file..." Importance="high" />
<Exec Command=""C:\Program Files (x86)\Windows Kits\10\bin\10.0.17134.0\x64\signtool.exe" sign /c "Code Signing - DTM" /v "$(TargetPath)"" />
<Message Text="Copy files..." Importance="high" />
<Message Text="$(TargetPath) > $(ALLUSERSPROFILE)\Autodesk\Revit\Addins\$(Configuration)\HOK-Addin.bundle\Contents" Importance="high" />
<Message Text="$(TargetDir)$(TargetName).addin > $(ALLUSERSPROFILE)\Autodesk\Revit\Addins\$(Configuration)" Importance="high" />
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(ALLUSERSPROFILE)\Autodesk\Revit\Addins\$(Configuration)\HOK-Addin.bundle\Contents" ContinueOnError="true" />
<Copy SourceFiles="$(TargetDir)$(TargetName).addin" DestinationFolder="$(ALLUSERSPROFILE)\Autodesk\Revit\Addins\$(Configuration)" ContinueOnError="true" />
</Target>
What I did, was to replace the standatd Post Build Command that runs Command Line routines, with a MSBuild Target and a Task.Giving it flags to run after Build is finished and Fody is done merging assemblies resolves my issue.
What also helps is the fact that Tasks have flags like ContinueOnError="true" that allow the task to keep trying until the file is available (if that was the issue) as opposed to command line utilities that would just fail.
Cheers!

How-To Set and Get Build Version in TFS SQL Project

I'm using TFS in Visual Studio 2013 & SSDT to create various SQL Database scripts. i.e. I'm doing all my SQL DB development via VS not SSMS.
Want I'm now trying to achieve is to generate/retreive a version number from an external text file when the project is built/published, based on the functionality posted here:
http://www.codeproject.com/Articles/468855/Working-with-MSBuild-Part-2
So I've added the following to the MyProject.sqlproj xml file:
<PropertyGroup>
<WorkingFolder>C:\Source Control\MISTP\Main\DB\SSMS\MyProject</WorkingFolder>
</PropertyGroup>
<Target Name="GetVersion">
<Message Text="GetVersion: Reading version number from VersionInfo.txt" />
<Attrib Files="$(WorkingFolder)\VersionInfo.txt" Normal="true" />
<Version VersionFile="$(WorkingFolder)\Build\VersionInfo.txt">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<Message Text="GetVersion: $(Major).$(Minor).$(Build).$(Revision)" />
</Target>
<Target Name="SetVersion" DependsOnTargets="GetVersion">
<Message Text="SetVersionInfo: Updating Versions in all files" />
<CreateItem Include="$(WorkingFolder)\**\*.*">
<Output TaskParameter="Include" ItemName="Files"/>
</CreateItem>
<Attrib Files="#(Files)" Normal="true" />
<FileUpdate Files="#(Files)" Regex="FileVersionAttribute\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText="FileVersionAttribute("$(Major).$(Minor).$(Build).$(Revision)")" />
<FileUpdate Files="#(Files)" Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText=" FileVersion ("$(Major).$(Minor).$(Build).$(Revision)")" />
<FileUpdate Files="#(Files)" Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText="FileVersion("$(Major).$(Minor).$(Build).$(Revision)")" />
</Target>
I have a VersionInfo.txt file located in:
C:\Source Control\MISTP\Main\DB\SSMS\MyProject\Build
Which simply contains the string: 1.2.3.4
However, this doesn't seem to actually do anything when I Build and/or Publish the project within VS. What am I missing?!
I'm new to MSBuild, but the syntax appears correct - and is largely lifted from the codeproject article - and the path to the file are ok.
It feels like the xml is not being executed, but I'm assuming that it's very presence in the .sqlproj file will result in it being executed.
Thanks
The target isn't triggered during the build. Update "SetVersion" target as following:
<Target Name="SetVersion" DependsOnTargets="GetVersion" AfterTargets="PostBuildEvent">

Publishing ClickOnce application with NANT script

My WPF application is deployed with ClickOnce.
In Visual Studio I open "Project properties / Publish".
There I have:
Publish location
Publish URL
Version
Signature
The problem is, that I have to publish every version for test and production.
The difference between them are properties publish location and publish URL. Currently I have to execute the process twice, while changing the values before publishing for production.
So the result of pressing publish is a folder containing folder "ApplicationFiles", the application manifest file and a setup.exe.
Then i decided to automate this process using NANT.
I build/publish the application first for testing (here i set the .csproj file location, publish folder and application varsion)
<target name="BuildTestApplication" depends="Clean" description="Build">
<echo message="Building..." />
<exec program="${msbuildExe}" workingdir="." verbose="true">
<arg value="${projectFile}" />
<arg value="/target:Clean;Publish" />
<arg value="/p:PublishDir=${testPublishFolder}" />
<arg value="/p:ApplicationVersion=${version}" />
<arg value="/p:Publisher="${publisherName}"" />
</exec>
<echo message="Built" />
</target>
With this I found out that build does not set the publisher. Plus I need to change the provider URL, since the application is also installed via internet (different URLs for test and production). So i did:
<target name="UpdateTestApplication" depends="BuildTestApplication" description="Update">
<echo message="Updating..." />
<exec program="${mageExe}" workingdir="." verbose="true">
<arg value="-Update" />
<arg value="${testPublishFolder}/EdpClient.application" />
<arg value="-ProviderUrl" />
<arg value=""${testPublishUrl}"" />
<arg value="-Publisher" />
<arg value=""${publisherName}"" />
</exec>
<echo message="Updated" />
</target>
With this I have updated the application manifest file with correct values (Publisher and ProviderUrl)...
I do the same for production build, meaning i build the application to another folder and update it with different ProviderUrl and add Publisher, since it has to be included in every mage update...
Now the problem is with setup.exe file.
Setup.exe is generated at build and it takes the values from the .csproj file.
Considering all of the above I have three issues:
1.
Is there a way of building the application with the correct parameters, so the setup.exe would contain the correct values?
2.
Also how would I update Assembly information (parameter version) before build? When publishing from VS i need to update it on "Probject properties / Application / Assembly Information"
3.
I noticed that when Publishing from VS the application manifest file is also generated in the "Application Files" folder, while publishing with MSBUILD it is not. Why is that?
Thank you in advance and best regards, no9
EDIT:
I fixed the problem #2 like so:
<!--UPDATE ASSEMBLY INFORMATION BEFORE BUILD-->
<target name="UpdateAssemblyInfo">
<asminfo output="${assemblyInfoFile}" language="CSharp">
<imports>
<import namespace="System" />
<import namespace="System.Reflection" />
<import namespace="System.Resources" />
<import namespace="System.Runtime.CompilerServices" />
<import namespace="System.Runtime.InteropServices" />
<import namespace="System.Windows" />
</imports>
<attributes>
<attribute type="AssemblyTitleAttribute" value="some value" />
<attribute type="AssemblyDescriptionAttribute" value="some value" />
<attribute type="AssemblyConfigurationAttribute" value="some value" />
<attribute type="AssemblyCompanyAttribute" value="some value" />
<attribute type="AssemblyProductAttribute" value="some value" />
<attribute type="AssemblyVersionAttribute" value="some value" />
<attribute type="AssemblyFileVersionAttribute" value="some value" />
<attribute type="AssemblyCopyrightAttribute" value="some value" />
<attribute type="AssemblyTrademarkAttribute" value="some value" />
<attribute type="AssemblyCultureAttribute" value="some value" />
<attribute type="CLSCompliantAttribute" value="boolean value" />
<attribute type="ComVisibleAttribute" value="boolean value" />
</attributes>
</asminfo>
<echo file="${assemblyInfoFile}" append="true">
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
</echo>
<echo message="Updated" />
</target>
Meaning I override Assembly.info file before build and add relevant values.
And the problem #3 like so:
<!--COPY APPLICATION MANIFEST TO APPLICATIONFILES FOLDER-->
<target name="CopyTestApplicationManifestToApplicationFilesFolder" depends="Dependency target name" description="Update">
<echo message="Copying..." />
<copy
file="source file"
tofile="target file" />
<echo message="Copied" />
</target>
I was able to create tasks that properly generated the assets you mentioned in your question (setup.exe,application folder, etc.) without having to explicitly sign manifest.
the "clean_and_publish_application" task does the following
Clean the project
Rebuild the project
Publish the project **Here i provide several parameters specifying my publish url, BootstrapperSDKPath,Application Version, and Application Revision
<target name="clean_and_publish_application" description="Build the application.">
<echo message="Clean the build directory"/>
<msbuild project ="${src.dir}\${target.assembly.name}.csproj">
<arg value="/property:Configuration=Debug;outdir=bin\Debug" />
<arg value="/t:clean" />
</msbuild>
<echo message="Rebuild the application"/>
<msbuild project ="${src.dir}\${target.assembly.name}.csproj">
<arg value="/property:Configuration=Debug;outdir=bin\Debug" />
<arg value="/t:rebuild" />
</msbuild>
<echo message="Publish the application"/>
<msbuild project ="${src.dir}\${target.assembly.name}.csproj">
<arg value="/p:publishurl=${publish.url};
GenerateBootstrapperSdkPath=
C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\Bootstrapper\;
ApplicationVersion=${app.version};
ApplicationRevision=${app.revision}" />
<arg value="/t:publish" />
</msbuild>
</target>
The BootstrapperSdkPath property is required for the creation of the setup.exe file. I hard coded the location because it doesn't change. MSBuild is looking for a setup.bin file within that directory.
The ApplicationVersion property is formatted as for example 2.0.0.%2a
The ApplicationRevision property is formatted as a number for example 24 (these values are the Publish Version we see in visual studio. I never actually update the AssemplyInfo.cs file at all.)
Any property you see listed in the .csproj file can be passed as an argument for msbuild. I found this documentation very helpful MSBuild Command-Line Reference
The above task does everything you need EXCEPT actually copy the files to your publish url (Still looking for the answer for this). So I just manually copy all the files from the app.config directory the publish creates
<target name="copy_src">
<echo message="Copying app.config folder"/>
<copy todir="${publish.url}" overwrite="true" failonerror="true">
<fileset basedir="${src.dir}\${app.config.location}">
<include name="**" />
</fileset>
</copy>
</target>
I'm running these scripts in Team City. Because I used the publish target, I didn't have to worry about signing any manifests. Also, I use the msbuild task from the NAnt.Contrib.Tasks.dll which you can download here NAntContrib

Calling a rakefile from NAnt

I am dealing with a relatively large project, and up until this point building has been done in a NAnt build script. Recently a small portion of development was done in ruby, and is built using a rake file. I would like to create a NAnt target that will call the rakefile. Current set-up in the NAnt build: (Note that the rakefile is in a different directory from the NAnt script)
<property overwrite="false" name="project.rootdirectory" value="${project::get-base-directory()}" />
<property overwrite="false" name="rake.exe" value="rake.bat" />
<target name="callrake">
<exec program="${rake.exe}" verbose="true">
<arg value="build:foo" />
<arg line="-f ${project.rootdirectory}/../pathtorakefile" />
</exec>
</target>
The error that I recieve when I run nant callrake is:
[exec] ruby.exe: No such file or directory -- c:/pathtoNAntscript/rake (LoadError)
c:/pathtoNAntscript/NAntscript.build
External Program Failed: rake.bat (return code was 1)
Ruby and rake are installed, and the Ruby bin is in the path variable. Not sure why NAnt cant seem to call rake.
This issue is solved by calling cmd.exe and passing it the rake command as text:
<exec program="cmd.exe" verbose="true">
<arg line="/c ${rake.exe} build:foo" />
<arg line="-f ${project.rootdirectory}/../pathtorakefile/Rakefile.rb" />
</exec>

PHP Mess Detector integration with Teamcity

I have Teamcity 7.0.3 installed with different projects. I want to use PHP Mess Detector. I want to have limited set of rules from PHP Mess Detector. And different set of rules for every project. So my questions are
1: How can I modify default rules of PHP Mess Detector?
2: How can I have different set of rules defined against each project in Teamcity?
My current build file content is :
<?xml version="1.0" encoding="UTF-8"?>
<project name="bnnpoa" default="analyse" basedir=".">
<property name="project-name" value="${ant.project.name}" />
<property name="work-dir" value="%system.teamcity.build.workingDir%" />
<property name="folder-to-check" value="${work-dir}\sites" />
<target name="analyse">
<exec executable="C:\php\PEAR\scripts\phpmd.bat">
<arg value="${folder-to-check}"/>
<arg value="html"/>
<arg value="C:\php\PEAR\resources\rulesets\naming.xml,C:\php\PEAR\resources\rulesets\codesize.xml,C:\php\PEAR\resources\rulesets\controversial.xml"/>
<arg value=">"/>
<arg value="${project-name}_analysis.htm"/>
</exec>
</target>
</project>
Add a build step and use same code for every project.
To define your own rules you can modify XML files or copy them , modify rules as per needs, place files in same path and other option is to create new XML and import required rules from files.

Resources