Browse Source

'pkg search' subcommand

default_compile_flags
vector-of-bool 3 years ago
parent
commit
01ea6dc6a9
17 changed files with 326 additions and 43 deletions
  1. +1
    -1
      src/dds/cli/cmd/pkg_repo_ls.cpp
  2. +60
    -0
      src/dds/cli/cmd/pkg_search.cpp
  3. +3
    -0
      src/dds/cli/dispatch_main.cpp
  4. +18
    -0
      src/dds/cli/options.cpp
  5. +9
    -0
      src/dds/cli/options.hpp
  6. +1
    -1
      src/dds/pkg/db.cpp
  7. +6
    -6
      src/dds/pkg/remote.cpp
  8. +76
    -0
      src/dds/pkg/search.cpp
  9. +33
    -0
      src/dds/pkg/search.hpp
  10. +15
    -0
      src/dds/util/string.hpp
  11. +3
    -3
      tests/test_build_deps.py
  12. +34
    -11
      tests/test_pkg_db.py
  13. +2
    -2
      tests/test_repoman.py
  14. +2
    -2
      tests/use-cryptopp/test_use_cryptopp.py
  15. +2
    -2
      tests/use-spdlog/use_spdlog_test.py
  16. +2
    -2
      tools/dds_ci/testing/__init__.py
  17. +59
    -13
      tools/dds_ci/testing/http.py

+ 1
- 1
src/dds/cli/cmd/pkg_repo_ls.cpp View File

auto pkg_db = opts.open_pkg_db(); auto pkg_db = opts.open_pkg_db();
neo::sqlite3::database_ref db = pkg_db.database(); neo::sqlite3::database_ref db = pkg_db.database();


auto st = db.prepare("SELECT name, remote_url, db_mtime FROM dds_pkg_remotes");
auto st = db.prepare("SELECT name, url, db_mtime FROM dds_pkg_remotes");
auto tups = neo::sqlite3::iter_tuples<std::string, std::string, std::optional<std::string>>(st); auto tups = neo::sqlite3::iter_tuples<std::string, std::string, std::optional<std::string>>(st);
for (auto [name, remote_url, mtime] : tups) { for (auto [name, remote_url, mtime] : tups) {
fmt::print("Remote '{}':\n", name); fmt::print("Remote '{}':\n", name);

+ 60
- 0
src/dds/cli/cmd/pkg_search.cpp View File

#include "../options.hpp"

#include <dds/error/nonesuch.hpp>
#include <dds/pkg/db.hpp>
#include <dds/pkg/search.hpp>
#include <dds/util/result.hpp>
#include <dds/util/string.hpp>

#include <boost/leaf/handle_exception.hpp>
#include <fansi/styled.hpp>
#include <fmt/format.h>
#include <range/v3/view/transform.hpp>

using namespace fansi::literals;

namespace dds::cli::cmd {

static int _pkg_search(const options& opts) {
auto cat = opts.open_pkg_db();
auto results = *dds::pkg_search(cat.database(), opts.pkg.search.pattern);
for (pkg_group_search_result const& found : results.found) {
fmt::print(
" Name: .bold[{}]\n"
"Versions: .bold[{}]\n"
" From: .bold[{}]\n"
" .bold[{}]\n\n"_styled,
found.name,
joinstr(", ", found.versions | ranges::views::transform(&semver::version::to_string)),
found.remote_name,
found.description);
}

if (results.found.empty()) {
dds_log(error,
"There are no packages that match the given pattern \".bold.red[{}]\""_styled,
opts.pkg.search.pattern.value_or("*"));
write_error_marker("pkg-search-no-result");
return 1;
}
return 0;
}

int pkg_search(const options& opts) {
return boost::leaf::try_catch(
[&] {
try {
return _pkg_search(opts);
} catch (...) {
capture_exception();
}
},
[](e_nonesuch missing) {
missing.log_error(
"There are no packages that match the given pattern \".bold.red[{}]\""_styled);
write_error_marker("pkg-search-no-result");
return 1;
});
}

} // namespace dds::cli::cmd

+ 3
- 0
src/dds/cli/dispatch_main.cpp View File

command pkg_repo_update; command pkg_repo_update;
command pkg_repo_ls; command pkg_repo_ls;
command pkg_repo_remove; command pkg_repo_remove;
command pkg_search;
command repoman_add; command repoman_add;
command repoman_import; command repoman_import;
command repoman_init; command repoman_init;
} }
neo::unreachable(); neo::unreachable();
} }
case pkg_subcommand::search:
return cmd::pkg_search(opts);
case pkg_subcommand::_none_:; case pkg_subcommand::_none_:;
} }
neo::unreachable(); neo::unreachable();

+ 18
- 0
src/dds/cli/options.cpp View File

#include <dds/toolchain/toolchain.hpp> #include <dds/toolchain/toolchain.hpp>


#include <debate/enum.hpp> #include <debate/enum.hpp>
#include <fansi/styled.hpp>


using namespace dds; using namespace dds;
using namespace debate; using namespace debate;
using namespace fansi::literals;


namespace { namespace {


.name = "repo", .name = "repo",
.help = "Manage package repositories", .help = "Manage package repositories",
})); }));
setup_pkg_search_cmd(pkg_group.add_parser({
.name = "search",
.help = "Search for packages available to download",
}));
} }


void setup_pkg_get_cmd(argument_parser& pkg_get_cmd) { void setup_pkg_get_cmd(argument_parser& pkg_get_cmd) {
= "What to do if any of the named repositories do not exist"; = "What to do if any of the named repositories do not exist";
} }


void setup_pkg_search_cmd(argument_parser& pkg_repo_search_cmd) noexcept {
pkg_repo_search_cmd.add_argument({
.help = std::string( //
"A name or glob-style pattern. Only matching packages will be returned. \n"
"Searching is case-insensitive. Only the .italic[name] will be matched (not the \n"
"version).\n\nIf this parameter is omitted, the search will return .italic[all] \n"
"available packages."_styled),
.valname = "<name-or-pattern>",
.action = put_into(opts.pkg.search.pattern),
});
}

void setup_sdist_cmd(argument_parser& sdist_cmd) noexcept { void setup_sdist_cmd(argument_parser& sdist_cmd) noexcept {
auto& sdist_grp = sdist_cmd.add_subparsers({ auto& sdist_grp = sdist_cmd.add_subparsers({
.valname = "<sdist-subcommand>", .valname = "<sdist-subcommand>",

+ 9
- 0
src/dds/cli/options.hpp View File

get, get,
import, import,
repo, repo,
search,
}; };


/** /**
/// Package IDs to download /// Package IDs to download
std::vector<string> pkgs; std::vector<string> pkgs;
} get; } get;

/**
* @brief Parameters for 'dds pkg search'
*/
struct {
/// The search pattern, if provided
opt_string pattern;
} search;
} pkg; } pkg;


struct { struct {

+ 1
- 1
src/dds/pkg/db.cpp View File

CREATE TABLE dds_pkg_remotes ( CREATE TABLE dds_pkg_remotes (
remote_id INTEGER PRIMARY KEY AUTOINCREMENT, remote_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE, name TEXT NOT NULL UNIQUE,
remote_url TEXT NOT NULL,
url TEXT NOT NULL,
db_etag TEXT, db_etag TEXT,
db_mtime TEXT db_mtime TEXT
); );

+ 6
- 6
src/dds/pkg/remote.cpp View File



void pkg_remote::store(nsql::database_ref db) { void pkg_remote::store(nsql::database_ref db) {
auto st = db.prepare(R"( auto st = db.prepare(R"(
INSERT INTO dds_pkg_remotes (name, remote_url)
INSERT INTO dds_pkg_remotes (name, url)
VALUES (?, ?) VALUES (?, ?)
ON CONFLICT (name) DO ON CONFLICT (name) DO
UPDATE SET remote_url = ?2
UPDATE SET url = ?2
)"); )");
nsql::exec(st, _name, _base_url.to_string()); nsql::exec(st, _name, _base_url.to_string());
} }


void dds::update_all_remotes(nsql::database_ref db) { void dds::update_all_remotes(nsql::database_ref db) {
dds_log(info, "Updating catalog from all remotes"); dds_log(info, "Updating catalog from all remotes");
auto repos_st = db.prepare("SELECT name, remote_url, db_etag, db_mtime FROM dds_pkg_remotes");
auto repos_st = db.prepare("SELECT name, url, db_etag, db_mtime FROM dds_pkg_remotes");
auto tups = nsql::iter_tuples<std::string, auto tups = nsql::iter_tuples<std::string,
std::string, std::string,
std::optional<std::string>, std::optional<std::string>,
std::optional<std::string>>(repos_st) std::optional<std::string>>(repos_st)
| ranges::to_vector; | ranges::to_vector;


for (const auto& [name, remote_url, etag, db_mtime] : tups) {
DDS_E_SCOPE(e_url_string{remote_url});
pkg_remote repo{name, neo::url::parse(remote_url)};
for (const auto& [name, url, etag, db_mtime] : tups) {
DDS_E_SCOPE(e_url_string{url});
pkg_remote repo{name, neo::url::parse(url)};
repo.update_pkg_db(db, etag, db_mtime); repo.update_pkg_db(db, etag, db_mtime);
} }



+ 76
- 0
src/dds/pkg/search.cpp View File

#include "./search.hpp"

#include <dds/dym.hpp>
#include <dds/error/nonesuch.hpp>
#include <dds/error/result.hpp>
#include <dds/util/log.hpp>
#include <dds/util/string.hpp>

#include <neo/sqlite3/database.hpp>
#include <neo/sqlite3/iter_tuples.hpp>

#include <range/v3/algorithm/sort.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/transform.hpp>

using namespace dds;
namespace nsql = neo::sqlite3;

result<pkg_search_results> dds::pkg_search(nsql::database_ref db,
std::optional<std::string_view> pattern) noexcept {
auto search_st = db.prepare(R"(
SELECT pkg.name,
group_concat(version, ';;'),
description,
remote.name,
remote.url
FROM dds_pkgs AS pkg
JOIN dds_pkg_remotes AS remote USING(remote_id)
WHERE lower(pkg.name) GLOB lower(:pattern)
GROUP BY pkg.name, remote_id, description
ORDER BY remote.name, pkg.name
)");
// If no pattern, grab _everything_
auto final_pattern = pattern.value_or("*");
dds_log(debug, "Searching for packages matching pattern '{}'", final_pattern);
search_st.bindings()[1] = final_pattern;
auto rows = nsql::iter_tuples<std::string, std::string, std::string, std::string, std::string>(
search_st);

std::vector<pkg_group_search_result> found;
for (auto [name, versions, desc, remote_name, remote_url] : rows) {
dds_log(debug,
"Found: {} with versions {} (Description: {}) from {} [{}]",
name,
versions,
desc,
remote_name,
remote_url);
auto version_strs = split(versions, ";;");
auto versions_semver
= version_strs | ranges::views::transform(&semver::version::parse) | ranges::to_vector;
ranges::sort(versions_semver);
found.push_back(pkg_group_search_result{
.name = name,
.versions = versions_semver,
.description = desc,
.remote_name = remote_name,
});
}

if (found.empty()) {
return boost::leaf::new_error([&] {
auto names_st = db.prepare("SELECT DISTINCT name from dds_pkgs");
auto tups = nsql::iter_tuples<std::string>(names_st);
auto names_vec = tups | ranges::views::transform([](auto&& row) {
auto [name] = row;
return name;
})
| ranges::to_vector;
auto nearest = dds::did_you_mean(final_pattern, names_vec);
return e_nonesuch{final_pattern, nearest};
});
}

return pkg_search_results{.found = std::move(found)};
}

+ 33
- 0
src/dds/pkg/search.hpp View File

#pragma once

#include <dds/error/result_fwd.hpp>

#include <semver/version.hpp>

#include <optional>
#include <string_view>
#include <vector>

namespace neo::sqlite3 {

class database_ref;

} // namespace neo::sqlite3

namespace dds {

struct pkg_group_search_result {
std::string name;
std::vector<semver::version> versions;
std::string description;
std::string remote_name;
};

struct pkg_search_results {
std::vector<pkg_group_search_result> found;
};

result<pkg_search_results> pkg_search(neo::sqlite3::database_ref db,
std::optional<std::string_view> query) noexcept;

} // namespace dds

+ 15
- 0
src/dds/util/string.hpp View File

return strings; return strings;
} }


template <typename Range>
inline std::string joinstr(std::string_view joiner, Range&& rng) {
auto iter = std::begin(rng);
auto end = std::end(rng);
std::string ret;
while (iter != end) {
ret.append(*iter);
++iter;
if (iter != end) {
ret.append(joiner);
}
}
return ret;
}

} // namespace string_utils } // namespace string_utils


} // namespace dds } // namespace dds

+ 3
- 3
tests/test_build_deps.py View File



import pytest import pytest


from dds_ci.testing import RepoFixture, Project
from dds_ci.testing import RepoServer, Project


SIMPLE_CATALOG = { SIMPLE_CATALOG = {
"packages": { "packages": {




@pytest.fixture() @pytest.fixture()
def test_repo(http_repo: RepoFixture) -> RepoFixture:
def test_repo(http_repo: RepoServer) -> RepoServer:
http_repo.import_json_data(SIMPLE_CATALOG) http_repo.import_json_data(SIMPLE_CATALOG)
return http_repo return http_repo




@pytest.fixture() @pytest.fixture()
def test_project(tmp_project: Project, test_repo: RepoFixture) -> Project:
def test_project(tmp_project: Project, test_repo: RepoServer) -> Project:
tmp_project.dds.repo_add(test_repo.url) tmp_project.dds.repo_add(test_repo.url)
return tmp_project return tmp_project



+ 34
- 11
tests/test_pkg_db.py View File

from dds_ci.dds import DDSWrapper from dds_ci.dds import DDSWrapper
from dds_ci.testing import Project, RepoFixture, PackageJSON
from dds_ci.testing import Project, RepoServer, PackageJSON
from dds_ci.testing.error import expect_error_marker from dds_ci.testing.error import expect_error_marker
from dds_ci.testing.http import HTTPRepoServerFactory, RepoServer

import pytest


NEO_SQLITE_PKG_JSON = { NEO_SQLITE_PKG_JSON = {
'packages': { 'packages': {
} }




def test_pkg_get(http_repo: RepoFixture, tmp_project: Project) -> None:
http_repo.import_json_data(NEO_SQLITE_PKG_JSON)
tmp_project.dds.repo_add(http_repo.url)
@pytest.fixture(scope='session')
def _test_repo(http_repo_factory: HTTPRepoServerFactory) -> RepoServer:
srv = http_repo_factory('test-pkg-db-repo')
srv.import_json_data(NEO_SQLITE_PKG_JSON)
return srv


def test_pkg_get(_test_repo: RepoServer, tmp_project: Project) -> None:
_test_repo.import_json_data(NEO_SQLITE_PKG_JSON)
tmp_project.dds.repo_add(_test_repo.url)
tmp_project.dds.pkg_get('neo-sqlite3@0.3.0') tmp_project.dds.pkg_get('neo-sqlite3@0.3.0')
assert tmp_project.root.joinpath('neo-sqlite3@0.3.0').is_dir() assert tmp_project.root.joinpath('neo-sqlite3@0.3.0').is_dir()
assert tmp_project.root.joinpath('neo-sqlite3@0.3.0/package.jsonc').is_file() assert tmp_project.root.joinpath('neo-sqlite3@0.3.0/package.jsonc').is_file()




def test_pkg_repo(http_repo: RepoFixture, tmp_project: Project) -> None:
def test_pkg_repo(_test_repo: RepoServer, tmp_project: Project) -> None:
dds = tmp_project.dds dds = tmp_project.dds
dds.repo_add(http_repo.url)
dds.repo_add(_test_repo.url)
dds.run(['pkg', 'repo', dds.catalog_path_arg, 'ls']) dds.run(['pkg', 'repo', dds.catalog_path_arg, 'ls'])




def test_pkg_repo_rm(http_repo: RepoFixture, tmp_project: Project) -> None:
http_repo.import_json_data(NEO_SQLITE_PKG_JSON)
def test_pkg_repo_rm(_test_repo: RepoServer, tmp_project: Project) -> None:
_test_repo.import_json_data(NEO_SQLITE_PKG_JSON)
dds = tmp_project.dds dds = tmp_project.dds
dds.repo_add(http_repo.url)
dds.repo_add(_test_repo.url)
# Okay: # Okay:
tmp_project.dds.pkg_get('neo-sqlite3@0.3.0') tmp_project.dds.pkg_get('neo-sqlite3@0.3.0')
# Remove the repo: # Remove the repo:
dds.run(['pkg', dds.catalog_path_arg, 'repo', 'ls']) dds.run(['pkg', dds.catalog_path_arg, 'repo', 'ls'])
dds.repo_remove(http_repo.repo_name)
dds.repo_remove(_test_repo.repo_name)
# Cannot double-remove a repo: # Cannot double-remove a repo:
with expect_error_marker('repo-rm-no-such-repo'): with expect_error_marker('repo-rm-no-such-repo'):
dds.repo_remove(http_repo.repo_name)
dds.repo_remove(_test_repo.repo_name)
# Now, fails: # Now, fails:
with expect_error_marker('pkg-get-no-pkg-id-listing'): with expect_error_marker('pkg-get-no-pkg-id-listing'):
tmp_project.dds.pkg_get('neo-sqlite3@0.3.0') tmp_project.dds.pkg_get('neo-sqlite3@0.3.0')


def test_pkg_search(_test_repo: RepoServer, tmp_project: Project) -> None:
_test_repo.import_json_data(NEO_SQLITE_PKG_JSON)
dds = tmp_project.dds
with expect_error_marker('pkg-search-no-result'):
dds.run(['pkg', dds.catalog_path_arg, 'search'])
dds.repo_add(_test_repo.url)
dds.run(['pkg', dds.catalog_path_arg, 'search'])
dds.run(['pkg', dds.catalog_path_arg, 'search', 'neo-sqlite3'])
dds.run(['pkg', dds.catalog_path_arg, 'search', 'neo-*'])
with expect_error_marker('pkg-search-no-result'):
dds.run(['pkg', dds.catalog_path_arg, 'search', 'nonexistent'])

+ 2
- 2
tests/test_repoman.py View File



from dds_ci.dds import DDSWrapper from dds_ci.dds import DDSWrapper
from dds_ci.testing.fixtures import Project from dds_ci.testing.fixtures import Project
from dds_ci.testing.http import RepoFixture
from dds_ci.testing.http import RepoServer
from dds_ci.testing.error import expect_error_marker from dds_ci.testing.error import expect_error_marker
from pathlib import Path from pathlib import Path


dds.run(['repoman', 'remove', tmp_repo, 'neo-fun@0.4.0']) dds.run(['repoman', 'remove', tmp_repo, 'neo-fun@0.4.0'])




def test_pkg_http(http_repo: RepoFixture, tmp_project: Project) -> None:
def test_pkg_http(http_repo: RepoServer, tmp_project: Project) -> None:
tmp_project.dds.run([ tmp_project.dds.run([
'repoman', '-ltrace', 'add', http_repo.server.root, 'neo-fun@0.4.0', 'repoman', '-ltrace', 'add', http_repo.server.root, 'neo-fun@0.4.0',
'https://github.com/vector-of-bool/neo-fun/archive/0.4.0.tar.gz?__dds_strpcmp=1' 'https://github.com/vector-of-bool/neo-fun/archive/0.4.0.tar.gz?__dds_strpcmp=1'

+ 2
- 2
tests/use-cryptopp/test_use_cryptopp.py View File



import pytest import pytest


from dds_ci.testing import RepoFixture, Project
from dds_ci.testing import RepoServer, Project
from dds_ci import proc, toolchain, paths from dds_ci import proc, toolchain, paths


CRYPTOPP_JSON = { CRYPTOPP_JSON = {




@pytest.mark.skipif(platform.system() == 'FreeBSD', reason='This one has trouble running on FreeBSD') @pytest.mark.skipif(platform.system() == 'FreeBSD', reason='This one has trouble running on FreeBSD')
def test_get_build_use_cryptopp(test_parent_dir: Path, tmp_project: Project, http_repo: RepoFixture) -> None:
def test_get_build_use_cryptopp(test_parent_dir: Path, tmp_project: Project, http_repo: RepoServer) -> None:
http_repo.import_json_data(CRYPTOPP_JSON) http_repo.import_json_data(CRYPTOPP_JSON)
tmp_project.dds.repo_add(http_repo.url) tmp_project.dds.repo_add(http_repo.url)
tmp_project.package_json = { tmp_project.package_json = {

+ 2
- 2
tests/use-spdlog/use_spdlog_test.py View File

from pathlib import Path from pathlib import Path


from dds_ci.testing import RepoFixture, ProjectOpener
from dds_ci.testing import RepoServer, ProjectOpener
from dds_ci import proc, paths, toolchain from dds_ci import proc, paths, toolchain




def test_get_build_use_spdlog(test_parent_dir: Path, project_opener: ProjectOpener, http_repo: RepoFixture) -> None:
def test_get_build_use_spdlog(test_parent_dir: Path, project_opener: ProjectOpener, http_repo: RepoServer) -> None:
proj = project_opener.open('project') proj = project_opener.open('project')
http_repo.import_json_file(proj.root / 'catalog.json') http_repo.import_json_file(proj.root / 'catalog.json')
proj.dds.repo_add(http_repo.url) proj.dds.repo_add(http_repo.url)

+ 2
- 2
tools/dds_ci/testing/__init__.py View File

from .fixtures import Project, ProjectOpener, PackageJSON, LibraryJSON from .fixtures import Project, ProjectOpener, PackageJSON, LibraryJSON
from .http import RepoFixture
from .http import RepoServer


__all__ = ( __all__ = (
'Project', 'Project',
'ProjectOpener', 'ProjectOpener',
'PackageJSON', 'PackageJSON',
'LibraryJSON', 'LibraryJSON',
'RepoFixture',
'RepoServer',
) )

+ 59
- 13
tools/dds_ci/testing/http.py View File

from pathlib import Path from pathlib import Path
from contextlib import contextmanager
import socket
from contextlib import contextmanager, ExitStack, closing
import json import json
from http.server import SimpleHTTPRequestHandler, HTTPServer from http.server import SimpleHTTPRequestHandler, HTTPServer
from typing import NamedTuple, Any, Iterator
from typing import NamedTuple, Any, Iterator, Callable
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from functools import partial from functools import partial
import tempfile import tempfile


import pytest import pytest
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.tmpdir import TempPathFactory

from dds_ci.dds import DDSWrapper


def _unused_tcp_port() -> int:
"""Find an unused localhost TCP port from 1024-65535 and return it."""
with closing(socket.socket()) as sock:
sock.bind(('127.0.0.1', 0))
return sock.getsockname()[1]




class DirectoryServingHTTPRequestHandler(SimpleHTTPRequestHandler): class DirectoryServingHTTPRequestHandler(SimpleHTTPRequestHandler):
httpd.shutdown() httpd.shutdown()




@pytest.fixture()
def http_tmp_dir_server(tmp_path: Path, unused_tcp_port: int) -> Iterator[ServerInfo]:
HTTPServerFactory = Callable[[Path], ServerInfo]


@pytest.fixture(scope='session')
def http_server_factory(request: FixtureRequest) -> HTTPServerFactory:
""" """
Creates an HTTP server that serves the contents of a new
temporary directory.
Spawn an HTTP server that serves the content of a directory.
""" """
with run_http_server(tmp_path, unused_tcp_port) as s:
yield s
def _make(p: Path) -> ServerInfo:
st = ExitStack()
server = st.enter_context(run_http_server(p, _unused_tcp_port()))
request.addfinalizer(st.pop_all)
return server

return _make




class RepoFixture:
class RepoServer:
""" """
A fixture handle to a dds HTTP repository, including a path and URL. A fixture handle to a dds HTTP repository, including a path and URL.
""" """
]) ])




RepoFactory = Callable[[str], Path]


@pytest.fixture(scope='session')
def repo_factory(tmp_path_factory: TempPathFactory, dds: DDSWrapper) -> RepoFactory:
def _make(name: str) -> Path:
tmpdir = tmp_path_factory.mktemp('test-repo-')
dds.run(['repoman', 'init', tmpdir, f'--name={name}'])
return tmpdir

return _make


HTTPRepoServerFactory = Callable[[str], RepoServer]


@pytest.fixture(scope='session')
def http_repo_factory(dds_exe: Path, repo_factory: RepoFactory,
http_server_factory: HTTPServerFactory) -> HTTPRepoServerFactory:
"""
Fixture factory that creates new repositories with an HTTP server for them.
"""
def _make(name: str) -> RepoServer:
repo_dir = repo_factory(name)
server = http_server_factory(repo_dir)
return RepoServer(dds_exe, server, name)

return _make


@pytest.fixture() @pytest.fixture()
def http_repo(dds_exe: Path, http_tmp_dir_server: ServerInfo, request: FixtureRequest) -> Iterator[RepoFixture]:
def http_repo(http_repo_factory: HTTPRepoServerFactory, request: FixtureRequest) -> RepoServer:
""" """
Fixture that creates a new empty dds repository and an HTTP server to serve Fixture that creates a new empty dds repository and an HTTP server to serve
it. it.
""" """
name = f'test-repo-{request.function.__name__}'
subprocess.check_call([str(dds_exe), 'repoman', 'init', str(http_tmp_dir_server.root), f'--name={name}'])
yield RepoFixture(dds_exe, http_tmp_dir_server, repo_name=name)
return http_repo_factory(f'test-repo-{request.function.__name__}')

Loading…
Cancel
Save