aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS18
-rw-r--r--brep/handler/ci/ci-load.in20
-rwxr-xr-xdoc/cli.sh2
-rw-r--r--libbrep/package.hxx2
-rw-r--r--load/load.cli14
-rw-r--r--load/load.cxx198
-rw-r--r--load/options-types.hxx20
-rw-r--r--load/types-parsers.cxx18
-rw-r--r--load/types-parsers.hxx9
-rw-r--r--manifest20
-rw-r--r--mod/build-config-module.cxx33
-rw-r--r--mod/build-config-module.hxx9
-rw-r--r--mod/buildfile2
-rw-r--r--mod/ci-common.cxx72
-rw-r--r--mod/ci-common.hxx46
-rw-r--r--mod/mod-build-configs.cxx14
-rw-r--r--mod/mod-build-force.cxx2
-rw-r--r--mod/mod-build-log.cxx2
-rw-r--r--mod/mod-build-result.cxx2
-rw-r--r--mod/mod-build-task.cxx5
-rw-r--r--mod/mod-builds.cxx2
-rw-r--r--mod/mod-ci.cxx64
-rw-r--r--mod/mod-ci.hxx31
-rw-r--r--mod/mod-package-details.cxx2
-rw-r--r--mod/mod-repository-details.cxx2
-rw-r--r--mod/mod-repository-root.cxx14
-rw-r--r--mod/mod-repository-root.hxx2
-rw-r--r--mod/module.cli26
-rw-r--r--repositories.manifest10
29 files changed, 495 insertions, 166 deletions
diff --git a/NEWS b/NEWS
index 5382c22..2c3f631 100644
--- a/NEWS
+++ b/NEWS
@@ -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"
diff --git a/doc/cli.sh b/doc/cli.sh
index 3c23f49..773fe1c 100755
--- a/doc/cli.sh
+++ b/doc/cli.sh
@@ -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
diff --git a/manifest b/manifest
index 0dea967..720f35e 100644
--- a/manifest
+++ b/manifest
@@ -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