diff options
-rw-r--r-- | NEWS | 18 | ||||
-rw-r--r-- | brep/handler/ci/ci-load.in | 20 | ||||
-rwxr-xr-x | doc/cli.sh | 2 | ||||
-rw-r--r-- | libbrep/package.hxx | 2 | ||||
-rw-r--r-- | load/load.cli | 14 | ||||
-rw-r--r-- | load/load.cxx | 198 | ||||
-rw-r--r-- | load/options-types.hxx | 20 | ||||
-rw-r--r-- | load/types-parsers.cxx | 18 | ||||
-rw-r--r-- | load/types-parsers.hxx | 9 | ||||
-rw-r--r-- | manifest | 20 | ||||
-rw-r--r-- | mod/build-config-module.cxx | 33 | ||||
-rw-r--r-- | mod/build-config-module.hxx | 9 | ||||
-rw-r--r-- | mod/buildfile | 2 | ||||
-rw-r--r-- | mod/ci-common.cxx | 72 | ||||
-rw-r--r-- | mod/ci-common.hxx | 46 | ||||
-rw-r--r-- | mod/mod-build-configs.cxx | 14 | ||||
-rw-r--r-- | mod/mod-build-force.cxx | 2 | ||||
-rw-r--r-- | mod/mod-build-log.cxx | 2 | ||||
-rw-r--r-- | mod/mod-build-result.cxx | 2 | ||||
-rw-r--r-- | mod/mod-build-task.cxx | 5 | ||||
-rw-r--r-- | mod/mod-builds.cxx | 2 | ||||
-rw-r--r-- | mod/mod-ci.cxx | 64 | ||||
-rw-r--r-- | mod/mod-ci.hxx | 31 | ||||
-rw-r--r-- | mod/mod-package-details.cxx | 2 | ||||
-rw-r--r-- | mod/mod-repository-details.cxx | 2 | ||||
-rw-r--r-- | mod/mod-repository-root.cxx | 14 | ||||
-rw-r--r-- | mod/mod-repository-root.hxx | 2 | ||||
-rw-r--r-- | mod/module.cli | 26 | ||||
-rw-r--r-- | repositories.manifest | 10 |
29 files changed, 495 insertions, 166 deletions
@@ -1,3 +1,21 @@ +Version 0.17.0 + + * Support for auxiliary machines/configurations. + + * Support for tenant-associated service notifications. These can be used, + for example, for third-party CI UI integration (such as GitHub). + + * Support for canceling CI requests. + + * Support for custom build bots. + + * The build-toolchain-email configuration option can now be used to specify + per-toolchain values. + + * New search-description configuration option. + + * New --ignore-unresolv-tests, --ignore-unresolv-cond loader options. + Version 0.16.0 * Note: brep_build database schema migration from version 18 is unsupported. diff --git a/brep/handler/ci/ci-load.in b/brep/handler/ci/ci-load.in index b3c05f0..6029b7b 100644 --- a/brep/handler/ci/ci-load.in +++ b/brep/handler/ci/ci-load.in @@ -10,6 +10,11 @@ # brep tenant id to this value and include the resulting URL in the response # message. # +# --cancel-url <url> +# CI task canceling URL base for the response. If specified, the handler will +# append the brep tenant id to this value and include the resulting URL in +# the response message. +# # <loader-path> # Loader program (normally brep-load(1)). # @@ -36,6 +41,7 @@ shopt -s nullglob # Expand no-match globs to nothing rather than themselves. # The handler's own options. # result_url= +cancel_url= while [[ "$#" -gt 0 ]]; do case $1 in --result-url) @@ -43,6 +49,11 @@ while [[ "$#" -gt 0 ]]; do result_url="${1%/}" shift ;; + --cancel-url) + shift + cancel_url="${1%/}" + shift + ;; *) break ;; @@ -355,4 +366,11 @@ run "$loader" "${loader_options[@]}" "$loadtab" run rm -r "$data_dir" trace "CI request for '$spec' is queued$message_suffix" -exit_with_manifest 200 "CI request is queued$message_suffix" + +msg="CI request is queued$message_suffix" + +if [[ -n "$cancel_url" ]]; then + msg="$msg"$'\n'"To cancel CI request: $cancel_url=$reference&reason=" +fi + +exit_with_manifest 200 "$msg" @@ -1,6 +1,6 @@ #! /usr/bin/env bash -version=0.17.0-a.0.z +version=0.18.0-a.0.z trap 'exit 1' ERR set -o errtrace # Trap in functions. diff --git a/libbrep/package.hxx b/libbrep/package.hxx index 61477a0..76c5836 100644 --- a/libbrep/package.hxx +++ b/libbrep/package.hxx @@ -133,7 +133,7 @@ namespace brep optional<version_constraint> constraint; // Resolved dependency package. Can be NULL if the repository load was - // shallow and the package dependency could not be resolved. + // shallow or the package dependency could not be resolved. // lazy_shared_ptr<package_type> package; diff --git a/load/load.cli b/load/load.cli index 2061c26..bda186a 100644 --- a/load/load.cli +++ b/load/load.cli @@ -7,6 +7,8 @@ include <cstdint>; // uint16_t include <libbrep/types.hxx>; +include <load/options-types.hxx>; + "\section=1" "\name=brep-load" "\summary=load repositories into brep package database" @@ -57,7 +59,7 @@ class options don't detect package dependency cycles." }; - bool --ignore-unresolved-tests + bool --ignore-unresolv-tests { "Ignore tests, examples, and benchmarks package manifest entries which cannot be resolved from the main package's complement repositories, @@ -65,6 +67,16 @@ class options be removed from the main package manifests outright." } + brep::ignore_unresolved_conditional_dependencies --ignore-unresolv-cond + { + "<pkg>", + "Ignore conditional package dependencies which cannot be resolved. The + valid <pkg> values are \cb{all} and \cb{tests}. If \cb{all} is specified, + then unresolved conditional dependencies are ignored in all packages. If + \cb{tests} is specified, then unresolved conditional dependencies are + only ignored in external tests, examples, and benchmarks packages." + } + std::string --tenant { "<id>", diff --git a/load/load.cxx b/load/load.cxx index ba2da1c..2b2cd56 100644 --- a/load/load.cxx +++ b/load/load.cxx @@ -33,6 +33,7 @@ #include <libbrep/database-lock.hxx> #include <load/load-options.hxx> +#include <load/options-types.hxx> using std::cout; using std::cerr; @@ -804,8 +805,8 @@ load_packages (const options& lo, } // A non-stub package is buildable if belongs to at least one - // buildable repository (see libbrep/package.hxx for details). - // Note that if this is an external test package it will be marked as + // buildable repository (see libbrep/package.hxx for details). Note + // that if this is an external test package it will be marked as // unbuildable later (see resolve_dependencies() for details). // if (rp->buildable && !p->buildable && !p->stub ()) @@ -1206,35 +1207,25 @@ find (const lazy_shared_ptr<repository>& r, return false; } -// Resolve package regular dependencies and external tests. Make sure that the -// best matching dependency belongs to the package repositories, their -// complements, recursively, or their immediate prerequisite repositories -// (only for regular dependencies). Set the buildable flag to false for the -// resolved external tests packages. Fail if unable to resolve a regular -// dependency, unless ignore_unresolved is true in which case leave this -// dependency NULL. Fail if unable to resolve an external test, unless -// ignore_unresolved or ignore_unresolved_tests is true in which case leave -// this dependency NULL, if ignore_unresolved_tests is false, and remove the -// respective tests manifest entry otherwise. Should be called once per -// internal package. +// Try to resolve package regular dependencies and external tests. Make sure +// that the best matching dependency belongs to the package repositories, +// their complements, recursively, or their immediate prerequisite +// repositories (only for regular dependencies). Set the buildable flag to +// false for the resolved external tests packages. Leave the package member +// NULL for unresolved dependencies. // static void -resolve_dependencies (package& p, - database& db, - bool ignore_unresolved, - bool ignore_unresolved_tests) +resolve_dependencies (package& p, database& db) { using brep::dependency; using brep::dependency_alternative; using brep::dependency_alternatives; + using brep::test_dependency; // Resolve dependencies for internal packages only. // assert (p.internal ()); - if (p.dependencies.empty () && p.tests.empty ()) - return; - auto resolve = [&p, &db] (dependency& d, bool test) { // Dependency should not be resolved yet. @@ -1324,6 +1315,60 @@ resolve_dependencies (package& p, return false; }; + // Update the package state if any dependency is resolved. + // + bool update (false); + + for (dependency_alternatives& das: p.dependencies) + { + for (dependency_alternative& da: das) + { + for (dependency& d: da) + { + if (resolve (d, false /* test */)) + update = true; + } + } + } + + for (test_dependency& td: p.tests) + { + if (resolve (td, true /* test */)) + update = true; + } + + if (update) + db.update (p); +} + +// Verify that the unresolved dependencies can be ignored. +// +// Specifically, fail for an unresolved regular dependency, unless +// ignore_unresolved is true or this is a conditional dependency and either +// ignore_unresolved_cond argument is 'all' or it is 'tests' and the specified +// package is a tests, examples, or benchmarks package. Fail for an unresolved +// external test, unless ignore_unresolved or ignore_unresolved_tests is +// true. If ignore_unresolved_tests is true, then remove the unresolved tests +// entry from the package manifest. Should be called once per internal package +// after resolve_dependencies() is called for all of them. +// +static void +verify_dependencies ( + package& p, + database& db, + bool ignore_unresolved, + bool ignore_unresolved_tests, + optional<ignore_unresolved_conditional_dependencies> ignore_unresolved_cond) +{ + using brep::dependency; + using brep::dependency_alternative; + using brep::dependency_alternatives; + using brep::test_dependency; + + // Verify dependencies for internal packages only. + // + assert (p.internal ()); + auto bail = [&p] (const dependency& d, const string& what) { cerr << "error: can't resolve " << what << ' ' << d << " for the package " @@ -1334,43 +1379,74 @@ resolve_dependencies (package& p, throw failed (); }; - for (dependency_alternatives& das: p.dependencies) + if (!ignore_unresolved) { - // Practically it is enough to resolve at least one dependency alternative - // to build a package. Meanwhile here we consider an error specifying in - // the manifest file an alternative which can't be resolved, unless - // unresolved dependencies are allowed. + // There must always be a reason why a package is not buildable. // - for (dependency_alternative& da: das) + assert (p.buildable || p.unbuildable_reason); + + bool test (!p.buildable && + *p.unbuildable_reason == unbuildable_reason::test); + + for (dependency_alternatives& das: p.dependencies) { - for (dependency& d: da) + for (dependency_alternative& da: das) { - if (!resolve (d, false /* test */) && !ignore_unresolved) - bail (d, "dependency"); + for (dependency& d: da) + { + if (d.package == nullptr) + { + if (da.enable && ignore_unresolved_cond) + { + switch (*ignore_unresolved_cond) + { + case ignore_unresolved_conditional_dependencies::all: continue; + case ignore_unresolved_conditional_dependencies::tests: + { + if (test) + continue; + + break; + } + } + } + + bail (d, "dependency"); + } + } } } } - for (auto i (p.tests.begin ()); i != p.tests.end (); ) + if (!ignore_unresolved || ignore_unresolved_tests) { - brep::test_dependency& td (*i); + // Update the package state if any test dependency is erased. + // + bool update (false); - if (!resolve (td, true /* test */)) + for (auto i (p.tests.begin ()); i != p.tests.end (); ) { - if (!ignore_unresolved && !ignore_unresolved_tests) - bail (td, to_string (td.type)); + test_dependency& td (*i); - if (ignore_unresolved_tests) + if (td.package == nullptr) { - i = p.tests.erase (i); - continue; + if (!ignore_unresolved && !ignore_unresolved_tests) + bail (td, to_string (td.type)); + + if (ignore_unresolved_tests) + { + i = p.tests.erase (i); + update = true; + continue; + } } + + ++i; } - ++i; + if (update) + db.update (p); } - - db.update (p); // Update the package state. } using package_ids = vector<package_id>; @@ -1429,7 +1505,12 @@ detect_dependency_cycle (const package_id& id, for (const auto& da: das) { for (const auto& d: da) - detect_dependency_cycle (d.package.object_id (), chain, db); + { + // Skip unresolved dependencies. + // + if (d.package != nullptr) + detect_dependency_cycle (d.package.object_id (), chain, db); + } } } @@ -1955,29 +2036,36 @@ try ops.shallow ()); } - // Resolve internal packages dependencies and, unless this is a shallow - // load, make sure there are no package dependency cycles. + // Try to resolve the internal packages dependencies and verify that the + // unresolved ones can be ignored. Unless this is a shallow load, make + // sure there are no package dependency cycles. // { session s; using query = query<package>; - for (auto& p: - db.query<package> ( - query::id.tenant == tnt && - query::internal_repository.canonical_name.is_not_null ())) - resolve_dependencies (p, - db, - ops.shallow (), - ops.ignore_unresolved_tests ()); + query q (query::id.tenant == tnt && + query::internal_repository.canonical_name.is_not_null ()); + + for (auto& p: db.query<package> (q)) + resolve_dependencies (p, db); + + for (auto& p: db.query<package> (q)) + { + verify_dependencies ( + p, + db, + ops.shallow (), + ops.ignore_unresolv_tests (), + (ops.ignore_unresolv_cond_specified () + ? ops.ignore_unresolv_cond () + : optional<ignore_unresolved_conditional_dependencies> ())); + } if (!ops.shallow ()) { package_ids chain; - for (const auto& p: - db.query<package> ( - query::id.tenant == tnt && - query::internal_repository.canonical_name.is_not_null ())) + for (const auto& p: db.query<package> (q)) detect_dependency_cycle (p.id, chain, db); } } diff --git a/load/options-types.hxx b/load/options-types.hxx new file mode 100644 index 0000000..25858f0 --- /dev/null +++ b/load/options-types.hxx @@ -0,0 +1,20 @@ +// file : load/options-types.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef LOAD_OPTIONS_TYPES_HXX +#define LOAD_OPTIONS_TYPES_HXX + +#include <libbrep/types.hxx> + +namespace brep +{ + // Ignore unresolved conditional dependencies. + // + enum class ignore_unresolved_conditional_dependencies + { + all, // For all packages. + tests // Only for external tests, examples, and benchmarks packages. + }; +} + +#endif // LOAD_OPTIONS_TYPES_HXX diff --git a/load/types-parsers.cxx b/load/types-parsers.cxx index 4c4ea9d..a18330d 100644 --- a/load/types-parsers.cxx +++ b/load/types-parsers.cxx @@ -39,4 +39,22 @@ namespace cli xs = true; parse_path (x, s); } + + void parser<ignore_unresolved_conditional_dependencies>:: + parse (ignore_unresolved_conditional_dependencies& x, bool& xs, scanner& s) + { + xs = true; + const char* o (s.next ()); + + if (!s.more ()) + throw missing_value (o); + + const string v (s.next ()); + if (v == "all") + x = ignore_unresolved_conditional_dependencies::all; + else if (v == "tests") + x = ignore_unresolved_conditional_dependencies::tests; + else + throw invalid_value (o, v); + } } diff --git a/load/types-parsers.hxx b/load/types-parsers.hxx index 1d2a6c9..fcf5113 100644 --- a/load/types-parsers.hxx +++ b/load/types-parsers.hxx @@ -9,6 +9,8 @@ #include <libbrep/types.hxx> +#include <load/options-types.hxx> + namespace cli { class scanner; @@ -22,6 +24,13 @@ namespace cli static void parse (brep::path&, bool&, scanner&); }; + + template <> + struct parser<brep::ignore_unresolved_conditional_dependencies> + { + static void + parse (brep::ignore_unresolved_conditional_dependencies&, bool&, scanner&); + }; } #endif // LOAD_TYPES_PARSERS_HXX @@ -1,6 +1,6 @@ : 1 name: brep -version: 0.17.0-a.0.z +version: 0.18.0-a.0.z project: build2 summary: build2 package repository web interface license: MIT @@ -16,8 +16,8 @@ build-warning-email: builds@build2.org requires: c++14 requires: postgresql >= 9.0 requires: apache2 ; Including development files (httpd.h header, etc). -depends: * build2 >= 0.16.0- -depends: * bpkg >= 0.16.0- +depends: * build2 >= 0.16.0 +depends: * bpkg >= 0.16.0 # @@ DEP Should probably become conditional dependency. #requires: ? cli ; Only required if changing .cli files. depends: libapr1 @@ -25,13 +25,13 @@ depends: libapreq2 depends: libcmark-gfm == 0.29.0-a.4 depends: libcmark-gfm-extensions == 0.29.0-a.4 depends: libstudxml ^1.1.0-b.10 -depends: libodb [2.5.0-b.26.1 2.5.0-b.27) -depends: libodb-pgsql [2.5.0-b.26.1 2.5.0-b.27) -depends: libbutl [0.17.0-a.0.1 0.17.0-a.1) -depends: libbpkg [0.17.0-a.0.1 0.17.0-a.1) -depends: libbbot [0.17.0-a.0.1 0.17.0-a.1) -depends: libbutl.bash [0.17.0-a.0.1 0.17.0-a.1) -depends: bpkg-util [0.17.0-a.0.1 0.17.0-a.1) +depends: libodb ^2.5.0-b.27 +depends: libodb-pgsql ^2.5.0-b.27 +depends: libbutl [0.18.0-a.0.1 0.18.0-a.1) +depends: libbpkg [0.18.0-a.0.1 0.18.0-a.1) +depends: libbbot [0.18.0-a.0.1 0.18.0-a.1) +depends: libbutl.bash [0.18.0-a.0.1 0.18.0-a.1) +depends: bpkg-util [0.18.0-a.0.1 0.18.0-a.1) # This package dependens on platform-specific implementation libraries that # are (currently) not packaged and need to come from the system package diff --git a/mod/build-config-module.cxx b/mod/build-config-module.cxx index 97c9f9e..1f4ad42 100644 --- a/mod/build-config-module.cxx +++ b/mod/build-config-module.cxx @@ -148,26 +148,35 @@ namespace brep } bool build_config_module:: - belongs (const build_target_config& cfg, const char* cls) const + derived (const string& c, const char* bc) const { + if (c == bc) + return true; + + // Go through base classes. + // const map<string, string>& im (target_conf_->class_inheritance_map); - for (const string& c: cfg.classes) + for (auto i (im.find (c)); i != im.end (); ) { - if (c == cls) + const string& base (i->second); + + if (base == bc) return true; - // Go through base classes. - // - for (auto i (im.find (c)); i != im.end (); ) - { - const string& base (i->second); + i = im.find (base); + } - if (base == cls) - return true; + return false; + } - i = im.find (base); - } + bool build_config_module:: + belongs (const build_target_config& cfg, const char* cls) const + { + for (const string& c: cfg.classes) + { + if (derived (c, cls)) + return true; } return false; diff --git a/mod/build-config-module.hxx b/mod/build-config-module.hxx index c1630b0..bbbe952 100644 --- a/mod/build-config-module.hxx +++ b/mod/build-config-module.hxx @@ -54,15 +54,20 @@ namespace brep default_all_ucs); } + // Return true if a class is derived from the base class, recursively. + // + bool + derived (const string&, const char* base_class) const; + // Check if the configuration belongs to the specified class. // bool belongs (const build_target_config&, const char*) const; bool - belongs (const build_target_config& cfg, const string& cls) const + belongs (const build_target_config& cfg, const string& classes) const { - return belongs (cfg, cls.c_str ()); + return belongs (cfg, classes.c_str ()); } // Target/configuration/toolchain combination that, in particular, can be diff --git a/mod/buildfile b/mod/buildfile index ff04ae5..2d6ef39 100644 --- a/mod/buildfile +++ b/mod/buildfile @@ -39,7 +39,7 @@ mod{brep}: {hxx ixx txx cxx}{* -module-options -{$libu_src}} \ # the debugging of the notifications machinery. # cxx.poptions += -DBREP_CI_TENANT_SERVICE -cxx.poptions += -DBREP_CI_TENANT_SERVICE_UNLOADED +#cxx.poptions += -DBREP_CI_TENANT_SERVICE_UNLOADED libus{mod}: ../web/xhtml/libus{xhtml} libue{mod}: ../web/xhtml/libue{xhtml} diff --git a/mod/ci-common.cxx b/mod/ci-common.cxx index 6ce47f2..c0ef89f 100644 --- a/mod/ci-common.cxx +++ b/mod/ci-common.cxx @@ -685,12 +685,13 @@ namespace brep return r; } - void ci_start:: - abandon (const basic_mark& error, - const basic_mark&, - const basic_mark* trace, - odb::core::database& db, - tenant_service&& service) const + optional<tenant_service> ci_start:: + cancel (const basic_mark&, + const basic_mark&, + const basic_mark* trace, + odb::core::database& db, + const string& type, + const string& id) const { using namespace odb::core; @@ -701,27 +702,44 @@ namespace brep using query = query<build_tenant>; shared_ptr<build_tenant> t ( - db.query_one<build_tenant> (query::service.id == service.id && - query::service.type == service.type)); - + db.query_one<build_tenant> (query::service.id == id && + query::service.type == type)); if (t == nullptr) - { - error << "unable to find tenant for service " << service.id << ' ' - << service.type; + return nullopt; - return; - } - else if (!t->unloaded_timestamp) - { - error << "tenant " << t->id << " for service " << service.id << ' ' - << service.type << " is already loaded"; + optional<tenant_service> r (move (t->service)); + t->service = nullopt; + t->archived = true; + db.update (t); - return; - } + tr.commit (); + + if (trace != nullptr) + *trace << "CI request " << t->id << " for service " << id << ' ' << type + << " is canceled"; + + return r; + } + + bool ci_start:: + cancel (const basic_mark&, + const basic_mark&, + const basic_mark* trace, + const string& reason, + odb::core::database& db, + const string& tid) const + { + using namespace odb::core; + + assert (!transaction::has_current ()); + + transaction tr (db.begin ()); + + shared_ptr<build_tenant> t (db.find<build_tenant> (tid)); + + if (t == nullptr) + return false; - // We could probably remove the tenant from the database, but let's just - // archive it and keep for troubleshooting. - // if (!t->archived) { t->archived = true; @@ -731,7 +749,11 @@ namespace brep tr.commit (); if (trace != nullptr) - *trace << "unloaded CI request " << t->id << " for service " - << service.id << ' ' << service.type << " is abandoned"; + *trace << "CI request " << tid << " is canceled: " + << (reason.size () < 50 + ? reason + : string (reason, 0, 50) + "..."); + + return true; } } diff --git a/mod/ci-common.hxx b/mod/ci-common.hxx index f332dc2..848bca1 100644 --- a/mod/ci-common.hxx +++ b/mod/ci-common.hxx @@ -98,16 +98,32 @@ namespace brep tenant_service&&, const repository_location& repository) const; - // Abandon previously created (as unloaded) CI request. + // Cancel previously created or started CI request. Return the service + // state or nullopt if there is no tenant for such a type/id pair. // // Note: should be called out of the database transaction. // - void - abandon (const basic_mark& error, - const basic_mark& warn, - const basic_mark* trace, - odb::core::database&, - tenant_service&&) const; + optional<tenant_service> + cancel (const basic_mark& error, + const basic_mark& warn, + const basic_mark* trace, + odb::core::database&, + const string& type, + const string& id) const; + + // Cancel previously created or started CI request. Return false if there + // is no tenant for the specified tenant id. Note that the reason argument + // is only used for tracing. + // + // Note: should be called out of the database transaction. + // + bool + cancel (const basic_mark& error, + const basic_mark& warn, + const basic_mark* trace, + const string& reason, + odb::core::database&, + const string& tenant_id) const; // Helpers. // @@ -120,22 +136,6 @@ namespace brep private: shared_ptr<options::ci_start> options_; }; - - class ci_cancel - { - public: - void - init (shared_ptr<options::ci_cancel>, shared_ptr<odb::core::database>); - - // @@ TODO Archive the tenant. - // - void - cancel (/*...*/); - - private: - shared_ptr<options::ci_cancel> options_; - shared_ptr<odb::core::database> build_db_; - }; } #endif // MOD_CI_COMMON_HXX diff --git a/mod/mod-build-configs.cxx b/mod/mod-build-configs.cxx index 9282544..ce79edb 100644 --- a/mod/mod-build-configs.cxx +++ b/mod/mod-build-configs.cxx @@ -30,8 +30,6 @@ build_configs (const build_configs& r) void brep::build_configs:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared<options::build_configs> ( s, unknown_mode::fail, unknown_mode::fail); @@ -127,19 +125,19 @@ handle (request& rq, response& rs) s << DIV(ID="filter-heading") << "Build Configuration Classes" << ~DIV << P(ID="filter"); + bool printed (false); for (auto b (cls.begin ()), i (b), e (cls.end ()); i != e; ++i) { - // Skip the 'hidden' class. + // Skip the hidden classes. // const string& c (*i); - if (c != "hidden") + if (!derived (c, "hidden")) { - // Note that here we rely on the fact that the first class in the list - // can never be 'hidden' (is always 'all'). - // - if (i != b) + if (printed) s << ' '; + else + printed = true; print_class_name (c, c == selected_class); diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx index 168a835..ea921e9 100644 --- a/mod/mod-build-force.cxx +++ b/mod/mod-build-force.cxx @@ -42,8 +42,6 @@ build_force (const build_force& r, const tenant_service_map& tsm) void brep::build_force:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared<options::build_force> ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx index c8e803b..5487f6e 100644 --- a/mod/mod-build-log.cxx +++ b/mod/mod-build-log.cxx @@ -34,8 +34,6 @@ build_log (const build_log& r) void brep::build_log:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared<options::build_log> ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx index 64503aa..3ba18e1 100644 --- a/mod/mod-build-result.cxx +++ b/mod/mod-build-result.cxx @@ -49,8 +49,6 @@ build_result (const build_result& r, const tenant_service_map& tsm) void brep::build_result:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared<options::build_result> ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx index 6be77f6..06ba4f8 100644 --- a/mod/mod-build-task.cxx +++ b/mod/mod-build-task.cxx @@ -2263,6 +2263,11 @@ handle (request& rq, response& rs) // fingerprint and challenge and reset the task manifest and the // session that we may have prepared. // + if (task_build != nullptr) + b = move (task_build); + + assert (b != nullptr); // Wouldn't be here otherwise. + agent_fp = move (b->agent_fingerprint); challenge = move (b->agent_challenge); task_response = task_response_manifest (); diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx index 30562f3..81d4649 100644 --- a/mod/mod-builds.cxx +++ b/mod/mod-builds.cxx @@ -50,8 +50,6 @@ builds (const builds& r) void brep::builds:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared<options::builds> ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-ci.cxx b/mod/mod-ci.cxx index 0045002..8c47bc4 100644 --- a/mod/mod-ci.cxx +++ b/mod/mod-ci.cxx @@ -22,6 +22,8 @@ using namespace butl; using namespace web; using namespace brep::cli; +// ci +// #ifdef BREP_CI_TENANT_SERVICE brep::ci:: ci (tenant_service_map& tsm) @@ -536,3 +538,65 @@ build_unloaded (tenant_service&& ts, } #endif #endif + +// ci_cancel +// +brep::ci_cancel:: +ci_cancel (const ci_cancel& r) + : database_module (r), + options_ (r.initialized_ ? r.options_ : nullptr) +{ +} + +void brep::ci_cancel:: +init (scanner& s) +{ + options_ = make_shared<options::ci_cancel> ( + s, unknown_mode::fail, unknown_mode::fail); + + if (options_->build_config_specified ()) + database_module::init (*options_, options_->build_db_retry ()); +} + +bool brep::ci_cancel:: +handle (request& rq, response& rs) +{ + HANDLER_DIAG; + + if (build_db_ == nullptr) + throw invalid_request (501, "not implemented"); + + params::ci_cancel params; + + try + { + name_value_scanner s (rq.parameters (1024)); + params = params::ci_cancel (s, unknown_mode::fail, unknown_mode::fail); + } + catch (const cli::exception& e) + { + throw invalid_request (400, e.what ()); + } + + const string& reason (params.reason ()); + + if (reason.empty ()) + throw invalid_request (400, "missing CI request cancellation reason"); + + // Verify the tenant id. + // + const string tid (params.id ()); + + if (tid.empty ()) + throw invalid_request (400, "invalid CI request id"); + + if (!cancel (error, warn, verb_ ? &trace : nullptr, reason, *build_db_, tid)) + throw invalid_request (400, "unknown CI request id"); + + // We have all the data, so don't buffer the response content. + // + ostream& os (rs.content (200, "text/plain;charset=utf-8", false)); + os << "CI request " << tid << " has been canceled"; + + return true; +} diff --git a/mod/mod-ci.hxx b/mod/mod-ci.hxx index a83b9d3..bd91e99 100644 --- a/mod/mod-ci.hxx +++ b/mod/mod-ci.hxx @@ -16,6 +16,7 @@ #include <mod/module-options.hxx> #include <mod/ci-common.hxx> +#include <mod/database-module.hxx> #if defined(BREP_CI_TENANT_SERVICE_UNLOADED) && !defined(BREP_CI_TENANT_SERVICE) # error BREP_CI_TENANT_SERVICE must be defined if BREP_CI_TENANT_SERVICE_UNLOADED is defined @@ -23,10 +24,6 @@ #ifdef BREP_CI_TENANT_SERVICE # include <mod/tenant-service.hxx> - -#ifdef BREP_CI_TENANT_SERVICE_UNLOADED -# include <mod/database-module.hxx> -#endif #endif namespace brep @@ -110,6 +107,32 @@ namespace brep tenant_service_map& tenant_service_map_; #endif }; + + class ci_cancel: public database_module, + private ci_start + { + public: + ci_cancel () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + ci_cancel (const ci_cancel&); + + virtual bool + handle (request&, response&) override; + + virtual const cli::options& + cli_options () const override {return options::ci_cancel::description ();} + + private: + virtual void + init (cli::scanner&) override; + + private: + shared_ptr<options::ci_cancel> options_; + }; } #endif // MOD_MOD_CI_HXX diff --git a/mod/mod-package-details.cxx b/mod/mod-package-details.cxx index fcd50da..1fb51da 100644 --- a/mod/mod-package-details.cxx +++ b/mod/mod-package-details.cxx @@ -37,8 +37,6 @@ package_details (const package_details& r) void brep::package_details:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared<options::package_details> ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-repository-details.cxx b/mod/mod-repository-details.cxx index 082903b..93b6c9e 100644 --- a/mod/mod-repository-details.cxx +++ b/mod/mod-repository-details.cxx @@ -39,8 +39,6 @@ repository_details (const repository_details& r) void brep::repository_details:: init (scanner& s) { - HANDLER_DIAG; - options_ = make_shared<options::repository_details> ( s, unknown_mode::fail, unknown_mode::fail); diff --git a/mod/mod-repository-root.cxx b/mod/mod-repository-root.cxx index 34b4007..bc861a8 100644 --- a/mod/mod-repository-root.cxx +++ b/mod/mod-repository-root.cxx @@ -133,6 +133,7 @@ namespace brep #else ci_ (make_shared<ci> ()), #endif + ci_cancel_ (make_shared<ci_cancel> ()), upload_ (make_shared<upload> ()) { } @@ -201,6 +202,10 @@ namespace brep #else : make_shared<ci> (*r.ci_)), #endif + ci_cancel_ ( + r.initialized_ + ? r.ci_cancel_ + : make_shared<ci_cancel> (*r.ci_cancel_)), upload_ ( r.initialized_ ? r.upload_ @@ -231,6 +236,7 @@ namespace brep append (r, build_configs_->options ()); append (r, submit_->options ()); append (r, ci_->options ()); + append (r, ci_cancel_->options ()); append (r, upload_->options ()); return r; } @@ -277,6 +283,7 @@ namespace brep sub_init (*build_configs_, "build_configs"); sub_init (*submit_, "submit"); sub_init (*ci_, "ci"); + sub_init (*ci_cancel_, "ci-cancel"); sub_init (*upload_, "upload"); // Parse own configuration options. @@ -473,6 +480,13 @@ namespace brep return handle ("ci", param); } + else if (func == "ci-cancel") + { + if (handler_ == nullptr) + handler_.reset (new ci_cancel (*ci_cancel_)); + + return handle ("ci-cancel", param); + } else if (func == "upload") { if (handler_ == nullptr) diff --git a/mod/mod-repository-root.hxx b/mod/mod-repository-root.hxx index aa60fda..990587e 100644 --- a/mod/mod-repository-root.hxx +++ b/mod/mod-repository-root.hxx @@ -25,6 +25,7 @@ namespace brep class build_configs; class submit; class ci; + class ci_cancel; class upload; class repository_root: public handler @@ -74,6 +75,7 @@ namespace brep shared_ptr<build_configs> build_configs_; shared_ptr<submit> submit_; shared_ptr<ci> ci_; + shared_ptr<ci_cancel> ci_cancel_; shared_ptr<upload> upload_; shared_ptr<options::repository_root> options_; diff --git a/mod/module.cli b/mod/module.cli index 5f63930..5133935 100644 --- a/mod/module.cli +++ b/mod/module.cli @@ -796,10 +796,6 @@ namespace brep } }; - class ci_cancel - { - }; - class ci: ci_start, build, build_db, page, repository_url, handler { // Classic CI-specific options. @@ -815,7 +811,11 @@ namespace brep } }; - class ci_github: ci_start, ci_cancel, build, build_db, handler + class ci_cancel: build, build_db, handler + { + }; + + class ci_github: ci_start, build, build_db, handler { // GitHub CI-specific options (e.g., request timeout when invoking // GitHub APIs). @@ -1099,6 +1099,22 @@ namespace brep string simulate; }; + // All parameters are non-optional. + // + class ci_cancel + { + // CI task tenant id. + // + // Note that the ci-cancel parameter is renamed to '_' by the root + // handler (see the request_proxy class for details). + // + string id | _; + + // CI task canceling reason. Must not be empty. + // + string reason; + }; + // Parameters other than challenge must be all present. // // Note also that besides these parameters there can be others. We don't diff --git a/repositories.manifest b/repositories.manifest index faed09f..e760afd 100644 --- a/repositories.manifest +++ b/repositories.manifest @@ -3,23 +3,23 @@ summary: build2 package repository web interface repository : role: prerequisite -location: ../libbutl.git##HEAD +location: ../libbutl.git#HEAD : role: prerequisite -location: ../libbpkg.git##HEAD +location: ../libbpkg.git#HEAD : role: prerequisite -location: ../libbbot.git##HEAD +location: ../libbbot.git#HEAD : role: prerequisite -location: ../libbutl.bash.git##HEAD +location: ../libbutl.bash.git#HEAD : role: prerequisite -location: ../bpkg-util.git##HEAD +location: ../bpkg-util.git#HEAD : role: prerequisite |