gh-pages/lib/index.js
2016-04-24 12:46:25 -06:00

245 lines
6.3 KiB
JavaScript

var path = require('path');
var fs = require('fs');
var Q = require('q');
var rimraf = require('rimraf');
var globby = require('globby');
var git = require('./git');
var copy = require('./util').copy;
function getCacheDir() {
return path.relative(process.cwd(), path.resolve(__dirname, '../.cache'));
}
function getRemoteUrl(dir, remote) {
var repo;
return git(['config', '--get', 'remote.' + remote + '.url'], dir)
.progress(function(chunk) {
repo = String(chunk).split(/[\n\r]/).shift();
})
.then(function() {
if (repo) {
return Q.resolve(repo);
} else {
return Q.reject(new Error(
'Failed to get repo URL from options or current directory.'));
}
})
.fail(function(err) {
return Q.reject(new Error(
'Failed to get remote.' + remote + '.url (task must either be ' +
'run in a git repository with a configured ' + remote + ' remote ' +
'or must be configured with the "repo" option).'));
});
}
function getRepo(options) {
if (options.repo) {
return Q.resolve(options.repo);
} else {
return getRemoteUrl(process.cwd(), options.remote);
}
}
/**
* Push a git branch to a remote (pushes gh-pages by default).
* @param {string} basePath The base path.
* @param {Object} config Publish options.
* @param {Function} callback Callback.
*/
exports.publish = function publish(basePath, config, callback) {
if (typeof config === 'function') {
callback = config;
config = {};
}
var defaults = {
add: false,
git: 'git',
clone: getCacheDir(),
dotfiles: false,
branch: 'gh-pages',
remote: 'origin',
src: '**/*',
only: '.',
push: true,
message: 'Updates',
silent: false,
logger: function() {}
};
// override defaults with any task options
// TODO: Require Node >= 4 and use Object.assign
var options = {};
for (var d in defaults) {
options[d] = defaults[d];
}
for (var c in config) {
options[c] = config[c];
}
function log(message) {
if (!options.silent) {
options.logger(message);
}
}
if (!callback) {
callback = function(err) {
if (err) {
log(err.message);
}
};
}
function done(err) {
try {
callback(err);
} catch (err2) {
log('Publish callback threw: ', err2.message);
}
}
try {
if (!fs.statSync(basePath).isDirectory()) {
done(new Error('The "base" option must be an existing directory'));
return;
}
} catch (err) {
done(err);
return;
}
var files = globby.sync(options.src, {
cwd: basePath,
dot: options.dotfiles
}).filter(function(file) {
return !fs.statSync(path.join(basePath, file)).isDirectory();
});
if (!Array.isArray(files) || files.length === 0) {
done(new Error('Files must be provided in the "src" property.'));
return;
}
var only = globby.sync(options.only, {cwd: basePath});
git.exe(options.git);
var repoUrl;
getRepo(options)
.then(function(repo) {
repoUrl = repo;
log('Cloning ' + repo + ' into ' + options.clone);
return git.clone(repo, options.clone, options.branch, options);
})
.then(function() {
return getRemoteUrl(options.clone, options.remote)
.then(function(url) {
if (url !== repoUrl) {
var message = 'Remote url mismatch. Got "' + url + '" ' +
'but expected "' + repoUrl + '" in ' + options.clone +
'. If you have changed your "repo" option, try ' +
'running the "clean" task first.';
return Q.reject(new Error(message));
} else {
return Q.resolve();
}
});
})
.then(function() {
// only required if someone mucks with the checkout between builds
log('Cleaning');
return git.clean(options.clone);
})
.then(function() {
log('Fetching ' + options.remote);
return git.fetch(options.remote, options.clone);
})
.then(function() {
log('Checking out ' + options.remote + '/' +
options.branch);
return git.checkout(options.remote, options.branch,
options.clone);
})
.then(function() {
if (!options.add) {
log('Removing files');
return git.rm(only.join(' '), options.clone);
} else {
return Q.resolve();
}
})
.then(function() {
log('Copying files');
return copy(files, basePath, options.clone);
})
.then(function() {
log('Adding all');
return git.add('.', options.clone);
})
.then(function() {
if (options.user) {
return git(['config', 'user.email', options.user.email],
options.clone)
.then(function() {
return git(['config', 'user.name', options.user.name],
options.clone);
});
} else {
return Q.resolve();
}
})
.then(function() {
log('Committing');
return git.commit(options.message, options.clone);
})
.then(function() {
if (options.tag) {
log('Tagging');
var deferred = Q.defer();
git.tag(options.tag, options.clone)
.then(function() {
return deferred.resolve();
})
.fail(function(error) {
// tagging failed probably because this tag alredy exists
log('Tagging failed, continuing');
options.logger(error);
return deferred.resolve();
});
return deferred.promise;
} else {
return Q.resolve();
}
})
.then(function() {
if (options.push) {
log('Pushing');
return git.push(options.remote, options.branch,
options.clone);
} else {
return Q.resolve();
}
})
.then(function() {
done();
}, function(error) {
if (options.silent) {
error = new Error(
'Unspecified error (run without silent option for detail)');
}
done(error);
});
};
/**
* Clean the cache directory.
*/
exports.clean = function clean() {
rimraf.sync(getCacheDir());
};