Gruntfile.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * decaffeinate suggestions:
  3. * DS101: Remove unnecessary use of Array.from
  4. * DS102: Remove unnecessary code created because of implicit returns
  5. * DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining
  6. * DS205: Consider reworking code to avoid use of IIFEs
  7. * DS207: Consider shorter variations of null checks
  8. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  9. */
  10. const coffee = require("coffee-script");
  11. const fs = require("fs");
  12. const {
  13. spawn
  14. } = require("child_process");
  15. const {
  16. exec
  17. } = require("child_process");
  18. const rimraf = require("rimraf");
  19. const Path = require("path");
  20. const semver = require("semver");
  21. const knox = require("knox");
  22. const crypto = require("crypto");
  23. const async = require("async");
  24. const settings = require("settings-sharelatex");
  25. const _ = require("underscore");
  26. const SERVICES = require("./config/services");
  27. module.exports = function(grunt) {
  28. let Helpers;
  29. let service;
  30. grunt.loadNpmTasks('grunt-bunyan');
  31. grunt.loadNpmTasks('grunt-execute');
  32. grunt.loadNpmTasks('grunt-available-tasks');
  33. grunt.loadNpmTasks('grunt-concurrent');
  34. grunt.loadNpmTasks("grunt-contrib-coffee");
  35. grunt.loadNpmTasks("grunt-shell");
  36. grunt.task.loadTasks("./tasks");
  37. const execute = {};
  38. for (service of Array.from(SERVICES)) {
  39. execute[service.name] =
  40. {src: `${service.name}/app.js`};
  41. }
  42. grunt.initConfig({
  43. execute,
  44. concurrent: {
  45. all: {
  46. tasks: (((() => {
  47. const result = [];
  48. for (service of Array.from(SERVICES)) { result.push(`run:${service.name}`);
  49. }
  50. return result;
  51. })())),
  52. options: {
  53. limit: SERVICES.length,
  54. logConcurrentOutput: true
  55. availabletasks: {
  56. tasks: {
  57. options: {
  58. filter: 'exclude',
  59. tasks: [
  60. 'concurrent',
  61. 'execute',
  62. 'bunyan',
  63. 'availabletasks'
  64. ],
  65. groups: {
  66. "Run tasks": [
  67. "run",
  68. "run:all",
  69. "default"
  70. ].concat(((() => {
  71. const result1 = [];
  72. for (service of Array.from(SERVICES)) { result1.push(`run:${service.name}`);
  73. }
  74. return result1;
  75. })())),
  76. "Misc": [
  77. "help"
  78. ],
  79. "Install tasks": ((() => {
  80. const result2 = [];
  81. for (service of Array.from(SERVICES)) { result2.push(`install:${service.name}`);
  82. }
  83. return result2;
  84. })()).concat(["install:all", "install"]),
  85. "Update tasks": ((() => {
  86. const result3 = [];
  87. for (service of Array.from(SERVICES)) { result3.push(`update:${service.name}`);
  88. }
  89. return result3;
  90. })()).concat(["update:all", "update"]),
  91. "Checks": ["check", "check:redis", "check:latexmk", "check:s3", "check:make", "check:mongo"]
  92. }
  93. }
  94. }
  95. }});
  96. for (service of Array.from(SERVICES)) {
  97. ((service => grunt.registerTask(`install:${service.name}`, `Download and set up the ${service.name} service`, function() {
  98. const done = this.async();
  99. return Helpers.installService(service, done);
  100. })))(service);
  101. }
  102. grunt.registerTask('install:all', "Download and set up all ShareLaTeX services",
  103. [].concat(
  104. ((() => {
  105. const result4 = [];
  106. for (service of Array.from(SERVICES)) { result4.push(`install:${service.name}`);
  107. }
  108. return result4;
  109. })())
  110. ).concat(['postinstall'])
  111. );
  112. grunt.registerTask('install', 'install:all');
  113. grunt.registerTask('postinstall', 'Explain postinstall steps', function() {
  114. return Helpers.postinstallMessage(this.async());
  115. });
  116. grunt.registerTask('update:all', "Checkout and update all ShareLaTeX services",
  117. ["check:make"].concat(
  118. ((() => {
  119. const result5 = [];
  120. for (service of Array.from(SERVICES)) { result5.push(`update:${service.name}`);
  121. }
  122. return result5;
  123. })())
  124. )
  125. );
  126. grunt.registerTask('update', 'update:all');
  127. grunt.registerTask('run', "Run all of the sharelatex processes", ['concurrent:all']);
  128. grunt.registerTask('run:all', 'run');
  129. grunt.registerTask('help', 'Display this help list', 'availabletasks');
  130. grunt.registerTask('default', 'run');
  131. grunt.registerTask("check:redis", "Check that redis is installed and running", function() {
  132. return Helpers.checkRedisConnect(this.async());
  133. });
  134. grunt.registerTask("check:mongo", "Check that mongo is installed", function() {
  135. return Helpers.checkMongoConnect(this.async());
  136. });
  137. grunt.registerTask("check", "Check that you have the required dependencies installed", ["check:redis", "check:mongo", "check:make"]);
  138. grunt.registerTask("check:make", "Check that make is installed", function() {
  139. return Helpers.checkMake(this.async());
  140. });
  141. return Helpers = {
  142. installService(service, callback) {
  143. if (callback == null) { callback = function(error) {}; }
  144. console.log(`Installing ${service.name}`);
  145. return Helpers.cloneGitRepo(service, function(error) {
  146. if (error != null) {
  147. return callback(error);
  148. } else {
  149. return callback();
  150. }
  151. });
  152. },
  153. cloneGitRepo(service, callback) {
  154. if (callback == null) { callback = function(error) {}; }
  155. const repo_src = service.repo;
  156. const dir = service.name;
  157. if (!fs.existsSync(dir)) {
  158. const proc = spawn("git", [
  159. "clone",
  160. repo_src,
  161. dir
  162. ], {stdio: "inherit"});
  163. return proc.on("close", () => Helpers.checkoutVersion(service, callback));
  164. } else {
  165. console.log(`${dir} already installed, skipping.`);
  166. return callback();
  167. }
  168. },
  169. checkoutVersion(service, callback) {
  170. if (callback == null) { callback = function(error) {}; }
  171. const dir = service.name;
  172. grunt.log.write(`checking out ${service.name} ${service.version}`);
  173. const proc = spawn("git", ["checkout", service.version], {stdio: "inherit", cwd: dir});
  174. return proc.on("close", () => callback());
  175. },
  176. postinstallMessage(callback) {
  177. if (callback == null) { callback = function(error) {}; }
  178. grunt.log.write(`\
  179. Services cloned:
  180. ${(() => {
  181. const result6 = [];
  182. for (service of Array.from(SERVICES)) { result6.push(service.name);
  183. }
  184. return result6;
  185. })()}
  186. To install services run:
  187. $ source bin/install-services
  188. This will install the required node versions and run \`npm install\` for each service.
  189. See https://github.com/sharelatex/sharelatex/pull/549 for more info.\
  190. `
  191. );
  192. return callback();
  193. },
  194. checkMake(callback) {
  195. if (callback == null) { callback = function(error) {}; }
  196. grunt.log.write("Checking make is installed... ");
  197. return exec("make --version", function(error, stdout, stderr) {
  198. if ((error != null) && error.message.match("not found")) {
  199. grunt.log.error("FAIL.");
  200. grunt.log.errorlns(`\
  201. Either make is not installed or is not in your path.
  202. On Ubuntu you can install make with:
  203. sudo apt-get install build-essential
  204. \
  205. `
  206. );
  207. return callback(error);
  208. } else if (error != null) {
  209. return callback(error);
  210. } else {
  211. grunt.log.write("OK.");
  212. return callback();
  213. }
  214. });
  215. },
  216. checkMongoConnect(callback) {
  217. if (callback == null) { callback = function(error) {}; }
  218. grunt.log.write("Checking can connect to mongo");
  219. const mongojs = require("mongojs");
  220. const db = mongojs(settings.mongo.url, ["tags"]);
  221. db.runCommand({ ping: 1 }, function(err, res) {
  222. if (!err && res.ok) {
  223. grunt.log.write("OK.");
  224. }
  225. return callback();
  226. });
  227. return db.on('error', function(err){
  228. err = "Can not connect to mongodb";
  229. grunt.log.error("FAIL.");
  230. grunt.log.errorlns(`\
  231. !!!!!!!!!!!!!! MONGO ERROR !!!!!!!!!!!!!!
  232. ShareLaTeX can not talk to the mongodb instance
  233. Check the mongodb instance is running and accessible on env var SHARELATEX_MONGO_URL
  234. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\
  235. `
  236. );
  237. throw new Error("Can not connect to Mongodb");
  238. return callback(err);
  239. });
  240. },
  241. checkRedisConnect(callback) {
  242. if (callback == null) { callback = function(error) {}; }
  243. grunt.log.write("Checking can connect to redis\n");
  244. const rclient = require("redis").createClient(settings.redis.web);
  245. rclient.ping(function(err, res) {
  246. if ((err == null)) {
  247. grunt.log.write("OK.");
  248. } else {
  249. throw new Error("Can not connect to redis");
  250. }
  251. return callback();
  252. });
  253. const errorHandler = _.once(function(err){
  254. err = "Can not connect to redis";
  255. grunt.log.error("FAIL.");
  256. grunt.log.errorlns(`\
  257. !!!!!!!!!!!!!! REDIS ERROR !!!!!!!!!!!!!!
  258. ShareLaTeX can not talk to the redis instance
  259. Check the redis instance is running and accessible on env var SHARELATEX_REDIS_HOST
  260. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\
  261. `
  262. );
  263. throw new Error("Can not connect to redis");
  264. return callback(err);
  265. });
  266. return rclient.on('error', errorHandler);
  267. }
  268. };
  269. };
  270. function __guard__(value, transform) {
  271. return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined;
  272. }