From 5163436b00711318baea4fc0ad43a4de8222354a Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sun, 14 May 2017 00:37:16 +0300 Subject: Implement builds page --- mod/mod-builds.cxx | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 mod/mod-builds.cxx (limited to 'mod/mod-builds.cxx') diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx new file mode 100644 index 0000000..6ffb517 --- /dev/null +++ b/mod/mod-builds.cxx @@ -0,0 +1,218 @@ +// file : mod/mod-builds.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include // *_url() + +using namespace std; +using namespace butl; +using namespace bbot; +using namespace odb::core; +using namespace brep::cli; + +// While currently the user-defined copy constructor is not required (we don't +// need to deep copy nullptr's), it is a good idea to keep the placeholder +// ready for less trivial cases. +// +brep::builds:: +builds (const builds& r) + : database_module (r), + options_ (r.initialized_ ? r.options_ : nullptr) +{ +} + +void brep::builds:: +init (scanner& s) +{ + MODULE_DIAG; + + options_ = make_shared ( + s, unknown_mode::fail, unknown_mode::fail); + + database_module::init (*options_, options_->package_db_retry ()); + + if (options_->build_config_specified ()) + database_module::init (static_cast (*options_), + static_cast (*options_), + options_->build_db_retry ()); + + if (options_->root ().empty ()) + options_->root (dir_path ("/")); +} + +template +static inline query +build_query (const C& configs) +{ + using query = query; + + return query::id.configuration.in_range (configs.begin (), configs.end ()) && + (query::state == "testing" || query::state == "tested"); +} + +bool brep::builds:: +handle (request& rq, response& rs) +{ + using namespace web::xhtml; + + MODULE_DIAG; + + if (build_db_ == nullptr) + throw invalid_request (501, "not implemented"); + + const size_t page_configs (options_->build_configurations ()); + const string& host (options_->host ()); + const dir_path& root (options_->root ()); + + params::builds params; + + try + { + name_value_scanner s (rq.parameters ()); + params = params::builds ( + s, unknown_mode::fail, unknown_mode::fail); + } + catch (const cli::exception& e) + { + throw invalid_request (400, e.what ()); + } + + size_t page (params.page ()); + const char* title ("Builds"); + + xml::serializer s (rs.content (), title); + + s << HTML + << HEAD + << TITLE << title << ~TITLE + << CSS_LINKS (path ("builds.css"), root) + << ~HEAD + << BODY + << DIV_HEADER (root, options_->logo (), options_->menu ()) + << DIV(ID="content"); + + transaction t (build_db_->begin ()); + + // Having packages and packages configurations builds in different databases, + // we unable to filter out builds for non-existent packages at the query + // level. Doing that in the C++ code would complicate it significantly. So + // we will print all the builds, relying on the sorting algorithm, that will + // likely to place expired ones at the end of the query result. + // + auto count ( + build_db_->query_value ( + build_query (*build_conf_names_))); + + s << DIV_COUNTER (count, "Build", "Builds"); + + // Enclose the subsequent tables to be able to use nth-child CSS selector. + // + s << DIV; + for (auto& b: build_db_->query ( + build_query (*build_conf_names_) + + "ORDER BY" + query::timestamp + "DESC" + + "OFFSET" + to_string (page * page_configs) + + "LIMIT" + to_string (page_configs))) + { + assert (b.machine); + + auto i (build_conf_map_->find (b.configuration.c_str ())); + assert (i != build_conf_map_->end ()); + const build_config& c (*i->second); + + s << TABLE(CLASS="proplist build") + << TBODY + << TR_NAME (b.package_name, string (), root) + << TR_VERSION (b.package_name, b.package_version, root) + << TR_VALUE ("config", b.configuration) + << TR_VALUE ("toolchain", + b.toolchain_name + '-' + + b.toolchain_version.string ()) + << TR_VALUE ("machine", *b.machine) + << TR_VALUE ("target", c.target ? c.target->string () : "default") + << TR(CLASS="result") + << TH << "result" << ~TH + << TD + << SPAN(CLASS="value"); + + if (b.state == build_state::testing) + s << "building"; + else + { + assert (b.state == build_state::tested); + + build_db_->load (b, b.results_section); + + // If no results available, then print the overall build status. + // Otherwise print unsuccessful operations statuses with the links to the + // respective logs, followed with a link to the operations combined log. + // Print the forced package rebuild link afterwards. + // + if (b.results.empty ()) + { + assert (b.status); + s << SPAN_BUILD_RESULT_STATUS (*b.status); + } + else + { + for (const auto& r: b.results) + { + if (r.status != result_status::success) + s << SPAN_BUILD_RESULT_STATUS (r.status) << '(' + << A + << HREF + << build_log_url (host, root, b, &r.operation) + << ~HREF + << r.operation + << ~A + << ')' << ' '; + } + + s << A + << HREF << build_log_url (host, root, b) << ~HREF + << "all" + << ~A; + } + + s << ' ' + << A + << HREF << force_rebuild_url (host, root, b) << ~HREF + << "rebuild" + << ~A; + } + + s << ~SPAN + << ~TD + << ~TR + << ~TBODY + << ~TABLE; + } + s << ~DIV; + + t.commit (); + + s << DIV_PAGER (page, count, page_configs, options_->build_pages (), + root.string () + "?builds") + << ~DIV + << ~BODY + << ~HTML; + + return true; +} -- cgit v1.1