Using npm to build and deploy Hugo

After moving to Hugo I wanted an easy way to deploy new posts. Opening an FTP client every time is far too much work!

As I’m already using npm to minify the JS and CSS I decided to use that to handle the deploying too.

The following requires both rsync and curl (as well as npm) to be installed. I’ve tested this on Linux and Mac. It should work on Windows too as long as rsync and curl are installed.

First create the package.json file and put the following in it:

{
  "name": "example.com",
  "version": "1.0.0",
  "private": true,
  "config" : {
    "sitemap" : "https://www.example.com/sitemap.xml",
    "destination" : "user@example.com:/home/example/public_html/"
  },
  "scripts": {
    "clean": "rimraf ./public",
    "prebuild": "npm run clean",
    "build": "hugo",
    "predeploy": "npm run build",
    "deploy": "rsync -crtvz --progress ./public/ $npm_package_config_destination",
    "ping-sitemap:google": "curl -so /dev/null --fail https://www.google.com/ping?sitemap=$npm_package_config_sitemap",
    "ping-sitemap:bing": "curl -so /dev/null --fail https://www.bing.com/ping?sitemap=$npm_package_config_sitemap",
    "ping-sitemap": "npm run ping-sitemap:google & ping-sitemap:bing"
  },
  "devDependencies": {
    "rimraf": "^2.5.4"
  }
}

Then change the sitemap and destination config values for your site and that’s it, ready to use!

Usage:

npm run deploy -- --dry-run
npm run deploy
npm run ping-sitemap

Using gulp

For comparrison, here is the gulp alternative:

npm install --save-dev gulp gulp-rsync rimraf minimist request

gulpfile.js:

const gulp = require('gulp');
const rsync = require('gulp-rsync');
const exec = require('child_process').exec;
const rimraf = require('rimraf');
const request = require('request');
const argv = require('minimist')(process.argv.slice(2));


function httpPing(url) {
  return new Promise((resolve, reject) => {
    request(url, function(err, response) {
      if (err) {
        reject(err);
      } else if (response.statusCode !== 200) {
        reject(Error('URL: ' + url + ' had status: ' + response.statusCode));
      } else {
        resolve();
      }
    });
  });
}

gulp.task('clean', function (cb) {
  rimraf('./public', cb);
});

gulp.task('build', ['clean'], function (cb) {
  exec('hugo', function (err, stdout, stderr) {
    console.log('\n-------- Hugo output--------\n');
    console.log(stdout);
    console.log('----------------------------\n');

    cb(err);
  });
});

gulp.task('deploy', ['build'], function () {
  // TODO: Change the hostname, username and destination below
  return gulp.src('public/**')
    .pipe(rsync({
      root: 'public/',
      hostname: 'example.com',
      username: 'ssh-user',
      dryrun: !!argv['dry-run'],
      incremental: true,
      progress: true,
      times: true,
      compress: true,
      destination: '/home/example.com/public_html'
    }));
});

gulp.task('ping-sitemap', function (cb) {
  // TODO: Change the sitemap URL
  const sitemapUrl = 'https://www.example.com/sitemap.xml';
  const sitemapParam = encodeURIComponent(sitemapUrl);
  const googlePing = 'https://www.google.com/ping?sitemap=' + sitemapParam;
  const bingPing = 'https://www.bing.com/ping?sitemap=' + sitemapParam;

  Promise.all([
    httpPing(googlePing),
    httpPing(bingPing)
  ]).then(() => cb(), cb);
});

Usage:

gulp deploy --dry-run
gulp deploy
gulp ping-sitemap

The gulp version is more extensible but for my needs the NPM one works well!

You need JavaScript enabled to see comments.