'How do I publish a .NET Core + Angular single page app for different environments?

Some additional context: I created a solution using the Angular project template with ASP.NET Core in Visual Studio 2019. The app has several different environments: DEV, STG, and Production. There are corresponding "appsettings.json" files for the .NET project ("appsettings.DEV.json", "appsettings.STG.json", "appsettings.Production.json"), as well as "environment.ts" files in the "ClientApp" folder ("environment.DEV.ts", "environment.STG.ts", "environment.Production.ts"). This app will be deployed to IIS on a Windows Server, and I have created ".pubxml" publish profiles targeting a folder for each of these environments.

The project template generates the following in the .csproj file:

<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
  <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr -- --prod" Condition=" '$(BuildServerSideRenderer)' == 'true' " />
</Target>

The .pubxml files I created set the value of ASPNETCORE_ENVIRONMENT like this:

<environmentVariables>
  <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="DEV" />
</environmentVariables>

I have also added build scripts for the Angular client app in the "package.json" file for each environment:

"scripts": {
  "build-dev": "ng build -c DEV",
  "build-stg": "ng build -c STG",
  "build-prod": "ng build -c Production",
}

However, I would like to specify which of those scripts to run, depending on which publish profile was selected. I tried modifying the ".csproj" file with the following, but it does not work. I can't figure out how to get the value of ASPNETCORE_ENVIRONMENT in the .csproj file:

<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
  <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build-dev" Condition="'$(ASPNETCORE_ENVIRONMENT)' == 'DEV'" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build-stg" Condition="'$(ASPNETCORE_ENVIRONMENT)' == 'STG'" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build-production" Condition="'$(ASPNETCORE_ENVIRONMENT)' == 'Production'" />
</Target>

What is the correct way to publish the entire app, building both the .NET app and the Angular app in the "ClientApp" folder for different environments?



Solution 1:[1]

I was able to resolve my issue. Here are the changes I made in order to successfully publish the entire .NET 5 & Angular 11 single page app to be deployed to IIS on Windows Server:

Solution configurations for each environment (DEV, STG, and Production) were added:

VS Solution Configurations

The ".csproj" file was updated with the following ($(Configuration) was used instead of $(ASPNETCORE_ENVIRONMENT)):

<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
  <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build-dev" Condition="'$(Configuration)' == 'DEV'" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build-stg" Condition="'$(Configuration)' == 'STG'" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build-production" Condition="'$(Configuration)' == 'Production'" />

  <!-- Include the newly-built files in the publish output -->
  <ItemGroup>
    <!--<DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />-->
    <DistFiles Include="$(SpaRoot)dist\**;" />
    <DistFiles Include="$(SpaRoot)node_modules\**" Condition="'$(BuildServerSideRenderer)' == 'true'" />
    <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
      <RelativePath>%(DistFiles.Identity)</RelativePath>
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
    </ResolvedFileToPublish>
  </ItemGroup>
</Target>

The scripts section of the "package.json" file was updated with the following:

"scripts": {
  "build-dev": "ng build -c DEV --base-href=/MyAppName/",
  "build-stg": "ng build -c STG --base-href=/MyAppName/",
  "build-prod": "ng build -c Production --base-href=/MyAppName/",
}

Setting the base-href value in the scripts for building the Angular app solved errors 404 I was seeing when the app is not published at the site root.

Finally, the ".pubxml" publish pofiles I created were removed, and instead I used the dotnet publish command with the -c configuration argument. Ex:

  • dotnet publish -c DEV
  • dotnet publish -c STG
  • dotnet publish -c Production

The files to deploy are output to the "\MyAppName\bin\{CONFIGURATION}\net5.0\publish\" folder.

Solution 2:[2]

You can set your environment configuration in each of your pubxml files as explained in the official documentation:

<PropertyGroup>
  ...
  <EnvironmentName>Development</EnvironmentName>
</PropertyGroup>

And then create one PublishRunWebpack... Target section for each environment in your csproj file:

<Target Name="PublishRunWebpackDevelopment" AfterTargets="ComputeFilesToPublish" Condition="'$(EnvironmentName)' == 'Development'">
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build-dev" />
  ...
</Target>

<Target Name="PublishRunWebpackStaging" AfterTargets="ComputeFilesToPublish" Condition="'$(EnvironmentName)' == 'Staging'">
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
  <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build-stg" />
  ...
</Target>

...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mfcallahan
Solution 2 Rafael Neto