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 --- etc/brep-module.conf | 14 ++- libbrep/build.hxx | 12 ++ mod/build-config.cxx | 42 +++++++ mod/build-config.hxx | 15 +++ mod/buildfile | 1 + mod/database-module.cxx | 22 +++- mod/database-module.hxx | 10 ++ mod/mod-build-force.cxx | 8 +- mod/mod-build-log.cxx | 12 +- mod/mod-build-result.cxx | 43 ++----- mod/mod-build-task.cxx | 19 +++- mod/mod-builds.cxx | 218 ++++++++++++++++++++++++++++++++++++ mod/mod-builds.hxx | 42 +++++++ mod/mod-package-version-details.cxx | 9 -- mod/mod-repository-details.cxx | 9 -- mod/mod-repository-root.cxx | 17 ++- mod/mod-repository-root.hxx | 2 + mod/options.cli | 24 +++- mod/page.cxx | 26 +++++ mod/page.hxx | 32 ++++++ www/buildfile | 13 ++- www/builds-body.css | 44 ++++++++ www/builds.css | 3 + www/builds.scss | 3 + 24 files changed, 565 insertions(+), 75 deletions(-) create mode 100644 mod/mod-builds.cxx create mode 100644 mod/mod-builds.hxx create mode 100644 www/builds-body.css create mode 100644 www/builds.css create mode 100644 www/builds.scss diff --git a/etc/brep-module.conf b/etc/brep-module.conf index fdd3819..53678bb 100644 --- a/etc/brep-module.conf +++ b/etc/brep-module.conf @@ -21,10 +21,11 @@ # web interface root. # menu Packages= +# menu Builds=?builds menu About=?about -# Number of results per page. +# Number of package search results per page. # # search-results 10 @@ -75,6 +76,17 @@ menu About=?about # build-config +# Number of packages build configurations per page. +# +# build-configurations 10 + + +# Number of pages in navigation (pager). +# +# build-pages 5 +# + + # Time to wait before considering a package for a forced rebuild. Must be # specified in seconds. Default is 10 minutes. # diff --git a/libbrep/build.hxx b/libbrep/build.hxx index f841e6c..90e6523 100644 --- a/libbrep/build.hxx +++ b/libbrep/build.hxx @@ -170,6 +170,18 @@ namespace brep build () : package_name (id.package.name), configuration (id.configuration) {} }; + + #pragma db view object(build) + struct build_count + { + size_t result; + + operator size_t () const {return result;} + + // Database mapping. + // + #pragma db member(result) column("count(" + build::package_name + ")") + }; } #endif // LIBBREP_BUILD_HXX diff --git a/mod/build-config.cxx b/mod/build-config.cxx index 9cbd1bf..5fd540f 100644 --- a/mod/build-config.cxx +++ b/mod/build-config.cxx @@ -6,8 +6,11 @@ #include +#include + namespace brep { + using namespace web; using namespace bbot; shared_ptr @@ -28,4 +31,43 @@ namespace brep configs[p] = c; return c; } + + string + build_log_url (const string& host, const dir_path& root, + const build& b, + const string* op) + { + // Note that '+' is the only package version character that potentially + // needs to be url-encoded, and only in the query part of the URL. We embed + // the package version into the URL path part and so don't encode it. + // + string url (host + root.representation () + + mime_url_encode (b.package_name) + '/' + + b.package_version.string () + "/log/" + + mime_url_encode (b.configuration) + '/' + + b.toolchain_version.string ()); + + if (op != nullptr) + { + url += '/'; + url += *op; + } + + return url; + } + + string + force_rebuild_url (const string& host, const dir_path& root, const build& b) + { + // Note that '+' is the only package version character that potentially + // needs to be url-encoded, and only in the query part of the URL. However + // we embed the package version into the URL query part, where it is not + // encoded by design. + // + return host + root.string () + + "?build-force&p=" + mime_url_encode (b.package_name) + + "&v=" + b.package_version.string () + + "&c=" + mime_url_encode (b.configuration) + + "&t=" + b.toolchain_version.string () + "&reason="; + } } diff --git a/mod/build-config.hxx b/mod/build-config.hxx index 13024dc..6058774 100644 --- a/mod/build-config.hxx +++ b/mod/build-config.hxx @@ -10,6 +10,8 @@ #include #include +#include + namespace brep { // Return pointer to the shared build configurations instance, creating one @@ -18,6 +20,19 @@ namespace brep // shared_ptr shared_build_config (const path&); + + // Return the package configuration build log url. By default the url is to + // the operations combined log. + // + string + build_log_url (const string& host, const dir_path& root, + const build&, + const string* operation = nullptr); + + // Return the package configuration forced rebuild url. + // + string + force_rebuild_url (const string& host, const dir_path& root, const build&); } #endif // MOD_BUILD_CONFIG_HXX diff --git a/mod/buildfile b/mod/buildfile index 2a71de8..616302c 100644 --- a/mod/buildfile +++ b/mod/buildfile @@ -27,6 +27,7 @@ mod{brep}: \ {hxx cxx}{ mod-build-log } \ {hxx cxx}{ mod-build-result } \ {hxx cxx}{ mod-build-task } \ + {hxx cxx}{ mod-builds } \ {hxx cxx}{ mod-package-details } \ {hxx cxx}{ mod-package-search } \ {hxx cxx}{ mod-package-version-details } \ diff --git a/mod/database-module.cxx b/mod/database-module.cxx index 47cf03a..9daff53 100644 --- a/mod/database-module.cxx +++ b/mod/database-module.cxx @@ -20,6 +20,7 @@ namespace brep { using namespace std; using namespace butl; + using namespace bbot; // 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 @@ -31,7 +32,9 @@ namespace brep retry_ (r.retry_), package_db_ (r.initialized_ ? r.package_db_ : nullptr), build_db_ (r.initialized_ ? r.build_db_ : nullptr), - build_conf_ (r.initialized_ ? r.build_conf_ : nullptr) + build_conf_ (r.initialized_ ? r.build_conf_ : nullptr), + build_conf_names_ (r.initialized_ ? r.build_conf_names_ : nullptr), + build_conf_map_ (r.initialized_ ? r.build_conf_map_ : nullptr) { } @@ -64,6 +67,23 @@ namespace brep throw_generic_error (EIO, os.str ().c_str ()); } + cstrings conf_names; + + using conf_map_type = map; + conf_map_type conf_map; + + for (const auto& c: *build_conf_) + { + const char* cn (c.name.c_str ()); + conf_map[cn] = &c; + conf_names.push_back (cn); + } + + build_conf_names_ = make_shared (move (conf_names)); + build_conf_map_ = make_shared (move (conf_map)); + build_db_ = shared_database (dbo.build_db_user (), dbo.build_db_password (), dbo.build_db_name (), diff --git a/mod/database-module.hxx b/mod/database-module.hxx index 13fd529..a61e2e4 100644 --- a/mod/database-module.hxx +++ b/mod/database-module.hxx @@ -5,8 +5,12 @@ #ifndef MOD_DATABASE_MODULE_HXX #define MOD_DATABASE_MODULE_HXX +#include + #include // database +#include // compare_c_string + #include #include @@ -63,6 +67,12 @@ namespace brep // shared_ptr build_db_; shared_ptr build_conf_; + shared_ptr build_conf_names_; + + shared_ptr> + build_conf_map_; private: virtual bool diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx index 90e6b92..ed63146 100644 --- a/mod/mod-build-force.cxx +++ b/mod/mod-build-force.cxx @@ -141,12 +141,8 @@ handle (request& rq, response& rs) // Make sure the build configuration still exists. // - auto i ( - find_if ( - build_conf_->begin (), build_conf_->end (), - [&id] (const build_config& c) {return c.name == id.configuration;})); - - if (i == build_conf_->end ()) + if (build_conf_map_->find (id.configuration.c_str ()) == + build_conf_map_->end ()) config_expired ("no configuration"); // Make sure the package still exists. diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx index bf64ddb..687a554 100644 --- a/mod/mod-build-log.cxx +++ b/mod/mod-build-log.cxx @@ -168,12 +168,8 @@ handle (request& rq, response& rs) // Make sure the build configuration still exists. // - auto i ( - find_if ( - build_conf_->begin (), build_conf_->end (), - [&id] (const build_config& c) {return c.name == id.configuration;})); - - if (i == build_conf_->end ()) + auto i (build_conf_map_->find (id.configuration.c_str ())); + if (i == build_conf_map_->end ()) config_expired ("no configuration"); // Make sure the package still exists. @@ -217,7 +213,9 @@ handle (request& rq, response& rs) << endl << "machine: " << *b->machine << " (" << *b->machine_summary << ")" << endl - << "target: " << (i->target ? i->target->string () : "default") << endl + << "target: " << (i->second->target + ? i->second->target->string () + : "default") << endl << endl; if (op.empty ()) diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx index d8b647a..0574996 100644 --- a/mod/mod-build-result.cxx +++ b/mod/mod-build-result.cxx @@ -4,7 +4,8 @@ #include -#include // find_if() +#include +#include #include #include @@ -13,11 +14,7 @@ #include -#include -#include - #include -#include #include #include @@ -25,11 +22,11 @@ #include #include +#include // *_url() using namespace std; using namespace butl; using namespace bbot; -using namespace web; using namespace brep::cli; using namespace odb::core; @@ -207,12 +204,8 @@ handle (request& rq, response&) // Make sure the build configuration still exists. // - auto i ( - find_if ( - build_conf_->begin (), build_conf_->end (), - [&id] (const build_config& c) {return c.name == id.configuration;})); - - if (i == build_conf_->end ()) + if (build_conf_map_->find (id.configuration.c_str ()) == + build_conf_map_->end ()) { warn_expired ("no build configuration"); return true; @@ -322,35 +315,23 @@ handle (request& rq, response&) sm.out << "No operations results available." << endl; else { - string url (options_->host () + options_->root ().representation ()); - string pkg (mime_url_encode (b->package_name)); - string cfg (mime_url_encode (b->configuration)); - - // Note that '+' is the only package version character that potentially - // needs to be url-encoded, and only in the query part of the URL. - // However, we print the package version either as part of URL path or - // as the build-force URL query part (where it is not encoded by - // design). - // - const version& pvr (b->package_version); - const version& tvr (b->toolchain_version); + const string& host (options_->host ()); + const dir_path& root (options_->root ()); + ostream& os (sm.out); assert (b->status); os << "combined: " << *b->status << endl << endl - << " " << url << pkg << '/' << pvr << "/log/" << cfg << '/' << tvr - << endl << endl; + << " " << build_log_url (host, root, *b) << endl << endl; for (const auto& r: b->results) os << r.operation << ": " << r.status << endl << endl - << " " << url << pkg << '/' << pvr << "/log/" << cfg << '/' - << tvr << '/' << r.operation << endl << endl; + << " " << build_log_url (host, root, *b, &r.operation) + << endl << endl; os << "Force rebuild (enter the reason, use '+' instead of spaces):" << endl << endl - << " " << options_->host () << options_->root () << "?build-force&p=" - << pkg << "&v=" << pvr << "&c=" << cfg << "&t=" << tvr << "&reason=" - << endl; + << " " << force_rebuild_url (host, root, *b) << endl; } sm.out.close (); diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx index d75c73d..3e02463 100644 --- a/mod/mod-build-task.cxx +++ b/mod/mod-build-task.cxx @@ -7,6 +7,10 @@ #include #include +#include +#include +#include + #include // compare_c_string #include // path_match() #include @@ -15,9 +19,6 @@ #include #include -#include -#include - #include #include @@ -56,10 +57,22 @@ init (scanner& s) options_->package_db_retry ()); if (options_->build_config_specified ()) + { database_module::init (static_cast (*options_), static_cast (*options_), options_->build_db_retry ()); + // Check that the database 'build' schema matches the current one. It's + // enough to perform the check in just a single module implementation + // (more details in the comment in package_search::init()). + // + const string ds ("build"); + if (schema_catalog::current_version (*build_db_, ds) != + build_db_->schema_version (ds)) + fail << "database 'build' schema differs from the current one (module " + << BREP_VERSION_ID << ")"; + } + if (options_->root ().empty ()) options_->root (dir_path ("/")); } 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; +} diff --git a/mod/mod-builds.hxx b/mod/mod-builds.hxx new file mode 100644 index 0000000..f0fbf07 --- /dev/null +++ b/mod/mod-builds.hxx @@ -0,0 +1,42 @@ +// file : mod/mod-builds.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_BUILDS_HXX +#define MOD_MOD_BUILDS_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class builds: public database_module + { + public: + builds () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + builds (const builds&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const {return options::builds::description ();} + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_BUILDS_HXX diff --git a/mod/mod-package-version-details.cxx b/mod/mod-package-version-details.cxx index 37a50c8..a359104 100644 --- a/mod/mod-package-version-details.cxx +++ b/mod/mod-package-version-details.cxx @@ -109,15 +109,6 @@ handle (request& rq, response& rs) << HEAD << TITLE << title << ~TITLE << CSS_LINKS (path ("package-version-details.css"), root) - // - // This hack is required to avoid the "flash of unstyled content", which - // happens due to the presence of the autofocus attribute in the input - // element of the search form. The problem appears in Firefox and has a - // (4-year old, at the time of this writing) bug report: - // - // https://bugzilla.mozilla.org/show_bug.cgi?id=712130. - // - << SCRIPT << " " << ~SCRIPT << ~HEAD << BODY << DIV_HEADER (root, options_->logo (), options_->menu ()) diff --git a/mod/mod-repository-details.cxx b/mod/mod-repository-details.cxx index be87d89..f4027b6 100644 --- a/mod/mod-repository-details.cxx +++ b/mod/mod-repository-details.cxx @@ -85,15 +85,6 @@ handle (request& rq, response& rs) << HEAD << TITLE << title << ~TITLE << CSS_LINKS (path ("repository-details.css"), root) - // - // This hack is required to avoid the "flash of unstyled content", which - // happens due to the presence of the autofocus attribute in the input - // element of the search form. The problem appears in Firefox and has a - // (4-year old, at the time of this writing) bug report: - // - // https://bugzilla.mozilla.org/show_bug.cgi?id=712130. - // - << SCRIPT << " " << ~SCRIPT << ~HEAD << BODY << DIV_HEADER (root, options_->logo (), options_->menu ()) diff --git a/mod/mod-repository-root.cxx b/mod/mod-repository-root.cxx index 00cdea4..cb0b82f 100644 --- a/mod/mod-repository-root.cxx +++ b/mod/mod-repository-root.cxx @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -61,7 +62,8 @@ namespace brep build_task_ (make_shared ()), build_result_ (make_shared ()), build_force_ (make_shared ()), - build_log_ (make_shared ()) + build_log_ (make_shared ()), + builds_ (make_shared ()) { } @@ -105,6 +107,10 @@ namespace brep r.initialized_ ? r.build_log_ : make_shared (*r.build_log_)), + builds_ ( + r.initialized_ + ? r.builds_ + : make_shared (*r.builds_)), options_ ( r.initialized_ ? r.options_ @@ -127,6 +133,7 @@ namespace brep append (r, build_result_->options ()); append (r, build_force_->options ()); append (r, build_log_->options ()); + append (r, builds_->options ()); return r; } @@ -168,6 +175,7 @@ namespace brep sub_init (*build_result_, "build_result"); sub_init (*build_force_, "build_force"); sub_init (*build_log_, "build_log"); + sub_init (*builds_, "builds"); // Parse own configuration options. // @@ -286,6 +294,13 @@ namespace brep return handle (rp, "build_force"); } + else if (fn == "builds") + { + if (handler_ == nullptr) + handler_.reset (new builds (*builds_)); + + return handle (rp, "builds"); + } else throw invalid_request (400, "unknown function"); } diff --git a/mod/mod-repository-root.hxx b/mod/mod-repository-root.hxx index f54fa62..85a9aac 100644 --- a/mod/mod-repository-root.hxx +++ b/mod/mod-repository-root.hxx @@ -21,6 +21,7 @@ namespace brep class build_result; class build_force; class build_log; + class builds; class repository_root: public module { @@ -63,6 +64,7 @@ namespace brep shared_ptr build_result_; shared_ptr build_force_; shared_ptr build_log_; + shared_ptr builds_; shared_ptr options_; // Sub-module the request is dispatched to. Initially is NULL. It is set diff --git a/mod/options.cli b/mod/options.cli index 888a591..9442e7f 100644 --- a/mod/options.cli +++ b/mod/options.cli @@ -211,7 +211,7 @@ namespace brep uint16_t search-results = 10 { "", - "Number of results per page. The default is 10." + "Number of package search results per page. The default is 10." } uint16_t search-pages = 5 @@ -301,6 +301,21 @@ namespace brep { }; + class builds: build, package_db, build_db, page, module + { + uint16_t build-configurations = 10 + { + "", + "Number of packages build configurations per page. The default is 10." + } + + uint16_t build-pages = 5 + { + "", + "Number of pages in navigation (pager). The default is 5." + } + }; + class repository_root: module { }; @@ -401,5 +416,12 @@ namespace brep // string reason; }; + + class builds + { + // Display packages build configurations list starting from this page. + // + uint16_t page | p; + }; } } diff --git a/mod/page.cxx b/mod/page.cxx index bf090ef..de7b5f5 100644 --- a/mod/page.cxx +++ b/mod/page.cxx @@ -111,6 +111,17 @@ namespace brep << ~DIV; } + // TR_VALUE + // + void TR_VALUE:: + operator() (serializer& s) const + { + s << TR(CLASS=label_) + << TH << label_ << ~TH + << TD << SPAN(CLASS="value") << value_ << ~SPAN << ~TD + << ~TR; + } + // TR_NAME // void TR_NAME:: @@ -135,6 +146,8 @@ namespace brep << ~TR; } + // TR_VERSION + // void TR_VERSION:: operator() (serializer& s) const { @@ -532,6 +545,14 @@ namespace brep << ~SPAN; } + // SPAN_BUILD_RESULT_STATUS + // + void SPAN_BUILD_RESULT_STATUS:: + operator() (serializer& s) const + { + s << SPAN(CLASS=to_string (status_)) << status_ << ~SPAN; + } + // P_DESCRIPTION // void P_DESCRIPTION:: @@ -663,6 +684,11 @@ namespace brep size_t from (current_page_ > offset ? current_page_ - offset : 0); size_t to (min (from + page_number_count_, pcount)); + // Display as many pages as allowed. + // + if (to - from < page_number_count_ && from > 0) + from -= min (from, page_number_count_ - (to - from)); + for (size_t p (from); p < to; ++p) { s << A(HREF=url (p)); diff --git a/mod/page.hxx b/mod/page.hxx index b7f895e..6d18f36 100644 --- a/mod/page.hxx +++ b/mod/page.hxx @@ -7,6 +7,8 @@ #include +#include + #include #include @@ -90,6 +92,22 @@ namespace brep const char* plural_; }; + // Generates table row element, that has the 'label: value' layout. + // + class TR_VALUE + { + public: + TR_VALUE (const string& l, const string& v) + : label_ (l), value_ (v) {} + + void + operator() (xml::serializer&) const; + + private: + const string& label_; + const string& value_; + }; + // Generates package name element. // class TR_NAME @@ -347,6 +365,20 @@ namespace brep const string& comment_; }; + // Generates package build result status element. + // + class SPAN_BUILD_RESULT_STATUS + { + public: + SPAN_BUILD_RESULT_STATUS (const bbot::result_status& s): status_ (s) {} + + void + operator() (xml::serializer&) const; + + private: + const bbot::result_status& status_; + }; + // Generates package description element. // class P_DESCRIPTION diff --git a/www/buildfile b/www/buildfile index 7a13eca..a9616b0 100644 --- a/www/buildfile +++ b/www/buildfile @@ -10,10 +10,11 @@ define scss: file scss{*}: extension = scss scss{*}: install = data/www/ -./: css{common brep-common \ - package-details package-details-body \ - package-search package-search-body \ - package-version-details package-version-details-body \ - repository-details repository-details-body} \ - scss{package-details package-search package-version-details \ +./: css{common brep-common \ + builds builds-body \ + package-details package-details-body \ + package-search package-search-body \ + package-version-details package-version-details-body \ + repository-details repository-details-body} \ + scss{builds package-details package-search package-version-details \ repository-details} diff --git a/www/builds-body.css b/www/builds-body.css new file mode 100644 index 0000000..67ed9a3 --- /dev/null +++ b/www/builds-body.css @@ -0,0 +1,44 @@ +/* + * Build count. + */ +#count +{ + font-size: 1.32em; + line-height: 1.4em; + color: #555; + + margin: 1.2em 0 0 0; +} + +/* + * Version table. + */ +.build +{ + margin-top: .8em; + margin-bottom: .8em; + + padding-top: .4em; + padding-bottom: .4em; +} +.build:nth-child(even) {background-color: rgba(0, 0, 0, 0.07);} + +.build th {width: 6.4em;} + +.build tr.name td .value, +.build tr.version td .value, +.build tr.config td .value, +.build tr.toolchain td .value, +.build tr.machine td .value, +.build tr.target td .value, +.build tr.result td .value +{ + /* style. */ + font-family: monospace; + font-size: 0.94em; +} + +.build .warning {color: #ffa500;} +.build .error {color: #ff0000;} +.build .abort {color: #ff0000;} +.build .abnormal {color: #ff0000;} diff --git a/www/builds.css b/www/builds.css new file mode 100644 index 0000000..a24d4ed --- /dev/null +++ b/www/builds.css @@ -0,0 +1,3 @@ +@import url(common.css); +@import url(brep-common.css); +@import url(builds-body.css); diff --git a/www/builds.scss b/www/builds.scss new file mode 100644 index 0000000..559d1c9 --- /dev/null +++ b/www/builds.scss @@ -0,0 +1,3 @@ +@import "common"; +@import "brep-common"; +@import "builds-body"; -- cgit v1.1