aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/brep-module.conf14
-rw-r--r--libbrep/build.hxx12
-rw-r--r--mod/build-config.cxx42
-rw-r--r--mod/build-config.hxx15
-rw-r--r--mod/buildfile1
-rw-r--r--mod/database-module.cxx22
-rw-r--r--mod/database-module.hxx10
-rw-r--r--mod/mod-build-force.cxx8
-rw-r--r--mod/mod-build-log.cxx12
-rw-r--r--mod/mod-build-result.cxx43
-rw-r--r--mod/mod-build-task.cxx19
-rw-r--r--mod/mod-builds.cxx218
-rw-r--r--mod/mod-builds.hxx42
-rw-r--r--mod/mod-package-version-details.cxx9
-rw-r--r--mod/mod-repository-details.cxx9
-rw-r--r--mod/mod-repository-root.cxx17
-rw-r--r--mod/mod-repository-root.hxx2
-rw-r--r--mod/options.cli24
-rw-r--r--mod/page.cxx26
-rw-r--r--mod/page.hxx32
-rw-r--r--www/buildfile13
-rw-r--r--www/builds-body.css44
-rw-r--r--www/builds.css3
-rw-r--r--www/builds.scss3
24 files changed, 565 insertions, 75 deletions
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 <map>
+#include <web/mime-url-encoding.hxx>
+
namespace brep
{
+ using namespace web;
using namespace bbot;
shared_ptr<const build_configs>
@@ -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 <libbrep/types.hxx>
#include <libbrep/utility.hxx>
+#include <libbrep/build.hxx>
+
namespace brep
{
// Return pointer to the shared build configurations instance, creating one
@@ -18,6 +20,19 @@ namespace brep
//
shared_ptr<const bbot::build_configs>
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<const char*,
+ const build_config*,
+ compare_c_string>;
+ 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<cstrings> (move (conf_names));
+ build_conf_map_ = make_shared<conf_map_type> (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 <map>
+
#include <odb/forward.hxx> // database
+#include <libbutl/utility.hxx> // compare_c_string
+
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
@@ -63,6 +67,12 @@ namespace brep
//
shared_ptr<odb::core::database> build_db_;
shared_ptr<const bbot::build_configs> build_conf_;
+ shared_ptr<const cstrings> build_conf_names_;
+
+ shared_ptr<const std::map<const char*,
+ const bbot::build_config*,
+ butl::compare_c_string>>
+ 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 <mod/mod-build-result.hxx>
-#include <algorithm> // find_if()
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
#include <libbutl/sendmail.hxx>
#include <libbutl/process-io.hxx>
@@ -13,11 +14,7 @@
#include <libbbot/manifest.hxx>
-#include <odb/database.hxx>
-#include <odb/transaction.hxx>
-
#include <web/module.hxx>
-#include <web/mime-url-encoding.hxx>
#include <libbrep/build.hxx>
#include <libbrep/build-odb.hxx>
@@ -25,11 +22,11 @@
#include <libbrep/package-odb.hxx>
#include <mod/options.hxx>
+#include <mod/build-config.hxx> // *_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 <map>
#include <chrono>
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
#include <libbutl/utility.hxx> // compare_c_string
#include <libbutl/filesystem.hxx> // path_match()
#include <libbutl/manifest-parser.hxx>
@@ -15,9 +19,6 @@
#include <libbbot/manifest.hxx>
#include <libbbot/build-config.hxx>
-#include <odb/database.hxx>
-#include <odb/transaction.hxx>
-
#include <web/module.hxx>
#include <libbrep/build.hxx>
@@ -56,10 +57,22 @@ init (scanner& s)
options_->package_db_retry ());
if (options_->build_config_specified ())
+ {
database_module::init (static_cast<options::build> (*options_),
static_cast<options::build_db> (*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 <mod/mod-builds.hxx>
+
+#include <libstudxml/serializer.hxx>
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+
+#include <web/xhtml.hxx>
+#include <web/module.hxx>
+
+#include <libbrep/build.hxx>
+#include <libbrep/build-odb.hxx>
+#include <libbrep/package.hxx>
+#include <libbrep/package-odb.hxx>
+
+#include <mod/page.hxx>
+#include <mod/options.hxx>
+#include <mod/build-config.hxx> // *_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<options::builds> (
+ 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::build> (*options_),
+ static_cast<options::build_db> (*options_),
+ options_->build_db_retry ());
+
+ if (options_->root ().empty ())
+ options_->root (dir_path ("/"));
+}
+
+template <typename T, typename C>
+static inline query<T>
+build_query (const C& configs)
+{
+ using query = query<T>;
+
+ 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_count> (
+ build_query<build_count> (*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> (
+ build_query<build> (*build_conf_names_) +
+ "ORDER BY" + query<build>::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 <libbrep/types.hxx>
+#include <libbrep/utility.hxx>
+
+#include <mod/options.hxx>
+#include <mod/database-module.hxx>
+
+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::builds> 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 <mod/module.hxx>
#include <mod/options.hxx>
+#include <mod/mod-builds.hxx>
#include <mod/mod-build-log.hxx>
#include <mod/mod-build-task.hxx>
#include <mod/mod-build-force.hxx>
@@ -61,7 +62,8 @@ namespace brep
build_task_ (make_shared<build_task> ()),
build_result_ (make_shared<build_result> ()),
build_force_ (make_shared<build_force> ()),
- build_log_ (make_shared<build_log> ())
+ build_log_ (make_shared<build_log> ()),
+ builds_ (make_shared<builds> ())
{
}
@@ -105,6 +107,10 @@ namespace brep
r.initialized_
? r.build_log_
: make_shared<build_log> (*r.build_log_)),
+ builds_ (
+ r.initialized_
+ ? r.builds_
+ : make_shared<builds> (*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> build_result_;
shared_ptr<build_force> build_force_;
shared_ptr<build_log> build_log_;
+ shared_ptr<builds> builds_;
shared_ptr<options::repository_root> 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
{
"<num>",
- "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
+ {
+ "<num>",
+ "Number of packages build configurations per page. The default is 10."
+ }
+
+ uint16_t build-pages = 5
+ {
+ "<num>",
+ "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 <libstudxml/forward.hxx>
+#include <libbbot/manifest.hxx>
+
#include <web/xhtml-fragment.hxx>
#include <libbrep/types.hxx>
@@ -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
+{
+ /* <code> 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";