Просмотр исходного кода

Merge pull request #73 from sharelatex/bg-handle-dot-files-in-resource-list

handle dot files in resource list
Brian Gough 9 лет назад
Родитель
Сommit
860929b198

+ 8 - 0
services/clsi/app/coffee/OutputCacheManager.coffee

@@ -63,6 +63,11 @@ module.exports = OutputCacheManager =
 				# copy all the output files into the new cache directory
 				results = []
 				async.mapSeries outputFiles, (file, cb) ->
+					# don't send dot files as output, express doesn't serve them
+					if OutputCacheManager._fileIsHidden(file.path)
+						logger.warn compileDir: compileDir, path: file.path, "ignoring dotfile in output"
+						return cb()
+					# copy other files into cache directory if valid
 					newFile = _.clone(file)
 					[src, dst] = [Path.join(compileDir, file.path), Path.join(cacheDir, file.path)]
 					OutputCacheManager._checkFileIsSafe src, (err, isSafe) ->
@@ -144,6 +149,9 @@ module.exports = OutputCacheManager =
 				removeDir dir, cb
 			, callback
 
+	_fileIsHidden: (path) ->
+		return path?.match(/^\.|\/./)?
+
 	_checkFileIsSafe: (src, callback = (error, isSafe) ->) ->
 		# check if we have a valid file to copy into the cache
 		fs.stat src, (err, stats) ->

+ 4 - 2
services/clsi/app/coffee/OutputFileFinder.coffee

@@ -29,8 +29,10 @@ module.exports = OutputFileFinder =
 		callback = (error, fileList) ->
 			_callback(error, fileList)
 			_callback = () ->
-				
-		args = [directory, "-name", ".*", "-prune", "-o", "-type", "f", "-print"]
+
+		# don't include clsi-specific files/directories in the output list
+		EXCLUDE_DIRS = ["-name", ".cache", "-o", "-name", ".archive","-o", "-name", ".project-*"]
+		args = [directory, "(", EXCLUDE_DIRS..., ")", "-prune", "-o", "-type", "f", "-print"]
 		logger.log args: args, "running find command"
 
 		proc = spawn("find", args)

+ 8 - 3
services/clsi/app/coffee/ResourceStateManager.coffee

@@ -50,14 +50,19 @@ module.exports = ResourceStateManager =
 				resources = ({path: path} for path in resourceList)
 				callback(null, resources)
 
-	checkResourceFiles: (resources, allFiles, directory, callback = (error) ->) ->
+	checkResourceFiles: (resources, allFiles, basePath, callback = (error) ->) ->
+		# check the paths are all relative to current directory
+		for file in resources or []
+			for dir in file?.path?.split('/')
+				if dir == '..'
+					return callback new Error("relative path in resource file list")
 		# check if any of the input files are not present in list of files
 		seenFile = {}
 		for file in allFiles
 			seenFile[file] = true
 		missingFiles = (resource.path for resource in resources when not seenFile[resource.path])
-		if missingFiles.length > 0
-			logger.err missingFiles:missingFiles, dir:directory, allFiles:allFiles, resources:resources, "missing input files for project"
+		if missingFiles?.length > 0
+			logger.err missingFiles:missingFiles, basePath:basePath, allFiles:allFiles, resources:resources, "missing input files for project"
 			return callback new Errors.FilesOutOfSyncError("resource files missing in incremental update")
 		else
 			callback()

+ 0 - 2
services/clsi/app/coffee/ResourceWriter.coffee

@@ -78,8 +78,6 @@ module.exports = ResourceWriter =
 					should_delete = true
 					if path.match(/^output\./) or path.match(/\.aux$/) or path.match(/^cache\//) # knitr cache
 						should_delete = false
-					if path == '.project-sync-state'
-						should_delete = false
 					if path == "output.pdf" or path == "output.dvi" or path == "output.log" or path == "output.xdv"
 						should_delete = true
 					if path == "output.tex" # created by TikzManager if present in output files

+ 109 - 0
services/clsi/test/unit/coffee/ResourceStateManagerTests.coffee

@@ -0,0 +1,109 @@
+SandboxedModule = require('sandboxed-module')
+sinon = require('sinon')
+should = require('chai').should()
+modulePath = require('path').join __dirname, '../../../app/js/ResourceStateManager'
+Path = require "path"
+Errors = require "../../../app/js/Errors"
+
+describe "ResourceStateManager", ->
+	beforeEach ->
+		@ResourceStateManager = SandboxedModule.require modulePath, requires:
+			"fs": @fs = {}
+			"logger-sharelatex": {log: sinon.stub(), err: sinon.stub()}
+			"./SafeReader": @SafeReader = {}
+		@basePath = "/path/to/write/files/to"
+		@resources = [
+			{path: "resource-1-mock"}
+			{path: "resource-2-mock"}
+			{path: "resource-3-mock"}
+		]
+		@state = "1234567890"
+		@resourceFileName = "#{@basePath}/.project-sync-state"
+		@resourceFileContents = "#{@resources[0].path}\n#{@resources[1].path}\n#{@resources[2].path}\nstateHash:#{@state}"
+		@callback = sinon.stub()
+
+	describe "saveProjectState", ->
+		beforeEach ->
+			@fs.writeFile = sinon.stub().callsArg(2)
+
+		describe "when the state is specified", ->
+			beforeEach ->
+				@ResourceStateManager.saveProjectState(@state, @resources, @basePath, @callback)
+
+			it "should write the resource list to disk", ->
+				@fs.writeFile
+					.calledWith(@resourceFileName, @resourceFileContents)
+					.should.equal true
+
+			it "should call the callback", ->
+				@callback.called.should.equal true
+
+		describe "when the state is undefined", ->
+			beforeEach ->
+				@state = undefined
+				@fs.unlink = sinon.stub().callsArg(1)
+				@ResourceStateManager.saveProjectState(@state, @resources, @basePath, @callback)
+
+			it "should unlink the resource file", ->
+				@fs.unlink
+					.calledWith(@resourceFileName)
+					.should.equal true
+
+			it "should not write the resource list to disk", ->
+				@fs.writeFile.called.should.equal false
+
+			it "should call the callback", ->
+				@callback.called.should.equal true
+
+	describe "checkProjectStateMatches", ->
+
+		describe "when the state matches", ->
+			beforeEach ->
+				@SafeReader.readFile = sinon.stub().callsArgWith(3, null, @resourceFileContents)
+				@ResourceStateManager.checkProjectStateMatches(@state, @basePath, @callback)
+
+			it "should read the resource file", ->
+				@SafeReader.readFile
+					.calledWith(@resourceFileName)
+					.should.equal true
+
+			it "should call the callback with the results", ->
+				@callback.calledWithMatch(null, @resources).should.equal true
+
+		describe "when the state does not match", ->
+			beforeEach ->
+				@SafeReader.readFile = sinon.stub().callsArgWith(3, null, @resourceFileContents)
+				@ResourceStateManager.checkProjectStateMatches("not-the-original-state", @basePath, @callback)
+
+			it "should call the callback with an error", ->
+				error = new Errors.FilesOutOfSyncError("invalid state for incremental update")
+				@callback.calledWith(error).should.equal true
+
+	describe "checkResourceFiles", ->
+		describe "when all the files are present", ->
+			beforeEach ->
+				@allFiles = [ @resources[0].path, @resources[1].path, @resources[2].path]
+				@ResourceStateManager.checkResourceFiles(@resources, @allFiles, @basePath, @callback)
+
+			it "should call the callback", ->
+				@callback.calledWithExactly().should.equal true
+
+		describe "when there is a missing file", ->
+			beforeEach ->
+				@allFiles = [ @resources[0].path, @resources[1].path]
+				@fs.stat = sinon.stub().callsArgWith(1, new Error())
+				@ResourceStateManager.checkResourceFiles(@resources, @allFiles, @basePath, @callback)
+
+			it "should call the callback with an error", ->
+				error = new Errors.FilesOutOfSyncError("resource files missing in incremental update")
+				@callback.calledWith(error).should.equal true
+
+		describe "when a resource contains a relative path", ->
+			beforeEach ->
+				@resources[0].path = "../foo/bar.tex"
+				@allFiles = [ @resources[0].path, @resources[1].path, @resources[2].path]
+				@ResourceStateManager.checkResourceFiles(@resources, @allFiles, @basePath, @callback)
+
+			it "should call the callback with an error", ->
+				@callback.calledWith(new Error("relative path in resource file list")).should.equal true
+