Gruntfile.coffee 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. fs = require "fs"
  2. spawn = require("child_process").spawn
  3. exec = require("child_process").exec
  4. rimraf = require "rimraf"
  5. Path = require "path"
  6. semver = require "semver"
  7. settings = require "settings-sharelatex"
  8. knox = require "knox"
  9. SERVICES = [{
  10. name: "web"
  11. repo: "https://github.com/sharelatex/web-sharelatex.git"
  12. }, {
  13. name: "document-updater"
  14. repo: "https://github.com/sharelatex/document-updater-sharelatex.git"
  15. }, {
  16. name: "clsi"
  17. repo: "https://github.com/sharelatex/clsi-sharelatex.git"
  18. }, {
  19. name: "filestore"
  20. repo: "https://github.com/sharelatex/filestore-sharelatex.git"
  21. }]
  22. module.exports = (grunt) ->
  23. grunt.loadNpmTasks 'grunt-bunyan'
  24. grunt.loadNpmTasks 'grunt-execute'
  25. grunt.loadNpmTasks 'grunt-available-tasks'
  26. grunt.loadNpmTasks 'grunt-concurrent'
  27. execute = {}
  28. for service in SERVICES
  29. execute[service.name] =
  30. src: "#{service.name}/app.js"
  31. grunt.initConfig
  32. execute: execute
  33. concurrent:
  34. all:
  35. tasks: ("run:#{service.name}" for service in SERVICES)
  36. options:
  37. limit: SERVICES.length
  38. logConcurrentOutput: true
  39. availabletasks:
  40. tasks:
  41. options:
  42. filter: 'exclude',
  43. tasks: [
  44. 'concurrent'
  45. 'execute'
  46. 'bunyan'
  47. 'availabletasks'
  48. ]
  49. groups:
  50. "Run tasks": [
  51. "run"
  52. "run:all"
  53. "default"
  54. ].concat ("run:#{service.name}" for service in SERVICES)
  55. "Misc": [
  56. "help"
  57. ]
  58. "Install tasks": ("install:#{service.name}" for service in SERVICES).concat(["install:all", "install"])
  59. "Update tasks": ("update:#{service.name}" for service in SERVICES).concat(["update:all", "update"])
  60. "Config tasks": ["install:config"]
  61. "Checks": ["check", "check:redis", "check:latexmk"]
  62. for service in SERVICES
  63. do (service) ->
  64. grunt.registerTask "install:#{service.name}", "Download and set up the #{service.name} service", () ->
  65. done = @async()
  66. Helpers.installService(service.repo, service.name, done)
  67. grunt.registerTask "update:#{service.name}", "Checkout and update the #{service.name} service", () ->
  68. done = @async()
  69. Helpers.updateService(service.name, done)
  70. grunt.registerTask "run:#{service.name}", "Run the ShareLaTeX #{service.name} service", ["bunyan", "execute:#{service.name}"]
  71. grunt.registerTask 'install:all', "Download and set up all ShareLaTeX services", ("install:#{service.name}" for service in SERVICES)
  72. grunt.registerTask 'install', 'install:all'
  73. grunt.registerTask 'update:all', "Checkout and update all ShareLaTeX services", ("update:#{service.name}" for service in SERVICES)
  74. grunt.registerTask 'update', 'update:all'
  75. grunt.registerTask 'run', "Run all of the sharelatex processes", ['concurrent:all']
  76. grunt.registerTask 'run:all', 'run'
  77. grunt.registerTask 'help', 'Display this help list', 'availabletasks'
  78. grunt.registerTask 'default', 'run'
  79. grunt.registerTask "check:redis", "Check that redis is installed and running", () ->
  80. Helpers.checkRedis @async()
  81. grunt.registerTask "check:latexmk", "Check that latexmk is installed", () ->
  82. Helpers.checkLatexmk @async()
  83. grunt.registerTask "check:s3", "Check that Amazon S3 credentials are configured", () ->
  84. Helpers.checkS3 @async()
  85. grunt.registerTask "check", "Check that you have the required dependencies installed", ["check:redis", "check:latexmk", "check:s3"]
  86. Helpers =
  87. installService: (repo_src, dir, callback = (error) ->) ->
  88. Helpers.cloneGitRepo repo_src, dir, (error) ->
  89. return callback(error) if error?
  90. Helpers.installNpmModules dir, (error) ->
  91. return callback(error) if error?
  92. Helpers.runGruntInstall dir, (error) ->
  93. return callback(error) if error?
  94. callback()
  95. updateService: (dir, callback = (error) ->) ->
  96. Helpers.updateGitRepo dir, (error) ->
  97. return callback(error) if error?
  98. Helpers.installNpmModules dir, (error) ->
  99. return callback(error) if error?
  100. Helpers.runGruntInstall dir, (error) ->
  101. return callback(error) if error?
  102. callback()
  103. cloneGitRepo: (repo_src, dir, callback = (error) ->) ->
  104. if !fs.existsSync(dir)
  105. proc = spawn "git", ["clone", repo_src, dir], stdio: "inherit"
  106. proc.on "close", () ->
  107. callback()
  108. else
  109. console.log "#{dir} already installed, skipping."
  110. callback()
  111. updateGitRepo: (dir, callback = (error) ->) ->
  112. proc = spawn "git", ["checkout", "master"], cwd: dir, stdio: "inherit"
  113. proc.on "close", () ->
  114. proc = spawn "git", ["pull"], cwd: dir, stdio: "inherit"
  115. proc.on "close", () ->
  116. callback()
  117. installNpmModules: (dir, callback = (error) ->) ->
  118. proc = spawn "npm", ["install"], stdio: "inherit", cwd: dir
  119. proc.on "close", () ->
  120. callback()
  121. runGruntInstall: (dir, callback = (error) ->) ->
  122. proc = spawn "grunt", ["install"], stdio: "inherit", cwd: dir
  123. proc.on "close", () ->
  124. callback()
  125. checkRedis: (callback = (error) ->) ->
  126. grunt.log.write "Checking Redis is running... "
  127. exec "redis-cli info", (error, stdout, stderr) ->
  128. if error? and error.message.match("Could not connect")
  129. grunt.log.error "FAIL. Redis is not running"
  130. return callback(error)
  131. else if error?
  132. return callback(error)
  133. else
  134. m = stdout.match(/redis_version:(.*)/)
  135. if !m?
  136. grunt.log.error "FAIL."
  137. grunt.log.error "Unknown redis version"
  138. error = new Error("Unknown redis version")
  139. else
  140. version = m[1]
  141. if semver.gt(version, "2.6.0")
  142. grunt.log.writeln "OK."
  143. grunt.log.writeln "Running Redis version #{version}"
  144. else
  145. grunt.log.error "FAIL."
  146. grunt.log.error "Redis version is too old (#{version}). Must be 2.6.0 or greater."
  147. error = new Error("Redis version is too old (#{version}). Must be 2.6.0 or greater.")
  148. callback(error)
  149. checkLatexmk: (callback = (error) ->) ->
  150. grunt.log.write "Checking latexmk is installed... "
  151. exec "latexmk --version", (error, stdout, stderr) ->
  152. if error? and error.message.match("command not found")
  153. grunt.log.error "FAIL."
  154. grunt.log.errorlns """
  155. Either latexmk is not installed or is not in your PATH.
  156. latexmk comes with TexLive 2013, and must be a version from 2013 or later.
  157. This is a not a fatal error, but compiling will not work without latexmk
  158. """
  159. return callback(error)
  160. else if error?
  161. return callback(error)
  162. else
  163. m = stdout.match(/Version (.*)/)
  164. if !m?
  165. grunt.log.error "FAIL."
  166. grunt.log.error "Unknown latexmk version"
  167. error = new Error("Unknown latexmk version")
  168. else
  169. version = m[1]
  170. if semver.gte(version + ".0", "4.39.0")
  171. grunt.log.writeln "OK."
  172. grunt.log.writeln "Running latexmk version #{version}"
  173. else
  174. grunt.log.error "FAIL."
  175. grunt.log.errorlns """
  176. latexmk version is too old (#{version}). Must be 4.39 or greater.
  177. This is a not a fatal error, but compiling will not work without latexmk
  178. """
  179. error = new Error("latexmk is too old")
  180. callback(error)
  181. checkS3: (callback = (error) ->) ->
  182. grunt.log.write "Checking S3 credentials... "
  183. try
  184. client = knox.createClient({
  185. key: settings.s3.key
  186. secret: settings.s3.secret
  187. bucket: settings.s3.buckets.user_files
  188. })
  189. catch e
  190. grunt.log.error "FAIL."
  191. grunt.log.errorlns """
  192. Please configure you Amazon S3 credentials in config/settings.development.coffee
  193. Amazon S3 (Simple Storage Service) is a cloud storage service provided by
  194. Amazon. ShareLaTeX uses S3 for storing binary files like images. You can
  195. sign up for an account and find out more at:
  196. http://aws.amazon.com/s3/
  197. """
  198. return callback()
  199. client.getFile "does-not-exist", (error, response) ->
  200. unless response? and response.statusCode == 404
  201. grunt.log.error "FAIL."
  202. grunt.log.errorlns """
  203. Could not connect to Amazon S3. Please check your credentials.
  204. """
  205. else
  206. grunt.log.write "OK."
  207. callback()