aboutsummaryrefslogtreecommitdiff
path: root/src/zen/cmds/builds_cmd.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-11-24 10:06:52 +0100
committerGitHub Enterprise <[email protected]>2025-11-24 10:06:52 +0100
commit6dcdddbf733b0aa323ffb7ecbe56c04b15c6c16a (patch)
tree78685156e98214e4e1125501a8c09cac37bc45f4 /src/zen/cmds/builds_cmd.cpp
parentchangelog (#661) (diff)
downloadarchived-zen-6dcdddbf733b0aa323ffb7ecbe56c04b15c6c16a.tar.xz
archived-zen-6dcdddbf733b0aa323ffb7ecbe56c04b15c6c16a.zip
update state when wildcard (#657)
* add --append option and improve state handling when using downloads for `zen builds download`
Diffstat (limited to 'src/zen/cmds/builds_cmd.cpp')
-rw-r--r--src/zen/cmds/builds_cmd.cpp886
1 files changed, 523 insertions, 363 deletions
diff --git a/src/zen/cmds/builds_cmd.cpp b/src/zen/cmds/builds_cmd.cpp
index fcda6e809..665676fcd 100644
--- a/src/zen/cmds/builds_cmd.cpp
+++ b/src/zen/cmds/builds_cmd.cpp
@@ -23,6 +23,7 @@
#include <zenhttp/httpclient.h>
#include <zenhttp/httpclientauth.h>
#include <zenhttp/httpcommon.h>
+#include <zenremotestore/builds/buildcontent.h>
#include <zenremotestore/builds/buildsavedstate.h>
#include <zenremotestore/builds/buildstoragecache.h>
#include <zenremotestore/builds/buildstorageoperations.h>
@@ -62,7 +63,7 @@ ZEN_THIRD_PARTY_INCLUDES_END
# include <unistd.h>
#endif
-#define EXTRA_VERIFY 0
+static const bool DoExtraContentVerify = false;
#define ZEN_CLOUD_STORAGE "Cloud Storage"
@@ -267,6 +268,29 @@ namespace {
static bool IsQuiet = false;
static ProgressBar::Mode ProgressMode = ProgressBar::Mode::Pretty;
+ enum EAppendNewContentMode
+ {
+ Replace,
+ Auto,
+ Append,
+ Invalid
+ };
+
+ EAppendNewContentMode AppendNewContentModeFromString(const std::string_view ModeString)
+ {
+ switch (HashStringAsLowerDjb2(ModeString))
+ {
+ case HashStringDjb2("false"):
+ return EAppendNewContentMode::Replace;
+ case HashStringDjb2("auto"):
+ return EAppendNewContentMode::Auto;
+ case HashStringDjb2("true"):
+ return EAppendNewContentMode::Append;
+ default:
+ return EAppendNewContentMode::Invalid;
+ }
+ }
+
#define ZEN_CONSOLE_VERBOSE(fmtstr, ...) \
if (IsVerbose) \
{ \
@@ -292,32 +316,7 @@ namespace {
std::span<const std::string> ExcludeWildcards,
const std::filesystem::path& Path)
{
- const std::string PathString = Path.generic_string();
- bool IncludePath = true;
- if (!IncludeWildcards.empty())
- {
- IncludePath = false;
- for (const std::string& IncludeWildcard : IncludeWildcards)
- {
- if (MatchWildcard(IncludeWildcard, PathString, /*CaseSensitive*/ false))
- {
- IncludePath = true;
- break;
- }
- }
- if (!IncludePath)
- {
- return false;
- }
- }
- for (const std::string& ExcludeWildcard : ExcludeWildcards)
- {
- if (MatchWildcard(ExcludeWildcard, PathString, /*CaseSensitive*/ false))
- {
- return false;
- }
- }
- return true;
+ return zen::IncludePath(IncludeWildcards, ExcludeWildcards, ToLower(Path.generic_string()), /*CaseSensitive*/ true);
}
class FilteredRate
@@ -490,6 +489,7 @@ namespace {
std::move(MetaData),
BuildsOperationUploadFolder::Options{.IsQuiet = IsQuiet,
.IsVerbose = IsVerbose,
+ .DoExtraContentValidation = DoExtraContentVerify,
.FindBlockMaxCount = FindBlockMaxCount,
.BlockReuseMinPercentLimit = BlockReuseMinPercentLimit,
.AllowMultiparts = AllowMultiparts,
@@ -1079,7 +1079,23 @@ namespace {
return SB.ToString();
}
- std::vector<std::pair<Oid, std::string>> ResolveBuildPartNames(BuildStorageBase& Storage,
+ CbObject GetBuild(BuildStorageBase& Storage, const Oid& BuildId)
+ {
+ Stopwatch GetBuildTimer;
+ CbObject BuildObject = Storage.GetBuild(BuildId);
+ if (!IsQuiet)
+ {
+ ZEN_CONSOLE("GetBuild took {}. Name: '{}', Payload size: {}",
+ NiceTimeSpanMs(GetBuildTimer.GetElapsedTimeMs()),
+ BuildObject["name"sv].AsString(),
+ NiceBytes(BuildObject.GetSize()));
+
+ ZEN_CONSOLE("{}", GetCbObjectAsNiceString(BuildObject, " "sv, "\n"sv));
+ }
+ return BuildObject;
+ }
+
+ std::vector<std::pair<Oid, std::string>> ResolveBuildPartNames(CbObjectView BuildObject,
const Oid& BuildId,
const std::vector<Oid>& BuildPartIds,
std::span<const std::string> BuildPartNames,
@@ -1087,18 +1103,6 @@ namespace {
{
std::vector<std::pair<Oid, std::string>> Result;
{
- Stopwatch GetBuildTimer;
- CbObject BuildObject = Storage.GetBuild(BuildId);
- if (!IsQuiet)
- {
- ZEN_CONSOLE("GetBuild took {}. Name: '{}', Payload size: {}",
- NiceTimeSpanMs(GetBuildTimer.GetElapsedTimeMs()),
- BuildObject["name"sv].AsString(),
- NiceBytes(BuildObject.GetSize()));
-
- ZEN_CONSOLE("{}", GetCbObjectAsNiceString(BuildObject, " "sv, "\n"sv));
- }
-
CbObjectView PartsObject = BuildObject["parts"sv].AsObjectView();
if (!PartsObject)
{
@@ -1251,7 +1255,8 @@ namespace {
OutBlockDescriptions,
OutRemoteContent.ChunkedContent.ChunkHashes,
OutRemoteContent.ChunkedContent.ChunkRawSizes,
- OutRemoteContent.ChunkedContent.ChunkOrders);
+ OutRemoteContent.ChunkedContent.ChunkOrders,
+ DoExtraContentVerify);
if (!IncludeWildcards.empty() || !ExcludeWildcards.empty())
{
@@ -1272,7 +1277,7 @@ namespace {
}
#if ZEN_BUILD_DEBUG
- ValidateChunkedFolderContent(OutRemoteContent, OutBlockDescriptions, OutLooseChunkHashes);
+ ValidateChunkedFolderContent(OutRemoteContent, OutBlockDescriptions, OutLooseChunkHashes, IncludeWildcards, ExcludeWildcards);
#endif // ZEN_BUILD_DEBUG
};
@@ -1364,276 +1369,60 @@ namespace {
return RemoteContent;
}
- ChunkedFolderContent GetLocalContent(ThreadWorkers& Workers,
- GetFolderContentStatistics& LocalFolderScanStats,
- ChunkingStatistics& ChunkingStats,
- const std::filesystem::path& Path,
- const std::filesystem::path& StateFilePath,
- ChunkingController& ChunkController,
- std::span<const std::filesystem::path> ReferencePaths,
- std::span<const std::string> IncludeWildcards,
- std::span<const std::string> ExcludeWildcards,
- FolderContent& OutLocalFolderContent)
+ std::vector<std::filesystem::path> GetNewPaths(const std::span<const std::filesystem::path> KnownPaths,
+ const std::span<const std::filesystem::path> Paths)
{
- FolderContent LocalFolderState;
- ChunkedFolderContent LocalContent;
-
- Stopwatch ReadStateTimer;
- bool HasLocalState = false;
- bool FileExists = IsFile(StateFilePath);
- if (FileExists)
+ tsl::robin_set<std::string> KnownPathsSet;
+ KnownPathsSet.reserve(KnownPaths.size());
+ for (const std::filesystem::path& LocalPath : KnownPaths)
{
- try
- {
- ReadStateFile(StateFilePath, LocalFolderState, LocalContent);
- if (!IsQuiet)
- {
- ZEN_CONSOLE("Read local state file {} in {}", StateFilePath, NiceTimeSpanMs(ReadStateTimer.GetElapsedTimeMs()));
- }
- HasLocalState = true;
- }
- catch (const std::exception& Ex)
- {
- ZEN_CONSOLE_WARN("Failed reading state file {}, falling back to scannning. Reason: {}", StateFilePath, Ex.what());
- }
+ KnownPathsSet.insert(LocalPath.generic_string());
}
+ std::vector<std::filesystem::path> NewPaths;
+ for (const std::filesystem::path& UntrackedPath : Paths)
{
- const uint32_t LocalPathCount = gsl::narrow<uint32_t>(LocalFolderState.Paths.size());
- const uint32_t RemotePathCount = gsl::narrow<uint32_t>(ReferencePaths.size());
-
- std::vector<std::filesystem::path> PathsToCheck;
- PathsToCheck.reserve(LocalPathCount + RemotePathCount);
-
- tsl::robin_set<std::string> FileSet;
- FileSet.reserve(LocalPathCount + RemotePathCount);
-
- for (const std::filesystem::path& LocalPath : LocalFolderState.Paths)
- {
- if (IncludePath(IncludeWildcards, ExcludeWildcards, LocalPath))
- {
- FileSet.insert(LocalPath.generic_string());
- PathsToCheck.push_back(LocalPath);
- }
- }
-
- for (const std::filesystem::path& RemotePath : ReferencePaths)
- {
- if (IncludePath(IncludeWildcards, ExcludeWildcards, RemotePath))
- {
- if (FileSet.insert(RemotePath.generic_string()).second)
- {
- PathsToCheck.push_back(RemotePath);
- }
- }
- }
-
+ if (!KnownPathsSet.contains(UntrackedPath.generic_string()))
{
- ProgressBar ProgressBar(ProgressMode, "Check Files");
- OutLocalFolderContent =
- GetValidFolderContent(Workers,
- LocalFolderScanStats,
- Path,
- PathsToCheck,
- [&ProgressBar, &LocalFolderScanStats](uint64_t PathCount, uint64_t CompletedPathCount) {
- std::string Details = fmt::format("{}/{} checked, {} found",
- CompletedPathCount,
- PathCount,
- LocalFolderScanStats.FoundFileCount.load());
- ProgressBar.UpdateState({.Task = "Checking files ",
- .Details = Details,
- .TotalCount = PathCount,
- .RemainingCount = PathCount - CompletedPathCount,
- .Status = ProgressBar::State::CalculateStatus(AbortFlag, PauseFlag)},
- false);
- });
- ProgressBar.Finish();
- }
- if (AbortFlag)
- {
- return {};
+ NewPaths.push_back(UntrackedPath);
}
}
+ return NewPaths;
+ }
- bool ScanContent = true;
- std::vector<uint32_t> PathIndexesOufOfDate;
- if (HasLocalState)
+ BuildSaveState GetLocalStateFromPaths(ThreadWorkers& Workers,
+ GetFolderContentStatistics& LocalFolderScanStats,
+ ChunkingStatistics& ChunkingStats,
+ const std::filesystem::path& Path,
+ ChunkingController& ChunkController,
+ std::span<const std::filesystem::path> PathsToCheck)
+ {
+ FolderContent FolderState;
+ ChunkedFolderContent ChunkedContent;
{
- if (!LocalFolderState.AreKnownFilesEqual(OutLocalFolderContent))
- {
- const size_t LocalStatePathCount = LocalFolderState.Paths.size();
- std::vector<std::filesystem::path> DeletedPaths;
- FolderContent UpdatedContent = GetUpdatedContent(LocalFolderState, OutLocalFolderContent, DeletedPaths);
- if (!DeletedPaths.empty())
- {
- LocalContent = DeletePathsFromChunkedContent(LocalContent, DeletedPaths);
- }
-
- if (!IsQuiet)
- {
- ZEN_CONSOLE("Updating state, {} local files deleted and {} local files updated out of {}",
- DeletedPaths.size(),
- UpdatedContent.Paths.size(),
- LocalStatePathCount);
- }
- if (UpdatedContent.Paths.size() > 0)
- {
- uint64_t ByteCountToScan = 0;
- for (const uint64_t RawSize : UpdatedContent.RawSizes)
- {
- ByteCountToScan += RawSize;
- }
- ProgressBar ProgressBar(ProgressMode, "Scan Files");
- FilteredRate FilteredBytesHashed;
- FilteredBytesHashed.Start();
- ChunkingStatistics LocalChunkingStats;
- ChunkedFolderContent UpdatedLocalContent = ChunkFolderContent(
- LocalChunkingStats,
- Workers.GetIOWorkerPool(),
- Path,
- UpdatedContent,
- ChunkController,
- GetUpdateDelayMS(ProgressMode),
- [&](bool IsAborted, bool IsPaused, std::ptrdiff_t) {
- FilteredBytesHashed.Update(LocalChunkingStats.BytesHashed.load());
- std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found",
- LocalChunkingStats.FilesProcessed.load(),
- UpdatedContent.Paths.size(),
- NiceBytes(LocalChunkingStats.BytesHashed.load()),
- NiceBytes(ByteCountToScan),
- NiceNum(FilteredBytesHashed.GetCurrent()),
- LocalChunkingStats.UniqueChunksFound.load(),
- NiceBytes(LocalChunkingStats.UniqueBytesFound.load()));
- ProgressBar.UpdateState({.Task = "Scanning files ",
- .Details = Details,
- .TotalCount = ByteCountToScan,
- .RemainingCount = ByteCountToScan - LocalChunkingStats.BytesHashed.load(),
- .Status = ProgressBar::State::CalculateStatus(IsAborted, IsPaused)},
- false);
- },
- AbortFlag,
- PauseFlag);
-
- ChunkingStats += LocalChunkingStats;
-
- FilteredBytesHashed.Stop();
- ProgressBar.Finish();
- if (AbortFlag)
- {
- return {};
- }
- LocalContent = MergeChunkedFolderContents(LocalContent, {{UpdatedLocalContent}});
- }
- }
- else
- {
- // Remove files from LocalContent no longer in LocalFolderState
- tsl::robin_set<std::string> LocalFolderPaths;
- LocalFolderPaths.reserve(LocalFolderState.Paths.size());
- for (const std::filesystem::path& LocalFolderPath : LocalFolderState.Paths)
- {
- LocalFolderPaths.insert(LocalFolderPath.generic_string());
- }
- std::vector<std::filesystem::path> DeletedPaths;
- for (const std::filesystem::path& LocalContentPath : LocalContent.Paths)
- {
- if (!LocalFolderPaths.contains(LocalContentPath.generic_string()))
- {
- DeletedPaths.push_back(LocalContentPath);
- }
- }
- if (!DeletedPaths.empty())
- {
- LocalContent = DeletePathsFromChunkedContent(LocalContent, DeletedPaths);
- }
- }
-
- // Check files that are present in current folder state that matches the ReferencePaths but not known to the local state
- {
- FolderContent UpdatedContent;
-
- tsl::robin_set<std::string> LocalPathIndexLookup;
- for (const std::filesystem::path& LocalPath : LocalContent.Paths)
- {
- LocalPathIndexLookup.insert(LocalPath.generic_string());
- }
-
- tsl::robin_set<std::string> RemotePathIndexLookup;
- for (const std::filesystem::path& RemotePath : ReferencePaths)
- {
- RemotePathIndexLookup.insert(RemotePath.generic_string());
- }
-
- for (uint32_t LocalFolderPathIndex = 0; LocalFolderPathIndex < OutLocalFolderContent.Paths.size(); LocalFolderPathIndex++)
- {
- const std::filesystem::path& LocalFolderPath = OutLocalFolderContent.Paths[LocalFolderPathIndex];
- const std::string GenericLocalFolderPath = LocalFolderPath.generic_string();
- if (RemotePathIndexLookup.contains(GenericLocalFolderPath))
- {
- if (!LocalPathIndexLookup.contains(GenericLocalFolderPath))
- {
- UpdatedContent.Paths.push_back(LocalFolderPath);
- UpdatedContent.RawSizes.push_back(OutLocalFolderContent.RawSizes[LocalFolderPathIndex]);
- UpdatedContent.Attributes.push_back(OutLocalFolderContent.Attributes[LocalFolderPathIndex]);
- UpdatedContent.ModificationTicks.push_back(OutLocalFolderContent.ModificationTicks[LocalFolderPathIndex]);
- }
- }
- }
-
- if (UpdatedContent.Paths.size() > 0)
- {
- uint64_t ByteCountToScan = 0;
- for (const uint64_t RawSize : UpdatedContent.RawSizes)
- {
- ByteCountToScan += RawSize;
- }
- ProgressBar ProgressBar(ProgressMode, "Scan Files");
- FilteredRate FilteredBytesHashed;
- FilteredBytesHashed.Start();
- ChunkingStatistics LocalChunkingStats;
- ChunkedFolderContent UpdatedLocalContent = ChunkFolderContent(
- LocalChunkingStats,
- Workers.GetIOWorkerPool(),
- Path,
- UpdatedContent,
- ChunkController,
- GetUpdateDelayMS(ProgressMode),
- [&](bool IsAborted, bool IsPaused, std::ptrdiff_t) {
- FilteredBytesHashed.Update(LocalChunkingStats.BytesHashed.load());
- std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found",
- LocalChunkingStats.FilesProcessed.load(),
- UpdatedContent.Paths.size(),
- NiceBytes(LocalChunkingStats.BytesHashed.load()),
- NiceBytes(ByteCountToScan),
- NiceNum(FilteredBytesHashed.GetCurrent()),
- LocalChunkingStats.UniqueChunksFound.load(),
- NiceBytes(LocalChunkingStats.UniqueBytesFound.load()));
- ProgressBar.UpdateState({.Task = "Scanning files ",
- .Details = Details,
- .TotalCount = ByteCountToScan,
- .RemainingCount = ByteCountToScan - LocalChunkingStats.BytesHashed.load(),
- .Status = ProgressBar::State::CalculateStatus(IsAborted, IsPaused)},
- false);
- },
- AbortFlag,
- PauseFlag);
- ChunkingStats += LocalChunkingStats;
- FilteredBytesHashed.Stop();
- ProgressBar.Finish();
- if (!AbortFlag)
- {
- LocalContent = MergeChunkedFolderContents(LocalContent, {{UpdatedLocalContent}});
- }
- }
- }
-
- ScanContent = false;
+ ProgressBar ProgressBar(ProgressMode, "Check Files");
+ FolderState = GetValidFolderContent(
+ Workers,
+ LocalFolderScanStats,
+ Path,
+ PathsToCheck,
+ [&ProgressBar, &LocalFolderScanStats](uint64_t PathCount, uint64_t CompletedPathCount) {
+ std::string Details =
+ fmt::format("{}/{} checked, {} found", CompletedPathCount, PathCount, LocalFolderScanStats.FoundFileCount.load());
+ ProgressBar.UpdateState({.Task = "Checking files ",
+ .Details = Details,
+ .TotalCount = PathCount,
+ .RemainingCount = PathCount - CompletedPathCount,
+ .Status = ProgressBar::State::CalculateStatus(AbortFlag, PauseFlag)},
+ false);
+ });
+ ProgressBar.Finish();
}
- if (ScanContent)
+ if (FolderState.Paths.size() > 0)
{
uint64_t ByteCountToScan = 0;
- for (const uint64_t RawSize : OutLocalFolderContent.RawSizes)
+ for (const uint64_t RawSize : FolderState.RawSizes)
{
ByteCountToScan += RawSize;
}
@@ -1641,18 +1430,18 @@ namespace {
FilteredRate FilteredBytesHashed;
FilteredBytesHashed.Start();
ChunkingStatistics LocalChunkingStats;
- LocalContent = ChunkFolderContent(
+ ChunkedContent = ChunkFolderContent(
LocalChunkingStats,
Workers.GetIOWorkerPool(),
Path,
- OutLocalFolderContent,
+ FolderState,
ChunkController,
GetUpdateDelayMS(ProgressMode),
[&](bool IsAborted, bool IsPaused, std::ptrdiff_t) {
FilteredBytesHashed.Update(LocalChunkingStats.BytesHashed.load());
std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found",
LocalChunkingStats.FilesProcessed.load(),
- OutLocalFolderContent.Paths.size(),
+ FolderState.Paths.size(),
NiceBytes(LocalChunkingStats.BytesHashed.load()),
NiceBytes(ByteCountToScan),
NiceNum(FilteredBytesHashed.GetCurrent()),
@@ -1661,7 +1450,7 @@ namespace {
ProgressBar.UpdateState({.Task = "Scanning files ",
.Details = Details,
.TotalCount = ByteCountToScan,
- .RemainingCount = (ByteCountToScan - LocalChunkingStats.BytesHashed.load()),
+ .RemainingCount = ByteCountToScan - LocalChunkingStats.BytesHashed.load(),
.Status = ProgressBar::State::CalculateStatus(IsAborted, IsPaused)},
false);
},
@@ -1670,12 +1459,161 @@ namespace {
ChunkingStats += LocalChunkingStats;
FilteredBytesHashed.Stop();
ProgressBar.Finish();
- if (AbortFlag)
+ }
+
+ return BuildSaveState{.State = BuildState{.ChunkedContent = std::move(ChunkedContent)},
+ .FolderState = FolderState,
+ .LocalPath = Path};
+ }
+
+ BuildSaveState GetLocalContent(ThreadWorkers& Workers,
+ GetFolderContentStatistics& LocalFolderScanStats,
+ ChunkingStatistics& ChunkingStats,
+ const std::filesystem::path& Path,
+ const std::filesystem::path& StateFilePath,
+ ChunkingController& ChunkController)
+ {
+ Stopwatch ReadStateTimer;
+ bool FileExists = IsFile(StateFilePath);
+ if (!FileExists)
+ {
+ ZEN_CONSOLE("No known local state file in {}, falling back to scanning", Path);
+ return {};
+ }
+
+ BuildSaveState SavedLocalState;
+ try
+ {
+ SavedLocalState = ReadBuildSaveStateFile(StateFilePath);
+ if (!IsQuiet)
+ {
+ ZEN_CONSOLE("Read local state file {} in {}", StateFilePath, NiceTimeSpanMs(ReadStateTimer.GetElapsedTimeMs()));
+ }
+ }
+ catch (const std::exception& Ex)
+ {
+ ZEN_CONSOLE_WARN("Failed reading state file {}, falling back to scannning. Reason: {}", StateFilePath, Ex.what());
+ return {};
+ }
+
+ FolderContent CurrentLocalFolderState;
+ {
+ ProgressBar ProgressBar(ProgressMode, "Check Known Files");
+ CurrentLocalFolderState = GetValidFolderContent(
+ Workers,
+ LocalFolderScanStats,
+ Path,
+ SavedLocalState.FolderState.Paths,
+ [&ProgressBar, &LocalFolderScanStats](uint64_t PathCount, uint64_t CompletedPathCount) {
+ std::string Details =
+ fmt::format("{}/{} checked, {} found", CompletedPathCount, PathCount, LocalFolderScanStats.FoundFileCount.load());
+ ProgressBar.UpdateState({.Task = "Checking files ",
+ .Details = Details,
+ .TotalCount = PathCount,
+ .RemainingCount = PathCount - CompletedPathCount,
+ .Status = ProgressBar::State::CalculateStatus(AbortFlag, PauseFlag)},
+ false);
+ });
+ ProgressBar.Finish();
+ }
+ if (AbortFlag)
+ {
+ return {};
+ }
+
+ if (!SavedLocalState.FolderState.AreKnownFilesEqual(CurrentLocalFolderState))
+ {
+ const size_t LocalStatePathCount = SavedLocalState.FolderState.Paths.size();
+ std::vector<std::filesystem::path> DeletedPaths;
+ FolderContent UpdatedContent = GetUpdatedContent(SavedLocalState.FolderState, CurrentLocalFolderState, DeletedPaths);
+ if (!DeletedPaths.empty())
+ {
+ SavedLocalState.State.ChunkedContent = DeletePathsFromChunkedContent(SavedLocalState.State.ChunkedContent, DeletedPaths);
+ }
+
+ if (!IsQuiet)
+ {
+ ZEN_CONSOLE("Updating state, {} local files deleted and {} local files updated out of {}",
+ DeletedPaths.size(),
+ UpdatedContent.Paths.size(),
+ LocalStatePathCount);
+ }
+ if (UpdatedContent.Paths.size() > 0)
+ {
+ uint64_t ByteCountToScan = 0;
+ for (const uint64_t RawSize : UpdatedContent.RawSizes)
+ {
+ ByteCountToScan += RawSize;
+ }
+ ProgressBar ProgressBar(ProgressMode, "Scan Known Files");
+ FilteredRate FilteredBytesHashed;
+ FilteredBytesHashed.Start();
+ ChunkingStatistics LocalChunkingStats;
+ ChunkedFolderContent UpdatedLocalContent = ChunkFolderContent(
+ LocalChunkingStats,
+ Workers.GetIOWorkerPool(),
+ Path,
+ UpdatedContent,
+ ChunkController,
+ GetUpdateDelayMS(ProgressMode),
+ [&](bool IsAborted, bool IsPaused, std::ptrdiff_t) {
+ FilteredBytesHashed.Update(LocalChunkingStats.BytesHashed.load());
+ std::string Details = fmt::format("{}/{} ({}/{}, {}B/s) scanned, {} ({}) chunks found",
+ LocalChunkingStats.FilesProcessed.load(),
+ UpdatedContent.Paths.size(),
+ NiceBytes(LocalChunkingStats.BytesHashed.load()),
+ NiceBytes(ByteCountToScan),
+ NiceNum(FilteredBytesHashed.GetCurrent()),
+ LocalChunkingStats.UniqueChunksFound.load(),
+ NiceBytes(LocalChunkingStats.UniqueBytesFound.load()));
+ ProgressBar.UpdateState({.Task = "Scanning files ",
+ .Details = Details,
+ .TotalCount = ByteCountToScan,
+ .RemainingCount = ByteCountToScan - LocalChunkingStats.BytesHashed.load(),
+ .Status = ProgressBar::State::CalculateStatus(IsAborted, IsPaused)},
+ false);
+ },
+ AbortFlag,
+ PauseFlag);
+
+ ChunkingStats += LocalChunkingStats;
+
+ FilteredBytesHashed.Stop();
+ ProgressBar.Finish();
+ if (AbortFlag)
+ {
+ return {};
+ }
+ SavedLocalState.State.ChunkedContent =
+ MergeChunkedFolderContents(SavedLocalState.State.ChunkedContent, {{UpdatedLocalContent}});
+ }
+ }
+ else
+ {
+ // Remove files from LocalContent no longer in LocalFolderState
+ tsl::robin_set<std::string> LocalFolderPaths;
+ LocalFolderPaths.reserve(SavedLocalState.FolderState.Paths.size());
+ for (const std::filesystem::path& LocalFolderPath : SavedLocalState.FolderState.Paths)
+ {
+ LocalFolderPaths.insert(LocalFolderPath.generic_string());
+ }
+ std::vector<std::filesystem::path> DeletedPaths;
+ for (const std::filesystem::path& LocalContentPath : SavedLocalState.State.ChunkedContent.Paths)
+ {
+ if (!LocalFolderPaths.contains(LocalContentPath.generic_string()))
+ {
+ DeletedPaths.push_back(LocalContentPath);
+ }
+ }
+ if (!DeletedPaths.empty())
{
- return {};
+ SavedLocalState.State.ChunkedContent = DeletePathsFromChunkedContent(SavedLocalState.State.ChunkedContent, DeletedPaths);
}
}
- return LocalContent;
+
+ SavedLocalState.FolderState = CurrentLocalFolderState;
+
+ return SavedLocalState;
}
ChunkedFolderContent ScanAndChunkFolder(
@@ -1705,17 +1643,17 @@ namespace {
return {};
}
- FolderContent _;
- ChunkedFolderContent Result = GetLocalContent(Workers,
- GetFolderContentStats,
- ChunkingStats,
- Path,
- ZenStateFilePath(Path / ZenFolderName),
- ChunkController,
- Content.Paths,
- {},
- {},
- _);
+ BuildState LocalContent =
+ GetLocalContent(Workers, GetFolderContentStats, ChunkingStats, Path, ZenStateFilePath(Path / ZenFolderName), ChunkController)
+ .State;
+
+ std::vector<std::filesystem::path> UntrackedPaths = GetNewPaths(LocalContent.ChunkedContent.Paths, Content.Paths);
+
+ BuildState UntrackedLocalContent =
+ GetLocalStateFromPaths(Workers, GetFolderContentStats, ChunkingStats, Path, ChunkController, UntrackedPaths).State;
+
+ ChunkedFolderContent Result = MergeChunkedFolderContents(LocalContent.ChunkedContent,
+ std::vector<ChunkedFolderContent>{UntrackedLocalContent.ChunkedContent});
const uint64_t TotalRawSize = std::accumulate(Result.RawSizes.begin(), Result.RawSizes.end(), std::uint64_t(0));
const uint64_t ChunkedRawSize =
@@ -1751,6 +1689,7 @@ namespace {
std::vector<std::string> ExcludeWildcards;
uint64_t MaximumInMemoryPayloadSize = 512u * 1024u;
bool PopulateCache = true;
+ bool AppendNewContent = false;
};
void DownloadFolder(ThreadWorkers& Workers,
@@ -1790,8 +1729,10 @@ namespace {
std::uint64_t PreferredMultipartChunkSize = 32u * 1024u * 1024u;
+ CbObject BuildObject = GetBuild(*Storage.BuildStorage, BuildId);
+
std::vector<std::pair<Oid, std::string>> AllBuildParts =
- ResolveBuildPartNames(*Storage.BuildStorage, BuildId, BuildPartIds, BuildPartNames, PreferredMultipartChunkSize);
+ ResolveBuildPartNames(BuildObject, BuildId, BuildPartIds, BuildPartNames, PreferredMultipartChunkSize);
std::vector<ChunkedFolderContent> PartContents;
@@ -1811,12 +1752,15 @@ namespace {
PartContents,
BlockDescriptions,
LooseChunkHashes);
-
+#if ZEN_BUILD_DEBUG
+ ValidateChunkedFolderContent(RemoteContent, BlockDescriptions, LooseChunkHashes, {}, {});
+#endif // ZEN_BUILD_DEBUG
const std::uint64_t LargeAttachmentSize = Options.AllowMultiparts ? PreferredMultipartChunkSize * 4u : (std::uint64_t)-1;
GetFolderContentStatistics LocalFolderScanStats;
ChunkingStatistics ChunkingStats;
- ChunkedFolderContent LocalContent;
- FolderContent LocalFolderContent;
+
+ BuildSaveState LocalState;
+
if (!Options.PrimeCacheOnly)
{
if (IsDir(Path))
@@ -1827,18 +1771,52 @@ namespace {
ChunkController = CreateStandardChunkingController(StandardChunkingControllerSettings{});
}
- LocalContent = GetLocalContent(Workers,
- LocalFolderScanStats,
- ChunkingStats,
- Path,
- ZenStateFilePath(Options.ZenFolderPath),
- *ChunkController,
- RemoteContent.Paths,
- Options.IncludeWildcards,
- Options.ExcludeWildcards,
- LocalFolderContent);
+ LocalState = GetLocalContent(Workers,
+ LocalFolderScanStats,
+ ChunkingStats,
+ Path,
+ ZenStateFilePath(Path / ZenFolderName),
+ *ChunkController);
+
+ std::vector<std::filesystem::path> UntrackedPaths = GetNewPaths(LocalState.State.ChunkedContent.Paths, RemoteContent.Paths);
+
+ BuildSaveState UntrackedLocalContent =
+ GetLocalStateFromPaths(Workers, LocalFolderScanStats, ChunkingStats, Path, *ChunkController, UntrackedPaths);
+
+ if (!UntrackedLocalContent.State.ChunkedContent.Paths.empty())
+ {
+ LocalState.State.ChunkedContent =
+ MergeChunkedFolderContents(LocalState.State.ChunkedContent,
+ std::vector<ChunkedFolderContent>{UntrackedLocalContent.State.ChunkedContent});
+
+ // TODO: Helper
+ LocalState.FolderState.Paths.insert(LocalState.FolderState.Paths.begin(),
+ UntrackedLocalContent.FolderState.Paths.begin(),
+ UntrackedLocalContent.FolderState.Paths.end());
+ LocalState.FolderState.RawSizes.insert(LocalState.FolderState.RawSizes.begin(),
+ UntrackedLocalContent.FolderState.RawSizes.begin(),
+ UntrackedLocalContent.FolderState.RawSizes.end());
+ LocalState.FolderState.Attributes.insert(LocalState.FolderState.Attributes.begin(),
+ UntrackedLocalContent.FolderState.Attributes.begin(),
+ UntrackedLocalContent.FolderState.Attributes.end());
+ LocalState.FolderState.ModificationTicks.insert(LocalState.FolderState.ModificationTicks.begin(),
+ UntrackedLocalContent.FolderState.ModificationTicks.begin(),
+ UntrackedLocalContent.FolderState.ModificationTicks.end());
+ }
+
+ if (Options.AppendNewContent)
+ {
+ RemoteContent = ApplyChunkedContentOverlay(LocalState.State.ChunkedContent,
+ RemoteContent,
+ Options.IncludeWildcards,
+ Options.ExcludeWildcards);
+ }
#if ZEN_BUILD_DEBUG
- ValidateChunkedFolderContent(LocalContent, {}, LocalContent.ChunkedContent.ChunkHashes);
+ ValidateChunkedFolderContent(RemoteContent,
+ BlockDescriptions,
+ LooseChunkHashes,
+ Options.IncludeWildcards,
+ Options.ExcludeWildcards);
#endif // ZEN_BUILD_DEBUG
}
else
@@ -1851,7 +1829,32 @@ namespace {
return;
}
- if (Options.EnableTargetFolderScavenging && !Options.CleanTargetFolder && CompareChunkedContent(RemoteContent, LocalContent))
+ LocalState.LocalPath = Path;
+
+ {
+ BuildsSelection::Build RemoteBuildState = {.Id = BuildId,
+ .IncludeWildcards = Options.IncludeWildcards,
+ .ExcludeWildcards = Options.ExcludeWildcards};
+ RemoteBuildState.Parts.reserve(BuildPartIds.size());
+ for (size_t PartIndex = 0; PartIndex < BuildPartIds.size(); PartIndex++)
+ {
+ RemoteBuildState.Parts.push_back(
+ {BuildsSelection::BuildPart{.Id = BuildPartIds[PartIndex],
+ .Name = PartIndex < BuildPartNames.size() ? BuildPartNames[PartIndex] : ""}});
+ }
+
+ if (Options.AppendNewContent)
+ {
+ LocalState.State.Selection.Builds.emplace_back(std::move(RemoteBuildState));
+ }
+ else
+ {
+ LocalState.State.Selection.Builds = std::vector<BuildsSelection::Build>{std::move(RemoteBuildState)};
+ }
+ }
+
+ if ((Options.EnableTargetFolderScavenging || Options.AppendNewContent) && !Options.CleanTargetFolder &&
+ CompareChunkedContent(RemoteContent, LocalState.State.ChunkedContent))
{
if (!IsQuiet)
{
@@ -1860,7 +1863,8 @@ namespace {
}
Stopwatch WriteStateTimer;
- CbObject StateObject = CreateStateObject(BuildId, AllBuildParts, PartContents, LocalFolderContent, Path);
+
+ CbObject StateObject = CreateBuildSaveStateObject(LocalState);
CreateDirectories(ZenStateFilePath(Options.ZenFolderPath).parent_path());
TemporaryFile::SafeWriteFile(ZenStateFilePath(Options.ZenFolderPath), StateObject.GetView());
if (!IsQuiet)
@@ -1868,7 +1872,11 @@ namespace {
ZEN_CONSOLE("Wrote local state in {}", NiceTimeSpanMs(WriteStateTimer.GetElapsedTimeMs()));
}
- AddDownloadedPath(Options.SystemRootDir, BuildId, AllBuildParts, ZenStateFilePath(Options.ZenFolderPath), Path);
+ AddDownloadedPath(Options.SystemRootDir,
+ BuildsDownloadInfo{.Selection = LocalState.State.Selection,
+ .LocalPath = Path,
+ .StateFilePath = ZenStateFilePath(Options.ZenFolderPath),
+ .Iso8601Date = DateTime::Now().ToIso8601()});
}
else
{
@@ -1884,9 +1892,8 @@ namespace {
{
ZEN_CONSOLE("Downloading build {}, parts:{} to '{}' ({})", BuildId, BuildPartString.ToView(), Path, NiceBytes(RawSize));
}
- FolderContent LocalFolderState;
- const ChunkedContentLookup LocalLookup = BuildChunkedContentLookup(LocalContent);
+ const ChunkedContentLookup LocalLookup = BuildChunkedContentLookup(LocalState.State.ChunkedContent);
const ChunkedContentLookup RemoteLookup = BuildChunkedContentLookup(RemoteContent);
ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Download, TaskSteps::StepCount);
@@ -1901,34 +1908,39 @@ namespace {
Workers.GetNetworkPool(),
BuildId,
Path,
- LocalContent,
+ LocalState.State.ChunkedContent,
LocalLookup,
RemoteContent,
RemoteLookup,
BlockDescriptions,
LooseChunkHashes,
- BuildsOperationUpdateFolder::Options{.IsQuiet = IsQuiet,
- .IsVerbose = IsVerbose,
- .AllowFileClone = Options.AllowFileClone,
- .UseSparseFiles = UseSparseFiles,
- .SystemRootDir = Options.SystemRootDir,
- .ZenFolderPath = Options.ZenFolderPath,
- .LargeAttachmentSize = LargeAttachmentSize,
- .PreferredMultipartChunkSize = PreferredMultipartChunkSize,
- .PartialBlockRequestMode = Options.PartialBlockRequestMode,
- .WipeTargetFolder = Options.CleanTargetFolder,
- .PrimeCacheOnly = Options.PrimeCacheOnly,
- .EnableOtherDownloadsScavenging = Options.EnableOtherDownloadsScavenging,
- .EnableTargetFolderScavenging = Options.EnableTargetFolderScavenging,
- .ValidateCompletedSequences = Options.PostDownloadVerify,
- .ExcludeFolders = DefaultExcludeFolders,
- .ExcludeExtensions = DefaultExcludeExtensions,
- .MaximumInMemoryPayloadSize = Options.MaximumInMemoryPayloadSize,
- .PopulateCache = Options.PopulateCache});
+ BuildsOperationUpdateFolder::Options{
+ .IsQuiet = IsQuiet,
+ .IsVerbose = IsVerbose,
+ .AllowFileClone = Options.AllowFileClone,
+ .UseSparseFiles = UseSparseFiles,
+ .SystemRootDir = Options.SystemRootDir,
+ .ZenFolderPath = Options.ZenFolderPath,
+ .LargeAttachmentSize = LargeAttachmentSize,
+ .PreferredMultipartChunkSize = PreferredMultipartChunkSize,
+ .PartialBlockRequestMode = Options.PartialBlockRequestMode,
+ .WipeTargetFolder = Options.CleanTargetFolder,
+ .PrimeCacheOnly = Options.PrimeCacheOnly,
+ .EnableOtherDownloadsScavenging = Options.EnableOtherDownloadsScavenging,
+ .EnableTargetFolderScavenging = Options.EnableTargetFolderScavenging || Options.AppendNewContent,
+ .ValidateCompletedSequences = Options.PostDownloadVerify,
+ .ExcludeFolders = DefaultExcludeFolders,
+ .ExcludeExtensions = DefaultExcludeExtensions,
+ .MaximumInMemoryPayloadSize = Options.MaximumInMemoryPayloadSize,
+ .PopulateCache = Options.PopulateCache});
{
ProgressBar::PushLogOperation(ProgressMode, "Download");
- auto _ = MakeGuard([]() { ProgressBar::PopLogOperation(ProgressMode); });
- Updater.Execute(LocalFolderState);
+ auto _ = MakeGuard([]() { ProgressBar::PopLogOperation(ProgressMode); });
+ FolderContent UpdatedLocalFolderState;
+ Updater.Execute(UpdatedLocalFolderState);
+
+ LocalState.State.ChunkedContent = RemoteContent;
+ LocalState.FolderState = std::move(UpdatedLocalFolderState);
}
VerifyFolderStatistics VerifyFolderStats;
@@ -1936,14 +1948,18 @@ namespace {
{
if (!Options.PrimeCacheOnly)
{
- AddDownloadedPath(Options.SystemRootDir, BuildId, AllBuildParts, ZenStateFilePath(Options.ZenFolderPath), Path);
+ AddDownloadedPath(Options.SystemRootDir,
+ BuildsDownloadInfo{.Selection = LocalState.State.Selection,
+ .LocalPath = Path,
+ .StateFilePath = ZenStateFilePath(Options.ZenFolderPath),
+ .Iso8601Date = DateTime::Now().ToIso8601()});
ProgressBar::SetLogOperationProgress(ProgressMode, TaskSteps::Verify, TaskSteps::StepCount);
VerifyFolder(Workers, RemoteContent, RemoteLookup, Path, Options.PostDownloadVerify, VerifyFolderStats);
Stopwatch WriteStateTimer;
- CbObject StateObject = CreateStateObject(BuildId, AllBuildParts, PartContents, LocalFolderState, Path);
+ CbObject StateObject = CreateBuildSaveStateObject(LocalState);
CreateDirectories(ZenStateFilePath(Options.ZenFolderPath).parent_path());
TemporaryFile::SafeWriteFile(ZenStateFilePath(Options.ZenFolderPath), StateObject.GetView());
@@ -2036,8 +2052,10 @@ namespace {
{
std::uint64_t PreferredMultipartChunkSize = 32u * 1024u * 1024u;
+ CbObject BuildObject = GetBuild(*Storage.BuildStorage, BuildId);
+
std::vector<std::pair<Oid, std::string>> AllBuildParts =
- ResolveBuildPartNames(*Storage.BuildStorage, BuildId, BuildPartIds, BuildPartNames, PreferredMultipartChunkSize);
+ ResolveBuildPartNames(BuildObject, BuildId, BuildPartIds, BuildPartNames, PreferredMultipartChunkSize);
Stopwatch GetBuildPartTimer;
@@ -2452,6 +2470,20 @@ BuildsCommand::BuildsCommand()
"<allowpartialblockrequests>");
};
+ auto AddAppendNewContentOptions = [this](cxxopts::Options& Ops) {
+ Ops.add_option("",
+ "",
+ "append",
+ "Decides if the remote data should replace or append to the current local data.\n"
+ " false = the local content will be replaced by the remote content\n"
+ " auto = if no path wildcards are given the local content will be replaced, if wildcards are given the "
+ "remote data will complement the local data\n"
+ " true = the remote data will be overlayed on top of local data\n"
+ "Defaults to 'auto'.",
+ cxxopts::value(m_AppendNewContent),
+ "<append>");
+ };
+
m_Options.add_option("",
"v",
"verb",
@@ -2609,6 +2641,7 @@ BuildsCommand::BuildsCommand()
AddZenFolderOptions(m_DownloadOptions);
AddWorkerOptions(m_DownloadOptions);
AddWildcardOptions(m_DownloadOptions);
+ AddAppendNewContentOptions(m_DownloadOptions);
m_DownloadOptions.add_option("cache",
"",
@@ -2730,6 +2763,9 @@ BuildsCommand::BuildsCommand()
m_TestOptions.add_option("", "l", "local-path", "Root file system folder used as base", cxxopts::value(m_Path), "<local-path>");
AddMultipartOptions(m_TestOptions);
AddPartialBlockRequestOptions(m_TestOptions);
+ AddWildcardOptions(m_TestOptions);
+ AddAppendNewContentOptions(m_TestOptions);
+
m_TestOptions.add_option("",
"",
"enable-scavenge",
@@ -3159,7 +3195,7 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
ForEachStrTok(Wildcard, ';', [&Wildcards](std::string_view Wildcard) {
if (!Wildcard.empty())
{
- std::string CleanWildcard(Wildcard);
+ std::string CleanWildcard(ToLower(Wildcard));
for (auto It = begin(CleanWildcard); It != end(CleanWildcard); It++)
{
if (*It == '\\')
@@ -3663,6 +3699,15 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
return Mode;
};
+ auto ParseAppendNewContent = [&]() -> EAppendNewContentMode {
+ EAppendNewContentMode Mode = AppendNewContentModeFromString(m_AppendNewContent);
+ if (Mode == EAppendNewContentMode::Invalid)
+ {
+ throw OptionParseException(fmt::format("'--append' ('{}') is invalid", m_AppendNewContent), SubOption->help());
+ }
+ return Mode;
+ };
+
if (SubOption == &m_DownloadOptions)
{
if (!IsQuiet)
@@ -3726,6 +3771,25 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
std::vector<std::string> BuildPartNames = ParseBuildPartNames();
EPartialBlockRequestMode PartialBlockRequestMode = ParseAllowPartialBlockRequests();
+ EAppendNewContentMode AppendNewContentMode = ParseAppendNewContent();
+
+ bool AppendNewContent = AppendNewContentMode == EAppendNewContentMode::Append;
+ if (AppendNewContentMode == EAppendNewContentMode::Auto)
+ {
+ if (IncludeWildcards.empty() && ExcludeWildcards.empty())
+ {
+ AppendNewContent = false;
+ }
+ else
+ {
+ AppendNewContent = true;
+ }
+ }
+
+ if (AppendNewContent && m_Clean)
+ {
+ throw OptionParseException("'--append' conflicts with '--clean'", SubOption->help());
+ }
DownloadFolder(Workers,
Storage,
@@ -3746,7 +3810,8 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
.IncludeWildcards = IncludeWildcards,
.ExcludeWildcards = ExcludeWildcards,
.MaximumInMemoryPayloadSize = GetMaxMemoryBufferSize(m_BoostWorkerMemory),
- .PopulateCache = m_UploadToZenCache});
+ .PopulateCache = m_UploadToZenCache,
+ .AppendNewContent = AppendNewContent});
if (AbortFlag)
{
@@ -3858,8 +3923,10 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
std::uint64_t PreferredMultipartChunkSize = 32u * 1024u * 1024u;
+ CbObject BuildObject = GetBuild(*Storage.BuildStorage, BuildId);
+
std::vector<std::pair<Oid, std::string>> AllBuildParts =
- ResolveBuildPartNames(*Storage.BuildStorage, BuildId, BuildPartIds, BuildPartNames, PreferredMultipartChunkSize);
+ ResolveBuildPartNames(BuildObject, BuildId, BuildPartIds, BuildPartNames, PreferredMultipartChunkSize);
std::vector<Oid> AllBuildPartIds;
AllBuildPartIds.reserve(AllBuildParts.size());
@@ -4160,6 +4227,10 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
const std::filesystem::path DownloadPath2 = m_Path.parent_path() / (m_BuildPartName + "_test2");
const std::filesystem::path DownloadPath3 = m_Path.parent_path() / (m_BuildPartName + "_test3");
+ CleanAndRemoveDirectory(Workers.GetIOWorkerPool(), DownloadPath);
+ CleanAndRemoveDirectory(Workers.GetIOWorkerPool(), DownloadPath2);
+ CleanAndRemoveDirectory(Workers.GetIOWorkerPool(), DownloadPath3);
+
auto ___ = MakeGuard([&Workers, DownloadPath, DownloadPath2, DownloadPath3]() {
CleanAndRemoveDirectory(Workers.GetIOWorkerPool(), DownloadPath);
CleanAndRemoveDirectory(Workers.GetIOWorkerPool(), DownloadPath2);
@@ -4233,6 +4304,95 @@ BuildsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
ValidateBuildPart(Workers, *Storage.BuildStorage, BuildId, BuildPartId, m_BuildPartName);
+ if (!m_IncludeWildcard.empty() || !m_ExcludeWildcard.empty())
+ {
+ auto __ = MakeGuard([&]() { CleanAndRemoveDirectory(Workers.GetIOWorkerPool(), DownloadPath); });
+
+ ZEN_CONSOLE("\nDownload Filtered Build {}, Part {} ({}) to '{}'", BuildId, BuildPartId, m_BuildPartName, DownloadPath);
+
+ std::vector<std::string> IncludeWildcards;
+ std::vector<std::string> ExcludeWildcards;
+ ParseFileFilters(IncludeWildcards, ExcludeWildcards);
+
+ DownloadFolder(Workers,
+ Storage,
+ BuildId,
+ {BuildPartId},
+ {},
+ DownloadPath,
+ DownloadOptions{.SystemRootDir = m_SystemRootDir,
+ .ZenFolderPath = DownloadPath / ZenFolderName,
+ .AllowMultiparts = m_AllowMultiparts,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
+ .CleanTargetFolder = true,
+ .PostDownloadVerify = true,
+ .PrimeCacheOnly = false,
+ .EnableOtherDownloadsScavenging = m_EnableScavenging,
+ .EnableTargetFolderScavenging = false,
+ .AllowFileClone = m_AllowFileClone,
+ .IncludeWildcards = IncludeWildcards,
+ .ExcludeWildcards = ExcludeWildcards,
+ .AppendNewContent = false});
+ if (AbortFlag)
+ {
+ throw std::runtime_error("Test aborted. (Download build)");
+ }
+
+ ZEN_CONSOLE("\nDownload Filtered Out Remaining of Build {}, Part {} ({}) to '{}'",
+ BuildId,
+ BuildPartId,
+ m_BuildPartName,
+ DownloadPath);
+ DownloadFolder(Workers,
+ Storage,
+ BuildId,
+ {BuildPartId},
+ {},
+ DownloadPath,
+ DownloadOptions{.SystemRootDir = m_SystemRootDir,
+ .ZenFolderPath = DownloadPath / ZenFolderName,
+ .AllowMultiparts = m_AllowMultiparts,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
+ .CleanTargetFolder = true,
+ .PostDownloadVerify = true,
+ .PrimeCacheOnly = false,
+ .EnableOtherDownloadsScavenging = m_EnableScavenging,
+ .EnableTargetFolderScavenging = true,
+ .AllowFileClone = m_AllowFileClone,
+ .IncludeWildcards = ExcludeWildcards,
+ .ExcludeWildcards = IncludeWildcards,
+ .AppendNewContent = true});
+ if (AbortFlag)
+ {
+ throw std::runtime_error("Test aborted. (Download build)");
+ }
+
+ ZEN_CONSOLE("\nDownload Full Build {}, Part {} ({}) to '{}'", BuildId, BuildPartId, m_BuildPartName, DownloadPath);
+ DownloadFolder(Workers,
+ Storage,
+ BuildId,
+ {BuildPartId},
+ {},
+ DownloadPath,
+ DownloadOptions{.SystemRootDir = m_SystemRootDir,
+ .ZenFolderPath = DownloadPath / ZenFolderName,
+ .AllowMultiparts = m_AllowMultiparts,
+ .PartialBlockRequestMode = PartialBlockRequestMode,
+ .CleanTargetFolder = false,
+ .PostDownloadVerify = true,
+ .PrimeCacheOnly = false,
+ .EnableOtherDownloadsScavenging = m_EnableScavenging,
+ .EnableTargetFolderScavenging = true,
+ .AllowFileClone = m_AllowFileClone,
+ .IncludeWildcards = {},
+ .ExcludeWildcards = {},
+ .AppendNewContent = false});
+ if (AbortFlag)
+ {
+ throw std::runtime_error("Test aborted. (Download build)");
+ }
+ }
+
ZEN_CONSOLE("\nDownload Build {}, Part {} ({}) to '{}'", BuildId, BuildPartId, m_BuildPartName, DownloadPath);
DownloadFolder(Workers,
Storage,