webpack | Build Time Environment Variables With Azure DevOps Yaml CI/CD
Problem Space
One of my recent projects has involved the use of a static module bundler called webpack to bundle a typescript site so I can serve static content from a Static Web App in Azure.
For a while now the site content has not deviated between environments [ dev / test / prod ] and therefore we have simply built and bundled the site for deployment.
Recent changes however have required that there be some deviation between environments, at which point the question raised, at what point should this deviation be set or retrieved?
const someConfigurationSetup = "Static but deviates between environments";
const someConfigurationSetupPartTwo = "Static but deviates between environments";
Due to the site having a backing Azure Function serving its API calls, I had two available options:
- Is the deviation something that should be served at runtime?
Points to the Azure Function APIs.
- Is the deviation something that should be baked in at build time?
Points to the webpack bundler.
The deviation in this case does not change throughout the running of the site and also has no security constraints preventing the deviation from being served as static content. My choice therefore proceeded with the second option: webpack bundler.
But how do I achieve this with the webpack bundler? I have a Azure DevOps CI/CD Yaml pipeline that builds and packages the site using npm and the webpack bundler ready for the environment deployments. What is the most appropriate method that will fit in with my current methods?
Solution
After some digging, I came across the webpack EnvironmentPlugin. The use of the Environment Plugin allows me to specify environment variables within my typescript that will then be resolved by the webpack bundler at build time.
This changes the typescript shown above to the following:
const someConfigurationSetup = process.env.CONFIG_SETUP_ONE;
const someConfigurationSetupPartTwo = process.env.CONFIG_SETUP_TWO;
By default if process.env.VARIABLE
is not defined by your webpack.config.js, you will receive a ReferenceError: process is not defined
.
Defining the environment variables in the webpack.config.js is done within the plugins section such as the following:
module.exports = async (env, options) => {
const config = [
{
devtool: ...
entry: {
...
},
resolve: {
...
},
module: {
rules: [
...
],
},
plugins: [
new DefinePlugin({
'process.env.CONFIG_SETUP_ONE': JSON.stringify(process.env.CONFIG_SETUP_ONE),
'process.env.CONFIG_SETUP_TWO': JSON.stringify(process.env.CONFIG_SETUP_TWO),
}),
...
],
},
{
...
}
];
return config;
};
With the plugin and variables defined, the webpack bundler will now resolve my variables with any environment variables defined through methods such as:
- Setting through PowerShell (environment variable setup will differ per scripting language):
$env:CONFIG_SETUP_ONE = "" $env:CONFIG_SETUP_TWO = "" npm run build
- Setting through .env file
If no environment variables are found, the variable resolve will result in one of the following:
undefined
for variables that must be provided during bundling.null
if they are optional.
In my case I would like the variables to be resolved within my Azure DevOps Yaml CI/CD Pipeline for the respective environment.
Thankfully, not much work was required here from my part. I am already making use of Library Variable Groups within my pipeline for the respective environment stages. In DevOps pipelines, variables are made available to scripts and tasks through environment variables such as the one I configured: CONFIG_SETUP_ONE. This has resulted in no change to the existing yaml task for the resolve to work:
- job: 'Build_Test_Solution'
pool:
vmImage: windows-latest
variables:
- group: Dev # Holds the Environment Variables such as CONFIG_SETUP_ONE, CONFIG_SETUP_TWO
steps:
- task: Npm@1
displayName: npm install
inputs:
workingDir: '$(System.DefaultWorkingDirectory)/Project'
verbose: false
- task: Npm@1
displayName: npm run build for test environment
inputs:
command: custom
workingDir: '$(System.DefaultWorkingDirectory)/Project'
verbose: false
customCommand: run build
Hope this helps, and have fun.