index.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. const findCacheDir = require('find-cache-dir');
  2. const Git = require('./git.js');
  3. const filenamify = require('filenamify');
  4. const copy = require('./util.js').copy;
  5. const getUser = require('./util.js').getUser;
  6. const fs = require('fs-extra');
  7. const globby = require('globby');
  8. const path = require('path');
  9. const util = require('util');
  10. const log = util.debuglog('gh-pages');
  11. /**
  12. * Get the cache directory.
  13. * @param {string} [optPath] Optional path.
  14. * @return {string} The full path to the cache directory.
  15. */
  16. function getCacheDir(optPath) {
  17. const dir = findCacheDir({name: 'gh-pages'});
  18. if (!optPath) {
  19. return dir;
  20. }
  21. return path.join(dir, filenamify(optPath));
  22. }
  23. exports.getCacheDir = getCacheDir;
  24. function getRepo(options) {
  25. if (options.repo) {
  26. return Promise.resolve(options.repo);
  27. } else {
  28. const git = new Git(process.cwd(), options.git);
  29. return git.getRemoteUrl(options.remote);
  30. }
  31. }
  32. exports.defaults = {
  33. dest: '.',
  34. add: false,
  35. git: 'git',
  36. depth: 1,
  37. dotfiles: false,
  38. branch: 'gh-pages',
  39. remote: 'origin',
  40. src: '**/*',
  41. remove: '.',
  42. push: true,
  43. history: true,
  44. message: 'Updates',
  45. silent: false,
  46. };
  47. /**
  48. * Push a git branch to a remote (pushes gh-pages by default).
  49. * @param {string} basePath The base path.
  50. * @param {Object} config Publish options.
  51. * @param {Function} callback Callback.
  52. * @return {Promise} A promise.
  53. */
  54. exports.publish = function publish(basePath, config, callback) {
  55. if (typeof config === 'function') {
  56. callback = config;
  57. config = {};
  58. }
  59. const options = Object.assign({}, exports.defaults, config);
  60. // For backward compatibility before fixing #334
  61. if (options.only) {
  62. options.remove = options.only;
  63. }
  64. if (!callback) {
  65. callback = function (err) {
  66. if (err) {
  67. log(err.message);
  68. }
  69. };
  70. }
  71. function done(err) {
  72. try {
  73. callback(err);
  74. } catch (err2) {
  75. log('Publish callback threw: %s', err2.message);
  76. }
  77. }
  78. try {
  79. if (!fs.statSync(basePath).isDirectory()) {
  80. done(new Error('The "base" option must be an existing directory'));
  81. return;
  82. }
  83. } catch (err) {
  84. done(err);
  85. return;
  86. }
  87. const files = globby
  88. .sync(options.src, {
  89. cwd: basePath,
  90. dot: options.dotfiles,
  91. })
  92. .filter((file) => {
  93. return !fs.statSync(path.join(basePath, file)).isDirectory();
  94. });
  95. if (!Array.isArray(files) || files.length === 0) {
  96. done(
  97. new Error('The pattern in the "src" property didn\'t match any files.')
  98. );
  99. return;
  100. }
  101. let repoUrl;
  102. let userPromise;
  103. if (options.user) {
  104. userPromise = Promise.resolve(options.user);
  105. } else {
  106. userPromise = getUser();
  107. }
  108. return userPromise.then((user) =>
  109. getRepo(options)
  110. .then((repo) => {
  111. repoUrl = repo;
  112. const clone = getCacheDir(repo);
  113. log('Cloning %s into %s', repo, clone);
  114. return Git.clone(repo, clone, options.branch, options);
  115. })
  116. .then((git) => {
  117. return git.getRemoteUrl(options.remote).then((url) => {
  118. if (url !== repoUrl) {
  119. const message =
  120. 'Remote url mismatch. Got "' +
  121. url +
  122. '" ' +
  123. 'but expected "' +
  124. repoUrl +
  125. '" in ' +
  126. git.cwd +
  127. '. Try running the `gh-pages-clean` script first.';
  128. throw new Error(message);
  129. }
  130. return git;
  131. });
  132. })
  133. .then((git) => {
  134. // only required if someone mucks with the checkout between builds
  135. log('Cleaning');
  136. return git.clean();
  137. })
  138. .then((git) => {
  139. log('Fetching %s', options.remote);
  140. return git.fetch(options.remote);
  141. })
  142. .then((git) => {
  143. log('Checking out %s/%s ', options.remote, options.branch);
  144. return git.checkout(options.remote, options.branch);
  145. })
  146. .then((git) => {
  147. if (!options.history) {
  148. return git.deleteRef(options.branch);
  149. } else {
  150. return git;
  151. }
  152. })
  153. .then((git) => {
  154. if (options.add) {
  155. return git;
  156. }
  157. log('Removing files');
  158. const files = globby
  159. .sync(options.remove, {
  160. cwd: path.join(git.cwd, options.dest),
  161. })
  162. .map((file) => path.join(options.dest, file));
  163. if (files.length > 0) {
  164. return git.rm(files);
  165. } else {
  166. return git;
  167. }
  168. })
  169. .then((git) => {
  170. log('Copying files');
  171. return copy(files, basePath, path.join(git.cwd, options.dest)).then(
  172. function () {
  173. return git;
  174. }
  175. );
  176. })
  177. .then((git) => {
  178. return Promise.resolve(
  179. options.beforeAdd && options.beforeAdd(git)
  180. ).then(() => git);
  181. })
  182. .then((git) => {
  183. log('Adding all');
  184. return git.add('.');
  185. })
  186. .then((git) => {
  187. if (!user) {
  188. return git;
  189. }
  190. return git.exec('config', 'user.email', user.email).then(() => {
  191. if (!user.name) {
  192. return git;
  193. }
  194. return git.exec('config', 'user.name', user.name);
  195. });
  196. })
  197. .then((git) => {
  198. log('Committing');
  199. return git.commit(options.message);
  200. })
  201. .then((git) => {
  202. if (options.tag) {
  203. log('Tagging');
  204. return git.tag(options.tag).catch((error) => {
  205. // tagging failed probably because this tag alredy exists
  206. log(error);
  207. log('Tagging failed, continuing');
  208. return git;
  209. });
  210. } else {
  211. return git;
  212. }
  213. })
  214. .then((git) => {
  215. if (options.push) {
  216. log('Pushing');
  217. return git.push(options.remote, options.branch, !options.history);
  218. } else {
  219. return git;
  220. }
  221. })
  222. .then(
  223. () => done(),
  224. (error) => {
  225. if (options.silent) {
  226. error = new Error(
  227. 'Unspecified error (run without silent option for detail)'
  228. );
  229. }
  230. done(error);
  231. }
  232. )
  233. );
  234. };
  235. /**
  236. * Clean the cache directory.
  237. */
  238. exports.clean = function clean() {
  239. fs.removeSync(getCacheDir());
  240. };