// ReSharper disable PublicConstructorInAbstractClass // ReSharper disable ClassNeverInstantiated.Global namespace TeamCity.CSharpInteractive; using HostApi; using JetBrains.TeamCity.ServiceMessages; internal class ReliableBuildContext : IBuildContext { private readonly ITeamCitySettings _teamCitySettings; private readonly IFileSystem _fileSystem; private readonly IMessagesReader _messagesReader; private readonly IBuildContext _baseBuildContext; private readonly Dictionary _sources = new(); public ReliableBuildContext( ITeamCitySettings teamCitySettings, IFileSystem fileSystem, IMessagesReader messagesReader, [Tag("base")] IBuildContext baseBuildContext) { _teamCitySettings = teamCitySettings; _fileSystem = fileSystem; _messagesReader = messagesReader; _baseBuildContext = baseBuildContext; } public IReadOnlyList ProcessMessage(in Output output, IServiceMessage message) { return ProcessMessage(output.StartInfo, output.ProcessId, message); } public IReadOnlyList ProcessMessage(IStartInfo startInfo, int processId, IServiceMessage message) { var source = message.GetValue("source"); if (string.IsNullOrWhiteSpace(source)) { return _baseBuildContext.ProcessMessage(startInfo, processId, message); } _sources.TryAdd(source, (startInfo, processId)); return Array.Empty(); } public IReadOnlyList ProcessOutput(in Output output) => _baseBuildContext.ProcessOutput(output); public IBuildResult Create(IStartInfo startInfo, int? exitCode) { if (_teamCitySettings.ServiceMessagesBackupPathEnvValue == null) { return _baseBuildContext.Create(startInfo, exitCode); } var items = from source in _sources let indicesFile = Path.Combine(_teamCitySettings.ServiceMessagesBackupPathEnvValue, source.Key) where _fileSystem.IsFileExist(indicesFile) let messagesFile = indicesFile + ".msg" where _fileSystem.IsFileExist(messagesFile) from message in _messagesReader.Read(indicesFile, messagesFile) select (message, outputStartInfo: source.Value.Item1, outputProcessId: source.Value.Item2); foreach (var (message, outputStartInfo, outputProcessId) in items) { _baseBuildContext.ProcessMessage(outputStartInfo, outputProcessId, message); } return _baseBuildContext.Create(startInfo, exitCode); } }