Add ftpush to Grunt Watch?

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 but I get all kinds of errors trying to install it with npm install grunt-ftp-deploy --save-dev.

Then I found 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!

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.

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:

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

Bryan

1 Like

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!

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.

Any updated instructions on how to implement this in the updated 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!

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'
  ]);
};