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.

210 lines
4.4 KiB

  1. "use strict";
  2. var Git = require("git-wrapper2-promise"),
  3. Promise = require('bluebird'),
  4. path = require('path'),
  5. os = require('os'),
  6. fs = Promise.promisifyAll(require("fs")),
  7. moment = require('moment'),
  8. _ = require('lodash'),
  9. URL = require('url');
  10. /**
  11. * Git Model
  12. */
  13. module.exports = {
  14. _git: null,
  15. _url: '',
  16. _repo: {
  17. path: '',
  18. branch: 'master',
  19. exists: false,
  20. sync: true
  21. },
  22. _signature: {
  23. name: 'Wiki',
  24. email: 'user@example.com'
  25. },
  26. _opts: {
  27. clone: {},
  28. push: {}
  29. },
  30. /**
  31. * Initialize Git model
  32. *
  33. * @param {Object} appconfig The application config
  34. * @return {Object} Git model instance
  35. */
  36. init(appconfig, sync) {
  37. let self = this;
  38. self._repo.sync = sync;
  39. //-> Build repository path
  40. if(_.isEmpty(appconfig.datadir.repo)) {
  41. self._repo.path = path.join(ROOTPATH, 'repo');
  42. } else {
  43. self._repo.path = appconfig.datadir.repo;
  44. }
  45. //-> Initialize repository
  46. self._initRepo(appconfig).then((repo) => {
  47. if(self._repo.sync) {
  48. self.resync();
  49. }
  50. });
  51. // Define signature
  52. self._signature.name = appconfig.git.signature.name || 'Wiki';
  53. self._signature.email = appconfig.git.signature.email || 'user@example.com';
  54. return self;
  55. },
  56. /**
  57. * Initialize Git repository
  58. *
  59. * @param {Object} appconfig The application config
  60. * @return {Object} Promise
  61. */
  62. _initRepo(appconfig) {
  63. let self = this;
  64. winston.info('[GIT] Checking Git repository...');
  65. //-> Check if path is accessible
  66. return fs.mkdirAsync(self._repo.path).catch((err) => {
  67. if(err.code !== 'EEXIST') {
  68. winston.error('Invalid Git repository path or missing permissions.');
  69. }
  70. }).then(() => {
  71. self._git = new Git({ 'git-dir': self._repo.path });
  72. //-> Check if path already contains a git working folder
  73. return self._git.isRepo().then((isRepo) => {
  74. self._repo.exists = isRepo;
  75. return (!isRepo) ? self._git.exec('init') : true;
  76. }).catch((err) => {
  77. self._repo.exists = false;
  78. });
  79. }).then(() => {
  80. // Initialize remote
  81. let urlObj = URL.parse(appconfig.git.url);
  82. urlObj.auth = appconfig.git.auth.username + ((appconfig.git.auth.type !== 'ssh') ? ':' + appconfig.git.auth.password : '');
  83. self._url = URL.format(urlObj);
  84. return self._git.exec('remote', 'show').then((cProc) => {
  85. let out = cProc.stdout.toString();
  86. if(_.includes(out, 'origin')) {
  87. return true;
  88. } else {
  89. return Promise.join(
  90. self._git.exec('config', ['--local', 'user.name', self._signature.name]),
  91. self._git.exec('config', ['--local', 'user.email', self._signature.email])
  92. ).then(() => {
  93. return self._git.exec('remote', ['add', 'origin', self._url]);
  94. })
  95. }
  96. });
  97. }).catch((err) => {
  98. winston.error('Git remote error!');
  99. throw err;
  100. }).then(() => {
  101. winston.info('[GIT] Git repository is now ready.');
  102. return true;
  103. });
  104. },
  105. /**
  106. * Sync with the remote repository
  107. *
  108. * @return {Promise} Resolve on sync success
  109. */
  110. resync() {
  111. let self = this;
  112. // Fetch
  113. winston.info('[GIT] Performing pull from remote repository...');
  114. return self._git.pull('origin', self._repo.branch).then((cProc) => {
  115. winston.info('[GIT] Pull completed.');
  116. })
  117. .catch((err) => {
  118. winston.error('Unable to fetch from git origin!');
  119. throw err;
  120. })
  121. .then(() => {
  122. // Check for changes
  123. return self._git.exec('log', 'origin/' + self._repo.branch + '..HEAD').then((cProc) => {
  124. let out = cProc.stdout.toString();
  125. if(_.includes(out, 'commit')) {
  126. winston.info('[GIT] Performing push to remote repository...');
  127. return self._git.push('origin', self._repo.branch).then(() => {
  128. return winston.info('[GIT] Push completed.');
  129. });
  130. } else {
  131. winston.info('[GIT] Repository is already in sync.');
  132. }
  133. return true;
  134. });
  135. })
  136. .catch((err) => {
  137. winston.error('Unable to push changes to remote!');
  138. throw err;
  139. });
  140. },
  141. /**
  142. * Commits a document.
  143. *
  144. * @param {String} entryPath The entry path
  145. * @return {Promise} Resolve on commit success
  146. */
  147. commitDocument(entryPath) {
  148. let self = this;
  149. let gitFilePath = entryPath + '.md';
  150. let commitMsg = '';
  151. return self._git.exec('ls-files', gitFilePath).then((cProc) => {
  152. let out = cProc.stdout.toString();
  153. return _.includes(out, gitFilePath);
  154. }).then((isTracked) => {
  155. commitMsg = (isTracked) ? 'Updated ' + gitFilePath : 'Added ' + gitFilePath;
  156. return self._git.add(gitFilePath);
  157. }).then(() => {
  158. return self._git.commit(commitMsg);
  159. });
  160. }
  161. };