Using CircleCI For publishing the NPM Package

4 min read

Node development at Relsell Global

Prologue

Before I start my blog on Using CircleCI for publishing the NPM Package, I would like to motivate you.

Every task at Relsell Global, done by following a piece of great advice by eminent late Mr. Steve Jobs.

If I try my best and fail

well I have

tried my best.

Execution

As a result, the marathon started with an idea to contribute to open source. We are skilled in Backend Node programming, so we concluded it would best to go ahead with developing a package/app in node js. But then our CEO gave us a challenge by saying

I know you people will find out a way to publish node package to the public NPM registry. But I want all that to be automated using CircleCI.

As you know challenges and problems are opportunities in disguise. Initially, we were anxious that it may take a lot of effort to do it now. But as a software devs, we like technical challenges. Right?

Developing a node app

To start with we developed a simple node package, which brings current temperature for the input city.

Let create a folder called weather-app in your system. Let’s open this folder in the command line or terminal. And initialize and empty npm project using

npm init -y 

Inside this folder create a file called app.js. Just copy paste below code

const geocode = require("./geocode/geocode.js");
const weather = require("./weather/weather.js");

var startProcess = argv => {
   var p = getGeoCoding(argv);
   p
   .then((results)=>{
      return getWeather(argv,results);
   })
   .then((result)=>{
      console.log(result);
   })
   .catch((err)=>{

   })
};

var getGeoCoding = argv => {
  return new Promise((resolve, reject) => {
    geocode.geocodeAddress(argv.a, argv.gapi_key, (errorMessage, results) => {
      if (errorMessage) {
        reject(errorMessage);
      } else {
        resolve(results);
      }
    });
  });
};

var getWeather = (argv, results) => {
  return new Promise((resolve, reject) => {
    weather.getWeather(
      argv.dapi_key,
      results.latitude,
      results.longitude,
      (errorMessage, weatherResults) => {
        if (errorMessage) {
          reject(errorMessage);
        } else {
          resolve(weatherResults);
        }
      }
    );
  });
};

module.exports = {
  startProcess,
  getWeather,
  getGeoCoding
};

Don’t worry if you can’t understand the above code, I will explain it one by one. The first two lines in the code require addition of different files. Create two folders inside the weather-app folder ie. geocode, and weather. Inside these two folders create two files geocode.js and weather.js respectively.

Add below codes to the geocode.js file

const axios = require('axios');

var geocodeAddress = (address,gmapkey,callback) => {
  var encodedAddress = encodeURIComponent(address);
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?address='+encodedAddress+'&key='+gmapkey;
  axios.get(url)
  .then((response)=>{
    callback(undefined,{
      latitude: response.data.results[0].geometry.location.lat,
      longitude: response.data.results[0].geometry.location.lng
    });
  })
  .catch((err)=>{
    callback('Unable to connect to Google servers');
  })
  .finally(()=>{

  });
 }


module.exports = {
  geocodeAddress
};

Add below code for the weather.js file

const axios = require('axios');

var getWeather = (apikey,lat,lng,callback) => {
  var url = `https://api.forecast.io/forecast/${apikey}/${lat},${lng}`; 
  axios.get(url)
  .then((response)=>{
    callback(undefined,{
            tempreture:response.data.currently.temperature,
            apparentTemperature: response.data.currently.apparentTemperature
          });
  })
  .catch((err)=>{
    callback('Unable to connect to forecast.io server '+err);
  })
  .finally(()=>{

  });

}

module.exports.getWeather = getWeather;

Now, you may remember from above, that we initialize an empty npm project. As a result, file called package.json got created. A file that has information regarding your package, dependencies on which rests on, etc.

We are using few dependencies like axios (for networking calls), yargs (for command line parameters parsing). Use below command,

npm install axios yargs 

Copy-paste below package.json file

{
  "name": "rg-weather-app-node",
  "version": "1.0.16",
  "description": "",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/successanil/WeatherAppNode.git"
  },
  "keywords": [
    "react-native",
    "react-component",
    "ios",
    "android",
    "weather"
  ],
  "author": "Author Name",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/successanil/WeatherAppNode/issues"
  },
  "homepage": "https://github.com/successanil/WeatherAppNode/#readme",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "axios": "^0.18.1",
    "npm-cli-login": "^0.1.1",
    "yargs": "^4.8.1"
  },
  "peerDependencies": {
    "axios": "^0.18.1",
    "yargs": "^4.8.1"
  },
  "devDependencies": {
    "metro-react-native-babel-preset": "^0.58.0"
  }
}

This app fetches its results from Google Maps API Google Maps API

and DARK SKY API Dark Sky API.

So you need to get required API keys from the above sites.

You can add below code to your app.js, above modules.exports line

var argv = {
    a:"<City Name>", 
    gapi_key:"<google maps key>",
    dapi_key:"<darksky api key>"
};

startProcess(argv);

You will get results as below

{ tempreture: <value>, apparentTemperature: <value> }

<value> is placeholder above.

Managing your source code using Git version control

run command

git init

Doing this will initialize your project with an empty git repository. For explaining the Git repository head to our other blog later.

Add a file .gitignore where you can specify what all should not go to the remote git repo.

I would emphasize on the fact, Please Add node_modules/ to it.

Then Add relevant files to the local git repository. Attach your remote repo with the local repository you have created. and finally, push your changes to a remote repository.

Preparing package for publishing

Since we have come up here, we are ready to publish our package to the npm registry. Let’s study a few lines in package.json file

There is a “name” attribute which will the name of the package in the npm registry which will be publicly seen by others.

Then there is a “version” that needs to be upgraded each time you plan to push an update for your package.

“description” we can describe what our package does in one line.

“repository” here we give a link to our opensource repository on GitHub or bitbucket which is public so people can see it. Do like below code

"repository": {
    "type": "git",
    "url": "git+https://github.com/successanil/WeatherAppNode.git"
  }

“keywords” are sort of array on the basis of which other people can find our package.

 "keywords": [
    "react-native",
    "react-component",
    "ios",
    "android",
    "weather"
  ]

You may add “author”. There is a few other information regarding your package that can be added.

"license": "ISC",
  "bugs": {
    "url": "https://github.com/successanil/WeatherAppNode/issues"
  },
  "homepage": "https://github.com/successanil/WeatherAppNode/#readme",

Then add metro as a dev dependency for your package.

"devDependencies": {
    "metro-react-native-babel-preset": "^0.58.0"
  }

And we are set to go run below commands,

npm login    (This may ask your some login info so ready with that) 
npm publish

Voila. And your package is published. Congrats on investing time for your growth.

Integrating CircleCI for automating the process of publishing

Head to Circle CI and create a free account. Add a new project there. I will write another blog about how to work with CircleCI, for now, let’s stick to the topic.

create an empty folder named .circleci and config.yml file inside it. Write below code in config.yml

version: 2.0
jobs:
  build:
    docker:
      - image: circleci/node
    steps:
      - checkout
      - run:
          name: Install npm 
          command: npm install
      - add_ssh_keys:
          fingerprints:
            - "05:74:84:21:d3:a9:a7:bb:7a:a7:5d:d8:7b:64:82:a3"    
      - run:
          name: Execute Index.js 
          command: node index.js -a Dehradun --gapi_key $GAPI_KEY --dapi_key $DAPI_KEY
      - run: 
          name: Auth With NPM
          command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
      - run:
          name: Publish to NPM
          command: npm publish
         
            

GAPI_KEY, DAPI_KEY, NPM_TOKEN are environment variables to be set at project settings in circleci console.

Once all this is connected, a single commit in your master branch for git will trigger automatic build via circleci and your new package will be published to the npm registry in a matter of few minutes.

Supplementary Related Blogs

We have written blogs for publishing react-native package manually to the npm registry.

Source code and package in NPM registry

Source code for the project is available at Link

Package at npm registry is available at Link

Conclusion

This brings us to the end of this blog. A lot of sincere hard work by our diligent developers is done for doing this. Thanks for their indomitable support. Sincere hard work always remains with you whether you succeed or fail in the first attempt. It might take some time to pay but it pays. Its the most reliable aspect of life.

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *