Gruntfile.js 9.7 KB

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