Adding additional content folders to Azure package - azure

Im using Azure SDK 2.5
I have a web role in a cloud service project. I would like to add a folder in some fashion such that it is deployed in the parent directory of the approot. I havent found a way to do this which kind of makes me wonder what use is the ability to define virtual directories in csdef.
So I thought I would try adding folders via the Contents/Content xml config in the csdef. I am either fundamentally misunderstanding what this bit of config does or its hopelessly broken.
Assuming this folder structure
/
/CloudService
/SomeOtherContent
If I define the following:
<Contents>
<Content destination="frontend">
<SourceDirectory path="..\SomeOtherContent" />
</Content>
</Contents>
and build I get:
error CloudServices089: Cannot find the source directory
'C:\src\template\src\Template.CloudService\bin\Debug\..\SomeOtherContent'
Ok so its starting the bin\Debug, so I'll just make it ..\..\..\SomeOtherContent
error CloudServices089: Cannot find the source directory
'C:\src\template\src\Template.CloudService\..\..\..\SomeOtherContent'
Yes thats right, the folder at which my relative path is resolved has changed!!! Its no longer bin\Debug. Wtf!? How can this be made to work? It works if i enter a full drive qualified absolute path.

So I solved this by having MSBuild resolve the path and push it in to an environment variable which I called FrontendDir.
<Contents>
<Content destination="frontend">
<SourceDirectory path="%FrontendDir%" />
</Content>
</Contents>
and in the ccproj I added:
<UsingTask
TaskName="SetEnvironmentVariableTask"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll">
<ParameterGroup>
<Name ParameterType="System.String" Required="true" />
<Value ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
Environment.SetEnvironmentVariable(Name, Value);
]]>
</Code>
</Task>
</UsingTask>
<Target Name="BeforeBuild" Condition=" '$(FrontendDir)' == '' ">
<Message Text="Setting Project Dir" Importance="high" />
<SetEnvironmentVariableTask Name="FrontendDir" Value="$(ProjectDir)\..\Template.FrontEnd\dist" />
</Target>
Its preferable to put the entire path into the env var here as you can then override it easily in your different build scenarios by overriding the value (eg. /p:FrontendDir="c:\foo")
So that works and works fairly well. I still say the behaviour I was seeing before with the relative path resolution changing folders is... broken. It just doesn't work with relative paths in any usable way.

You are seeing the same error but from different msbuild targets.
The first error (when using ..\..\) is thrown at PreValidateServiceModel which passes in the Source location and checks the path
ServiceDefinitionFile="#(SourceServiceDefinition)"
ServiceConfigurationFile="#(SourceServiceConfiguration)"
C:\src\Azure\ServiceDefinition.csdef : error CloudServices089: Cannot
find the source directory 'C:\src\Azure\..\..\Installers\' in role
WebHost. [C:\src\Azure\Azure.ccproj]
Done building target "PreValidateServiceModel" in project "Azure.ccproj" -- FAILED.
The second error is thrown at ValidateServiceFiles which passes in the Target location
ServiceDefinitionFile="#(TargetServiceDefinition)"
ServiceConfigurationFile="#(TargetServiceConfiguration)">
C:\src\Azure\bin\Release\ServiceDefinition.csdef : error CloudServices089: Cannot
find the source directory
'C:\src\Azure\bin\Release\Installers\'
in role WebHost. [C:\src\Azure\Azure.ccproj]
Done building target "ValidateServiceFiles" in project "Azure.ccproj" -- FAILED.
If you reflect on C:\Program Files\Microsoft SDKs\Azure.NET SDK\v2.9\bin\ServiceDescription.dll you can see the ProcessRoleContents method doing the validation but using the SourceFile to resolve the location.
One option is to make sure the target folder exists (even if empty) before the build starts.
It would be better if the PreValidation resolved the path and when the Target is saved, it had the full path.
I ended up editing the ccproj, and adding this
<Target Name="BeforeAddRoleContent">
<ItemGroup>
<AzureRoleContent Include="Installers\">
<RoleName>Azure</RoleName>
<Destination></Destination>
</AzureRoleContent>
</ItemGroup>
</Target>
Referencing runtime content from .ccproj (Azure SDK 2.9)

Related

Include static files in Azure WebJob's publish-settings.json

Is it possible to specify in a WebJob's webjob-publish-settings.json file that a folder of static/flat files should be included?
Update:
As per #davidebbo's suggestion, I've tried modifying the .csproj file, but without success.
<Target Name="BeforeBuild">
<ItemGroup>
<Content Include="$(ProjectDir)\Templates\*.xslt" />
</ItemGroup>
</Target>
I've tried both BeforeBuild and AfterBuild, and with/without the $(ProjectDir) variable, but the folder never appears in the bin directory.
It is not something you can do using webjob-publish-settings.json. Instead, you can do this by setting the 'Copy to Output Directory' property of the relevant files to Copy always or Copy if newer.

Include files in MSBuild that are not part of project

I'm trying to create an automated build for my web application project.
We use a standard CMS project and have tweaked some parts of it. Only the tweaked files are part of our project, but I want to include the full CMS in the deployment package.
So I've created a custom .targets file to define a task to include the CMS files during the build:
<Target Name="GetCMSFiles">
<ItemGroup>
<!-- Include the CMS files into the package -->
<_CMSFiles Include="..\packages\CMSFiles\**\*" />
<FilesForPackagingFromProject Include="%(_CMSFiles.Identity)">
<DestinationRelativePath>
%(RecursiveDir)%(Filename)%(Extension)
</DestinationRelativePath>
</FilesForPackagingFromProject>
</ItemGroup>
</Target>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<!-- VS2010 -->
<CopyAllFilesToSingleFolderForPackageDependsOn>
GetCMSFiles;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<!-- VS2012 -->
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
GetCMSFiles;
$(CopyAllFilesToSingleFolderForMsdeployDependsOn);
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
</PropertyGroup>
This works fine, but the problem is that the files from our project do not get copied to the deployment folder. So in other words, it does not overwrite the files that already exist after I copied them with the GetCMSFiles task.
The way I see it there are two options:
Force the CopyAllFilesToSingleFolder to overwrite any existing files in the deployment folder.
Have a condition in the GetCMSFiles task to only include files that don't already exist in the project.
But I'm not sure whether this is possible and how to achieve this. Any ideas?

Specifying results filename for vstest.console.exe

May be a silly question, but does anybody know how to specify the output filename of a VSTEST.Console.exe run? My command line is as follows:
vstest.console.exe [assembly] /logger:trx
At the end of the run, the following comes up in the console:
ResultsFile: somepath\TestResults\{username}_{workstation} {timestamp}.trx
I tried using the .runsettings file to specify the output location, but that only seems to control the output directory, but not the output file. Have not found anything else that would seem to control it.
I want to parse the TRX file and generate a report out of it (this already works, but if I can't specify the output path of the TRX file, I won't know where to pick it up from in the scripts!)
I have to be missing something here...
EDIT: See #AnaFranco's answer - apparently since VS2017 the file name can be configured like so:
vstest.console.exe [assembly] /logger:trx;LogFileName=[filename].trx
I'll leave the old answer for posterity and pre-2017 versions.
Nope, you're not missing anything. The TRX logger doesn't support any parameters (unlike the TFS publisher logger).
The logger assembly is located in "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\Extensions\Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.dll". If you check it out in your favorite .NET decompiler, you'll see the method TrxLogger.GetTrxFileName. It uses some basic knowledge about the current test run to produce the mangled name of form {username}_{workstation} {timestamp}.trx and is in no appreciable way configurable.
As far as I can tell, the TRX file is created in the TestResults\ folder under the current working directory unless otherwise configured. What you can do is:
Create a new temporary folder
Change the current directory to it
Run the test runner
Scan the folder for the result .trx file using your favorite recursive file search method and you're done
At least that is what I do in our build (MSBuild, sob):
<ItemGroup>
<TestResult Include="**\*.trx"/>
</ItemGroup>
I.e, gather all .trx files under the current directory and stuff them into the #(TestResult) item group for further processing.
This has worked for me for testing .net core I haven't tried it with .net framework:
vstest.console.exe [assembly] /logger:trx;LogFileName=[filename].trx
Maybe its a new thing
Update: This works for .net framework projects too using the latest Test platform and vstest.console.exe
Apparently, you can specify a directory where to put the *.trx file (not of the file itself though). This is, however, done via .runsettings file rather than via command line.
Excerpt from Bhuvaneshwari's blog:
If the default location of results need to be overriden user need to
pass this value using a runsettings file.
Example:
Mstest.exe /testcontainer:abc.dll /results:C:\Results.trx
Vstest.console.exe abc.dll /settings:output.runsettings
where the context of the
.runsettings file would be something like below :
<?xml version="1.0" encoding="UTF-8"?>
<RunSettings>
<RunConfiguration>
<ResultsDirectory>c:\</ResultsDirectory>
</RunConfiguration>
</RunSettings>
I had this issue as well. I decided to write a MSBuild target that executes vstest.console via the EXEC task, handling all its outputs, including the coverage results.
Basically, I captured the vstest output and used a regex to capture the *.trx and *.coverage portion of the output, which turned out to be really easy. Plus, it removes the TestResults directory to keep the workspace nice and clean.
In the end, you will get the *.trx file and the *.coverage file (optionally).
The script may look a bit complex, but it was necessary to fit our needs. I tried to clean it up a bit.
Hope this helps.
<Target Name="Test" DependsOnTargets="Build">
<!-- Declare the defaults and arrange parameters -->
<PropertyGroup>
<ArtifactsPath Condition=" '$(ArtifactsPath)' == '' ">Artifacts</ArtifactsPath>
<VSTestSessionName Condition=" '$(VSTestSessionName)' == ''">TestResults</VSTestSessionName>
<VSTestExe Condition=" '$(VSTestExe)' == '' ">C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe</VSTestExe>
<VSTestFailBuildOnTestFail Condition=" '$(VSTestFailBuildOnTestFail)' == '' ">false</VSTestFailBuildOnTestFail>
<VSTestInIsolation Condition=" '$(VSTestInIsolation)' == '' ">true</VSTestInIsolation>
<VSTestUseVsixExtensions Condition=" '$(VSTestUseVsixExtensions)' == '' ">true</VSTestUseVsixExtensions>
<VSTestFramework Condition=" '$(VSTestFramework)' == '' ">framework45</VSTestFramework>
<VSTestLogger Condition=" '$(VSTestLogger)' == '' ">trx</VSTestLogger>
<ErrorCode>0</ErrorCode>
</PropertyGroup>
<ItemGroup>
<VSTestResultsPath Include="$(VSTestResultsPath)" />
<VSTestParams Include="#(VSTestFiles ->'"%(FullPath)"', ' ')" />
<VSTestParams Condition="$(VSTestEnableCodeCoverage)" Include="/EnableCodeCoverage" />
<VSTestParams Condition="$(VSTestInIsolation)" Include="/InIsolation" />
<VSTestParams Include="/UseVsixExtensions:$(VSTestUseVsixExtensions)" />
<VSTestParams Include="/Framework:$(VSTestFramework)" />
<VSTestParams Include="/Logger:$(VSTestLogger)" />
<VSTestParams Condition="$(VSTestCaseFilter) != ''" Include="/TestCaseFilter:"$(VSTestCaseFilter)"" />
<VSTestParams Condition="$(VSTestRunSettings) != ''" Include="/Settings:"$(VSTestRunSettings)"" />
</ItemGroup>
<Message Text="TestAssembly: %(VSTestFiles.Identity)" Importance="high"/>
<Exec ContinueOnError="!$(VSTestFailBuildOnTestFail)" ConsoleToMSBuild="true" WorkingDirectory="$(WorkingDirectory)" Condition=" '#(VSTestFiles)' != ''"
Command=""$(VSTestExe)" #(VSTestParams, ' ')">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
<Output TaskParameter="ConsoleOutput" PropertyName="OutputOfExec" />
</Exec>
<Message Importance="high" Text="VSTest exitcode: $(ErrorCode)"/>
<!-- Use the VSTest output to discover the Results & Coverage files respectively -->
<PropertyGroup>
<!-- Unencoded Regex: (?<=(Results file: )).*?(?=\;)|(?<=(Attachments:;)).*?(?=\;) -->
<ResultsFileRegexPattern>(?<=(Results File: )).*.trx</ResultsFileRegexPattern>
<CoverageFileRegexPattern>(?<=(Attachments:;)).*.coverage</CoverageFileRegexPattern>
<SourceResultsFile>$([System.Text.RegularExpressions.Regex]::Match($(OutputOfExec), $(ResultsFileRegexPattern)))</SourceResultsFile>
<SourceCoverageFile Condition="$(VSTestEnableCodeCoverage)">$([System.Text.RegularExpressions.Regex]::Match($(OutputOfExec), $(CoverageFileRegexPattern)))</SourceCoverageFile>
</PropertyGroup>
<ItemGroup>
<TestArtifact Include="$(SourceResultsFile)" />
<TestArtifact Include="$(SourceCoverageFile)" />
</ItemGroup>
<Warning Condition=" '$(SourceResultsFile)' == '' " Text=".trx file not found" />
<Warning Condition=" $(VSTestEnableCodeCoverage) and '$(SourceCoverageFile)' == '' " Text=".coverage file not found" />
<!-- Copy files to the artifact directory -->
<Copy SourceFiles="#(TestArtifact)" DestinationFiles="#(TestArtifact->'$(ArtifactsPath)\$(VSTestSessionName)%(Extension)')" />
<!-- Clear the test results temporary directory -->
<RemoveDir Directories="#(TestResults)" />
<ItemGroup>
<TestFile Include="$(ArtifactsPath)\**\$(VSTestSessionName).trx" />
<CoverageFile Include="$(ArtifactsPath)\**\$(VSTestSessionName).coverage" />
</ItemGroup>
<Message Text="TestReport: #(TestFile)" />
<Message Text="CoverageReport: #(CoverageFile)" />
</Target>

Create folder inside debug or release con console application

i have a console application in vs2010 (C#) and in the project, i have a Folder added by me (right click on project.. add->folder) and i want that when i compile the application (debug or release), then the folder will be created (if not exists) in the debug or release directory.
Is that possible?
The console application is a daemon that access to a database and send emails with templates allocated in that folder.
I hope you can help me. Thanks!
There's no "automatic" way to get VS to create folders (other than the specified output folder) during a build, but there's two pretty easys ways to accomplish it.
Use a post-build event, which you set up in the Build Events tab of your project's properties. This is basically a batch file that you run after the build completes, something like this:
IF NOT EXIST $(OutDir)MySubFolder MKDIR $(OutDir)MySubFolder
XCOPY /D $(ProjectDir)MySubFolder\*.tmpl $(OutDir)MySubFolder
Use MSBuild's AfterBuild event. This is my preferred method, mostly because it integrates better with our automated build process, but it's a little more involved:
Right-click on your project node and Unload it
Right-click on the unloaded project node and Edit the file
Near the bottom is a commented-out pair of XML nodes. Uncomment the AfterBuild target and replace it with something like this:
<Target Name="AfterBuild">
<MakeDir Directory="$(OutDir)MySubFolder" Condition="!Exists('$(OutDir)MySubFolder')" />
<CreateItem Include="$(ProjectDir)MySubFolder\*.tmpl">
<Output TaskParameter="Include" ItemName="Templates" />
</CreateItem>
<Copy SourceFiles="#Templates" DestinationFolder="$(OutDir)MySubFolder" ContinueOnError="True" />
</Target>
Save the changes, close the .csproj file, then right-click and Reload the project.
I solve it, like this:
in the csproj:
<Target Name="AfterBuild">
<MakeDir Directories="$(OutDir)EmailTemplates" Condition="!Exists('$(OutDir)EmailTemplates')" />
<ItemGroup>
<Templates Include="$(ProjectDir)EmailTemplates\*.*" />
</ItemGroup>
<Copy SourceFiles="#(Templates)" DestinationFolder="$(OutDir)EmailTemplates" />
</Target>
Thank you for your help!

using AppData location in NLog

My NLog targets is like this:
<targets>
<target xsi:type="Console" name="console"
layout="${longdate}|${level}|${message}" />
<target xsi:type="File" name="ErrorLog" fileName="${basedir}/error.txt"
layout="${longdate}
Trace: ${stacktrace}
${message}" />
<target xsi:type="File" name="AccessLog" fileName="${basedir}/access.txt"
layout="${shortdate} | ${message}" />
</targets>
But this causes problems if the user isn't an admin on their machine, because they will not have write access to "Program Files". How can I get something like %AppData% to NLog instead of BaseDir?
You're looking for the NLog special folders.
Example:
...fileName="${specialfolder:folder=ApplicationData}/Program/file.txt"...
Oren's answer should be the right answer. However, for the life of me I couldn't get it to work with my .NET 4.0 website using nLog 2.0.0.0. I ended up using simply
fileName="${basedir}app_data\logs\${shortdate}.log"
${specialfolder:ApplicationData} also works
The previous answers helped solve the problem I was having, but a couple of years later and the solution is now somewhat different under v4.3. The directory and filename are combined with the path.
#theGecko's link is still current for the syntax, but the page is deficient of an example:
https://github.com/nlog/NLog/wiki/Special-Folder-Layout-Renderer
The following example would write the file myLog.log to the current users application data roaming directory C:\USers\current.user\AppData\Roaming\My\Path\Somewhere:
fileName="${specialfolder:dir=My/Path/Somewhere/:file=myFile.log:folder=ApplicationData}"
For logging to the project directory:
While the previous answers work for the original question, searching for how to log to the project APP_DATA directory leads to this question. And while bkaid's answer works for ASP.NET and for using the APP_DATA folder specifically, for .NET Core and .NET 5 the solution is a bit different, because that motif has been abandoned in favor of defining a wwwroot folder for only those things which should be served, and the remainder being private. The answer for .NET Core/5, then, is to write to the solution root directory:
First, ensure the NLog.Web.AspNetCore assembly is added to nlog.config:
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
Then use one of the layout renderers provided by that extension, in this case ${aspnet-appbasepath} which references the solution root directory:
<targets>
<target name="file"
type="File"
xsi:type="File"
fileName="${aspnet-appbasepath}/log/${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}"/>
</targets>
This will write the file to <solution folder>/log/2021-07-01.log, which will never be served by the public-facing website. Other layout renderers provided by this assembly are listed on the NLog website.

Resources