Running custom MSBuild tasks as part of the git deployment to Azure WebSites - msbuild

Problem
As part of my csproj I have a custom MSBuild task that executes the YUICompressor and generates a compiled css and js file.
<PropertyGroup>
<CssOutputFile>$(OutDir)..\Styles\compiled.css</CssOutputFile>
<JavaScriptOutputFile>$(OutDir)..\Scripts\compiled.js</JavaScriptOutputFile>
<BuildDependsOn Condition="'$(Configuration)' != 'Debug'">
$(BuildDependsOn);
CompressorTarget;
</BuildDependsOn>
</PropertyGroup>
This runs fine as part of the git deployment and the file is being generated, however the Azure Web Sites deployment engine will then copy all the output files to another folder. In that process it seems it takes whatever you have in your csproj instead of whatever you have in the folder. That menas that the generated compiled.css and compiled.js won't be copied (because they are not in my csproj)

What Azure does to deploy your project should be exactly the same as if you do the following:
Right click on the project and choose Publish
Change the Publish Method to 'File System'
Enter a path and click Publish
So generally, you'll want to make sure that your build process works such that you get the right file when you do this local publish. If it does, then chances are you'll get the same results when git pushing to Azure.

The workaround I used for now is adding an empty compiled.css and .js file to the csproj and I wanted to write this question in case someone goes through the same thing.
It would be great if someone from MS can comment if there are plans on doing something different for this scenario.

Depending on where you place the compiled scripts, you can use star-includes in your project file:
<ItemGroup>
<Content Include="assets\**\*" />
</ItemGroup>
If Azure uses your project file to determine what gets deployed (which seems somewhat strange to start with), then that should work.

Related

MSBuild wildcard matching of files for deployment

I am hoping to be able to use MSBuild to capture a subtree of files produced during the build of a project using Microsoft.NET.Sdk.Web and include them in deployment. So far, I have found that if I simply create the files inside the project folder before deployment, then it works but only for certain filetypes. DLLs, for instance, are excluded, presumably assumed to be non-content items. I have been poking around how the deployment stuff works, and have found the <ResolvedFileToPublish> element that I can put into <ItemGroup>, but I haven't figured out how it might be possible to employ this with wildcards. Specifically, I have a post-build step that places files into a folder deployment within the project, and I want all files in that subtree to be included in the package that is produced by /p:DeployOnBuild=true. How can I tack my files onto the deployment stage so that they're included in the ZIP even if they don't look like content items?
I have found a solution, in the form of adding a new <Task> set to run immediately after the internal tasks which collect files for publishing. This is not suitable for a long-term solution, since it ties to internal state, but this is a temporary fix and as such I think it's alright.
By adding this to the .csproj:
<Target Name="__CopyDeploymentToPublish" AfterTargets="_CopyResolvedFilesToPublishAlways">
<Exec Command="PowerShell.exe -Version 3.0 -ExecutionPolicy Unrestricted $(SolutionDir)deploy_webapp.ps1 -Source $(SolutionDir)src\IQ.Auth.OAuth2.Web -Target $(PublishDir)" />
</Target>
...my PowerShell script runs right after the standard deployment logic aggregates the files it intends to package up. I can at that point do whatever I want to the files and the way they're left is what'll end up in the ZIP file.

Include additional files in build using MSBuild

In the past we have used a combination of TeamCity and MsDeploy to deploy our projects. We've recently moved into using Octopus deploy, and TeamCity purely for the Build. This meant changing the build process in TeamCity and removing any references to MsDeploy.
In the past we've used confiiguration such as the below in our project file which included additional files and directories:
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
CustomCollectFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<Target Name="CustomCollectFiles" BeforeTargets="BeforeBuild">
<ItemGroup>
<UCommerceAssemblies Include="$(MSBuildThisFileDirectory)..\..\Resources\UCommerce\ucommerce\*" />
<FilesForPackagingFromProject Include="%(UCommerceAssemblies.Identity)">
<DestinationRelativePath>bin\uCommerce\%(Filename)%(Extension)</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
However this now doesnt seem to get invoked whatsoever. It appears (to me anyhow) that these pipelines were created for use with Publishing from Visual Studio, or using MSDeploy, however we need to just include these files in the directory either before or after the build has taken place. There seems to be tons of references across the web about doing this, however they all refer to using MSDeploy.
Can anyone shed any light on how I can include additional files/directories in the build without using MSDeploy?
Thanks for your time in advance
dotdev
As you are using Octopus for deployment you only need to include additional files (reference assemblies, etc.) into code package sent to Octopus.
To do this one needs to use OctoPack for project packaging. Then add .nuspec file into the project, for example this line in .nuspec will add all files from some different location into the .nupkg package under "bin\additional" and will be deployed correctly by octopus.
 
Nuspec docs
Similar solution is discuses here.

Is it possible for csproj/msbuild to include files as optional content?

I have an optional config file in my application that is used for instance-specific configuration. The application works without it, and it is only necessary if you want to configure some additional features. It therefore shouldn't get included in the source control as every developer and client deployment doesn't need it and those who do will have different values.
I have a problem figuring out how to configure this for continuous deployment. I can generate the file on the build server without issues. However, since MsDeploy reads the csproj to determine which files to deploy, this file has to be tracked by my csproj to actually be moved to the deploy server. But if I have it tracked by my csproj, then it becomes no longer optional and I can't build the application without it. I'm using Mercurial which doesn't have a commit-one-version-ignore-subsequent feature (git's --assume-unchanged) so options seem pretty limited on that front. I am a very strong believer that it should be possible to clone a repo and run the project immediately, so I really don't like the idea of comitting something that cannot build.
Is there a way in the csproj file to indicate that a file should be included as content if present and ignored otherwise?
If this is the only .config file in the directory, then you can edit .csproj file manually to reference your file via file mask:
<ItemGroup>
<Compile Include="*.config"/>
</ItemGroup>

Modify files after deployment package is created

I'm in the process of creating a web deployment package through an automatic build trigger on the server.
The package should take care of everything (including creation of a specific website, apppool, and the latest code) on any server desired.
I extracted manually a deploy package from a configured local IIS site, containing all information needed by MsDeploy to create the site, apppool, etc...
They are present in following files
archive.xml
parameters.xml
systeminfo.xml
The idea is now that I would create automatic a deploy package on the build server, that contains the new compiled code, but with the above xml files in the .zip package.
Right now, I'm building the application, after which I execute a PowerShell script that will manually overwrite the files in the .zip with the ones I have.
However, I know you can extend the Target file (with a .wpp.targets file in your project) to plug into the pipeline and modify things along the way.
Unfortunately I'm getting a little lost with the information I found.
I'd like to:
1) configure the creation of the deployment package to use my existing .xml files.
2) if that's not possible, overwrite the files with my own files after the package creation.
My goal is to have a full executable deploy package after the build is finished, so I won't need to PowerShell script anymore.
Any information that will point me closer to a solution or helps me to understand more clearly msbuild targets and/or webdeploy is very appreciated.
I managed doing this by extending the Package MsBuild target.
Adding a .wpp.targets file in the root of the web project with following content.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSBuild.ExtensionPack.4.0\MSBuild.ExtensionPack.tasks"/>
<PropertyGroup>
<DeployFilesDirectory>$(MSBuildProjectDirectory)\Deploy\</DeployFilesDirectory>
</PropertyGroup>
<PropertyGroup>
<OnAfterPackageUsingManifest>
$(OnAfterPackageUsingManifest);
CopyDeployFiles;
ReplaceSetParametersFile;
<!--ZipDeploymentFiles;-->
</OnAfterPackageUsingManifest>
</PropertyGroup>
<Target Name="CopyDeployFiles">
<Message Text="Copy Deploy Files"></Message>
<ItemGroup>
<Files Include="$(DeployFilesDirectory)*.xml" Exclude="$(DeployFilesDirectory)setParameters.xml"></Files>
</ItemGroup>
<MSBuild.ExtensionPack.Compression.Zip TaskAction="AddFiles"
CompressFiles="#(Files)"
ZipFileName="$(PackageFileName)"
RemoveRoot="$(DeployFilesDirectory)"/>
</Target>
<Target Name="ReplaceSetParametersFile" DependsOnTargets="GenerateSampleDeployScript">
<Message Text="Replace Default SetParameters File"></Message>
<Copy DestinationFiles="$(GenerateSampleParametersValueLocationDefault)"
SourceFiles="$(DeployFilesDirectory)setParameters.xml"></Copy>
</Target>
</Project>
The first target is executed after MsDeploy has created the package and will replace the .xml files within the .zip file. I'm using the MsBuild.ExtensionPack Zip support.
The second target is executed after the build has created the sample .cmd and setParameters files and will overwrite the setParameters.xml with my own as well.
It takes a while to understand the concepts of MsBuild targets etc, but once you understand it becomes indeed very powerful.
Creating the package is now as simple as just launching the MsBuild
msbuild "D:\Projects\MyWebProject.csproj" /T:Package /P:Configuration=Release;Platform="AnyCPU";PackageLocation="D:\DeployPackage\package.zip";PublishProfile=MyProfile
And deploying is the same as before
package.deploy.cmd /Y –setParamFile :myParameterFile.xml
Assuming your paths stay the same, you can achieve this by specifying the existing zip as your -dest:package=package.zip. MSDeploy will automatically overwrite the files inside the zip.

Web Deploy - Using Relative Paths for local file system deployment

I am wanting to use Web Deploy to run a custom deployment setup.
As I am wanting to have this work fine when running on many different environments (team members local machines, 4 different builds servers) I want to deploy to a local path that is relative.
What I am wanting to do is:
Deploy to a local relative path
Have the after build step do magical things...
However when i enter the local file path to deploy to as: "..\Deploy_Production"
web deploy complains with this:
2>Connecting to ..\Deploy_Live...
2>Unable to create the Web site '../Deploy_Live'. The URL http://:0 is invalid.
Its as if Web deploy thinks that the relative file path is a website URL. Using "..\" instead doesn't help my cause.
How do you get WebDeploy to deploy to a local relative path?
Edit 1:
I have tried to use a ConvertToAbsolutePath task before build, to no avail:
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<SiteUrlToLaunchAfterPublish>http://mywebsite.com</SiteUrlToLaunchAfterPublish>
<publishUrl>..\Deploy_Production</publishUrl>
<DeleteExistingFiles>False</DeleteExistingFiles>
</PropertyGroup>
<Target Name="BeforeBuild">
<ConvertToAbsolutePath Paths="$(publishUrl)">
<Output TaskParameter="AbsolutePaths" PropertyName="publishUrl" />
</ConvertToAbsolutePath>
</Target>
Edit 2:
The above works, but only when running commandline builds against the Solution file not a project file
We have a bug here, when publishing using File system you have to provide a full path. We actually found this bug earlier this week. It will be fixed in our next update. In this case when the relative path is passed it incorrectly thinks that its an IIS path.
As a workaround you can edit the .pubxml to make the publishUrl a fullpath. Fortunately you can use an MSBuild property so that this works in team scenarios. Here is what you should do, edit your .pubxml file and update the value of publishUrl to be the following.
<publishUrl>$(MSBuildThisFileDirectory)..\..\..\Deploy_Production</publishUrl>
This path will be relative to the .pubxml file itself. I've verified that this works from both the command line as well as the publish dialog. If you have any issues with this let me know, but the fix should hopefully be released in a few months [no guarantees of course :) ].