public void Update(IVariables variables) { bool VariableNameIsMappedPath(string v) { if (v.StartsWith("Octopus", StringComparison.OrdinalIgnoreCase) && !v.StartsWith("Octopus:", StringComparison.OrdinalIgnoreCase)) { // Only include variables starting with 'Octopus' // if it also has a colon (:) return(false); } return(map.ContainsKey(v)); } var replaced = 0; foreach (var name in variables.GetNames().Where(VariableNameIsMappedPath)) { try { log.Verbose(StructuredConfigMessages.StructureFound(name)); replaced++; map[name](variables.Get(name)); } catch (Exception e) { log.WarnFormat("Unable to set value for {0}. The following error occurred: {1}", name, e.Message); } } if (replaced == 0) { log.Info(StructuredConfigMessages.NoStructuresFound); } }
ITopLevelExpression TryReplaceValue(ITopLevelExpression expr, IVariables variables) { switch (expr) { case KeyValuePairExpression pair: var logicalName = pair.Key?.Text?.LogicalValue ?? ""; if (!IsOctopusVariableName(logicalName) && variables.IsSet(logicalName)) { log.Verbose(StructuredConfigMessages.StructureFound(logicalName)); var logicalValue = variables.Get(logicalName); var encodedValue = Encode.Value(logicalValue); var newValueExpr = new ValueExpression(new StringValue(logicalValue, encodedValue)); // In cases where a key was specified with neither separator nor value // we have to add a separator, otherwise the value becomes part of the key. var separator = pair.Separator ?? new SeparatorExpression(":"); return(new KeyValuePairExpression(pair.Key, separator, newValueExpr)); } else { return(expr); } default: return(expr); } }
public void ModifyFile(string filePath, IVariables variables) { try { void LogReplacement(string key) => log.Verbose(StructuredConfigMessages.StructureFound(key)); var replaced = 0; var variablesByKey = variables .Where(v => !OctopusReservedVariablePattern.IsMatch(v.Key)) .DistinctBy(v => v.Key) .ToDictionary <KeyValuePair <string, string>, string, Func <string?> >(v => v.Key, v => () => { LogReplacement(v.Key); replaced++; return(variables.Get(v.Key)); }, StringComparer.OrdinalIgnoreCase); // Read and transform the input file var fileText = fileSystem.ReadFile(filePath, out var encoding); var lineEnding = fileText.GetMostCommonLineEnding(); var outputEvents = new List <ParsingEvent>(); var indentDetector = new YamlIndentDetector(); using (var reader = new StringReader(fileText)) { var scanner = new Scanner(reader, false); var parser = new Parser(scanner); var classifier = new YamlEventStreamClassifier(); (IYamlNode startEvent, string?replacementValue)? structureWeAreReplacing = null; while (parser.MoveNext()) { var ev = parser.Current; if (ev == null) { continue; } indentDetector.Process(ev); if (ev is Comment c) { ev = c.RestoreLeadingSpaces(); } var node = classifier.Process(ev); if (structureWeAreReplacing == null) { // Not replacing: searching for things to replace, copying events to output. if (node is YamlNode <Scalar> scalar && variablesByKey.TryGetValue(scalar.Path, out var newValue)) { outputEvents.Add(scalar.Event.ReplaceValue(newValue())); } else if (node is YamlNode <MappingStart> mappingStart && variablesByKey.TryGetValue(mappingStart.Path, out var mappingReplacement)) { structureWeAreReplacing = (mappingStart, mappingReplacement()); } else if (node is YamlNode <SequenceStart> sequenceStart && variablesByKey.TryGetValue(sequenceStart.Path, out var sequenceReplacement)) { structureWeAreReplacing = (sequenceStart, sequenceReplacement()); }
public void ModifyFile(string filePath, IVariables variables) { var encodingPrecedence = new List <Encoding>(CalamariPhysicalFileSystem.DefaultInputEncodingPrecedence); var fileBytes = fileSystem.ReadAllBytes(filePath); if (TryGetDeclaredEncoding(fileBytes) is {} declaredEncoding) { encodingPrecedence.Insert(0, declaredEncoding); } var fileText = fileSystem.ReadAllText(fileBytes, out var encoding, encodingPrecedence); var lineEnding = fileText.GetMostCommonLineEnding(); var doc = new XmlDocument(); try { doc.LoadXml(fileText); } catch (XmlException e) { throw new StructuredConfigFileParseException(e.Message, e); } var nsManager = BuildNsManagerFromDocument(doc); var navigator = doc.CreateNavigator(); var replaced = 0; foreach (var variable in variables) { if (TryGetXPathFromVariableKey(variable.Key, nsManager) is {} xPathExpression) { var selectedNodes = navigator.XPath2SelectNodes(xPathExpression, nsManager); var variableValue = variables.Get(variable.Key); foreach (XPathNavigator selectedNode in selectedNodes) { log.Verbose(StructuredConfigMessages.StructureFound(variable.Key)); replaced++; switch (selectedNode.UnderlyingObject) { case XmlText text: text.Data = variableValue; break; case XmlAttribute attribute: attribute.Value = variableValue; break; case XmlComment comment: comment.Data = variableValue; break; case XmlElement element: if (element.ChildNodes.Count == 1 && element.ChildNodes[0].NodeType == XmlNodeType.CDATA) { // Try to preserve CDatas in the output. element.ChildNodes[0].Value = variableValue; } else if (ContainsElements(element)) { TrySetInnerXml(element, variable.Key, variableValue); } else { element.InnerText = variableValue; } break; case XmlCharacterData cData: cData.Data = variableValue; break; case XmlProcessingInstruction processingInstruction: processingInstruction.Data = variableValue; break; case XmlNode node: log.Warn($"XML Node of type '{node.NodeType}' is not supported"); break; default: log.Warn($"XPath returned an object of type '{selectedNode.GetType().FullName}', which is not supported"); break; } } } } if (replaced == 0) { log.Info(StructuredConfigMessages.NoStructuresFound); } fileSystem.OverwriteFile(filePath, textWriter => { var xmlWriterSettings = new XmlWriterSettings { Indent = true, NewLineChars = lineEnding == StringExtensions.LineEnding.Dos ? "\r\n" : "\n", OmitXmlDeclaration = doc.FirstChild.NodeType != XmlNodeType.XmlDeclaration }; using (var writer = XmlWriter.Create(textWriter, xmlWriterSettings)) { doc.Save(writer); writer.Close(); } }, encoding); }