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.

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