VueJS - How do I design a project that can handle multiple .env file

Viewed 149

Current Architecture

So, I have a large scale vue project that serves multiple types of users from multiple subdomains. I maintain the project through netlify and point specific git branches to specific subdomains.

Let's call them Site One, Site Two and Site Three. I have three separate git branches to control them something like main, mini and micro branch.

Each subdomain/sites or branches has different features turned on or off depending on the targeted users.

I controlled them through a separate settings.json file until now. by branching off to different branches, I made separate commits to update the settings.json to achieve the behavior of each sites. (Don't think now it's a good idea though)

Here is a rough idea on how the settings.json looks like (which varies branch to branch):

{
  available_products: ['one', 'two', 'three'],
  feature_one_enabled: false,
  product_journey: 'ZIPPED'
  .....
  mini_mode: true,
}
// and many more flags;

Flaw

Here comes the pain point, when I find a bug that exists in all the branches and requires fixing in all of them, I can't just merge a branch into another because there were other changes targeted for the other branches.

Also the features incoming are most of the time unique and the current ones are periodically changing.

Question 1: is it a good practice to keep configuration flags to turn on or off the features in a .json file? Or should I go with .env files.

What now

So I have attempted with the fixing of the branch modeling and and keeping the codebase always unique, keeping checks for the configuration setting properties to enable features depending on the branch or the settings.json .env file.

Followed this branching model btw.

So, the branch and keeping the codebase identical in every branch is solved. Here comes the configurations thing. I can provide different .env values to netlify to control the features and all.

But when I am doing local development it will become a bit hassle because I have to set two .env file for each branch (total 6 for { n = 3 branches * 2 modes })

Question 2: how can I keep all the .env files locally and change them automatically through some npm command or branch changes ?

Or do I have to make some changes to the current architecture?

1 Answers

So, answer is for those whoever stumbles upon this question and wonder if I could ever solve this.

Yes

I could solve this with the help of a self made plugin and some other helper script files by modifying some vue-cli and webpack configurations.

The concept raised from this thread discussing if it's possible to asynchronously (coming up next) pass some data to the webpack configurations just before running the npm server.

The key solution was using chainWebpack for modifying the config.

Why asynchronously

Like I said, in order to make the local development painless, I wanted to use .env files according to my current git branch. But the default command shouldn't be changed and I wanted no extra command for that.

Lets code

I used dotenv-webpack to set my .env files and moved the files from the root / directory to a subdirectory called /env

/node_modules
/src
/public
/tests
/env // <- this

Having them in the root directory was messing up the priority of webpack deciding the *.local files despite of passing different directory to the config

And the directory now contains files like:

.env.branch-one
.env.branch-two
.env.branch-three
.env.branch-four
...

Since I wanted a single command to do it all, I added a local plugin and registered my command to npm script. So in my devDependencies in package.json I added this:

"vue-cli-plugin-dynamic-env": "file:src/plugins/vue-cli-plugin-dynamic-env",

And updated the current run serve command:

"scripts": {
  ...
  "serve": "vue-cli-service serve:auto",
  ...
},

Here is the plugin structure (in /src/plugins/vue-cli-plugin-dynamic-env):

index.js // defines the plugin
generator.js // didn't do much here since it's only local plugin
getCurrentBranchName.js // helper for getting branch name
package.json

Now in the index.js file, I can import and use dotenv-webpack to pass custom env files with the help of chainWebpack, and I can use my helper script to get the current git branch to generate the branch name.

And then register a new command serve:auto which was previously added to package.json and at the end of the command definition, I can programmatically run the built in vue-cli serve command.

Here is how it looks: :

const Dotenv = require('dotenv-webpack');
const activeGitBranch = require('./getCurrentBranchName');

module.exports = (api) => {
  api.registerCommand('serve:auto', async (args) => {
    const branchName = await activeGitBranch(); // getting async value here
    api.chainWebpack((config) => {
      config.plugin('env').use(Dotenv, [{
        // fallback to default file if branch name does'nt match a .env file
        path: branchName ? `./env/.env.${branchName}` : './env/.env', 
        safe: false,
      }]);
    });
    await api.service.run('serve', args); // programmatic call to serve command
  });
};

module.exports.defaultModes = {
  'serve:auto': 'development', // setting default env mode, this is requried.
};

If you're wondering what does getCurrentBranchName.js file do and how. Here it is:

const { exec } = require('child_process');

module.exports = function currentBranchName() {
  return new Promise((resolve, reject) => {
    exec('git rev-parse --abbrev-ref HEAD', (error, stdout, stderr) => {
      // console.log(branchName);
      if (error) { // handle your error
        if (error.code === 128) {
          reject(error);
        }
      }
      if (stderr) {
        reject(stderr);
      }
      resolve(stdout.trim());
    });
  });
};

And that's it.

Cheers
Related