public void run(PackageResult packageResult, ChocolateyConfiguration config) { var installDirectory = packageResult != null ? packageResult.InstallLocation : string.Empty; if (string.IsNullOrWhiteSpace(installDirectory) || installDirectory.is_equal_to(ApplicationParameters.InstallLocation) || installDirectory.is_equal_to(ApplicationParameters.PackagesLocation)) { var logMessage = "Install location is not specific enough, cannot capture files:{0} Erroneous install location captured as '{1}'".format_with(Environment.NewLine, installDirectory); if (packageResult != null) { packageResult.Messages.Add(new ResultMessage(ResultType.Warn, logMessage)); } this.Log().Error(logMessage); return; } var transformFiles = _fileSystem.get_files(installDirectory, "*" + ApplicationParameters.ConfigFileTransformExtension, SearchOption.AllDirectories); foreach (var transformFile in transformFiles.or_empty_list_if_null()) { this.Log().Debug(() => "Preparing transform for '{0}'".format_with(transformFile)); var targetFileName = _fileSystem.get_file_name(transformFile.Replace(ApplicationParameters.ConfigFileTransformExtension, string.Empty)); // target files must exist, otherwise one is added next to the transform var targetFiles = _fileSystem.get_files(installDirectory, targetFileName, SearchOption.AllDirectories); var targetFilesTest = targetFiles as IList <string> ?? targetFiles.ToList(); if (!targetFilesTest.Any()) { targetFiles = new[] { transformFile.Replace(ApplicationParameters.ConfigFileTransformExtension, string.Empty) }; this.Log().Debug(() => "No matching files found for transform {0}.{1} Creating '{2}'".format_with(_fileSystem.get_file_name(transformFile), Environment.NewLine, targetFiles.FirstOrDefault())); } foreach (var targetFile in targetFilesTest.or_empty_list_if_null()) { GlobalMutex.enter( () => { var backupTargetFile = targetFile.Replace(ApplicationParameters.PackagesLocation, ApplicationParameters.PackageBackupLocation); FaultTolerance.try_catch_with_logging_exception( () => { // if there is a backup, we need to put it back in place // the user has indicated they are using transforms by putting // the transform file into the folder, so we will override // the replacement of the file and instead pull from the last // backup and let the transform to its thing instead. if (_fileSystem.file_exists(backupTargetFile)) { this.Log().Debug(() => "Restoring backup configuration file for '{0}'.".format_with(targetFile)); _fileSystem.copy_file(backupTargetFile, targetFile, overwriteExisting: true); } }, "Error replacing backup config file", throwError: false, logWarningInsteadOfError: true); try { this.Log().Info(() => "Transforming '{0}' with the data from '{1}'".format_with(_fileSystem.get_file_name(targetFile), _fileSystem.get_file_name(transformFile))); using (var transformation = new XmlTransformation(_fileSystem.read_file(transformFile), isTransformAFile: false, logger: null)) { using (var document = new XmlTransformableDocument { PreserveWhitespace = true }) { using (var inputStream = _fileSystem.open_file_readonly(targetFile)) { document.Load(inputStream); } // before applying the XDT transformation, let's make a // backup of the file that should be transformed, in case // things don't go correctly this.Log().Debug(() => "Creating backup configuration file for '{0}'.".format_with(targetFile)); _fileSystem.copy_file(targetFile, backupTargetFile, overwriteExisting: true); bool succeeded = transformation.Apply(document); if (succeeded) { this.Log().Debug(() => "Transform applied successfully for '{0}'".format_with(targetFile)); using (var memoryStream = new MemoryStream()) { document.Save(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); using (var fileStream = _fileSystem.create_file(targetFile)) { fileStream.SetLength(0); memoryStream.CopyTo(fileStream); } } // need to test that the transformed configuration file is valid // XML. We can test this by trying to load it again into an XML document try { this.Log().Debug(() => "Verifying transformed configuration file..."); document.Load(targetFile); this.Log().Debug(() => "Transformed configuration file verified."); } catch (Exception) { this.Log().Warn(() => "Transformed configuration file doesn't contain valid XML. Restoring backup file..."); _fileSystem.copy_file(backupTargetFile, targetFile, overwriteExisting: true); this.Log().Debug(() => "Backup file restored."); } } else { // at this point, there is no need to restore the backup file, // as the resulting transform hasn't actually been written to disk. this.Log().Warn(() => "Transform failed for '{0}'".format_with(targetFile)); } } } } catch (Exception) { FaultTolerance.try_catch_with_logging_exception( () => { // something went wrong with the transformation, so we should restore // the original configuration file from the backup this.Log().Warn(() => "There was a problem transforming configuration file, restoring backup file..."); _fileSystem.copy_file(backupTargetFile, targetFile, overwriteExisting: true); this.Log().Debug(() => "Backup file restored."); }, "Error restoring backup configuration file."); } }, MUTEX_TIMEOUT); } } }
public XmlType deserialize <XmlType>(string xmlFilePath, int retryCount) { return(FaultTolerance.retry(retryCount, () => GlobalMutex.enter( () => { this.Log().Trace("Entered mutex to deserialize '{0}'".format_with(xmlFilePath)); return FaultTolerance.try_catch_with_logging_exception( () => { var xmlSerializer = new XmlSerializer(typeof(XmlType)); using (var fileStream = _fileSystem.open_file_readonly(xmlFilePath)) using (var fileReader = new StreamReader(fileStream)) using (var xmlReader = XmlReader.Create(fileReader)) { if (!xmlSerializer.CanDeserialize(xmlReader)) { this.Log().Warn("Cannot deserialize response of type {0}", typeof(XmlType)); return default(XmlType); } try { return (XmlType)xmlSerializer.Deserialize(xmlReader); } catch (InvalidOperationException ex) { // Check if its just a malformed document. if (ex.Message.Contains("There is an error in XML document")) { // If so, check for a backup file and try an parse that. if (_fileSystem.file_exists(xmlFilePath + ".backup")) { using (var backupStream = _fileSystem.open_file_readonly(xmlFilePath + ".backup")) using (var backupReader = new StreamReader(backupStream)) using (var backupXmlReader = XmlReader.Create(backupReader)) { var validConfig = (XmlType)xmlSerializer.Deserialize(backupXmlReader); // If there's no errors and it's valid, go ahead and replace the bad file with the backup. if (validConfig != null) { // Close fileReader so that we can copy the file without it being locked. fileReader.Close(); _fileSystem.copy_file(xmlFilePath + ".backup", xmlFilePath, overwriteExisting: true); } return validConfig; } } } throw; } finally { foreach (var updateFile in _fileSystem.get_files(_fileSystem.get_directory_name(xmlFilePath), "*.update").or_empty_list_if_null()) { this.Log().Debug("Removing '{0}'".format_with(updateFile)); FaultTolerance.try_catch_with_logging_exception( () => _fileSystem.delete_file(updateFile), errorMessage: "Unable to remove update file", logDebugInsteadOfError: true, isSilent: true ); } } } }, "Error deserializing response of type {0}".format_with(typeof(XmlType)), throwError: true); }, MUTEX_TIMEOUT), waitDurationMilliseconds: 200, increaseRetryByMilliseconds: 200)); }
public static GetChocolatey GetChocolatey() { return(GlobalMutex.enter(() => set_up(), 10)); }
public void serialize <XmlType>(XmlType xmlType, string xmlFilePath, bool isSilent) { _fileSystem.create_directory_if_not_exists(_fileSystem.get_directory_name(xmlFilePath)); FaultTolerance.retry(3, () => GlobalMutex.enter( () => { this.Log().Trace("Entered mutex to serialize '{0}'".format_with(xmlFilePath)); FaultTolerance.try_catch_with_logging_exception( () => { var xmlSerializer = new XmlSerializer(typeof(XmlType)); this.Log().Trace("Opening memory stream for xml file creation."); using (var memoryStream = new MemoryStream()) using (var streamWriter = new StreamWriter(memoryStream, encoding: new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)) { AutoFlush = true } ){ xmlSerializer.Serialize(streamWriter, xmlType); streamWriter.Flush(); // Grab the hash of both files and compare them. this.Log().Trace("Hashing original file at '{0}'".format_with(xmlFilePath)); var originalFileHash = _hashProvider.hash_file(xmlFilePath); memoryStream.Position = 0; if (!originalFileHash.is_equal_to(_hashProvider.hash_stream(memoryStream))) { this.Log().Trace("The hashes were different."); // If there wasn't a file there in the first place, just write the new one out directly. if (string.IsNullOrEmpty(originalFileHash)) { this.Log().Debug("There was no original file at '{0}'".format_with(xmlFilePath)); memoryStream.Position = 0; _fileSystem.write_file(xmlFilePath, () => memoryStream); this.Log().Trace("Closing xml memory stream."); memoryStream.Close(); streamWriter.Close(); return; } // Otherwise, create an update file, and resiliently move it into place. var tempUpdateFile = xmlFilePath + "." + Process.GetCurrentProcess().Id + ".update"; this.Log().Trace("Creating a temp file at '{0}'".format_with(tempUpdateFile)); memoryStream.Position = 0; this.Log().Trace("Writing file '{0}'".format_with(tempUpdateFile)); _fileSystem.write_file(tempUpdateFile, () => memoryStream); memoryStream.Close(); streamWriter.Close(); this.Log().Trace("Replacing file '{0}' with '{1}'.".format_with(xmlFilePath, tempUpdateFile)); _fileSystem.replace_file(tempUpdateFile, xmlFilePath, xmlFilePath + ".backup"); } } }, errorMessage: "Error serializing type {0}".format_with(typeof(XmlType)), throwError: true, isSilent: isSilent); }, MUTEX_TIMEOUT), waitDurationMilliseconds: 200, increaseRetryByMilliseconds: 200); }
public static GetChocolatey GetChocolatey(bool initializeLogging) { return(GlobalMutex.enter(() => set_up(initializeLogging), 10)); }