Browse Source

Error handling when package cache has an invalid item

default_compile_flags
vector-of-bool 3 years ago
parent
commit
198dd5e6d2
4 changed files with 124 additions and 14 deletions
  1. +15
    -0
      src/dds/error/handle.cpp
  2. +26
    -0
      src/dds/error/handle.hpp
  3. +54
    -14
      src/dds/pkg/cache.cpp
  4. +29
    -0
      tests/test_pkg_db.py

+ 15
- 0
src/dds/error/handle.cpp View File

@@ -0,0 +1,15 @@
#include "./handle.hpp"

#include <dds/util/log.hpp>

#include <boost/leaf/handle_exception.hpp>
#include <boost/leaf/result.hpp>
#include <fmt/ostream.h>

using namespace dds;

void dds::leaf_handle_unknown_void(std::string_view message,
const boost::leaf::verbose_diagnostic_info& info) {
dds_log(warn, message);
dds_log(warn, "An unhandled error occurred:\n{}", info);
}

+ 26
- 0
src/dds/error/handle.hpp View File

@@ -0,0 +1,26 @@
#pragma once

#include <neo/fwd.hpp>

#include <string_view>

namespace boost::leaf {

class verbose_diagnostic_info;

} // namespace boost::leaf

namespace dds {

void leaf_handle_unknown_void(std::string_view message,
const boost::leaf::verbose_diagnostic_info&);

template <typename T>
auto leaf_handle_unknown(std::string_view message, T&& val) {
return [val = NEO_FWD(val), message](const boost::leaf::verbose_diagnostic_info& info) {
leaf_handle_unknown_void(message, info);
return val;
};
}

} // namespace dds

+ 54
- 14
src/dds/pkg/cache.cpp View File

@@ -1,13 +1,17 @@
#include "./cache.hpp"

#include <dds/error/errors.hpp>
#include <dds/error/handle.hpp>
#include <dds/pkg/db.hpp>
#include <dds/sdist/dist.hpp>
#include <dds/sdist/library/manifest.hpp>
#include <dds/solve/solve.hpp>
#include <dds/util/log.hpp>
#include <dds/util/paths.hpp>
#include <dds/util/string.hpp>

#include <boost/leaf/handle_exception.hpp>
#include <fansi/styled.hpp>
#include <neo/ref.hpp>
#include <range/v3/action/sort.hpp>
#include <range/v3/action/unique.hpp>
@@ -17,7 +21,7 @@
#include <range/v3/view/transform.hpp>

using namespace dds;
using namespace fansi::literals;
using namespace ranges;

void pkg_cache::_log_blocking(path_ref dirpath) noexcept {
@@ -29,28 +33,64 @@ void pkg_cache::_init_cache_dir(path_ref dirpath) noexcept { fs::create_director

fs::path pkg_cache::default_local_path() noexcept { return dds_data_dir() / "pkg"; }

pkg_cache pkg_cache::_open_for_directory(bool writeable, path_ref dirpath) {
auto try_read_sdist = [](path_ref p) -> std::optional<sdist> {
if (starts_with(p.filename().string(), ".")) {
return std::nullopt;
}
try {
return sdist::from_directory(p);
} catch (const std::runtime_error& e) {
dds_log(error,
namespace {

std::optional<sdist> try_open_sdist_for_directory(path_ref p) noexcept {
if (starts_with(p.filename().string(), ".")) {
return std::nullopt;
}
return boost::leaf::try_catch( //
[&] { return std::make_optional(sdist::from_directory(p)); },
[&](boost::leaf::catch_<std::runtime_error> exc) {
dds_log(warn,
"Failed to load source distribution from directory '{}': {}",
p.string(),
e.what());
exc.value().what());
return std::nullopt;
}
};
},
[&](e_package_manifest_path*,
e_library_manifest_path* lman_path,
e_pkg_namespace_str* is_namespace,
e_pkg_name_str* is_pkgname,
e_name_str bad_name,
invalid_name_reason why) {
dds_log(
warn,
"Failed to load a source distribution contained in the package cache directory");
dds_log(warn,
"The invalid source distribution is in [.bold.yellow[{}]]"_styled,
p.string());
if (is_namespace) {
dds_log(warn,
"Invalid package namespace '.bold.yellow[{}]'"_styled,
bad_name.value);
} else if (is_pkgname) {
dds_log(warn, "Invalid package name '.bold.yellow[{}]'"_styled, bad_name.value);
} else if (lman_path) {
dds_log(
warn,
"Invalid library name '.bold.yellow[{}]' (Defined in [.bold.yellow[{}]])"_styled,
bad_name.value,
lman_path->value);
}
dds_log(warn, " (.bold.yellow[{}])"_styled, invalid_name_reason_str(why));
dds_log(warn, "We will ignore this directory and not load it as an available package");
return std::nullopt;
},
leaf_handle_unknown(fmt::format("Failed to load source distribution from directory [{}]",
p.string()),
std::nullopt));
}

} // namespace

pkg_cache pkg_cache::_open_for_directory(bool writeable, path_ref dirpath) {
auto entries =
// Get the top-level `name-version` dirs
fs::directory_iterator(dirpath) //
| neo::lref //
// Convert each dir into an `sdist` object
| ranges::views::transform(try_read_sdist) //
| ranges::views::transform(try_open_sdist_for_directory) //
// Drop items that failed to load
| ranges::views::filter([](auto&& opt) { return opt.has_value(); }) //
| ranges::views::transform([](auto&& opt) { return *opt; }) //

+ 29
- 0
tests/test_pkg_db.py View File

@@ -1,3 +1,5 @@
import json

from dds_ci.dds import DDSWrapper
from dds_ci.testing import Project, RepoServer, PackageJSON
from dds_ci.testing.error import expect_error_marker
@@ -70,3 +72,30 @@ def test_pkg_search(_test_repo: RepoServer, tmp_project: Project) -> None:
dds.run(['pkg', dds.pkg_db_path_arg, 'search', 'neo-*'])
with expect_error_marker('pkg-search-no-result'):
dds.run(['pkg', dds.pkg_db_path_arg, 'search', 'nonexistent'])


def test_pkg_cache_invalid_nofail(tmp_project: Project) -> None:
"""
Check that dds will not fail a build just because the package cache has an invalid
object within.
"""
sdist_dir = tmp_project.dds.repo_dir / 'bad@1.2.3'
sdist_dir.mkdir(parents=True)
tmp_project.build()

# Write an invalid source distribution
pkman_path = sdist_dir / 'package.json5'
pkman_path.write_text(json.dumps({}))
tmp_project.build()

pkman_path.write_text('lol') # Inavlid JSON
tmp_project.build()

pkman_path.write_text('''
{
name: 'invalid name',
namespace: 'test',
version: '1.2.3'
}
''')
tmp_project.build()

Loading…
Cancel
Save