We'recurrently setting up an SPA with Angular 4, hosting it with Docker on Azure. Usually, the environments (Prod, Development, Testing) are set in Angular with the Environment-Configs, which are compiled, if we use the Angular-CLI build command. This is great, but the workflow with Docker is a bit different:
- Build the Angular App
- Initialize the Docker container
- Set the Docker Container environment variables
- Start the Docker container
This means we have a timing problem, because on compile-time, we can't say, on which environment the App will run. For the server (.net core), this is no problem, since we can use the ASPNETCORE_ENVIRONMENT variable, but we didn't find a solution to make the App aware of the environment-type.
I'm almost certain we're not the only people around with this problem, yet I didn't find proper solution so far. Is there some possibility around we aren't aware of?
Since my main goal was to not create a Container per environment but also to keep the build process environment-agnostic, I came up with the following solution:
I'm using the docker-stack file to add a command (My Docker image is NGinx):
command: /bin/bash -c "cp /usr/share/nginx/html/assets/app-settings/appsettings.__EnvironmentName__.json /usr/share/nginx/html/assets/app-settings/appsettings.json && find "/usr/share/nginx/html/assets/app-settings/" -type f -name "*.json" -not -name "appsettings.json" -exec rm {} ';' && exec nginx -g 'daemon off;'"
The command does three things:
- Copy a environment specific appsettings.json (see below) as
appsettings.json
- Remove all appsettings.xxx.json except
appsettings.json
- Start nginx
The release process also replaces EnvironmentName with the specific environment it is deploying to. This means, each container has now an appsettings.json in place with the environment-specific data.
On the angular code, I'm using the environment-files just for compile-time, environment-agnostic, information:
export const environment = {
production: false
};
The appsettings are saved as assets:
To read the runtime information of appsettings.json, I've created a appsettings provider service, using a local http call:
@Injectable()
export class AppSettingsProviderService {
private appSettings: AppSettings;
public constructor(private http: LocalHttpService) {
}
public async provideAppSettingsAsync(): Promise<AppSettings> {
if (this.appSettings) {
return Promise.resolve(this.appSettings);
}
// This path needs to be relative to the files in the dist-folder
this.appSettings = await this.http.getAsync<AppSettings>('./assets/app-settings/appsettings.json');
return this.appSettings;
}
}
This works also during development, since just the appsettings.json is used in this case and even with ng serve, the code is compiled into dist, meaning the relative path is still correct.