# Add ftpush to Grunt Watch?

**URL:** https://discourse.roots.io/t/add-ftpush-to-grunt-watch/889
**Category:** sage
**Created:** 2013-12-12T10:41:48Z
**Posts:** 7

## Post 1 by @Twansparant — 2013-12-12T10:41:48Z

Hi there,

I’m looking for a way to automatically upload my compiled css and js files to the server when my **app.less** files or **\_main.js** javascript have changed. So I bumped into [grunt-ftp-deploy](https://github.com/zonak/grunt-ftp-deploy) but I get all kinds of errors trying to install it with `npm install grunt-ftp-deploy --save-dev`.

Then I found [grunt-ftpush](https://github.com/inossidabile/grunt-ftpush) which does install correctly. But I’m not sure what is the right way to add the **ftpush** task to the **grunt watch** task or if it’s even possible to do it like this.

So in my **Gruntfile.js** I added this at the bottom:

```
grunt.loadNpmTasks('grunt-ftpush');
```

and this within the **watch** task:

```
ftpush: {
  build: {
    auth: {
      host: 'ftp.mydomain.com',
      port: 21,
      authKey: 'dev'
    },
    src: 'assets/css/',
    dest: '/assets/css/'
  }
}
```

But when I run **grunt watch** I get this error:

```
Running "watch" task
Waiting...Verifying property watch.ftpush.files exists in config...ERROR
>> Unable to process task.
Warning: Required config property "watch.ftpush.files" missing.
```

I tried adding the ftpush option `simple: true` but that didn’t help.  
I also added the `.ftppass` file in the theme root with the correct login credentials.

What am I doing wrong or what whould be the right way to add the `ftpush` task after the less & js tasks?  
Did anyone get this setup properly?

Thanks a lot!

---

## Post 2 by @Foxaii — 2013-12-12T13:52:13Z

Are you trying to ftp to a production server? If yes, please don’t connect it to watch. Just set up a separate task outside watch and use the command line when, and only when, you know the CSS/JS is ready for production.

If you’re using it to ftp to staging/dev then you should read through the watch documentation. Essentially you need to set up a grunt task outside of watch. Once the grunt task is working you set up the watch task. The watch task defines the files to watch (this hasn’t been set, hence your error) and then runs the grunt task.

---

## Post 3 by @bryan_fillmer — 2013-12-15T13:09:06Z

After reading this last night I did some work on getting ftpush to work on a current project I’m working on. After I worked out all the kinks I setup a fresh fork of Roots on GitHub with a default ftpush setup:

> **[bfillmer/roots](https://github.com/bfillmer/roots)**
>
> WordPress starter theme based on HTML5 Boilerplate & Bootstrap - bfillmer/roots

Hopefully this helps someone that wants easy auto-upload functionality.

Bryan

---

## Post 4 by @Twansparant — 2013-12-16T11:57:35Z

Wow, that’s really cool. Thanks a lot for sharing this!  
What are you suppose to fill in these lines:

```
src: 'path/to/source/folder',
dest: '/path/to/destination/folder',
```

Is that the full path to the roots theme folder or are these 2 variables not used by the `ftpush watch` task?  
Thanks!

**EDIT:**  
By trial and error, I left the `src` blank since it’s already in the right folder and in `dest` I filled in the full path to the roots theme folder from the root of your webserver eg:

```
dest: 'httpdocs/wp-content/themes/roots',
```

Works like a charm! Really happy with this!

---

## Post 5 by @bryan_fillmer — 2013-12-17T20:26:18Z

Yep, you got it, src is relative to the gruntfile location, dest is relative to where the ftp normally dumps you in on the server.

---

## Post 6 by @Twansparant — 2014-09-04T12:05:20Z

Any updated instructions on how to implement this in the updated [Gruntfile.js](https://github.com/roots/roots/blob/master/Gruntfile.js) file?  
How am I suppose to add the **ftpush** command in the **Grunt watch** function?

```
ftpush: {
  build: {
    auth: {
      host:'ftp.myhost.com',
      port:21,
      authKey:'deploy'
    },
    src: '',
    dest: '/public_html/wp-content/themes/roots',
    exclusions: [
      '**/.DS_Store',
      '**/Thumbs.db',
      ' **/node_modules/**',
      '**/.gitignore',
      '.editorconfig',
      '.ftppass',
      '.grunt',
      '.jshintrc'
    ],
    keep: [],
    simple: true
  }
},
watch: {
  less: {
    files: [
      'assets/less/*.less',
      'assets/less/**/*.less'
    ],
    tasks: ['less:dev', 'autoprefixer:dev']
  },
  js: {
    files: [
      jsFileList,
      '<%= jshint.all %>'
    ],
    tasks: ['jshint', 'concat']
  },
  livereload: {
    // Browser live reloading
    // https://github.com/gruntjs/grunt-contrib-watch#live-reloading
    options: {
      livereload: false
    },
    files: [
      'assets/css/main.css',
      'assets/js/scripts.js',
      'templates/*.php',
      '*.php'
    ]
  }
}
```

Thanks!

---

## Post 7 by @Twansparant — 2014-09-05T12:25:15Z

With trial and error, this seems to work:

```
'use strict';
module.exports = function(grunt) {
  // Load all tasks
  require('load-grunt-tasks')(grunt);
  // Show elapsed time
  require('time-grunt')(grunt);

  var jsFileList = [
    'assets/vendor/bootstrap/js/transition.js',
    'assets/vendor/bootstrap/js/alert.js',
    'assets/vendor/bootstrap/js/button.js',
    'assets/vendor/bootstrap/js/carousel.js',
    'assets/vendor/bootstrap/js/collapse.js',
    'assets/vendor/bootstrap/js/dropdown.js',
    'assets/vendor/bootstrap/js/modal.js',
    'assets/vendor/bootstrap/js/tooltip.js',
    'assets/vendor/bootstrap/js/popover.js',
    'assets/vendor/bootstrap/js/scrollspy.js',
    'assets/vendor/bootstrap/js/tab.js',
    'assets/vendor/bootstrap/js/affix.js',
    'assets/js/plugins/*.js',
    'assets/js/_*.js'
  ];

  grunt.initConfig({
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      all: [
        'Gruntfile.js',
        'assets/js/*.js',
        '!assets/js/scripts.js',
        '!assets/**/*.min.*'
      ]
    },
    less: {
      dev: {
        files: {
          'assets/css/main.css': [
            'assets/less/main.less'
          ]
        },
        options: {
          compress: false,
          // LESS source map
          // To enable, set sourceMap to true and update sourceMapRootpath based on your install
          sourceMap: true,
          sourceMapFilename: 'assets/css/main.css.map',
          sourceMapRootpath: '/app/themes/roots/'
        }
      },
      build: {
        files: {
          'assets/css/main.min.css': [
            'assets/less/main.less'
          ]
        },
        options: {
          compress: true
        }
      }
    },
    concat: {
      options: {
        separator: ';',
      },
      dist: {
        src: [jsFileList],
        dest: 'assets/js/scripts.js',
      },
    },
    uglify: {
      dist: {
        files: {
          'assets/js/scripts.min.js': [jsFileList]
        }
      }
    },
    autoprefixer: {
      options: {
        browsers: ['last 2 versions', 'ie 8', 'ie 9', 'android 2.3', 'android 4', 'opera 12']
      },
      dev: {
        options: {
          map: {
            prev: 'assets/css/'
          }
        },
        src: 'assets/css/main.css'
      },
      build: {
        src: 'assets/css/main.min.css'
      }
    },
    modernizr: {
      build: {
        devFile: 'assets/vendor/modernizr/modernizr.js',
        outputFile: 'assets/js/vendor/modernizr.min.js',
        files: {
          'src': [
            ['assets/js/scripts.min.js'],
            ['assets/css/main.min.css']
          ]
        },
        uglify: true,
        parseFiles: true
      }
    },
    version: {
      default: {
        options: {
          format: true,
          length: 32,
          manifest: 'assets/manifest.json',
          querystring: {
            style: 'roots_css',
            script: 'roots_js'
          }
        },
        files: {
          'lib/scripts.php': 'assets/{css,js}/{main,scripts}.min.{css,js}'
        }
      }
    },
    ftpush: {
      build: {
        auth: {
          host:'ftp.myhost.com',
          port:21,
          authKey:'deploy'
        },
        src: '',
        dest: '/public_html/wp-content/themes/roots',
        exclusions: [
          '**/.DS_Store',
          '**/Thumbs.db',
          ' **/node_modules/**',
          '**/.gitignore',
          '.editorconfig',
          '.ftppass',
          '.grunt',
          '.jshintrc'
        ],
        keep: [],
        simple: true
      }
    },
    watch: {
      less: {
        files: [
          'assets/less/*.less',
          'assets/less/**/*.less'
        ],
        tasks: ['less:dev', 'autoprefixer:dev']
      },
      js: {
        files: [
          jsFileList,
          '<%= jshint.all %>'
        ],
        tasks: ['jshint', 'concat']
      },
      livereload: {
        // Browser live reloading
        // https://github.com/gruntjs/grunt-contrib-watch#live-reloading
        options: {
          livereload: false
        },
        files: [
          'assets/css/main.css',
          'assets/js/scripts.js',
          'templates/*.php',
          '*.php'
        ]
      },
      ftpush: {
        files: [' **/*','!** /node_modules/**'],
        tasks: ['ftpush']
      }
    }
  });

  // Register tasks
  grunt.registerTask('default', [
    'dev'
  ]);
  grunt.registerTask('dev', [
    'jshint',
    'less:dev',
    'autoprefixer:dev',
    'concat',
    'ftpush'
  ]);
  grunt.registerTask('build', [
    'jshint',
    'less:build',
    'autoprefixer:build',
    'uglify',
    'modernizr',
    'version',
    'ftpush'
  ]);
};
```

---

## Post 8 by @ben — 2022-02-26T02:58:43Z


