Eneko
Eneko

Reputation: 2107

Build angular app with multiple environments and multiple flavours

We have a product that includes an Angular web app that uses Firebase with multiple costumers. Each costumer has its own Firebase instances (dev and prod). Firebase is used for hosting the web app and the app needs Firebase credentials for accessing Firestore, Auth and Cloud Storage.

We would like to setup angular.json in such a way that when deploying to a new client we have to add the minimum posible of lines to pick the correct Firebase configuration.

It is not difficult to have multiple environments using fileReplacements options, but I don't see how to share configs between them, so we don't have to put prod and dev specific configs in all customers.

I think we could build a custom CLI builder that swaps config files, but that seems a huge task for something so simple. All customers share the same code base.

My experience with build tools is limited, I hope I am missing an easy way to accomplish this.

Thanks in advance!

Upvotes: 1

Views: 1187

Answers (2)

Eneko
Eneko

Reputation: 2107

As August 2019 angular-cli does not support multi-dimension environment. I ended writing up my own simple node script for serving and building.

The script copies environments/{projectId}.ts to environments/environment.ts and launches ng build|serve. I removed production:fileReplacements in angular.json so --prod mode does not replace the file.

Now, when and new costumer arrives I only have to create projectId.*.ts files in environments with firebase credentials and other custom variables.

For developing I use node build-cli.js serve customer-dev -o and for building node build-cli.js build customer-prod --prod.

build-cli.js

const fs = require('fs');
const path = require('path');

const argv = require('yargs').demandCommand(2).argv;

// You can make the script self-explainable very easy with yargs,
// highly recommended if you are in a team!

const command = argv._[0];
const projectId = argv._[1];

// Absolute path of environments
const pathOrigen = path.join(__dirname, 'src', 'environments', projectId + '.ts');
const pathDestino = path.join(__dirname, 'src', 'environments', 'environment.ts');

// Test file exists and so...

try {
  fs.copyFileSync(pathOrigen, pathDestino);
} catch (error) {
  console.error(error);
  process.exit(0);
}


// Collect options for ng (-o and --prod).
const options = [command];
if (argv.o) {
  options.push('-o');
  if (command === 'build') {
    console.error('Errror: build and -o are not compatible.\n\n');
    yargs.showHelp('log');
    process.exit(0);
  }
}
if (argv.prod) {
  options.push('--prod');
}
// Launch ng with selected options
let childRunning = true;
console.log('Launching ng ' + options.join(' '));
const child = require('child_process').spawn(
  'ng',
  options, {
    stdio: 'inherit'
  }
);

// If child dies we exit
child.on('close', (code) => {
  childRunning = false;
  process.exit(0);
});

// If we exit we kill the child
process.on('exit', function() {
  if (childRunning) {
    child.kill();
  }
});

Upvotes: 0

Doug Stevenson
Doug Stevenson

Reputation: 317487

Since you're using Firebase Hosting, there is a special URL you can use that will give you your project's specific configuration. This is described in the documentation. Specifically, the second on SDK auto-configuration. If you load the following relative URL from your web pages like this script include:

<script src="/__/firebase/init.js"></script>

It will provide the default configuration for the project and initialize the Firebase SDK. You can also load this script dynamically if you don't want to use a script include.

If you do this, you will also have to test your web site locally with the Firebase Hosting emulator, as it will correctly interpret the special URL for the current project.

Upvotes: 1

Related Questions