// file : mod/build.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include #include #include #include #include #include #include #include #include namespace brep { using namespace std; using namespace web; 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 + tenant_dir (root, b.tenant).representation () + mime_url_encode (b.package_name.string (), false) + '/' + b.package_version.string () + "/log/" + mime_url_encode (b.target.string (), false /* query */) + '/' + mime_url_encode (b.target_config_name, false /* query */) + '/' + mime_url_encode (b.package_config_name, false /* query */) + '/' + mime_url_encode (b.toolchain_name, false /* query */) + '/' + b.toolchain_version.string ()); if (op != nullptr) { url += '/'; url += *op; } return url; } string build_force_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 + tenant_dir (root, b.tenant).string () + "?build-force&pn=" + mime_url_encode (b.package_name.string ()) + "&pv=" + b.package_version.string () + "&tg=" + mime_url_encode (b.target.string ()) + "&tc=" + mime_url_encode (b.target_config_name) + "&pc=" + mime_url_encode (b.package_config_name) + "&tn=" + mime_url_encode (b.toolchain_name) + "&tv=" + b.toolchain_version.string () + "&reason="; } void send_notification_email (const options::build_email_notification& o, const odb::core::connection_ptr& conn, const build& b, const build_package& p, const build_package_config& pc, const string& what, const basic_mark& error, const basic_mark* trace) { using namespace odb::core; using namespace butl; assert (b.state == build_state::built && b.status); // Bail out if sending build notification emails is disabled for this // toolchain for this package. // { const map& tes (o.build_toolchain_email ()); auto i (tes.find (b.id.toolchain_name)); build_email mode (i != tes.end () ? i->second : build_email::latest); if (mode == build_email::none) { return; } else if (mode == build_email::latest) { transaction t (conn->begin ()); database& db (t.database ()); const auto& id (query::build_package::id); buildable_package lp ( db.query_value ( (id.tenant == b.tenant && id.name == b.package_name) + order_by_version_desc (id.version) + "LIMIT 1")); t.commit (); if (lp.package->version != p.version) return; } } string subj (what + ' ' + to_string (*b.status) + ": " + b.package_name.string () + '/' + b.package_version.string () + ' ' + b.target_config_name + '/' + b.target.string () + ' ' + b.package_config_name + ' ' + b.toolchain_name + '-' + b.toolchain_version.string ()); // Send notification emails to the interested parties. // auto send_email = [&b, &subj, &o, &error, trace] (const string& to) { try { if (trace != nullptr) *trace << "email '" << subj << "' to " << to; // Redirect the diagnostics to webserver error log. // sendmail sm ([trace] (const char* args[], size_t n) { if (trace != nullptr) *trace << process_args {args, n}; }, 2, o.email (), subj, {to}); if (b.results.empty ()) { sm.out << "No operation results available." << endl; } else { const string& host (o.host ()); const dir_path& root (o.root ()); ostream& os (sm.out); os << "combined: " << *b.status << endl << endl << " " << build_log_url (host, root, b) << endl << endl; for (const auto& r: b.results) os << r.operation << ": " << r.status << endl << endl << " " << build_log_url (host, root, b, &r.operation) << endl << endl; os << "Force rebuild (enter the reason, use '+' instead of spaces):" << endl << endl << " " << build_force_url (host, root, b) << endl; } sm.out.close (); if (!sm.wait ()) error << "sendmail " << *sm.exit; } // Handle process_error and io_error (both derive from system_error). // catch (const system_error& e) { error << "sendmail error: " << e; } }; // Send the build notification email if a non-empty package build email is // specified. // if (const optional& e = pc.effective_email (p.build_email)) { if (!e->empty ()) send_email (*e); } // Send the build warning/error notification emails, if requested. // if (*b.status >= result_status::warning) { if (const optional& e = pc.effective_warning_email (p.build_warning_email)) send_email (*e); } if (*b.status >= result_status::error) { if (const optional& e = pc.effective_error_email (p.build_error_email)) send_email (*e); } } }