Currently I have three different environments in Angular4:
Now, in vsts build pipeline I have set up multi-configuration where one definition prepares artifacts for debug and release:
I am using "Replace tokens" task to replace variables per debug and release environment, before I run npm install and npm run {either debug or release) which then runs webpack to pack files for debug or release environment.
I saw there is an option to replace variables in Release where you can replace variables in your .json file(like appsettings.json):
But problem is when webpack packs source code into one bundle.js I think I cannot use release to replace environment variables? Can I?
So what I want to achieve is decouple debug and release builds. This is simple I just recreate separate definitions for debug and release, where I am only replacing variables per environment. Second stage is to actually remove Replace tokens task from build pipeline and use Release variables section to replace variables per environment set up in Release. How is that possible for Angular after webpack builds bundle in js?
You define and manage these variables in the Variables tab in a release pipeline. In the Pipeline Variables page, open the Scope drop-down list and select "Release". By default, when you add a variable, it is set to Release scope. Share values across all of the tasks within one specific stage by using stage variables.
To use a variable group, open your pipeline. Select Variables > Variable groups, and then choose Link variable group. In a build pipeline, you see a list of available groups. In a release pipeline, for example, you also see a drop-down list of stages in the pipeline.
Share variables between Tasks across the Jobs (of the same Stage) We need to use the isOutput=true flag when you desire to use the variable in another Task located in another Job.
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:
main.20f8aa2b341c1c2f6442.bundle.js
). We need to get exact file name to pass to our Tokenizer.Here are the tasks:
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)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With