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()); }