Blog

30
May 2015

Gavin Pickin

Cordova Hooks - Deeper Dive into my Environment Setup Hook Written in NodeJS

Android, Cordova / Phonegap, Dev.Objective, Ionic, IOS, Mobile Development, Node.js

After my session about Dev.Objective() called “Getting your Hooks into Cordova - Workflows and Build Helpers”, I decided I should do a deep dive into each of my Hooks I discussed in my presentation, and today we’ll look at my Environment Setup Hook, written in NodeJS.

My presentation mini site is available here on my blog at http://www.gpickin.com/devobj2015/cordovahooks/

As I add more blog posts about this topic, I will continue to update the mini site.
The minisite has links to the gists of all the hooks I had, the slides and links to the few blog posts I did find on the topic.

For more information on what hooks are, please refer to my Session Review and Overview here: Post Conference - Session Review - Getting your hooks into Cordova- Workflows and Build Helpers

One of the first Hooks in my hybrid mobile workflow with Cordova, is my environment setup hook.
This hook is setup to run in the BEFORE_BUILD.
This hook is written with NodeJS, so it will work on MacOSX and Windows, and this hook is useful with iOS, Android, Windows, any Hybrid Mobile Platform. 

When working on an app, on your development machine, or in staging for a customer, or when pushing to the App store, you are most likely going to have certain aspects of your code that behaves differently. You can manually change these variables, or comment out your console.log when pushing to the app store, but thats a big pain, a pain point… so this is the solution I came up with.

First step, I decided to remove the tight coupling with the environment, by removing the API urls, API keys, debug flags etc from inside the code itself, and created a file called env.js which contained a variable with those environmental settings in it, that I include in my project. This allowed me to manually uncomment the right Javascript Object, and when I build, then my app would refer to app.env.api_key to access the correct api key.

Next step was to automate this process, this is where the hook came in.
I setup a file for each environment I wanted to use. env.debug.js and env.staging.js and env.prod.js in this case. 

The hook copies the contents of the right file, into env.js, and then my app loads that object and the code references that for anything environment related.

Lets look at the code, and I’ll break it down as we go.
https://gist.github.com/gpickin/9b6edbfa49d2879b4359

 

#!/usr/bin/env node

First line… you need a line to define what type of hook it is, so the right application can process it. In this case, its Node

 

var environmentList = ['debug','staging','prod'];

This next line sets up the list of environments I want my app to support. This also enforces the environments, if one is passed that is not in this list, the app will default to the first item in the array, you’ll see that soon.

 

var fs = require("fs");
var path = require("path");

Next we include 2 node modules we’ll make use of in this hook.

 

console.log('-----------------------------');
console.log("Running hook: "+path.basename(process.env.CORDOVA_HOOK));

Next we output some text to the console, to provide an update to the process, a separator, and a line telling the user what Hook is starting to run.
var buildEnvironment = environmentList[0];
This is where we set the build Environment to the default, which is the first in the array. If all else fails, the hook sets up the app for this environment.

 

if (process.env.target && environmentList.indexOf( process.env.target ) > 0 ) {
    buildEnvironment = process.env.target;
    console.log( 'Setting environment');
}
else {
    console.log( 'Using Default Environment' );
}

Next, if there is a target environment passed in, and it actually exists in our array of environments we intend to support, then we set the buildEnvironment to that target.
Otherwise, we leave the environment set to the default, and proceed. 
In both cases, we output a message to the CLI so the user can debug if needed.

 

console.log('Using environment: ' + buildEnvironment);

We output the name of the environment we will be using, again for debugging.

 

var theSourceFile = path.join( path.resolve(),'www','js') + '/env.' + buildEnvironment + '.js';
var theDestinationFile = path.join( path.resolve(),'www','js') + '/env.js';
console.log( theSourceFile );

Next we set the source and destination file paths, so we can work with them.
We output the name of the source file to the CLI.

 

if (fs.existsSync(theSourceFile)) {
    console.log('it exists');
    ...
}
else {
    console.log('Config File ' + theSourceFile + ' NOT FOUND');
    console.log(theDestinationFile + ' NOT CHANGED');
    console.log('-----------------------------');
}

The next section has a large if statement, if the source file exists, we log that it exists, and then proceed (code below), otherwise we log the fact the source file was not found, and the env file was not updated… and the hook ends.

 

fs.readFile( theSourceFile, function(err, buf) {
    ...
});

Inside the if, if the source file exists, we read the file, and the result of this async function, is err, data, where data is the contents of the file. Since there was no encoding passed, data is in the form of a buffer. 

 

if (typeof buf !== 'undefined') {
    ...
}
else {
    console.log('Config File ' + theSourceFile + ' NOT FOUND');
    console.log(theDestinationFile + ' NOT CHANGED');
    console.log('-----------------------------');
}

Next we check the buffer, if the buffer is not undefined, we process more. Otherwise we output the source file was not found, and the destination file was not changed, and we stop processing the hook.

 

console.log('Read from file ' + theSourceFile);
fs.writeFile(theDestinationFile, buf.toString() , function(err) {
console.log('Wrote to file ' + theDestinationFile);
console.log('-----------------------------');
if (err) throw err;

Next we output that we read the file, and then we convert the buffer.toString() and save to the env.js file. We output that we have completed, and the hook has ended.

Now, how do we actually use this hook? On *nix systems, like Mac OSX, we can just prepend environment settings like below
#: target=prod cordova build ios

On windows, you have to change things up… you use the set command, to set environment variables first, then use the cordova build command
#: set target=prod
#: cordova build windows

Here is the whole file… also available at https://gist.github.com/gpickin/9b6edbfa49d2879b4359

 

 

The beauty of this is, it is simple, and easily extended, so you could add targets for iOS, Android etc too, and then build even more complex setups.
Its saved me hours of time already, let me know if you use something like this, and what else it could be used for.

Thanks for reading.

Blog Search