| @@ -165,6 +165,62 @@ void catalog::store(const package_info& pkg) { | |||
| } | |||
| } | |||
| std::optional<package_info> catalog::get(const package_id& pk_id) const noexcept { | |||
| auto& st = _stmt_cache(R"( | |||
| SELECT | |||
| pkg_id, | |||
| name, | |||
| version, | |||
| git_url, | |||
| git_ref, | |||
| lm_name, | |||
| lm_namespace | |||
| FROM dds_cat_pkgs | |||
| WHERE name = ? AND version = ? | |||
| )"_sql); | |||
| st.reset(); | |||
| st.bindings = std::forward_as_tuple(pk_id.name, pk_id.version.to_string()); | |||
| auto opt_tup = sqlite3::unpack_single_opt<std::int64_t, | |||
| std::string, | |||
| std::string, | |||
| std::optional<std::string>, | |||
| std::optional<std::string>, | |||
| std::optional<std::string>, | |||
| std::optional<std::string>>(st); | |||
| if (!opt_tup) { | |||
| return std::nullopt; | |||
| } | |||
| const auto& [pkg_id, name, version, git_url, git_ref, lm_name, lm_namespace] = *opt_tup; | |||
| assert(pk_id.name == name); | |||
| assert(pk_id.version == semver::version::parse(version)); | |||
| assert(git_url); | |||
| assert(git_ref); | |||
| auto deps = sqlite3::exec_iter<std::string, std::string>( // | |||
| _stmt_cache, | |||
| R"( | |||
| SELECT dep_name, low | |||
| FROM dds_cat_pkg_deps | |||
| WHERE pkg_id = ? | |||
| )"_sql, | |||
| std::tie(pkg_id)) | |||
| | ranges::views::transform([](auto&& pair) { | |||
| const auto& [name, ver] = pair; | |||
| return dependency{name, semver::version::parse(ver)}; | |||
| }) // | |||
| | ranges::to_vector; | |||
| return package_info{ | |||
| pk_id, | |||
| deps, | |||
| git_remote_listing{ | |||
| *git_url, | |||
| *git_ref, | |||
| lm_name ? std::make_optional(lm::usage{*lm_name, *lm_namespace}) : std::nullopt, | |||
| }, | |||
| }; | |||
| } | |||
| std::vector<package_id> catalog::by_name(std::string_view sv) const noexcept { | |||
| return sqlite3::exec_iter<std::string, std::string>( // | |||
| _stmt_cache, | |||
| @@ -4,6 +4,7 @@ | |||
| #include <dds/package_id.hpp> | |||
| #include <dds/repo/remote.hpp> | |||
| #include <dds/util/fs.hpp> | |||
| #include <dds/catalog/git.hpp> | |||
| #include <neo/sqlite3/database.hpp> | |||
| #include <neo/sqlite3/statement.hpp> | |||
| @@ -38,6 +39,7 @@ public: | |||
| static catalog open(path_ref db_path) { return open(db_path.string()); } | |||
| void store(const package_info& info); | |||
| std::optional<package_info> get(const package_id& id) const noexcept; | |||
| std::vector<package_id> by_name(std::string_view sv) const noexcept; | |||
| std::vector<dependency> dependencies_of(const package_id& pkg) const noexcept; | |||
| @@ -0,0 +1,63 @@ | |||
| #include "./get.hpp" | |||
| #include <dds/catalog/catalog.hpp> | |||
| #include <dds/proc.hpp> | |||
| #include <spdlog/spdlog.h> | |||
| using namespace dds; | |||
| namespace { | |||
| temporary_sdist do_pull_sdist(const package_info& listing, const git_remote_listing& git) { | |||
| auto tmpdir = dds::temporary_dir::create(); | |||
| using namespace std::literals; | |||
| spdlog::info("Cloning Git repository: {} [{}] ...", git.url, git.ref); | |||
| auto command = {"git"s, | |||
| "clone"s, | |||
| "--depth=1"s, | |||
| "--branch"s, | |||
| git.ref, | |||
| git.url, | |||
| tmpdir.path().generic_string()}; | |||
| auto git_res = run_proc(command); | |||
| if (!git_res.okay()) { | |||
| throw std::runtime_error( | |||
| fmt::format("Git clone operation failed [Git command: {}] [Exitted {}]:\n{}", | |||
| quote_command(command), | |||
| git_res.retc, | |||
| git_res.output)); | |||
| } | |||
| spdlog::info("Create sdist from clone ..."); | |||
| if (git.auto_lib.has_value()) { | |||
| spdlog::info("Generating library data automatically"); | |||
| auto pkg_strm = dds::open(tmpdir.path() / "package.dds", std::ios::binary | std::ios::out); | |||
| pkg_strm << "Name: " << listing.ident.name << '\n' // | |||
| << "Version: " << listing.ident.version.to_string() << '\n' // | |||
| << "Namespace: " << git.auto_lib->namespace_; | |||
| auto lib_strm = dds::open(tmpdir.path() / "library.dds", std::ios::binary | std::ios::out); | |||
| lib_strm << "Name: " << git.auto_lib->name; | |||
| } | |||
| sdist_params params; | |||
| params.project_dir = tmpdir.path(); | |||
| auto sd_tmp_dir = dds::temporary_dir::create(); | |||
| params.dest_path = sd_tmp_dir.path(); | |||
| params.force = true; | |||
| auto sd = create_sdist(params); | |||
| return {sd_tmp_dir, sd}; | |||
| } | |||
| } // namespace | |||
| temporary_sdist dds::get_package_sdist(const package_info& pkg) { | |||
| auto tsd = std::visit([&](auto&& remote) { return do_pull_sdist(pkg, remote); }, pkg.remote); | |||
| if (!(tsd.sdist.manifest.pk_id == pkg.ident)) { | |||
| throw std::runtime_error(fmt::format( | |||
| "The package name@version in the generated sdist does not match the name listed in " | |||
| "the remote listing file (expected '{}', but got '{}')", | |||
| pkg.ident.to_string(), | |||
| tsd.sdist.manifest.pk_id.to_string())); | |||
| } | |||
| return tsd; | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| #pragma once | |||
| #include <dds/sdist.hpp> | |||
| #include <dds/temp.hpp> | |||
| namespace dds { | |||
| struct package_info; | |||
| struct temporary_sdist { | |||
| temporary_dir tmpdir; | |||
| dds::sdist sdist; | |||
| }; | |||
| temporary_sdist get_package_sdist(const package_info&); | |||
| } // namespace dds | |||
| @@ -0,0 +1,20 @@ | |||
| #pragma once | |||
| #include <dds/util/fs.hpp> | |||
| #include <libman/package.hpp> | |||
| #include <optional> | |||
| #include <string> | |||
| namespace dds { | |||
| struct git_remote_listing { | |||
| std::string url; | |||
| std::string ref; | |||
| std::optional<lm::usage> auto_lib; | |||
| void clone(path_ref path) const; | |||
| }; | |||
| } // namespace dds | |||
| @@ -1,5 +1,6 @@ | |||
| #include <dds/build.hpp> | |||
| #include <dds/catalog/catalog.hpp> | |||
| #include <dds/catalog/get.hpp> | |||
| #include <dds/repo/remote.hpp> | |||
| #include <dds/repo/repo.hpp> | |||
| #include <dds/sdist.hpp> | |||
| @@ -159,7 +160,19 @@ struct cli_catalog { | |||
| "requirement", | |||
| "The package IDs to obtain"}; | |||
| int run() { return 23; } | |||
| int run() { | |||
| auto cat = dds::catalog::open(path.Get()); | |||
| for (const auto& req : requirements.Get()) { | |||
| auto id = dds::package_id::parse(req); | |||
| auto info = cat.get(id); | |||
| if (!info) { | |||
| throw std::runtime_error( | |||
| fmt::format("No package in the catalog matched the given ID")); | |||
| } | |||
| dds::get_package_sdist(*info); | |||
| } | |||
| return 0; | |||
| } | |||
| } get{*this}; | |||
| int run() { | |||
| @@ -4,6 +4,9 @@ | |||
| #include <dds/sdist.hpp> | |||
| #include <dds/temp.hpp> | |||
| #include <dds/catalog/git.hpp> | |||
| #include <dds/catalog/get.hpp> | |||
| #include <libman/library.hpp> | |||
| #include <semver/version.hpp> | |||
| @@ -15,19 +18,6 @@ | |||
| namespace dds { | |||
| struct temporary_sdist { | |||
| temporary_dir tmpdir; | |||
| dds::sdist sdist; | |||
| }; | |||
| struct git_remote_listing { | |||
| std::string url; | |||
| std::string ref; | |||
| std::optional<lm::usage> auto_lib; | |||
| void clone(path_ref path) const; | |||
| }; | |||
| struct remote_listing { | |||
| package_id pk_id; | |||
| std::variant<git_remote_listing> remote; | |||
| @@ -0,0 +1,33 @@ | |||
| import json | |||
| from tests import dds, DDS | |||
| from tests.fileutil import ensure_dir | |||
| def test_get(dds: DDS): | |||
| dds.scope.enter_context(ensure_dir(dds.build_dir)) | |||
| cat_path = dds.build_dir / 'catalog.db' | |||
| dds.catalog_create(cat_path) | |||
| json_path = dds.build_dir / 'catalog.json' | |||
| import_data = { | |||
| 'version': 1, | |||
| 'packages': { | |||
| 'neo-sqlite3': { | |||
| '0.2.2': { | |||
| 'depends': {}, | |||
| 'git': { | |||
| 'url': 'https://github.com/vector-of-bool/neo-sqlite3.git', | |||
| 'ref': '0.2.2', | |||
| }, | |||
| }, | |||
| }, | |||
| }, | |||
| } | |||
| dds.scope.enter_context( | |||
| dds.set_contents(json_path, | |||
| json.dumps(import_data).encode())) | |||
| dds.catalog_import(cat_path, json_path) | |||
| dds.catalog_get(cat_path, 'neo-sqlite3@0.2.2') | |||