VSTS build - replace Angular4 environment variables in Release stage

The simplest and most efficient approach is to create tokens in your Angular Environment file and use a Tokenizer in your Release to replace those tokens which got compiled in to your Main Bundle. With this approach none of your existing code has to change.

This means you will be managing your environment variables in CI/CD where they belong, instead of your project. (Your project will still need a default environment file for running locally and maybe others for local testing)

To do this, first create an Angular Environment for deployment, such as environment.deploy.ts. This is what your build will use (only one build, multiple releases).

Here is an example environment.deploy.ts:

export const environment = {
  displayLeftPanel: "__env.displayLeftPanel__".toLowerCase() === "true",
  siteName: "__env.siteName__",
  apiUrl: "__env.apiUrl__",
};

(I add env. in front of the names to ensure token names do not clash with existing names in the bundle)

In your Release Variables configure these variable for each environment:

env.displayLeftPanel
env.siteName
env.apiUrl

For your release deployment you will want task such as the below (The below is for IIS, but you can use this as a road map for anything else)

The below tasks address the following issues:

  • The build artifact is typically zipped up and we cannot tokenize inside a zip file, so we have to first unzip it.
  • The WebPack bundle names have random IDs in them (example: main.20f8aa2b341c1c2f6442.bundle.js). We need to get exact file name to pass to our Tokenizer.
  • Then we just use a Tokenizer such as the "Tokenize with XPath/Regular expressions"

Here are the tasks:

  • IIS Web App Manage
  • Extract Files: *.zip to a folder like unzipped
  • PowerShell: Get Main Bundle Name. Something like: (you may need to adjust the path)

    $MainBundleFileName = (get-item $(System.DefaultWorkingDirectory)/main.*.bundle.js).FullName; Write-Host "##vso[task.setvariable variable=MainBundleFileName;]$MainBundleFileName"

  • Tokenizer: Perform variable substitutions on the Main Bundle > $(MainBundleFileName)

  • IIS Web App Deploy: The unzipped folder

You can get your config values out of the environment.xx.ts files and put them into json config files that you'll retrieve at runtime when angular bootstraps.

When releasing, use the token replace task you mentionned to replace the tokens in the json files.

The structure of the json file does not really matter, as long as the structure is the same for the config object client side (to make it easier to use). If the structure is not the same, you just need to transform retrieved data to assign it to your config object.

config.json

{
  "envName": "@@envName@@",
  "ApplicationInsights": { "InstrumentationKey": "@@xxx@@" }
}

Then you have a matching class in your angular app

export class MyConfig
{
  readonly envName: string;
  readonly ApplicationInsights:
  {
      readonly InstrumentationKey: string
  }

}

Once you've retrieved the json data angular side (called jsonData), assign it to a config object

config-service.ts

export let CONFIG: MyConfig;

//Modify jsonData if needed
let t = new MyConfig();
CONFIG = Object.assign(t, jsonData);

component.ts

import {CONFIG} from '../config-service.ts';
//...
//use it
let url = CONFIG.ApplicationInsights.InstrumentationKey;

Full implementation

https://stackoverflow.com/a/49559443/1160794