You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wiki/models/git.js

298 lines
6.4 KiB

"use strict";
var NodeGit = require("nodegit"),
Promise = require('bluebird'),
path = require('path'),
os = require('os'),
fs = Promise.promisifyAll(require("fs")),
moment = require('moment'),
_ = require('lodash');
/**
* Git Model
*/
module.exports = {
_git: null,
_repo: {
path: '',
branch: 'master',
exists: false,
inst: null,
sync: true
},
_signature: {
name: 'Wiki',
email: 'user@example.com'
},
_opts: {
clone: {},
push: {}
},
/**
* Initialize Git model
*
* @param {Object} appconfig The application config
* @return {Object} Git model instance
*/
init(appconfig) {
let self = this;
//-> Build repository path
if(_.isEmpty(appconfig.git.path) || appconfig.git.path === 'auto') {
self._repo.path = path.join(ROOTPATH, 'repo');
} else {
self._repo.path = appconfig.git.path;
}
//-> Initialize repository
self._initRepo(appconfig).then((repo) => {
self._repo.inst = repo;
if(self._repo.sync) {
self.resync();
}
});
// Define signature
self._signature.name = appconfig.git.userinfo.name || 'Wiki';
self._signature.email = appconfig.git.userinfo.email || 'user@example.com';
return self;
},
/**
* Initialize Git repository
*
* @param {Object} appconfig The application config
* @return {Object} Promise
*/
_initRepo(appconfig) {
let self = this;
winston.info('[GIT] Initializing Git repository...');
//-> Check if path is accessible
return fs.mkdirAsync(self._repo.path).catch((err) => {
if(err.code !== 'EEXIST') {
winston.error('Invalid Git repository path or missing permissions.');
}
}).then(() => {
//-> Check if path already contains a git working folder
return fs.statAsync(path.join(self._repo.path, '.git')).then((stat) => {
self._repo.exists = stat.isDirectory();
}).catch((err) => {
self._repo.exists = false;
});
}).then(() => {
//-> Init repository
let repoInitOperation = null;
self._repo.branch = appconfig.git.branch;
self._repo.sync = appconfig.git.remote;
self._opts.clone = self._generateCloneOptions(appconfig);
self._opts.push = self._generatePushOptions(appconfig);
if(self._repo.exists) {
winston.info('[GIT] Using existing repository...');
repoInitOperation = NodeGit.Repository.open(self._repo.path);
} else if(appconfig.git.remote) {
winston.info('[GIT] Cloning remote repository for first time...');
repoInitOperation = NodeGit.Clone(appconfig.git.url, self._repo.path, self._opts.clone);
} else {
winston.info('[GIT] Using offline local repository...');
repoInitOperation = NodeGit.Repository.init(self._repo.path, 0);
}
return repoInitOperation;
}).catch((err) => {
winston.error('Unable to open or clone Git repository!');
winston.error(err);
}).then((repo) => {
if(self._repo.sync) {
NodeGit.Remote.setPushurl(repo, 'origin', appconfig.git.url);
}
return repo;
winston.info('[GIT] Git repository is now ready.');
});
},
/**
* Generate Clone Options object
*
* @param {Object} appconfig The application configuration
* @return {Object} CloneOptions object
*/
_generateCloneOptions(appconfig) {
let cloneOptions = new NodeGit.CloneOptions();
cloneOptions.fetchOpts = this._generateFetchOptions(appconfig);
return cloneOptions;
},
_generateFetchOptions(appconfig) {
let fetchOptions = new NodeGit.FetchOptions();
fetchOptions.callbacks = this._generateRemoteCallbacks(appconfig);
return fetchOptions;
},
_generatePushOptions(appconfig) {
let pushOptions = new NodeGit.PushOptions();
pushOptions.callbacks = this._generateRemoteCallbacks(appconfig);
return pushOptions;
},
_generateRemoteCallbacks(appconfig) {
let remoteCallbacks = new NodeGit.RemoteCallbacks();
let credFunc = this._generateCredentials(appconfig);
remoteCallbacks.credentials = () => { return credFunc; };
remoteCallbacks.transferProgress = _.noop;
if(os.type() === 'Darwin') {
remoteCallbacks.certificateCheck = () => { return 1; }; // Bug in OS X, bypass certs check workaround
} else {
remoteCallbacks.certificateCheck = _.noop;
}
return remoteCallbacks;
},
_generateCredentials(appconfig) {
let cred = null;
switch(appconfig.git.auth.type) {
case 'basic':
cred = NodeGit.Cred.userpassPlaintextNew(
appconfig.git.auth.user,
appconfig.git.auth.pass
);
break;
case 'oauth':
cred = NodeGit.Cred.userpassPlaintextNew(
appconfig.git.auth.token,
"x-oauth-basic"
);
break;
case 'ssh':
cred = NodeGit.Cred.sshKeyNew(
appconfig.git.auth.user,
appconfig.git.auth.publickey,
appconfig.git.auth.privatekey,
appconfig.git.auth.passphrase
);
break;
default:
cred = NodeGit.Cred.defaultNew();
break;
}
return cred;
},
resync() {
let self = this;
// Fetch
return self._repo.inst.fetch('origin', self._opts.clone.fetchOpts)
.catch((err) => {
winston.error('Unable to fetch from git origin!' + err);
})
// Merge
.then(() => {
return self._repo.inst.mergeBranches(self._repo.branch, 'origin/' + self._repo.branch);
})
.catch((err) => {
winston.error('Unable to merge from remote head!' + err);
})
// Push
.then(() => {
return self._repo.inst.getRemote('origin').then((remote) => {
// Get modified files
return self._repo.inst.refreshIndex().then((index) => {
return self._repo.inst.getStatus().then(function(arrayStatusFile) {
let addOp = [];
// Add to next commit
_.forEach(arrayStatusFile, (v) => {
addOp.push(arrayStatusFile[0].path());
});
console.log('DUDE1');
// Create Commit
let sig = NodeGit.Signature.create(self._signature.name, self._signature.email, moment().utc().unix(), 0);
return self._repo.inst.createCommitOnHead(addOp, sig, sig, "Wiki Sync").then(() => {
console.log('DUDE2');
return remote.connect(NodeGit.Enums.DIRECTION.PUSH, self._opts.push.callbacks).then(() => {
console.log('DUDE3');
// Push to remote
return remote.push( ["refs/heads/master:refs/heads/master"], self._opts.push).then((errNum) => {
console.log('DUDE' + errNum);
}).catch((err) => {
console.log(err);
});
});
});
});
})
/**/
});
}).catch((err) => {
winston.error('Unable to push to git origin!' + err);
});
}
};