Publishing a ClickOnce application in TFS Team Build - tfs

We have several .exe projects in our solution. What we are trying to achieve is when the project is built using TFS Team Build, we want MSBuild to create a ClickOnce file in the output folder. This was fairly easy to do: I simply added Publish to the DefaultTargets property of my project:
<Project DefaultTargets="Publish;Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
...
</Project>
This works, however, the ClickOnce application is created even when the project is built locally (e.g. in Debug mode). Is there a way to specify a condition so that Publish is invoked only when the condition is true? I tried to add a a new Target like this:
<Target Condition="$(TeamBuildOutDir) != ''" Name="Publish" />
But this doesn't work. I believe I need additional properties in my target tag to let the MSBuild know that I want a ClickOnce target on $(TeamBuildOutDir) != '', but I'm not sure how to do that. Any help is appreciated.
Edit
I tried this (without Condition="$(TeamBuildOutDir) != ''") just to see if it would work locally:
<Target Name="AfterBuild">
<MSBuild
Projects="$(MSBuildProjectFullPath)"
Targets="Publish"
Properties="PublishDir=C:\ClickOnce"/>
</Target>
However, I'm getting the following error:
There is a circular dependency in the target dependency graph involving target "AfterBuild"
But there is NO circular dependency in the project! The project builds just fine without this Target tag. I'm really confused.

What about using the BuildingInsideVisualStudio property to determine when to call publish?
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.package.projectfileconstants.buildinginsidevisualstudio(v=vs.80).aspx

Related

sfproj - outpath Build error

Iam trying to build the sfproj using msbuild on my build machine , This is what Iam doing.
<target ="package">
<foreach item="File" property="sfproj">
<in>
<items refid="servicefabric.files.sfproj" />
</in>
<do>
<exec program="${msbuild14.exe}">
<arg value="${sfproj}" />
<arg value="/p:Configuration=${config}" />
<arg value="/p:Platform=x64" />
<arg value="/target:Package" />
</exec>
</do>
</foreach>
</target>
The error Iam getting on the build machine is
(_CheckForInvalidConfigurationAndPlatform target) ->
10:25:10 [exec] C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(724,5): error : The OutputPath property is not set for project 'App.sfproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Debug' Platform='x64'. You may be seeing this message because you are trying to build a project without a solution file, and have specified a non-default Configuration or Platform that doesn't exist for this project.
It appears that the Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.1.0 Nuget package contains an MSBuild target that skips building of the sfproj itself (that or makes certain options present in order for it to succeed).
In my case I had accidentally checked in some Nuget package folders into source control that were incomplete. Visual Studio saw the presence of the folder so it didn't try to redownload it. You need to make sure that the build folder is present and it contains an MSBuild target file. Your best bet is just to delete the packages folder entirely to ensure you're starting off with a fresh start.
https://ijustwrite.software/2016/07/20/ouputpath-property-not-set/
I ran into a problem with the same symptoms. It happened to me after upgrading the Azure SDK, but it seems to me that it could easily happen in a few ways. The trouble is that there are two paths in the sfproj file that lead into the directory where nuget packages are stored, oddly enough on the first and last line of the file. This causes problems because you may have overridden where nuget packages are stored (this is not at all uncommon). Since these are relative paths, it's easy for nuget to get the packages to where you have specified, but for the lines in the project to point to "..\packages" instead, which makes Visual Studio want to restore the packages, but to never think they have been restored as it's looking for them in the wrong place.
To fix it all you need to do is modify the paths in the sfproj file to point to wherever you have your nuget packages set to download (this setting is in the nuget.config file, which can be in any directory above your project directory).
These are the two broken lines in my sfproj file:
<Import Project="..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.props" Condition="Exists('..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.props')" />
<Import Project="..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets" Condition="Exists('..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets')" />
Since I have the value ThirdPartyLibraries\NuGetPackages in my nuget.config file (which is two directories above my project file), these lines had to be modified to be the following in order to work again :
<Import Project="..\..\ThirdPartyLibraries\NuGetPackages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.props" Condition="Exists('..\..\ThirdPartyLibraries\NuGetPackages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.props')" />
<Import Project="..\..\ThirdPartyLibraries\NuGetPackages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets" Condition="Exists('..\..\ThirdPartyLibraries\NuGetPackages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.3.0\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets')" />
And that fixes it right up. I was lucky: I had two sfproj files, one with this issue, and one without, so all I had to do was diff to see the trouble.

Including files outside of the project in MSBuild

I have found other posts here on StackOverflow that deal with my issue I am experiencing, for example:
MSBuild: Deploying files that are not included in the project as well as Include Files in MSBuild that are not a part of the project
I wanted to share the code that I was able to create after reading these posts and ask for some help as to why it might not be working?
To elaborate on what exactly is not wrong and what I intend to do. I am using Visual Studio 2012, and TFS 2012.
I have a batch file called CreateMyFiles.bat, and what I would like to do is execute this and then take the files it outputs (it outputs them to my Includes/Javascript/Bundled folder) and include them in part of the build in MSBuild (so that they are deployed to the target IIS server).
When I edited my local .csproj in my local Visual Studio and added the code below to the bottom of the file and reloaded, I was able to right click on my web projbect, select 'publish', and then select my local file-based publishing profile which did indeed deploy my files to the correct location. It worked!
I then checked in my code to TFS, and went to 'builds' on TFS, and queued a new build. Sure enough, I was able to see the files output to the same directory on the build server. Now, i'm not 100% sure about MSBuild but I noticed that just like when I hit publish locally, it created a _publishedWebsite folder on the build server as well (a directory above the source). The thing is, within this publishedwebsite folder, my manually created files were not present. Furthermore, going to the target web server after the build was done unfortunately did not have the files I wanted.
So it seems like if I were to manually select publish, the code below works, but if I were to queue a build with TFS, it does not work. Does MSBuild use publish? Could that be the reason it does not work below?
Here is the code I've placed in my .csproj file:
<Target Name="CustomCollectFiles">
<Exec Command="CreateMyFiles.bat" /> <!-- Generate Files -->
<ItemGroup>
<!-- Create an identity called _CustomFiles, and associate it to the files I created -->
<_CustomFiles Include="Includes\JavaScript\Bundled\*" />
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>Includes\JavaScript\Bundled\*%(Filename)%(Extension) </DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
<!-- Hook into the pipeline responsible for gathering files and tell it to add my files -->
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
</PropertyGroup>
I'm really stuck on this and wanted to ask for some help as to why the files might not be going. I suspect MSBuild doesn't use publish and that's why it works locally (because i'm selecting publish)?
Thanks so much for your help
UPDATE
Tried this as per comments below, but this time the files didn't even appear (so it seemed to not even run the tasks now). Any idea why? Did I type this right?
<Target Name="CustomCollectFiles">
<Exec Command="CreateMyFiles.bat" />
<!-- Generate Files -->
<ItemGroup>
<!-- Create an identity called _CustomFiles, and associate it to the files I created -->
<_CustomFiles Include="Includes\JavaScript\Bundled\*" />
<FilesForPackagingFromProject Include="%(_CustomFiles.Identity)">
<DestinationRelativePath>Includes\JavaScript\Bundled\*%(Filename)%(Extension) </DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
<!-- Hook into the pipeline responsible for gathering files and tell it to add my files -->
<PropertyGroup>
<PipelineCollectFilesPhaseDependsOn>
CustomCollectFiles;
$(PipelineCollectFilesPhaseDependsOn);
</PipelineCollectFilesPhaseDependsOn>
</PropertyGroup>
UPDATE 2
When I take the above code, and place it into my pubxml file and then execute an actual publish, it works, but as far as I know our process is to just queue a build from TFS. Is it possible to hook into the above code block when simply queuing a build? Or do I need to publish?
to do a publish from TFS build you need to add the following arguments
/p:DeployOnBuild=true;PublishProfile=Release
obviously using your own PublishProfile name
In VS2012, the target was renamed from:
CopyAllFilesToSingleFolderForPackageDependsOn
to:
CopyAllFilesToSingleFolderForMsdeployDependsOn
Update: looks like the above Targets are not getting called from within VS2012 targets, can you replace it with a call to the Target "PipelineCollectFilesPhaseDependsOn"? That should fix it.
<PropertyGroup>
<PipelineCollectFilesPhaseDependsOn>
CustomCollectFiles;
$(PipelineCollectFilesPhaseDependsOn);
</PipelineCollectFilesPhaseDependsOn>
</PropertyGroup>

How to use MSBuild with TFS and SSIS

I'm a complete novice in using TFS Build definition and MSBuild scripts.
I want to automate my SSIS build and deployments and create a build definition which will build and deploy my SSIS project whenever I queue it up.
I found this project: http://sqlsrvintegrationsrv.codeplex.com/releases/view/82369
which allows you to create a DLL which you can place in C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies
Then you can call the SSIS.MSBuild.proj (See end for this) with certain parameters like this in a visual studio command line:
MSBuild SSIS.MSBuild.proj /t:SSISBuild,SSISDeploy /p:SSISProj="MySSISProject",Configuration="DEV",,SSISServer="AB-CDE-FGH-I1\DEV",ProjectName="MySSISProject"
or I can put it in a BAT file like this:
%systemroot%\Microsoft.Net\Framework\v4.0.30319\MSBuild.exe SSIS.MSBuild.proj /t:SSISBuild,SSISDeploy /p:SSISProj="MySSISProject",Configuration="DEV",,SSISServer="AB-CDE-FGH-I1\DEV",ProjectName="MySSISProject"
It works fine when you run the BAT file, it builds and deploys the SSIS project.
Questions:
How can I use this so it is automated, so I can manually kick off a build and deployment from within VS/TFS? Using a build definition.
How can I ensure the correct configurations are selected, and the correct destination server? For example we have SSIS configurations for DEV, SIT, SYS, UAT, PRD. Each with its own server name. Do I need a separate build definition for each environment or is there a way to use one build definition?
Anything useful in using powershell somehow?
Here is SSIS.MSBuild.proj:
<?xml version="1.0" encoding="Windows-1252"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="SSISBuild;SSISDeploy">
<!--Requires a property called $(SSISProj) to be defined when this script is called-->
<UsingTask TaskName="DeploymentFileCompilerTask" AssemblyFile="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.SqlServer.IntegrationServices.Build.dll" />
<Target Name="SSISBuild" Condition="'$(SSISProj)' != ''">
<PropertyGroup>
<SSISProjPath>$(SSISProj)\$(SSISProj).dtproj</SSISProjPath>
</PropertyGroup>
<Message Text="**************Building SSIS project: $(SSISProjPath) for configuration: $(CONFIGURATION)**************" />
<DeploymentFileCompilerTask
InputProject="$(SSISProjPath)"
Configuration="$(CONFIGURATION)"
ProtectionLevel="DontSaveSensitive">
</DeploymentFileCompilerTask>
</Target>
<UsingTask TaskName="DeployProjectToCatalogTask" AssemblyFile="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.SqlServer.IntegrationServices.Build.dll" />
<Target Name="SSISDeploy" Condition="'$(SSISProj)' != ''">
<Message Text="**************Publishing SSIS project: $(SSISProj) to: $(SSISServer) to folder: $(PROJECTNAME)**************" />
<PropertyGroup>
<ISPac>$(SSISProj)\bin\$(CONFIGURATION)\$(SSISProj).ispac</ISPac>
</PropertyGroup>
<DeployProjectToCatalogTask
DeploymentFile="$(ISPac)"
Instance="$(SSISServer)"
Folder="$(PROJECTNAME)"
CreateFolder="true"/>
</Target>
</Project>
EDIT I tried adding some MSBuild Arguments to the TFS Build Definition. I tried various combinations of arguments, some with quotes, some without. I couldn't get it to work.
"C:\Users\me\Desktop\Buildssis\SSIS.MSBuild.proj" /t:SSISBuild,SSISDeploy /p:SSISProj="MySSISProject",Configuration="SIT",SSISServer="AB-CDE-FGH-I1\DEV",ProjectName="MySSISProject"
But I always get this error:
MSBUILD : error MSB1008: Only one project can be specified.
Switch: C:\Users\me\Desktop\Buildssis\SSIS.MSBuild.proj
For switch syntax, type "MSBuild /help"
I think you are prety close about the solution. you can call your package in console application and set the variables there. And for different DEV, SIT, SYS, UAT, PRD. you can have configuration file for the the console application. and then you can set the package variables in the console app. I hope it solve the preoblem. As much i could understand.
Please let me know if this is not related to your problem then explain your question a bit more.
To answer your question, the best way would be to use an UpgradeBuildTemplate for your team build.
Modify the build script to calls the tasks that you have created in the "AfterCompile" target of the build. See below
http://msdn.microsoft.com/en-us/library/aa337604(v=vs.100).aspx
You can pass build parameters in your team build definition. If you edit your build definition and edit Process, you will see option to pass MSBuild Arguments.

How to I perform a web.config transform with MSBuild or MSDeploy?

I've tried a number of different configurations with this and I haven't achieved my result.
TL;DR
I'm trying to add config transforms into my build process and am looking for the right way to do it from MSBuild so that it shows up in my deployments via MSDeploy.
Background
I have an WebApp (MVC3), a Core app (CS Class Lib), and two test class libs, one for each.
I have a build script in my solution that uses MSBuild to compile.
One of those MSBuild targets deploys to an IIS server using MSDeploy
This process is working so far both manually and via CruiseControl.NET
Goal
I would like to add Web.Config transforms to this process. I figured I would do something simple at first, like an app setting called "PEAppsEnvironmentName", which I would make Dev, Test, or Prod based on the current environment.
Theory So Far
To me, it appears that when packaging with MSDeploy, I'm not transforming the config file.
When I run MSBuild with the DeployOnBuild option set to true, it creates another package that has the appropriately transformed config. It just seems like somehow I can't get it all to match up. The end result is that the web page displays "None" (the initial setting) instead of the transformed "Development" string.
I think if I could find out how to use MSDeploy during the packaging phase to transform the MSConfig, I'd be good to go.
Code
My web.config file
<appSettings>
<add key ="PEAppsEnvironmentName" value="None"/>
...
</appSettings>
My Web.Dev.config file
<appSettings>
<add key ="PEAppsEnvironmentName" xdt:Transform="Replace" xdt:Locator="Match(key)" value="Development" />
</appSettings>
My MSBuild Targets
Property group showing default config is "Dev"
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Dev</Configuration>
</PropertyGroup>
My MSBuild "Compile" Target
<Target Name="Compile" DependsOnTargets="Init">
<MSBuild Projects="#(SolutionFile)" Targets="Rebuild" Properties="OutDir=%(BuildArtifacts.FullPath);DeployOnBuild=True"/>
</Target>
My MSBuild "Package" Target
<Target Name="Package" DependsOnTargets="Compile;Test">
<PropertyGroup>
<PackageDir>%(PackageFile.RootDir)%(PackageFile.Directory)</PackageDir>
<Source>%(WebSite.FullPath)</Source>
<Destination>%(PackageFile.FullPath)</Destination>
</PropertyGroup>
<MakeDir Directories="$(PackageDir)"/>
<Exec Command='"#(MSDeploy)" -verb:sync -source:iisApp="$(Source)" -dest:package="$(Destination)" '/>
</Target>
My MSBuild "Deploy" Target
(scrubbed for PWs, etc.)
<Target Name='Deploy' DependsOnTargets='Package'>
<PropertyGroup>
<Source>%(PackageFile.FullPath)</Source>
</PropertyGroup>
<Exec Command ='"#(MsDeploy)" -verb:sync -source:package="$(Source)" -dest:iisApp=PEApps,computerName=$(WebServerName),username=[User],password=[Password]'/>
</Target>
There was a lot to this question, I'm not sure if I'm fully on the same page as you but I'll summarize my impression of what you are asking. You have an existing web project which is in a solution with other projects. You need to be able to package the web project so that you can publish it to multiple destinations.
I have created a NuGet package which can be used for this exact purpose. It's called package-web. When you add it to your web project it will update the packaging process. When you create a package a few additional files will be included in the package, including all the web.config transform files. A .ps1 file will be created next to the package as well. You can use this script to publish the package. It will prompt you for which transform to run and for all the Web Deploy parameters. You can also save the responses to a file and then just pass them to the .ps1 file so that you can perform non-interactive publishes. I created a 5 minute video on it at http://nuget.org/packages/PackageWeb
package web: http://sedodream.com/2012/03/14/PackageWebUpdatedAndVideoBelow.aspx. FYI this is not yet working with VS 2012 but I'm working on the fix and should have it updated by the time VS 2012 is released.
If you don't find that useful you can see how I implemented the solution at https://github.com/sayedihashimi/package-web and you should see examples of everything that you need to do to roll your own.
FYI if you need to transform any files besides web.config on package create then you should take a look at my VS extension SlowCheetah. Here is a blog about how to integrate it into a build server.

TFS Build Error While Invoking custom build file via TFS Build definition

I use TFS 2010, VS 2010.
I wrote a custom msbuild proj file which I use locally to build my solution. I use MSBUILD.exe TFSBuild.proj and it does everything in my local machine. (I have a taregt called 'DoMyBuild' which kicks off the build and does everything.)
I have used this in the DefaultTargets attribute as below:
<Project DefaultTargets="DoMyBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
I am trying hard to configure this to use my build server with a build definition. In the build definition, under Process - I configured 'Upgrade Template' and in the build process paramters, have given the path to this TFSBuild.Proj file.
Ideally. TFS should start 'DoMyBuild' target as I read. But it gives a error looking for 'EndToEndIteration' not defined. So, I believe it is still doing a DesktopBuild which I dont want. I want to use my custom target to kick start. Is this possible ?
Any help is very much appreciated.
Thanks, Mani
You problem is discussed here.
Make sure you have at least an EndToEndIteration target defined in your MSBuild project.
<Target Name="EndToEndIteration"
Condition=" '$(IsDesktopBuild)'!='true' "
DependsOnTargets="$(EndToEndIterationDependsOn)" />

Resources