Continuous integration on server side – Part 2

Create own grunt task

In the previous post I mentioned an extra task, cleaning folders and removing the folders as well.

We found a tool to clean a folder called grunt-contrib-clean.
But we had two problems with that tool.

  • It doesn’t support removing the folder itself. It only clears the content of the specified folder.
  • You can use relative path from a specified base directory but you can’t use back navigation in the path.

At this point I need to admit that I didn’t do a brighter resource after checking the grunt-contrib-clean tool because I wanted to write an own grunt task and it was good opportunity to start writing one.

At first I started with checking the documentation on the webpage of the grunt.
I thought that it will be a good start but after reading the “Create own task” section I didn’t have the clue how to really start the coding.

Rather as a second option I checked the implementation of the uglify and the yuidoc tasks.
As I mentioned it in the previous post we use that two tools in our application and we found grunt task to execute them as well.

And that’s the part of the javascript/opensource world I love the most. You can read and learn from other’s implementation.
There are really good examples outside, like the two grunt tasks I chose.

After checking this two implementations I had the clue how to start.

My first implementation was this:

        
        module.exports = function (grunt) {

    	grunt.registerTask('myclean', "Remove folder and it's content recursive", function () {
    		grunt.log.ok();
    	}

and it worked! : )

I just loaded it into my Gruntfile.js:

 
  	grunt.loadTasks('core/tasks'); is the way to load it into the the actual Gruntfile.js

and after running it in my terminal the result was:

terminal_ok

At his point I had the skeleton and I could start to fill it with content.

As I mentioned it earlier I wanted to use relative path in the settings of the task.
I wanted to use ‘..’ in the path to step back one folder.

And I wanted to remove not just the full content of the specified folder but the folder itself as well.

Let’s start coding our own task.

As the result we wanted to see the following customisable task in the Gruntfile.js:

You can get the user setting of your task in the implementation of the task with the options command

     	
    	        var options = this.options({
        });

It loads you the options from the gruntfile.
Of course you need to check the existence of the expected option

                    
        if (!options.cleanPath) {
            grunt.log.writeln('cleanPath is empty or not defined');
            return false;
        }

After that you are safe here and you can use the ‘cleanPath’ filled with the value that the user specified

I show my implementation about creating the right path to clean:

        
        var baseDir = path.normalize(__dirname + '/../..'); //Server folder
                var parts = value.split('/');
                var endPath = '';

                for (var i = 0; i < parts.length; i++) {

                    if (parts[i].indexOf('..') != -1) {
                        baseDir += '/../';
                    } else {
                        endPath = path.join(endPath, parts[i]);
                    }
                }

                var actualDir = path.normalize(baseDir);

                var preparedPath = path.join(actualDir, endPath);

                deleteFolderRecursive(preparedPath);

With this implementation I can set the clearPath in my Gruntfile with the following value: “../../Deployment/Server”
and it will navigate from the current folder (Source/Server) back to the main project folder and then into the other sibling folder I gave to it.

And the delete part:

         
        var deleteFolderRecursive = function (cleanPath) {

            if (fs.existsSync(cleanPath)) {
                fs.readdirSync(cleanPath).forEach(function (file) {

                    var curPath = cleanPath + "/" + file;
                    if (fs.statSync(curPath).isDirectory()) {
                        deleteFolderRecursive(curPath);
                    } else { // delete file
                        fs.unlinkSync(curPath);
                    }
                });
                fs.rmdirSync(cleanPath);
            }
        };      

…witch deletes the specified folder and its content as well.

I don’t want to explain my code because it not so complicated I just want to highlight some grunt and node feature that I used and that are good to know for you as well

  • __dirname – gets the actual directory path, where the file is located.
  • path.normalize – it will parse the ‘..’ back navigation if it is at the end of the path
    • folder1/folder1.1/.. –> folder1
    • folder1/folder1.1/../folder1.2 –> will not navigate to folder1/folder1.2
  • path.join – after splitting the file path by ‘/’ it will create a correct path from the parts.
  • grunt.util has many other useful functions
    I used only _.each() to iterate through the input array but there are methods to check the types of the input parameters and a lot of other good stuff.

Task vs. Multitask

If you create task with the “registerTask” method call you can define the task later only with one settings.

  
        myclean: {
                options: {
                    cleanPath: ['./docs', '../../Deployment/Server']
                }
            }

But what if you don’t want to always clear all the paths you defined at first.
What if in certain situations you only want to remove he ‘docs’ folder.

With ‘registerTask’ you can’t do that, BUT with ‘registerMultiTask’ it is possible.
You only need to rewrite the method name. The content of the task can remain the same and grunt will do for you everything else.

From that time you can set more execution options and name them with an arbitrary name.

  
        myclean: {
            default: {
                options: {
                    cleanPath: ['./docs', '../../Deployment/Server']
                }
            },
            onlyDocs: {
                options: {
                    cleanPath: ['./docs']
                }
            }
        }

and later that you can use them like:

          
	grunt.registerTask('default', ['myclean:default', 'yuidoc', 'uglify']);

	grunt.registerTask('onlyDocs', ['myclean:onlyDocs','yuidoc']);

After that you can run the grunt with different switches:

Write ‘grunt’ into the terminal and it will run the ‘grunt default’. First it cleans every folder, then creates the documentation and then uglifies and copies the files into the Development folder.
And running ‘grunt onlyDocs’ executes the second set of tasks. It clears only the ‘docs’ folder and regenerate the documentation.

That’s it.

Spoiler

This was our start with grunt on server side. We created the base tasks and a self-made task as well. But the journey doesn’t end here. Int he following post of this series we will check how to configure grunt to run tests and run our express server.

Advertisements

2 thoughts on “Continuous integration on server side – Part 2

    • Thank you for showing me this! Contributing could be a really good thing. Next time I will check the issue list of the tool what I use, I may find a solution to an issue or maybe somebody did it already. In this case I need to admit that I really wanted to write an own task and I couldn’t find a better excuse to write one. : )

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s