diff options
Diffstat (limited to 'src/zen/cmds/projectstore_cmd.cpp')
| -rw-r--r-- | src/zen/cmds/projectstore_cmd.cpp | 301 |
1 files changed, 162 insertions, 139 deletions
diff --git a/src/zen/cmds/projectstore_cmd.cpp b/src/zen/cmds/projectstore_cmd.cpp index d31c34fd0..c6a3434f8 100644 --- a/src/zen/cmds/projectstore_cmd.cpp +++ b/src/zen/cmds/projectstore_cmd.cpp @@ -2,16 +2,21 @@ #include "projectstore_cmd.h" +#include "zenserviceclient.h" + #include <zencore/basicfile.h> #include <zencore/compactbinarybuilder.h> #include <zencore/compactbinaryutil.h> #include <zencore/compress.h> +#include <zencore/except_fmt.h> #include <zencore/filesystem.h> #include <zencore/fmtutils.h> #include <zencore/logging.h> +#include <zencore/logging/broadcastsink.h> #include <zencore/parallelwork.h> #include <zencore/process.h> #include <zencore/scopeguard.h> +#include <zencore/session.h> #include <zencore/stream.h> #include <zencore/timer.h> #include <zencore/workthreadpool.h> @@ -21,16 +26,20 @@ #include <zenhttp/httpclientauth.h> #include <zenhttp/httpcommon.h> #include <zenremotestore/builds/buildstoragecache.h> +#include <zenremotestore/builds/buildstorageresolve.h> #include <zenremotestore/builds/buildstorageutil.h> #include <zenremotestore/builds/jupiterbuildstorage.h> #include <zenremotestore/jupiter/jupiterhost.h> -#include <zenremotestore/operationlogoutput.h> #include <zenremotestore/projectstore/projectstoreoperations.h> #include <zenremotestore/projectstore/remoteprojectstore.h> #include <zenremotestore/transferthreadworkers.h> +#include <zenutil/authutils.h> +#include <zenutil/logging.h> +#include <zenutil/progress.h> +#include <zenutil/sessionsclient.h> #include <zenutil/workerpools.h> -#include "../progressbar.h" +#include "consoleprogress.h" ZEN_THIRD_PARTY_INCLUDES_START #include <json11.hpp> @@ -112,6 +121,26 @@ namespace projectstore_impl { } } + // `OplogMirrorCommand::Run` uses a latching boolean flag rather than the + // SignalCounter above, because it drives a worker pool that aborts on any + // interrupt. Kept separate from SignalCallbackHandler so neither interferes + // with the other when both are installed in the same process. + static std::atomic<bool> MirrorAbortFlag{false}; + + static void MirrorSignalCallbackHandler(int SigNum) + { + if (SigNum == SIGINT) + { + MirrorAbortFlag.store(true); + } +#if ZEN_PLATFORM_WINDOWS + if (SigNum == SIGBREAK) + { + MirrorAbortFlag.store(true); + } +#endif + } + void ExecuteAsyncOperation(HttpClient& Http, std::string_view Url, IoBuffer&& Payload, bool PlainProgress) { signal(SIGINT, SignalCallbackHandler); @@ -131,13 +160,16 @@ namespace projectstore_impl { throw std::runtime_error(fmt::format("invalid job id returned, received '{}'", JobIdText)); } - ProgressBar ProgressBar(PlainProgress ? ProgressBar::Mode::Plain : ProgressBar::Mode::Pretty, ""sv); + std::unique_ptr<ProgressBase> ProgressOwner( + CreateConsoleProgress(PlainProgress ? ConsoleProgressMode::Plain : ConsoleProgressMode::Pretty)); + std::unique_ptr<ProgressBase::ProgressBar> Bar = ProgressOwner->CreateProgressBar(""sv); + std::string ActiveTask; - auto OuputMessages = [&](CbObjectView StatusObject) { + auto OutputMessages = [&](CbObjectView StatusObject) { CbArrayView Messages = StatusObject["Messages"sv].AsArrayView(); if (Messages.Num() > 0) { - ProgressBar.ForceLinebreak(); + Bar->ForceLinebreak(); for (auto M : Messages) { std::string_view Message = M.AsString(); @@ -169,33 +201,36 @@ namespace projectstore_impl { uint64_t RemainingCount = StatusObject["RemainingCount"sv].AsUInt64(); uint64_t ProgressElapsedTimeMs = StatusObject["ProgressElapsedTimeMs"sv].AsUInt64((uint64_t)-1); - if (!ProgressBar.IsSameTask(CurrentOp)) + if (ActiveTask != CurrentOp) { - ProgressBar.Finish(); + Bar->Finish(); + ActiveTask = ""; } - if (!ProgressBar.HasActiveTask()) + if (ActiveTask.empty()) { - OuputMessages(StatusObject); + OutputMessages(StatusObject); MessagesDone = true; + ActiveTask = std::string(CurrentOp); } - ProgressBar.UpdateState({.Task = std::string(CurrentOp), - .Details = std::string(CurrentOpDetails), - .TotalCount = TotalCount, - .RemainingCount = RemainingCount, - .OptionalElapsedTime = ProgressElapsedTimeMs}, - false); + Bar->UpdateState({.Task = std::string(CurrentOp), + .Details = std::string(CurrentOpDetails), + .TotalCount = TotalCount, + .RemainingCount = RemainingCount, + .OptionalElapsedTime = ProgressElapsedTimeMs}, + false); } if ((Status == "Complete") || (Status == "Aborted")) { - ProgressBar.Finish(); + Bar->Finish(); + ActiveTask = ""; } if (!MessagesDone) { - OuputMessages(StatusObject); + OutputMessages(StatusObject); } if (Status == "Complete") @@ -246,13 +281,13 @@ namespace projectstore_impl { #endif // ZEN_PLATFORM_WINDOWS if (HttpClient::Response DeleteResult = Http.Delete(fmt::format("/admin/jobs/{}", JobId))) { - ProgressBar.ForceLinebreak(); + Bar->ForceLinebreak(); ZEN_CONSOLE("Requested cancel..."); Cancelled = true; } else { - ProgressBar.ForceLinebreak(); + Bar->ForceLinebreak(); ZEN_CONSOLE("Failed cancelling job {}", DeleteResult); } continue; @@ -530,14 +565,8 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - - HttpClient Http = CreateHttpClient(m_HostName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) @@ -569,7 +598,7 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_OplogName = ResolveOplog(Http, m_ProjectName, m_OplogName); if (m_OplogName.empty()) { - throw std::runtime_error(fmt::format("Can't find oplog in project '{}'", m_OplogName, m_ProjectName)); + throw zen::runtime_error("Can't find oplog '{}' in project '{}'", m_OplogName, m_ProjectName); } if (m_DryRun) { @@ -620,19 +649,13 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - if (!m_OplogName.empty() && m_ProjectName.empty()) { throw OptionParseException("'--project' is required", m_Options.help()); } - HttpClient Http = CreateHttpClient(m_HostName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); std::string Url; if (m_ProjectName.empty()) @@ -709,19 +732,13 @@ CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - if (m_ProjectId.empty()) { throw OptionParseException("'--project' is required", m_Options.help()); } - HttpClient Http = CreateHttpClient(m_HostName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); std::string Url = fmt::format("/prj/{}", m_ProjectId); @@ -779,20 +796,14 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - if (m_ProjectId.empty()) { throw OptionParseException("'--project' is required", m_Options.help()); } - HttpClient Http = CreateHttpClient(m_HostName); - m_ProjectId = ResolveProject(Http, m_ProjectId); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); + m_ProjectId = ResolveProject(Http, m_ProjectId); if (m_ProjectId.empty()) { throw std::runtime_error("Project can not be found"); @@ -1010,20 +1021,14 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_BoostWorkerMemory = true; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - if (m_ProjectName.empty()) { throw OptionParseException("'--project' is required", m_Options.help()); } - HttpClient Http = CreateHttpClient(m_HostName); - m_ProjectName = ResolveProject(Http, m_ProjectName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); + m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { throw std::runtime_error("Project can not be found"); @@ -1114,7 +1119,7 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg std::string TargetUrlBase = m_ZenUrl; if (TargetUrlBase.find("://") == std::string::npos) { - // Assume https URL + // Assume http URL TargetUrlBase = fmt::format("http://{}", TargetUrlBase); } @@ -1303,7 +1308,14 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg std::filesystem::path MetadataPath(m_BuildsMetadataPath); IoBuffer MetaDataJson = ReadFile(MetadataPath).Flatten(); std::string_view Json(reinterpret_cast<const char*>(MetaDataJson.GetData()), MetaDataJson.GetSize()); - CbFieldIterator MetaData = LoadCompactBinaryFromJson(Json); + std::string JsonError; + CbFieldIterator MetaData = LoadCompactBinaryFromJson(Json, JsonError); + if (!JsonError.empty()) + { + throw zen::runtime_error("builds metadata file '{}' is malformed. Reason: '{}'", + MetadataPath.string(), + JsonError); + } Writer.AddBinary("metadata"sv, MetaData.GetBuffer()); } if (!m_BuildsMetadata.empty()) @@ -1313,7 +1325,7 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg size_t SplitPos = Pair.find('='); if (SplitPos == std::string::npos || SplitPos == 0) { - throw std::runtime_error(fmt::format("builds metadata key-value pair '{}' is malformed", Pair)); + throw zen::runtime_error("builds metadata key-value pair '{}' is malformed", Pair); } MetaDataWriter.AddString(Pair.substr(0, SplitPos), Pair.substr(SplitPos + 1)); return true; @@ -1323,7 +1335,7 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg } } Writer.EndObject(); // "builds" - TargetDescription = fmt::format("[builds] {}/{}/{}/{}", m_CloudUrl, m_JupiterNamespace, m_JupiterBucket, m_BuildsId); + TargetDescription = fmt::format("[builds] {}/{}/{}/{}", m_BuildsUrl, m_JupiterNamespace, m_JupiterBucket, m_BuildsId); } if (!m_ZenUrl.empty()) { @@ -1517,13 +1529,6 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_BoostWorkerMemory = true; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - if (m_ProjectName.empty()) { throw OptionParseException("'--project' is required", m_Options.help()); @@ -1541,8 +1546,9 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg m_Options.help()); } - HttpClient Http = CreateHttpClient(m_HostName); - m_ProjectName = ResolveProject(Http, m_ProjectName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); + m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) { throw std::runtime_error("Project can not be found"); @@ -1738,7 +1744,11 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg } } Writer.EndObject(); // "builds" - SourceDescription = fmt::format("[builds] {}/{}/{}/{}", m_CloudUrl, m_JupiterNamespace, m_JupiterBucket, m_BuildsId); + SourceDescription = fmt::format("[builds] {}/{}/{}/{}", + m_BuildsHost.empty() ? m_BuildsOverrideHost : m_BuildsHost, + m_JupiterNamespace, + m_JupiterBucket, + m_BuildsId); } if (!m_ZenUrl.empty()) { @@ -1806,14 +1816,8 @@ SnapshotOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - - HttpClient Http = CreateHttpClient(m_HostName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); if (m_ProjectName.empty()) { @@ -1869,14 +1873,8 @@ ProjectStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ar return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - - HttpClient Http = CreateHttpClient(m_HostName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); if (HttpClient::Response Result = Http.Get("/stats/prj", HttpClient::Accept(ZenContentType::kJSON))) { ZEN_CONSOLE("{}", Result.ToText()); @@ -1922,14 +1920,8 @@ ProjectOpDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char* return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - - HttpClient Http = CreateHttpClient(m_HostName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) @@ -2038,14 +2030,8 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - - HttpClient Http = CreateHttpClient(m_HostName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) @@ -2108,16 +2094,59 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg std::unordered_set<std::u8string> FileNames; std::atomic<uint64_t> WrittenByteCount = 0; - std::atomic<bool> AbortFlag(false); + // Install Ctrl-C handler so SIGINT aborts the worker pool rather than killing + // the process. Without this the local AbortFlag would shadow whatever global + // handler is installed elsewhere and interrupts would be dropped. RAII so + // the previous handler is restored when the function returns or throws. + MirrorAbortFlag.store(false); + ScopedSignalHandler SigIntGuard(SIGINT, MirrorSignalCallbackHandler); +#if ZEN_PLATFORM_WINDOWS + ScopedSignalHandler SigBreakGuard(SIGBREAK, MirrorSignalCallbackHandler); +#endif + std::atomic<bool>& AbortFlag = MirrorAbortFlag; Stopwatch WriteStopWatch; + // Filenames come from the remote oplog, which may be compromised or untrusted. + // Reject anything that could escape the mirror root via an absolute path, drive + // letter / UNC / device path prefix, or '..' component before it is joined to + // RootPath. Returns nullptr when the filename is safe. + auto UnsafeFileNameReason = [](const std::filesystem::path& FileName) -> const char* { + if (FileName.empty()) + { + return "filename is empty"; + } + if (FileName.has_root_name()) + { + return "filename has a root name (drive letter, UNC share, or device path)"; + } + if (FileName.has_root_directory()) + { + return "filename is absolute"; + } + for (const std::filesystem::path& Component : FileName) + { + const std::u8string C = Component.u8string(); + if (C.empty() || C == u8"..") + { + return "filename contains a '..' or empty component"; + } + } + return nullptr; + }; + auto EmitFilesForDataArray = [&](CbArrayView DataArray) { for (auto DataIter : DataArray) { if (CbObjectView Data = DataIter.AsObjectView()) { std::filesystem::path FileName(Data["filename"sv].AsU8String()); + if (const char* Reason = UnsafeFileNameReason(FileName)) + { + ZEN_CONSOLE_ERROR("Rejecting unsafe filename '{}' from remote oplog: {}", FileName.string(), Reason); + AbortFlag.store(true); + break; + } if (!m_FilenameFilter.empty()) { std::string FileNameLowerCase = ToLower(FileName.string()); @@ -2162,7 +2191,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg IoBuffer ChunkData = m_Decompress ? TryDecompress(ChunkResponse.ResponsePayload) : ChunkResponse.ResponsePayload; - if (!MoveToFile(TargetPath, ChunkData)) + if (std::error_code MoveEc = MoveToFile(TargetPath, ChunkData); MoveEc) { WriteFile(TargetPath, ChunkData); } @@ -2196,17 +2225,18 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg ZEN_CONSOLE("Fetched oplog in {}", NiceTimeSpanMs(uint64_t(Response.ElapsedSeconds * 1000.0))); if (CbObject ResponseObject = Response.AsObject()) { - std::unique_ptr<ProgressBar> EmitProgressBar; + std::unique_ptr<ProgressBase> ProgressOwner2(CreateConsoleProgress(ConsoleProgressMode::Pretty)); + std::unique_ptr<ProgressBase::ProgressBar> EmitProgressBar; { - ProgressBar ParseProgressBar(ProgressBar::Mode::Pretty, ""); - CbArrayView Entries = ResponseObject["entries"sv].AsArrayView(); - uint64_t Remaining = Entries.Num(); + std::unique_ptr<ProgressBase::ProgressBar> ParseProgressBar = ProgressOwner2->CreateProgressBar(""); + CbArrayView Entries = ResponseObject["entries"sv].AsArrayView(); + uint64_t Remaining = Entries.Num(); for (auto EntryIter : Entries) { if (!AbortFlag) { CbObjectView Entry = EntryIter.AsObjectView(); - ParseProgressBar.UpdateState( + ParseProgressBar->UpdateState( {.Task = "Parsing oplog", .Details = "", .TotalCount = Entries.Num(), .RemainingCount = Remaining}, false); Remaining--; @@ -2219,7 +2249,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg } if (!EmitProgressBar) { - EmitProgressBar = std::make_unique<ProgressBar>(ProgressBar::Mode::Pretty, ""sv); + EmitProgressBar = ProgressOwner2->CreateProgressBar(""sv); WriteStopWatch.Reset(); } @@ -2229,7 +2259,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg ++OplogEntryCount; } } - ParseProgressBar.Finish(); + ParseProgressBar->Finish(); } WorkRemaining.CountDown(); @@ -2262,7 +2292,7 @@ OplogMirrorCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (AbortFlag) { - throw std::runtime_error("Failed top mirror oplog"); + throw std::runtime_error("Failed to mirror oplog"); } } else @@ -2306,14 +2336,8 @@ OplogValidateCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a return; } - m_HostName = ResolveTargetHostSpec(m_HostName); - - if (m_HostName.empty()) - { - throw OptionParseException("Unable to resolve server specification", m_Options.help()); - } - - HttpClient Http = CreateHttpClient(m_HostName); + ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name}); + HttpClient& Http = Service.Http(); m_ProjectName = ResolveProject(Http, m_ProjectName); if (m_ProjectName.empty()) @@ -2472,7 +2496,7 @@ OplogDownloadCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a }; ParseSystemOptions(); - ProgressBar::Mode ProgressMode = ProgressBar::Mode::Pretty; + ConsoleProgressMode ProgressMode = ConsoleProgressMode::Pretty; auto ParseOutputOptions = [&]() { if (m_Verbose && m_Quiet) @@ -2494,23 +2518,19 @@ OplogDownloadCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a if (m_LogProgress) { - ProgressMode = ProgressBar::Mode::Log; + ProgressMode = ConsoleProgressMode::Log; } else if (m_PlainProgress) { - ProgressMode = ProgressBar::Mode::Plain; - } - else if (m_Verbose) - { - ProgressMode = ProgressBar::Mode::Plain; + ProgressMode = ConsoleProgressMode::Plain; } else if (m_Quiet) { - ProgressMode = ProgressBar::Mode::Quiet; + ProgressMode = ConsoleProgressMode::Quiet; } else { - ProgressMode = ProgressBar::Mode::Pretty; + ProgressMode = ConsoleProgressMode::Pretty; } }; ParseOutputOptions(); @@ -2565,7 +2585,7 @@ OplogDownloadCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a m_BoostWorkerMemory = true; } - std::unique_ptr<OperationLogOutput> OperationLogOutput(CreateConsoleLogOutput(ProgressMode)); + std::unique_ptr<ProgressBase> Progress(CreateConsoleProgress(ProgressMode)); TransferThreadWorkers Workers(m_BoostWorkerCount, /*SingleThreaded*/ false); if (!m_Quiet) @@ -2594,7 +2614,7 @@ OplogDownloadCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a /*Hidden*/ false, m_Verbose); - BuildStorageResolveResult ResolveRes = ResolveBuildStorage(*OperationLogOutput, + BuildStorageResolveResult ResolveRes = ResolveBuildStorage(ConsoleLog(), ClientSettings, m_Host, m_OverrideHost, @@ -2629,6 +2649,8 @@ OplogDownloadCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a .MaximumInMemoryDownloadSize = m_BoostWorkerMemory ? RemoteStoreOptions::DefaultMaxBlockSize : 1024u * 1024u}, [&AbortFlag]() { return AbortFlag.load(); }); Storage.CacheHost = ResolveRes.Cache; + + Storage.SetupCacheSession(ResolveRes.Cache.Address, Name, GetSessionId()); } if (!m_Quiet) @@ -2677,7 +2699,7 @@ OplogDownloadCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a } ProjectStoreOperationOplogState State( - *OperationLogOutput, + ConsoleLog(), Storage, BuildId, {.IsQuiet = m_Quiet, .IsVerbose = m_Verbose, .ForceDownload = m_ForceDownload, .TempFolderPath = StorageTempPath}); @@ -2704,7 +2726,8 @@ OplogDownloadCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** a } std::atomic<bool> PauseFlag; - ProjectStoreOperationDownloadAttachments Op(*OperationLogOutput, + ProjectStoreOperationDownloadAttachments Op(ConsoleLog(), + *Progress, Storage, AbortFlag, PauseFlag, |