From 7db53790ca2d2c004bfd00b503eca59a8d084870 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 1 Aug 2024 20:03:48 +0300 Subject: Add support for loading package version reviews --- libbrep/package.cxx | 2 + libbrep/package.hxx | 45 ++++++++- libbrep/package.xml | 8 ++ libbrep/review-manifest.cxx | 220 ++++++++++++++++++++++++++++++++++++++++++++ libbrep/review-manifest.hxx | 80 ++++++++++++++++ 5 files changed, 350 insertions(+), 5 deletions(-) create mode 100644 libbrep/review-manifest.cxx create mode 100644 libbrep/review-manifest.hxx (limited to 'libbrep') diff --git a/libbrep/package.cxx b/libbrep/package.cxx index 4eb6fe8..391a583 100644 --- a/libbrep/package.cxx +++ b/libbrep/package.cxx @@ -84,6 +84,7 @@ namespace brep build_auxiliaries_type ac, package_build_bot_keys bk, package_build_configs bcs, + optional rvs, optional lc, optional fr, optional sh, @@ -119,6 +120,7 @@ namespace brep build_auxiliaries (move (ac)), build_bot_keys (move (bk)), build_configs (move (bcs)), + reviews (move (rvs)), internal_repository (move (rp)), location (move (lc)), fragment (move (fr)), diff --git a/libbrep/package.hxx b/libbrep/package.hxx index c06d8d3..668dc5c 100644 --- a/libbrep/package.hxx +++ b/libbrep/package.hxx @@ -20,7 +20,7 @@ // #define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 34 -#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 34, closed) +#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 35, closed) namespace brep { @@ -224,9 +224,8 @@ namespace brep // certificate // #pragma db value - class certificate + struct certificate { - public: string fingerprint; // SHA256 fingerprint. Note: foreign-mapped in build. string name; // CN component of Subject. string organization; // O component of Subject. @@ -536,6 +535,35 @@ namespace brep #pragma db member(package_build_bot_key_key::outer) column("config_index") #pragma db member(package_build_bot_key_key::inner) column("index") + // Number of the passed and failed reviews and the path to the + // reviews.manifest file this information comes form. The path is relative + // to the root of the package metadata directory. + // + #pragma db value + struct reviews_summary + { + // May not be both zero. + // + size_t pass; + size_t fail; + + path manifest_file; + }; + + inline bool + operator== (const reviews_summary& x, const reviews_summary& y) + { + return x.pass == y.pass && + x.fail == y.fail && + x.manifest_file == y.manifest_file; + } + + inline bool + operator!= (const reviews_summary& x, const reviews_summary& y) + { + return !(x == y); + } + // Tweak package_id mapping to include a constraint (this only affects the // database schema). // @@ -589,6 +617,7 @@ namespace brep build_auxiliaries_type, package_build_bot_keys, package_build_configs, + optional, optional location, optional fragment, optional sha256sum, @@ -691,6 +720,9 @@ namespace brep // odb::section build_section; + optional reviews; + odb::section reviews_section; + // Note that it is foreign-mapped in build. // lazy_shared_ptr internal_repository; @@ -916,8 +948,11 @@ namespace brep id_column("") key_column("") value_column("key_") value_not_null \ section(unused_section) - #pragma db member(build_section) load(lazy) update(always) - #pragma db member(unused_section) load(lazy) update(manual) + #pragma db member(reviews) section(reviews_section) + + #pragma db member(build_section) load(lazy) update(always) + #pragma db member(reviews_section) load(lazy) update(always) + #pragma db member(unused_section) load(lazy) update(manual) // other_repositories // diff --git a/libbrep/package.xml b/libbrep/package.xml index 42cad89..8b6c706 100644 --- a/libbrep/package.xml +++ b/libbrep/package.xml @@ -1,4 +1,12 @@ + + + + + + + + diff --git a/libbrep/review-manifest.cxx b/libbrep/review-manifest.cxx new file mode 100644 index 0000000..3592e69 --- /dev/null +++ b/libbrep/review-manifest.cxx @@ -0,0 +1,220 @@ +// file : libbrep/review-manifest.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include + +using namespace std; +using namespace butl; + +namespace brep +{ + using parser = manifest_parser; + using parsing = manifest_parsing; + using serializer = manifest_serializer; + using serialization = manifest_serialization; + using name_value = manifest_name_value; + + // review_result + // + string + to_string (review_result r) + { + switch (r) + { + case review_result::pass: return "pass"; + case review_result::fail: return "fail"; + case review_result::unchanged: return "unchanged"; + } + + assert (false); + return string (); + } + + review_result + to_review_result (const string& r) + { + if (r == "pass") return review_result::pass; + else if (r == "fail") return review_result::fail; + else if (r == "unchanged") return review_result::unchanged; + else throw invalid_argument ("invalid review result '" + r + '\''); + } + + // review_manifest + // + review_manifest:: + review_manifest (parser& p, bool iu) + : review_manifest (p, p.next (), iu) + { + // Make sure this is the end. + // + name_value nv (p.next ()); + if (!nv.empty ()) + throw parsing (p.name (), nv.name_line, nv.name_column, + "single review manifest expected"); + } + + review_manifest:: + review_manifest (parser& p, name_value nv, bool iu) + { + auto bad_name ([&p, &nv](const string& d) { + throw parsing (p.name (), nv.name_line, nv.name_column, d);}); + + auto bad_value ([&p, &nv](const string& d) { + throw parsing (p.name (), nv.value_line, nv.value_column, d);}); + + // Make sure this is the start and we support the version. + // + if (!nv.name.empty ()) + throw parsing (p.name (), nv.name_line, nv.name_column, + "start of review manifest expected"); + + if (nv.value != "1") + throw parsing (p.name (), nv.value_line, nv.value_column, + "unsupported format version"); + + bool need_base (false); + bool need_details (false); + + for (nv = p.next (); !nv.empty (); nv = p.next ()) + { + string& n (nv.name); + string& v (nv.value); + + if (n == "reviewed-by") + { + if (!reviewed_by.empty ()) + bad_name ("reviewer redefinition"); + + if (v.empty ()) + bad_value ("empty reviewer"); + + reviewed_by = move (v); + } + else if (n.size () > 7 && n.compare (0, 7, "result-") == 0) + { + string name (n, 7, n.size () - 7); + + if (find_if (results.begin (), results.end (), + [&name] (const review_aspect& r) + { + return name == r.name; + }) != results.end ()) + bad_name (name + " review result redefinition"); + + try + { + review_result r (to_review_result (v)); + + if (r == review_result::fail) + need_details = true; + + if (r == review_result::unchanged) + need_base = true; + + results.push_back (review_aspect {move (name), r}); + } + catch (const invalid_argument& e) + { + bad_value (e.what ()); + } + } + else if (n == "base-version") + { + if (base_version) + bad_name ("base version redefinition"); + + try + { + base_version = bpkg::version (v); + } + catch (const invalid_argument& e) + { + bad_value (e.what ()); + } + } + else if (n == "details-url") + { + if (details_url) + bad_name ("details url redefinition"); + + try + { + details_url = url (v); + } + catch (const invalid_argument& e) + { + bad_value (e.what ()); + } + } + else if (!iu) + bad_name ("unknown name '" + n + "' in review manifest"); + } + + // Verify all non-optional values were specified. + // + if (reviewed_by.empty ()) + bad_value ("no reviewer specified"); + + if (results.empty ()) + bad_value ("no result specified"); + + if (!base_version && need_base) + bad_value ("no base version specified"); + + if (!details_url && need_details) + bad_value ("no details url specified"); + } + + void review_manifest:: + serialize (serializer& s) const + { + // @@ Should we check that all non-optional values are specified and all + // values are valid? + // + s.next ("", "1"); // Start of manifest. + + auto bad_value ([&s](const string& d) { + throw serialization (s.name (), d);}); + + if (reviewed_by.empty ()) + bad_value ("empty reviewer"); + + s.next ("reviewed-by", reviewed_by); + + for (const review_aspect& r: results) + s.next ("result-" + r.name, to_string (r.result)); + + if (base_version) + s.next ("base-version", base_version->string ()); + + if (details_url) + s.next ("details-url", details_url->string ()); + + s.next ("", ""); // End of manifest. + } + + // review_manifests + // + review_manifests:: + review_manifests (parser& p, bool iu) + { + // Parse review manifests. + // + for (name_value nv (p.next ()); !nv.empty (); nv = p.next ()) + emplace_back (p, move (nv), iu); + } + + void review_manifests:: + serialize (serializer& s) const + { + // Serialize review manifests. + // + for (const review_manifest& m: *this) + m.serialize (s); + + s.next ("", ""); // End of stream. + } +} diff --git a/libbrep/review-manifest.hxx b/libbrep/review-manifest.hxx new file mode 100644 index 0000000..260fdec --- /dev/null +++ b/libbrep/review-manifest.hxx @@ -0,0 +1,80 @@ +// file : libbrep/review-manifest.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_REVIEW_MANIFEST_HXX +#define LIBBREP_REVIEW_MANIFEST_HXX + +#include + +#include + +#include +#include + +namespace brep +{ + enum class review_result: uint8_t + { + pass, + fail, + unchanged + }; + + string + to_string (review_result); + + review_result + to_review_result (const string&); // May throw invalid_argument. + + inline ostream& + operator<< (ostream& os, review_result r) + { + return os << to_string (r); + } + + struct review_aspect + { + string name; // code, build, test, doc, etc + review_result result; + }; + + class review_manifest + { + public: + string reviewed_by; + vector results; + optional base_version; + optional details_url; + + review_manifest (string r, + vector rs, + optional bv, + optional u) + : reviewed_by (move (r)), + results (move (rs)), + base_version (move (bv)), + details_url (move (u)) {} + + public: + review_manifest () = default; + review_manifest (butl::manifest_parser&, bool ignore_unknown = false); + review_manifest (butl::manifest_parser&, + butl::manifest_name_value start, + bool ignore_unknown = false); + + void + serialize (butl::manifest_serializer&) const; + }; + + class review_manifests: public vector + { + public: + review_manifests () = default; + review_manifests (butl::manifest_parser&, bool ignore_unknown = false); + + void + serialize (butl::manifest_serializer&) const; + }; +} + +#endif // LIBBREP_REVIEW_MANIFEST_HXX -- cgit v1.1