summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2023-12-11 12:25:55 +0100
committerPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2023-12-12 14:37:18 +0100
commit93b92ef2669bed7345eadabbf71ee30c4b7929af (patch)
tree3da25625b7a3335e88f4bf94e2080ee28ce84d36 /src
parente6123964c4470426a444d190ed5b1c58a74b7731 (diff)
downloadjustbuild-93b92ef2669bed7345eadabbf71ee30c4b7929af.tar.gz
serve target: Move server-side helper methods in own library
Diffstat (limited to 'src')
-rw-r--r--src/buildtool/serve_api/serve_service/TARGETS33
-rw-r--r--src/buildtool/serve_api/serve_service/target.cpp262
-rw-r--r--src/buildtool/serve_api/serve_service/target.hpp34
-rw-r--r--src/buildtool/serve_api/serve_service/target_utils.cpp283
-rw-r--r--src/buildtool/serve_api/serve_service/target_utils.hpp61
5 files changed, 376 insertions, 297 deletions
diff --git a/src/buildtool/serve_api/serve_service/TARGETS b/src/buildtool/serve_api/serve_service/TARGETS
index d169ae4b..0f921dee 100644
--- a/src/buildtool/serve_api/serve_service/TARGETS
+++ b/src/buildtool/serve_api/serve_service/TARGETS
@@ -72,22 +72,22 @@
, "deps":
[ ["src/buildtool/logging", "logging"]
, ["@", "gsl", "", "gsl"]
- , ["src/buildtool/common", "config"]
+ , ["src/buildtool/common/remote", "remote_common"]
, ["src/buildtool/execution_api/remote", "config"]
, ["src/buildtool/execution_api/common", "create_execution_api"]
+ , ["src/buildtool/execution_api/common", "common"]
]
, "stage": ["src", "buildtool", "serve_api", "serve_service"]
, "private-deps":
[ ["@", "fmt", "", "fmt"]
, ["@", "json", "", "json"]
+ , "target_utils"
, ["src/buildtool/build_engine/base_maps", "entity_name"]
, ["src/buildtool/build_engine/base_maps", "entity_name_data"]
, ["src/buildtool/build_engine/expression", "expression"]
, ["src/buildtool/build_engine/expression", "expression_ptr_interface"]
, ["src/buildtool/build_engine/target_map", "configured_target"]
- , ["src/buildtool/build_engine/target_map", "target_map"]
- , ["src/buildtool/file_system", "git_cas"]
- , ["src/buildtool/file_system", "git_repo"]
+ , ["src/buildtool/build_engine/target_map", "result_map"]
, ["src/buildtool/graph_traverser", "graph_traverser"]
, ["src/buildtool/main", "analyse"]
, ["src/buildtool/main", "build_utils"]
@@ -110,4 +110,29 @@
, "stage": ["src", "buildtool", "serve_api", "serve_service"]
, "private-deps": [["src/buildtool/execution_api/remote", "config"]]
}
+, "target_utils":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["target_utils"]
+ , "hdrs": ["target_utils.hpp"]
+ , "srcs": ["target_utils.cpp"]
+ , "deps":
+ [ ["@", "gsl", "", "gsl"]
+ , ["src/buildtool/common/remote", "remote_common"]
+ , ["src/buildtool/common", "config"]
+ , ["src/buildtool/logging", "logging"]
+ ]
+ , "stage": ["src", "buildtool", "serve_api", "serve_service"]
+ , "private-deps":
+ [ ["@", "fmt", "", "fmt"]
+ , ["@", "json", "", "json"]
+ , ["src/buildtool/file_system", "file_root"]
+ , ["src/buildtool/file_system", "git_cas"]
+ , ["src/buildtool/file_system", "git_repo"]
+ , ["src/buildtool/file_system", "object_type"]
+ , ["src/buildtool/logging", "log_level"]
+ , ["src/buildtool/serve_api/remote", "config"]
+ , ["src/buildtool/storage", "config"]
+ , ["src/buildtool/storage", "storage"]
+ ]
+ }
}
diff --git a/src/buildtool/serve_api/serve_service/target.cpp b/src/buildtool/serve_api/serve_service/target.cpp
index 12ca64ad..b4a0968a 100644
--- a/src/buildtool/serve_api/serve_service/target.cpp
+++ b/src/buildtool/serve_api/serve_service/target.cpp
@@ -14,12 +14,7 @@
#include "src/buildtool/serve_api/serve_service/target.hpp"
-#include <filesystem>
-#include <string>
-#include <utility>
-
#include "fmt/core.h"
-#include "fmt/format.h"
#include "nlohmann/json.hpp"
#include "src/buildtool/build_engine/base_maps/entity_name.hpp"
#include "src/buildtool/build_engine/base_maps/entity_name_data.hpp"
@@ -30,8 +25,6 @@
#include "src/buildtool/build_engine/target_map/result_map.hpp"
#include "src/buildtool/common/artifact.hpp"
#include "src/buildtool/common/artifact_digest.hpp"
-#include "src/buildtool/file_system/git_cas.hpp"
-#include "src/buildtool/file_system/git_repo.hpp"
#include "src/buildtool/file_system/object_type.hpp"
#include "src/buildtool/graph_traverser/graph_traverser.hpp"
#include "src/buildtool/main/analyse.hpp"
@@ -39,210 +32,12 @@
#include "src/buildtool/multithreading/task_system.hpp"
#include "src/buildtool/progress_reporting/progress_reporter.hpp"
#include "src/buildtool/serve_api/remote/config.hpp"
+#include "src/buildtool/serve_api/serve_service/target_utils.hpp"
#include "src/buildtool/storage/config.hpp"
#include "src/buildtool/storage/storage.hpp"
#include "src/buildtool/storage/target_cache_key.hpp"
#include "src/utils/cpp/verify_hash.hpp"
-auto TargetService::IsTreeInRepo(std::string const& tree_id,
- std::filesystem::path const& repo_path,
- std::shared_ptr<Logger> const& logger)
- -> bool {
- if (auto git_cas = GitCAS::Open(repo_path)) {
- if (auto repo = GitRepo::Open(git_cas)) {
- // wrap logger for GitRepo call
- auto wrapped_logger = std::make_shared<GitRepo::anon_logger_t>(
- [logger, repo_path, tree_id](auto const& msg, bool fatal) {
- if (fatal) {
- auto err = fmt::format(
- "ServeTarget: While checking existence of tree {} "
- "in repository {}:\n{}",
- tree_id,
- repo_path.string(),
- msg);
- logger->Emit(LogLevel::Info, err);
- }
- });
- if (auto res = repo->CheckTreeExists(tree_id, wrapped_logger)) {
- return *res;
- }
- }
- }
- return false; // tree not found
-}
-
-auto TargetService::GetServingRepository(std::string const& tree_id,
- std::shared_ptr<Logger> const& logger)
- -> std::optional<std::filesystem::path> {
- // try the Git cache repository
- if (IsTreeInRepo(tree_id, StorageConfig::GitRoot(), logger)) {
- return StorageConfig::GitRoot();
- }
- // check the known repositories
- for (auto const& path : RemoteServeConfig::KnownRepositories()) {
- if (IsTreeInRepo(tree_id, path, logger)) {
- return path;
- }
- }
- return std::nullopt; // tree cannot be served
-}
-
-auto TargetService::DetermineRoots(
- std::string const& main_repo,
- std::filesystem::path const& repo_config_path,
- gsl::not_null<RepositoryConfig*> const& repository_config,
- std::shared_ptr<Logger> const& logger) -> std::optional<std::string> {
- // parse repository configuration file
- auto repos = nlohmann::json::object();
- try {
- std::ifstream fs(repo_config_path);
- repos = nlohmann::json::parse(fs);
- if (not repos.is_object()) {
- return fmt::format(
- "Repository configuration file {} does not contain a map.",
- repo_config_path.string());
- }
- } catch (std::exception const& ex) {
- return fmt::format("Parsing repository config file {} failed with:\n{}",
- repo_config_path.string(),
- ex.what());
- }
- if (not repos.contains(main_repo)) {
- return fmt::format(
- "Repository configuration does not contain expected main "
- "repository {}",
- main_repo);
- }
- // populate RepositoryConfig instance
- std::string error_msg;
- for (auto const& [repo, desc] : repos.items()) {
- // root parser
- auto parse_keyword_root =
- [&desc = desc, &repo = repo, &error_msg = error_msg, logger](
- std::string const& keyword) -> std::optional<FileRoot> {
- auto it = desc.find(keyword);
- if (it != desc.end()) {
- if (auto parsed_root =
- FileRoot::ParseRoot(repo, keyword, *it, &error_msg)) {
- // check that root has absent-like format
- if (not parsed_root->first.IsAbsent()) {
- error_msg = fmt::format(
- "Expected {} to have absent Git tree format, but "
- "found {}",
- keyword,
- it->dump());
- return std::nullopt;
- }
- // find the serving repository for the root tree
- auto tree_id = *parsed_root->first.GetAbsentTreeId();
- auto repo_path = GetServingRepository(tree_id, logger);
- if (not repo_path) {
- error_msg = fmt::format(
- "{} tree {} is not known", keyword, tree_id);
- return std::nullopt;
- }
- // set the root as present
- if (auto root = FileRoot::FromGit(
- *repo_path,
- tree_id,
- parsed_root->first.IgnoreSpecial())) {
- return root;
- }
- }
- error_msg =
- fmt::format("Failed to parse {} {}", keyword, it->dump());
- return std::nullopt;
- }
- error_msg =
- fmt::format("Missing {} for repository {}", keyword, repo);
- return std::nullopt;
- };
-
- std::optional<FileRoot> ws_root = parse_keyword_root("workspace_root");
- if (not ws_root) {
- return error_msg;
- }
- auto info = RepositoryConfig::RepositoryInfo{std::move(*ws_root)};
-
- if (auto target_root = parse_keyword_root("target_root")) {
- info.target_root = std::move(*target_root);
- }
- else {
- return error_msg;
- }
-
- if (auto rule_root = parse_keyword_root("rule_root")) {
- info.rule_root = std::move(*rule_root);
- }
- else {
- return error_msg;
- }
-
- if (auto expression_root = parse_keyword_root("expression_root")) {
- info.expression_root = std::move(*expression_root);
- }
- else {
- return error_msg;
- }
-
- auto it_bindings = desc.find("bindings");
- if (it_bindings != desc.end()) {
- if (not it_bindings->is_object()) {
- return fmt::format(
- "bindings has to be a string-string map, but found {}",
- it_bindings->dump());
- }
- for (auto const& [local_name, global_name] : it_bindings->items()) {
- if (not repos.contains(global_name)) {
- return fmt::format(
- "Binding {} for {} in {} does not refer to a "
- "defined repository.",
- global_name,
- local_name,
- repo);
- }
- info.name_mapping[local_name] = global_name;
- }
- }
- else {
- return fmt::format("Missing bindings for repository {}", repo);
- }
-
- auto parse_keyword_file_name =
- [&desc = desc, &repo = repo, &error_msg = error_msg](
- std::string* keyword_file_name,
- std::string const& keyword) -> bool {
- auto it = desc.find(keyword);
- if (it != desc.end()) {
- *keyword_file_name = *it;
- return true;
- }
- error_msg =
- fmt::format("Missing {} for repository {}", keyword, repo);
- return false;
- };
-
- if (not parse_keyword_file_name(&info.target_file_name,
- "target_file_name")) {
- return error_msg;
- }
-
- if (not parse_keyword_file_name(&info.rule_file_name,
- "rule_file_name")) {
- return error_msg;
- }
-
- if (not parse_keyword_file_name(&info.expression_file_name,
- "expression_file_name")) {
- return error_msg;
- }
-
- repository_config->SetInfo(repo, std::move(info));
- }
- // success
- return std::nullopt;
-}
-
auto TargetService::ServeTarget(
::grpc::ServerContext* /*context*/,
const ::justbuild::just_serve::ServeTargetRequest* request,
@@ -577,61 +372,6 @@ auto TargetService::ServeTarget(
return ::grpc::Status{::grpc::StatusCode::INTERNAL, msg};
}
-auto TargetService::GetBlobContent(std::filesystem::path const& repo_path,
- std::string const& tree_id,
- std::string const& rel_path,
- std::shared_ptr<Logger> const& logger)
- -> std::optional<std::pair<bool, std::optional<std::string>>> {
- if (auto git_cas = GitCAS::Open(repo_path)) {
- if (auto repo = GitRepo::Open(git_cas)) {
- // check if tree exists
- auto wrapped_logger = std::make_shared<GitRepo::anon_logger_t>(
- [logger, repo_path, tree_id](auto const& msg, bool fatal) {
- if (fatal) {
- auto err = fmt::format(
- "ServeTargetVariables: While checking if tree {} "
- "exists in repository {}:\n{}",
- tree_id,
- repo_path.string(),
- msg);
- logger->Emit(LogLevel::Debug, err);
- }
- });
- if (repo->CheckTreeExists(tree_id, wrapped_logger) == true) {
- // get tree entry by path
- if (auto entry_info =
- repo->GetObjectByPathFromTree(tree_id, rel_path)) {
- wrapped_logger = std::make_shared<GitRepo::anon_logger_t>(
- [logger, repo_path, blob_id = entry_info->id](
- auto const& msg, bool fatal) {
- if (fatal) {
- auto err = fmt::format(
- "ServeTargetVariables: While retrieving "
- "blob {} from repository {}:\n{}",
- blob_id,
- repo_path.string(),
- msg);
- logger->Emit(LogLevel::Debug, err);
- }
- });
- // get blob content
- return repo->TryReadBlob(entry_info->id, wrapped_logger);
- }
- // trace failure to get entry
- auto err = fmt::format(
- "ServeTargetVariables: Failed to retrieve entry {} in "
- "tree {} from repository {}",
- rel_path,
- tree_id,
- repo_path.string());
- logger->Emit(LogLevel::Debug, err);
- return std::pair(false, std::nullopt); // could not read blob
- }
- }
- }
- return std::nullopt; // tree not found or errors while retrieving tree
-}
-
auto TargetService::ServeTargetVariables(
::grpc::ServerContext* /*context*/,
const ::justbuild::just_serve::ServeTargetVariablesRequest* request,
diff --git a/src/buildtool/serve_api/serve_service/target.hpp b/src/buildtool/serve_api/serve_service/target.hpp
index 5cb18426..b8257152 100644
--- a/src/buildtool/serve_api/serve_service/target.hpp
+++ b/src/buildtool/serve_api/serve_service/target.hpp
@@ -18,12 +18,13 @@
#include <filesystem>
#include <memory>
#include <optional>
+#include <string>
#include "gsl/gsl"
#include "justbuild/just_serve/just_serve.grpc.pb.h"
#include "src/buildtool/common/remote/remote_common.hpp"
-#include "src/buildtool/common/repository_config.hpp"
#include "src/buildtool/execution_api/common/create_execution_api.hpp"
+#include "src/buildtool/execution_api/common/execution_api.hpp"
#include "src/buildtool/execution_api/remote/config.hpp"
#include "src/buildtool/logging/logger.hpp"
@@ -77,37 +78,6 @@ class TargetService final : public justbuild::just_serve::Target::Service {
// used for storing and retrieving target-level cache entries
gsl::not_null<IExecutionApi::Ptr> const local_api_{
CreateExecutionApi(std::nullopt)};
-
- /// \brief Check if tree exists in the given repository.
- [[nodiscard]] static auto IsTreeInRepo(
- std::string const& tree_id,
- std::filesystem::path const& repo_path,
- std::shared_ptr<Logger> const& logger) -> bool;
-
- /// \brief For a given tree id, find the known repository that can serve it.
- [[nodiscard]] static auto GetServingRepository(
- std::string const& tree_id,
- std::shared_ptr<Logger> const& logger)
- -> std::optional<std::filesystem::path>;
-
- /// \brief Parse the stored repository configuration blob and populate the
- /// RepositoryConfig instance.
- /// \returns nullopt on success, error message as a string otherwise.
- [[nodiscard]] static auto DetermineRoots(
- std::string const& main_repo,
- std::filesystem::path const& repo_config_path,
- gsl::not_null<RepositoryConfig*> const& repository_config,
- std::shared_ptr<Logger> const& logger) -> std::optional<std::string>;
-
- /// \brief Get the blob content at given path inside a Git tree.
- /// \returns If tree found, pair of "no-internal-errors" flag and content of
- /// blob at the path specified if blob exists, nullopt otherwise.
- [[nodiscard]] static auto GetBlobContent(
- std::filesystem::path const& repo_path,
- std::string const& tree_id,
- std::string const& rel_path,
- std::shared_ptr<Logger> const& logger)
- -> std::optional<std::pair<bool, std::optional<std::string>>>;
};
#endif // INCLUDED_SRC_BUILD_SERVE_API_SERVE_SERVICE_TARGET_HPP
diff --git a/src/buildtool/serve_api/serve_service/target_utils.cpp b/src/buildtool/serve_api/serve_service/target_utils.cpp
new file mode 100644
index 00000000..c9268f63
--- /dev/null
+++ b/src/buildtool/serve_api/serve_service/target_utils.cpp
@@ -0,0 +1,283 @@
+// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/buildtool/serve_api/serve_service/target_utils.hpp"
+
+#include <exception>
+#include <fstream>
+#include <string>
+
+#include "fmt/core.h"
+#include "nlohmann/json.hpp"
+#include "src/buildtool/file_system/file_root.hpp"
+#include "src/buildtool/file_system/git_cas.hpp"
+#include "src/buildtool/file_system/git_repo.hpp"
+#include "src/buildtool/file_system/object_type.hpp"
+#include "src/buildtool/logging/log_level.hpp"
+#include "src/buildtool/serve_api/remote/config.hpp"
+#include "src/buildtool/storage/config.hpp"
+#include "src/buildtool/storage/storage.hpp"
+
+auto IsTreeInRepo(std::string const& tree_id,
+ std::filesystem::path const& repo_path,
+ std::shared_ptr<Logger> const& logger) -> bool {
+ if (auto git_cas = GitCAS::Open(repo_path)) {
+ if (auto repo = GitRepo::Open(git_cas)) {
+ // wrap logger for GitRepo call
+ auto wrapped_logger = std::make_shared<GitRepo::anon_logger_t>(
+ [logger, repo_path, tree_id](auto const& msg, bool fatal) {
+ if (fatal) {
+ auto err = fmt::format(
+ "ServeTarget: While checking existence of tree {} "
+ "in repository {}:\n{}",
+ tree_id,
+ repo_path.string(),
+ msg);
+ logger->Emit(LogLevel::Info, err);
+ }
+ });
+ if (auto res = repo->CheckTreeExists(tree_id, wrapped_logger)) {
+ return *res;
+ }
+ }
+ }
+ return false; // tree not found
+}
+
+auto GetServingRepository(std::string const& tree_id,
+ std::shared_ptr<Logger> const& logger)
+ -> std::optional<std::filesystem::path> {
+ // try the Git cache repository
+ if (IsTreeInRepo(tree_id, StorageConfig::GitRoot(), logger)) {
+ return StorageConfig::GitRoot();
+ }
+ // check the known repositories
+ for (auto const& path : RemoteServeConfig::KnownRepositories()) {
+ if (IsTreeInRepo(tree_id, path, logger)) {
+ return path;
+ }
+ }
+ return std::nullopt; // tree cannot be served
+}
+
+auto DetermineRoots(std::string const& main_repo,
+ std::filesystem::path const& repo_config_path,
+ gsl::not_null<RepositoryConfig*> const& repository_config,
+ std::shared_ptr<Logger> const& logger)
+ -> std::optional<std::string> {
+ // parse repository configuration file
+ auto repos = nlohmann::json::object();
+ try {
+ std::ifstream fs(repo_config_path);
+ repos = nlohmann::json::parse(fs);
+ if (not repos.is_object()) {
+ return fmt::format(
+ "Repository configuration file {} does not contain a map.",
+ repo_config_path.string());
+ }
+ } catch (std::exception const& ex) {
+ return fmt::format("Parsing repository config file {} failed with:\n{}",
+ repo_config_path.string(),
+ ex.what());
+ }
+ if (not repos.contains(main_repo)) {
+ return fmt::format(
+ "Repository configuration does not contain expected main "
+ "repository {}",
+ main_repo);
+ }
+ // populate RepositoryConfig instance
+ std::string error_msg;
+ for (auto const& [repo, desc] : repos.items()) {
+ // root parser
+ auto parse_keyword_root =
+ [&desc = desc, &repo = repo, &error_msg = error_msg, logger](
+ std::string const& keyword) -> std::optional<FileRoot> {
+ auto it = desc.find(keyword);
+ if (it != desc.end()) {
+ if (auto parsed_root =
+ FileRoot::ParseRoot(repo, keyword, *it, &error_msg)) {
+ // check that root has absent-like format
+ if (not parsed_root->first.IsAbsent()) {
+ error_msg = fmt::format(
+ "Expected {} to have absent Git tree format, but "
+ "found {}",
+ keyword,
+ it->dump());
+ return std::nullopt;
+ }
+ // find the serving repository for the root tree
+ auto tree_id = *parsed_root->first.GetAbsentTreeId();
+ auto repo_path = GetServingRepository(tree_id, logger);
+ if (not repo_path) {
+ error_msg = fmt::format(
+ "{} tree {} is not known", keyword, tree_id);
+ return std::nullopt;
+ }
+ // set the root as present
+ if (auto root = FileRoot::FromGit(
+ *repo_path,
+ tree_id,
+ parsed_root->first.IgnoreSpecial())) {
+ return root;
+ }
+ }
+ error_msg =
+ fmt::format("Failed to parse {} {}", keyword, it->dump());
+ return std::nullopt;
+ }
+ error_msg =
+ fmt::format("Missing {} for repository {}", keyword, repo);
+ return std::nullopt;
+ };
+
+ std::optional<FileRoot> ws_root = parse_keyword_root("workspace_root");
+ if (not ws_root) {
+ return error_msg;
+ }
+ auto info = RepositoryConfig::RepositoryInfo{std::move(*ws_root)};
+
+ if (auto target_root = parse_keyword_root("target_root")) {
+ info.target_root = std::move(*target_root);
+ }
+ else {
+ return error_msg;
+ }
+
+ if (auto rule_root = parse_keyword_root("rule_root")) {
+ info.rule_root = std::move(*rule_root);
+ }
+ else {
+ return error_msg;
+ }
+
+ if (auto expression_root = parse_keyword_root("expression_root")) {
+ info.expression_root = std::move(*expression_root);
+ }
+ else {
+ return error_msg;
+ }
+
+ auto it_bindings = desc.find("bindings");
+ if (it_bindings != desc.end()) {
+ if (not it_bindings->is_object()) {
+ return fmt::format(
+ "bindings has to be a string-string map, but found {}",
+ it_bindings->dump());
+ }
+ for (auto const& [local_name, global_name] : it_bindings->items()) {
+ if (not repos.contains(global_name)) {
+ return fmt::format(
+ "Binding {} for {} in {} does not refer to a "
+ "defined repository.",
+ global_name,
+ local_name,
+ repo);
+ }
+ info.name_mapping[local_name] = global_name;
+ }
+ }
+ else {
+ return fmt::format("Missing bindings for repository {}", repo);
+ }
+
+ auto parse_keyword_file_name =
+ [&desc = desc, &repo = repo, &error_msg = error_msg](
+ std::string* keyword_file_name,
+ std::string const& keyword) -> bool {
+ auto it = desc.find(keyword);
+ if (it != desc.end()) {
+ *keyword_file_name = *it;
+ return true;
+ }
+ error_msg =
+ fmt::format("Missing {} for repository {}", keyword, repo);
+ return false;
+ };
+
+ if (not parse_keyword_file_name(&info.target_file_name,
+ "target_file_name")) {
+ return error_msg;
+ }
+
+ if (not parse_keyword_file_name(&info.rule_file_name,
+ "rule_file_name")) {
+ return error_msg;
+ }
+
+ if (not parse_keyword_file_name(&info.expression_file_name,
+ "expression_file_name")) {
+ return error_msg;
+ }
+
+ repository_config->SetInfo(repo, std::move(info));
+ }
+ // success
+ return std::nullopt;
+}
+
+auto GetBlobContent(std::filesystem::path const& repo_path,
+ std::string const& tree_id,
+ std::string const& rel_path,
+ std::shared_ptr<Logger> const& logger)
+ -> std::optional<std::pair<bool, std::optional<std::string>>> {
+ if (auto git_cas = GitCAS::Open(repo_path)) {
+ if (auto repo = GitRepo::Open(git_cas)) {
+ // check if tree exists
+ auto wrapped_logger = std::make_shared<GitRepo::anon_logger_t>(
+ [logger, repo_path, tree_id](auto const& msg, bool fatal) {
+ if (fatal) {
+ auto err = fmt::format(
+ "ServeTargetVariables: While checking if tree {} "
+ "exists in repository {}:\n{}",
+ tree_id,
+ repo_path.string(),
+ msg);
+ logger->Emit(LogLevel::Debug, err);
+ }
+ });
+ if (repo->CheckTreeExists(tree_id, wrapped_logger) == true) {
+ // get tree entry by path
+ if (auto entry_info =
+ repo->GetObjectByPathFromTree(tree_id, rel_path)) {
+ wrapped_logger = std::make_shared<GitRepo::anon_logger_t>(
+ [logger, repo_path, blob_id = entry_info->id](
+ auto const& msg, bool fatal) {
+ if (fatal) {
+ auto err = fmt::format(
+ "ServeTargetVariables: While retrieving "
+ "blob {} from repository {}:\n{}",
+ blob_id,
+ repo_path.string(),
+ msg);
+ logger->Emit(LogLevel::Debug, err);
+ }
+ });
+ // get blob content
+ return repo->TryReadBlob(entry_info->id, wrapped_logger);
+ }
+ // trace failure to get entry
+ auto err = fmt::format(
+ "ServeTargetVariables: Failed to retrieve entry {} in "
+ "tree {} from repository {}",
+ rel_path,
+ tree_id,
+ repo_path.string());
+ logger->Emit(LogLevel::Debug, err);
+ return std::pair(false, std::nullopt); // could not read blob
+ }
+ }
+ }
+ return std::nullopt; // tree not found or errors while retrieving tree
+}
diff --git a/src/buildtool/serve_api/serve_service/target_utils.hpp b/src/buildtool/serve_api/serve_service/target_utils.hpp
new file mode 100644
index 00000000..ee94ff38
--- /dev/null
+++ b/src/buildtool/serve_api/serve_service/target_utils.hpp
@@ -0,0 +1,61 @@
+// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDED_SRC_BUILD_SERVE_API_SERVE_SERVICE_TARGET_UTILS_HPP
+#define INCLUDED_SRC_BUILD_SERVE_API_SERVE_SERVICE_TARGET_UTILS_HPP
+
+#include <filesystem>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+
+#include "gsl/gsl"
+#include "src/buildtool/common/remote/remote_common.hpp"
+#include "src/buildtool/common/repository_config.hpp"
+#include "src/buildtool/logging/logger.hpp"
+
+// Methods used by ServeTarget remote service
+
+/// \brief Check if tree exists in the given repository.
+[[nodiscard]] auto IsTreeInRepo(std::string const& tree_id,
+ std::filesystem::path const& repo_path,
+ std::shared_ptr<Logger> const& logger) -> bool;
+
+/// \brief For a given tree id, find the known repository that can serve it.
+[[nodiscard]] auto GetServingRepository(std::string const& tree_id,
+ std::shared_ptr<Logger> const& logger)
+ -> std::optional<std::filesystem::path>;
+
+/// \brief Parse the stored repository configuration blob and populate the
+/// RepositoryConfig instance.
+/// \returns nullopt on success, error message as a string otherwise.
+[[nodiscard]] auto DetermineRoots(
+ std::string const& main_repo,
+ std::filesystem::path const& repo_config_path,
+ gsl::not_null<RepositoryConfig*> const& repository_config,
+ std::shared_ptr<Logger> const& logger) -> std::optional<std::string>;
+
+// Methods used by ServeTargetVariables remote service
+
+/// \brief Get the blob content at given path inside a Git tree.
+/// \returns If tree found, pair of "no-internal-errors" flag and content of
+/// blob at the path specified if blob exists, nullopt otherwise.
+[[nodiscard]] auto GetBlobContent(std::filesystem::path const& repo_path,
+ std::string const& tree_id,
+ std::string const& rel_path,
+ std::shared_ptr<Logger> const& logger)
+ -> std::optional<std::pair<bool, std::optional<std::string>>>;
+
+#endif // INCLUDED_SRC_BUILD_SERVE_API_SERVE_SERVICE_TARGET_UTILS_HPP