Wednesday, June 19, 2024

Obfuscar: how to stop build of .NET app with error when obfuscation failed

In my previous post I showed how to obfuscate ASP.Net Core web app using Obfuscar. There was however one problem: if obfuscation will fail it will be ignored by VS by default. In the current post I will show how to make such errors more visible i.e. how to cause build to fail if obfuscation failed.

First of all we need to move obfuscation logic to PowerShell file (postpublish.ps1 in this example)  located in project folder and specify it in post publish event:

  <Target Name="PostBuild" AfterTargets="Publish" Condition=" '$(Configuration)' == 'Release' ">
    <Exec Command="pwsh -ExecutionPolicy Unrestricted $(ProjectDir)postpublish.ps1 -targetFramework $(TargetFramework) -buildConfig $(Configuration) -projectDir $(ProjectDir) -publishDir $(PublishDir)" />
  </Target>

(if you are interested why pwsh command is used instead of powershell check the following article Use PowerShell in Visual Studio build events when build Docker image for .Net app)

It is up to you to implement actual obfuscation logic, in my example I will use cross platform Obfuscar dotnet global tool:

$s = dotnet tool list -g | Out-String
if (!$s.Contains("obfuscar.globaltool")) {
	Write-Host "Obfuscar global tool not installed. Installing it..."
	& dotnet tool install --global Obfuscar.GlobalTool
} else {
	Write-Host "Obfuscar global tool is already installed"
}

$targetDir = [System.IO.Path]::Combine($projectDir, $publishDir)
$obfuscarXmlPath = [System.IO.Path]::Combine($targetDir, "obfuscar.xml")
Write-Host "Obfuscating assemblies..."
[void]($output = & obfuscar.console $obfuscarXmlPath)
$output
if ($LASTEXITCODE -ne 0) {
	Write-Host "postbuild.ps1: General error Code: Obfuscation failed"
	return
}

Here we first check whether obfuscar.globaltool already installed, if not we install it. Then we run obfuscar.globaltool with specified config file (obfuscar.xml) and check exit code: if it is not 0 (0 code means success) we write the following message to the output:

 postbuild.ps1: General error Code: Obfuscation failed

That is actual trick: message has special format recognized by Visual Studio. In this case VS will understand that error happened in post publish event and will show error in the build output. More details can be found here: Emitting custom warnings/errors in Visual Studio custom build events. With this approach you will see build errors if obfuscation failed.

Wednesday, June 5, 2024

Use PowerShell in Visual Studio build events when build Docker image for .Net app

Imagine that we have .NET application and created Docker file for it which often contains build and runtime stages (in this example I use .Net6 but it is also valid for higher versions):

# build stage
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
COPY ...
RUN dotnet restore ...
RUN dotnet publish ...

# runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS final
...

and that our VS solution contains build steps which use PowerShell (assume that it is cross platform PowerShell v.7.x). These steps will run during "dotnet publish" step on build stage. You may face with the following error:

powershell command not found

Quick way to fix it is to use "pwsh" command instead of "powershell". It will work because PowerShell became part of .Net SDK docker images since 3.0 preview like described in the following article: Installing PowerShell with one line as a .NET Core global tool. Note also that .Net SDK images are Linux based where "pwsh" command is usual for running PowerShell.

If because of some reason you don't want to change "powershell" to "pwsh" in the code (e.g. if you need to build apps both on Windows and Linux) you may use the following approach: in Docker file add the following command:

RUN ln -s pwsh /usr/bin/powershell

It will add symlink "powershell" which in turn will run pwsh. After that your powershell commands in build events will work both on Windows and Linux including Docker images.