Pill: Include files in your publish profile for C# projects

Learn how to configure your publish profile in C# to include files external to the project in the final publish result.

When publishing an ASP.NET core web project, it’s often necessary to include certain files external to the Visual Studio solution but that are logical part of the project. A typical example is frontend build from angular projects. For web projects, it’s also common to include some static resources that might be outside of the web project, like images or files.

At this point, we want the Azure DevOps pipeline to correctly include all these external files in the final artifacts.

We have two solution to this problem. The first is to customize the pipeline. For example, we can include PowerShell instructions that retrieve the files for us and copy them into the right folders. Another approach is customizing the Publish profile that you can find inside your project folder. This type of file with .pubxml extension is used by the msbuild publishing process to customize publishing process and can easily configured to automatically include some external files into the final release.

In this case, it’s possible to add another ItemGroup that includes files or entire directories with paths relative to the web project being published. You can use standard wildcard to include entire directory recursively. An example is shown in this code segment below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    <ItemGroup>
        <_CustomFiles Include="..\..\..\frontend\Jarvis.UI\dist\**\*" />
        <_CustomAppFile Include="..\..\..\..\jarvis.application" />
        <_CustomImages Include="..\..\..\Frontend\Jarvis.Web.Images\**\*" />
        <DotNetPublishFiles Include="@(_CustomFiles)">
            <DestinationRelativePath>dist/%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
        </DotNetPublishFiles>
        <DotNetPublishFiles Include="@(_CustomAppFile)">
            <DestinationRelativePath>%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
        </DotNetPublishFiles>
        <DotNetPublishFiles Include="@(_CustomImages)">
            <DestinationRelativePath>www\images\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
        </DotNetPublishFiles>
    </ItemGroup>

I start including a series of path/files where the name of the node is the logical name of the file group. Then, it’s possible to use the DotNetPublishFiles node to copy all file(s) included by that logical name using a relative destination path, thus deciding where these files will be included in relation to the publication.

For example, the entire Angular part is placed inside a subfolder called dist. Clearly, in the pipeline, a previous step must be included that allows publishing the application with NPM.

Including all these files directly in the publication profile also simplifies the process for a developer to publish using the right-click and be assured that all elements are actually included in the publication. Usually, to simplify the whole process, a PowerShell build file is generated that correctly follows the front-end publishing first and then simply uses MSBuild to carry out the final project publication.

In this way, I have a single point in the project where I can specify different publishing profiles, for example, to publish only the server part of .NET, or decide to include the Angular part, etc. I don’t need to modify publication scripts if I have to add/modif extra files list, but I can simply change the publish file included in the project.

Although I’m in favor of a principle where all the publication is done via Powershell (or script in general), including the resources inside the publication file makes the project clearer because it allows anyone to understand what additional files beyond those generated by the project build are needed to generate a release package. Also your Azure DevOps scripts for pipeline will be easier to follow because you avoid doing file manipulation by yourself.

Publishing files are available for any application type in Visual Studio / .NET environment, so you can use for standard Console application.

Gian Maria.