docs: function Deploy(options, done) {
    var releaseName = moment().format('YYYYMMDDHHmmss'),
        that = this;
  
    this.options = options;
    this._commands = [];
  
    this._tasks = {};
    this._rollbacks = [];
  
    // before and after tasks hashes
    this._before = {};
    this._after  = {};
  
    options.branch = options.branch || 'master';
    options.keepReleases = options.keepReleases || 3;
    options.tasks = options.tasks || {};
  
    this._folders = {
      logsPath:     path.join(options.deployTo, '/logs'),
      sharedPath:   path.join(options.deployTo, '/shared'),
      releasesPath: path.join(options.deployTo, '/releases'),
  
      currentPath:  path.join(options.deployTo, '/current'),
    };
  
    this._folders.releasePath = path.join(this._folders.releasesPath, releaseName);
  
    this.run('ls -1 {{releasesPath}}', { quiet: true });
    this.exec(function (err, results) {
      var ls = results[0],
          releases = ls.trim().split('\n'),
          length = releases.length,
          folders = that._folders;
  
      _.extend(folders, {
        currentRelease:  length > 0 ? path.join(folders.releasesPath, releases[length - 1]) : null,
        previousRelease: length > 1 ? path.join(folders.releasesPath, releases[length - 2]) : null,
      });
  
      // for current release it will be redefined to {{releasePath}}
      folders.latestRelease = folders.currentRelease;
  
      registerTasks();
      registerUserTasks();
      done();
    });
  
    function registerTasks() {
      var task = that.task.bind(that),
          invokeTask = that.invokeTask.bind(that);
  
      task('setup', function (run) {
        run('mkdir -p {{releasesPath}} {{logsPath}} {{sharedPath}}');
      });
  
      // restart your app, currently it's empty, could be redefined
      task('restart');
  
      // Rollback
      task('rollback', function (run) {
        if ( that._folders.previousRelease ) {
          run('rm -Rf {{currentPath}}; ln -s {{previousRelease}} {{currentPath}}');
          invokeTask('rollback:cleanup', this.async());
        } else {
          throw 'Rolling back is impossible, there are less then 2 releases.';
        }
      });
  
      task('rollback:cleanup', function (run) {
        run('if [ `readlink {{currentPath}}` != {{currentRelease}} ]; then rm -rf {{currentRelease}}; fi');
      });
  
      // Deploy
      task('deploy', function (run) {
        that._folders.latestRelease = that._folders.releasePath;
  
        that.invokeTasks(
          ['updateCode', 'npm', 'createSymlink', 'restart', 'deploy:cleanup'],
          this.async()
        );
      });
  
      task('updateCode', function (run, runLocally, onRollback) {
        onRollback(function () {
          run("rm -rf {{releasePath}}; true");
        });
  
        run('mkdir -p {{releasePath}}');
        run('git clone -q -b {{branch}} {{repository}} {{releasePath}}');
      });
  
      task('npm', function (run) {
        run('cd {{latestRelease}} && test -f {{latestRelease}}/package.json && npm install || true');
      });
  
      task('createSymlink', function (run, runLocally, onRollback) {
        onRollback(function () {
          if ( that._folders.previousRelease ) {
            run ('rm -f {{currentPath}}; ln -s {{previousRelease}} {{currentPath}}; true');
          } else {
            console.log('No previous release to rollback to, rollback of symlink skipped');
          }
        });
  
        run('rm -f {{currentPath}} && ln -s {{latestRelease}} {{currentPath}}');
      });
  
      task('deploy:cleanup', function (run) {
        run([
          'ls -1td {{releasesPath}}/*',
          'tail -n +' + (that.options.keepReleases + 1),
          'xargs rm -rf'
        ].join(' | '), { quiet: true });
      });
    }
  
    /**
     * Register user-callbacks defined tasks
     */
    function registerUserTasks() {
      var tasks  = options.tasks,
          task   = that.task.bind(that),
          before = that.before.bind(that),
          after  = that.after.bind(that),
          taskName;
  
      // register user tasks
      for (taskName in tasks) {
        if ( tasks.hasOwnProperty(taskName) ) {
          task(taskName, tasks[taskName]);
        }
      }
  
      // register predefined user tasks order
      before('deploy', 'beforeDeploy');
  
      before('updateCode', 'beforeUpdateCode');
      after('updateCode', 'afterUpdateCode');
  
      before('npm', 'beforeNpm');
      after('npm', 'afterNpm');
  
      before('createSymlink', 'beforeCreateSymlink');
      after('createSymlink', 'afterCreateSymlink');
  
      before('restart', 'beforeRestart');
      after('restart', 'afterRestart');
  
      after('deploy', 'afterDeploy');
    }
  }