Bug#1119301: trixie-pu: package tango/10.0.2+dfsg1-2+deb13u1
Santiago Ruano Rincón
santiagorr at riseup.net
Tue Oct 28 21:14:37 GMT 2025
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: tango at packages.debian.org, 1118207-submitter at bugs.debian.org, 1118207 at bugs.debian.org
Control: affects -1 + src:tango
User: release.debian.org at packages.debian.org
Usertags: pu
Dear Release Team,
I hereby would like to request a trixie point update for tango,
specifically to fix an important issue that is breaking communication
between different versions of tango, as shipped in Debian bookworm and
trixie. The fix has to be done on trixie's.
[ Reason ]
In Debian, the bug mentioned above is #1118207. Upstream released 10.0.3
to fix it, relating to two different but dependent issues:
- https://gitlab.com/tango-controls/cppTango/-/issues/1524
- https://gitlab.com/tango-controls/cppTango/-/issues/1533
[ Impact ]
Tango environments including bookworm and trixie machines will remain
unable to communicate among them.
[ Tests ]
Upstream patches include test suites, that are not run during the
regular package building target, because the test suite relates on
docker and an accessible mariadb DB. Which is complex (and actually
not allowed in the debian buildd because of external network access by
docker) for the Debian build infra, but I have run it on a local
environment:
...
[ 24%] Building CXX object lib/cpp/tests/CMakeFiles/Catch2Tests.dir/catch2_event_reconnection.cpp.o
cd /root/tango-10.0.2+dfsg1/obj-x86_64-linux-gnu/lib/cpp/tests && /usr/bin/c++ -DTANGO_HAS_FROM_CHARS_DOUBLE -DTANGO_TEST_CATCH2_DEFAULT_POLL_PERIOD=100 -DTANGO_TEST_CATCH2_FILEDB_DIRECTORY_PATH=\"/root/tango-10.0.2+dfsg1/obj-x86_64-linux-gnu/lib/cpp/tests/catch2_test_filedb\" -DTANGO_TEST_CATCH2_LOG_DIRECTORY_PATH=\"/root/tango-10.0.2+dfsg1/obj-x86_64-linux-gnu/lib/cpp/tests/catch2_test_logs\" -DTANGO_TEST_CATCH2_OUTPUT_DIRECTORY_PATH=\"/root/tango-10.0.2+dfsg1/obj-x86_64-linux-gnu/lib/cpp/tests/catch2_server_output\" -DTANGO_TEST_CATCH2_RESOURCE_PATH=\"/root/tango-10.0.2+dfsg1/lib/cpp/tests/resources\" -DTANGO_TEST_CATCH2_SERVER_BINARY_NAME=\"TestServer\" -DTANGO_TEST_CATCH2_SERVER_BINARY_PATH=\"/root/tango-10.0.2+dfsg1/obj-x86_64-linux-gnu/lib/cpp/tests/TestServer\" -DTANGO_TEST_CATCH2_TEST_BINARY_NAME=\"Catch2Tests\" -D_FORTIFY_SOURCE=2 -D_REENTRANT -I/root/tango-10.0.2+dfsg1/lib/cpp/tests/catch2 -I/root/tango-10.0.2+dfsg1/lib/cpp/src/include -I/root/tango-10.0.2+dfsg1/lib/cpp/log4tango/include -I/root/tango-10.0.2+dfsg1/obj-x86_64-linux-gnu/lib/cpp/src/include -I/root/tango-10.0.2+dfsg1/obj-x86_64-linux-gnu/lib/cpp/log4tango/include -g -O2 -ffile-prefix-map=/root/tango-10.0.2+dfsg1=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -Wdate-time -D_FORTIFY_SOURCE=2 -O3 -DNDEBUG -std=c++17 -fstack-protector-strong -MD -MT lib/cpp/tests/CMakeFiles/Catch2Tests.dir/catch2_event_reconnection.cpp.o -MF CMakeFiles/Catch2Tests.dir/catch2_event_reconnection.cpp.o.d -o CMakeFiles/Catch2Tests.dir/catch2_event_reconnection.cpp.o -c /root/tango-10.0.2+dfsg1/lib/cpp/tests/catch2_event_reconnection.cpp
...
autopkgtest successfully pass on Salsa CI:
https://salsa.debian.org/science-team/tango/-/pipelines/964593
The main reverse dependency of tango is pytango, that successfully
builds, as it is visible in the same Salsa CI pipeline.
pytango autopkgtests have been checked on Debusine:
https://debusine.debian.net/debian/developers/work-request/213572/
[ Risks ]
I consider the risks are minimal. Upstream developers have tried to
create the minimal changes to fix this issue.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in (old)stable
[x] the issue is verified as fixed in unstable
[ Changes ]
The main change is explained above. The other include change is making
sure that Salsa CI correctly works for the package on trixie.
[ Other info ]
N/A.
-------------- next part --------------
diff -Nru tango-10.0.2+dfsg1/debian/changelog tango-10.0.2+dfsg1/debian/changelog
--- tango-10.0.2+dfsg1/debian/changelog 2025-05-15 12:27:43.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/changelog 2025-10-28 11:49:21.000000000 -0300
@@ -1,3 +1,17 @@
+tango (10.0.2+dfsg1-2+deb13u1) trixie; urgency=medium
+
+ * Team upload.
+ * Fix broken communication between major versions: libtango9 cannot receive
+ events from libtango10 (Closes: #1118207)
+ * d/gitlab-ci.yml (Salsa CI):
+ - Set RELEASE to trixie in d/gitlab-ci.yml to explicitly trigger
+ trixie-based pipelines.
+ - Disable the reprotest job. Releases older than unstable are not very
+ well supported by the Salsa CI's reprotest job, and this failing without
+ a good reason.
+
+ -- Santiago Ruano Rinc?n <santiago at freexian.com> Tue, 28 Oct 2025 11:49:21 -0300
+
tango (10.0.2+dfsg1-2) unstable; urgency=low
* Team upload.
diff -Nru tango-10.0.2+dfsg1/debian/gitlab-ci.yml tango-10.0.2+dfsg1/debian/gitlab-ci.yml
--- tango-10.0.2+dfsg1/debian/gitlab-ci.yml 2025-02-10 18:07:39.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/gitlab-ci.yml 2025-10-28 11:46:18.000000000 -0300
@@ -1,3 +1,7 @@
include:
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
+
+variables:
+ RELEASE: 'trixie'
+ SALSA_CI_DISABLE_REPROTEST: 'true'
diff -Nru tango-10.0.2+dfsg1/debian/patches/0001-catch2_event_old_client-Add-test-to-catch-duplicate-.patch tango-10.0.2+dfsg1/debian/patches/0001-catch2_event_old_client-Add-test-to-catch-duplicate-.patch
--- tango-10.0.2+dfsg1/debian/patches/0001-catch2_event_old_client-Add-test-to-catch-duplicate-.patch 1969-12-31 21:00:00.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/patches/0001-catch2_event_old_client-Add-test-to-catch-duplicate-.patch 2025-10-27 10:47:24.000000000 -0300
@@ -0,0 +1,422 @@
+From 92e8c8ec490628d75052c6d8029e7107148de6d4 Mon Sep 17 00:00:00 2001
+From: Thomas Ives <tri at observatorysciences.co.uk>
+Date: Thu, 28 Aug 2025 15:06:03 +0100
+Subject: [PATCH 1/5] catch2_event_old_client: Add test to catch duplicate
+ event issue
+
+This test reproduces the issue described in #1521.
+
+There are three test cases added here which cover different places where
+we have the duplicate events issue.
+
+The first test case covers the case where events are being pushed
+manually both with and without change detection. This finds the
+duplicate event issue in the
+`ZmqEventSupplier::detect_and_push_<x>_event` methods along with the
+`ZmqEventSupplier::push_event_loop` method.
+
+The second test case covers events sent by the polling loop, this also
+triggers the issue with the
+`ZmqEventSupplier::detect_and_push_<x>_event` and is included mostly so
+that we can check periodic events work.
+
+The final test case covers attribute configuration events, which
+triggers the duplicate event issue with the
+`EventSupplier::push_att_config_event` method.
+---
+ tests/CMakeLists.txt | 1 +
+ tests/catch2_event_old_client.cpp | 374 ++++++++++++++++++++++++++++++
+ 2 files changed, 375 insertions(+)
+ create mode 100644 tests/catch2_event_old_client.cpp
+
+Index: tango/lib/cpp/tests/catch2_event_old_client.cpp
+===================================================================
+--- /dev/null
++++ tango/lib/cpp/tests/catch2_event_old_client.cpp
+@@ -0,0 +1,374 @@
++#include "catch2_common.h"
++
++#include "tango/internal/utils.h"
++
++#include <memory>
++
++using CallbackMockType = TangoTest::CallbackMock<Tango::EventData>;
++
++constexpr const char *k_attr_name = "basicAttr";
++constexpr const char *k_no_detect_attr_name = "noDetectAttr";
++constexpr static Tango::DevLong k_polling_period = TANGO_TEST_CATCH2_DEFAULT_POLL_PERIOD;
++
++void simulate_old_clients_subscription(Tango::DeviceProxy *admin,
++ Tango::EventType ev_type,
++ int idlver,
++ std::string_view device_name,
++ std::string_view attr_name)
++{
++ // Here we trick the device server into thinking that an old client has
++ // subscribed by calling ZmqEventSubscriptionChange. The order here
++ // matters, we want to subscribe to idl5 first, otherwise the duplicates
++ // will get discarded on the client side.
++ for(int oldver = idlver - 1; oldver >= 4; --oldver)
++ {
++ std::vector<std::string> request;
++ request.reserve(5);
++ request.emplace_back(device_name);
++ request.emplace_back(attr_name);
++ request.emplace_back("subscribe");
++ {
++ std::stringstream ss;
++ if(oldver >= 5)
++ {
++ ss << "idl5_";
++ }
++ ss << Tango::EventName[ev_type];
++ request.emplace_back(ss.str());
++ }
++ {
++ std::stringstream ss;
++ ss << oldver;
++ request.emplace_back(ss.str());
++ }
++ Tango::DeviceData din;
++ din << request;
++
++ REQUIRE_NOTHROW(admin->command_inout("ZmqEventSubscriptionChange", din));
++ }
++}
++
++template <typename Base>
++class DuplicatePushedEvent : public Base
++{
++ public:
++ using Base::Base;
++
++ ~DuplicatePushedEvent() override { }
++
++ void init_device() override
++ {
++ value = 0;
++ }
++
++ void read_attr(Tango::Attribute &att) override
++ {
++ att.set_value(&value);
++ }
++
++ void push_change()
++ {
++ value += 1;
++ this->push_change_event(k_attr_name, &value);
++ this->push_change_event(k_no_detect_attr_name, &value);
++ }
++
++ void push_archive()
++ {
++ value += 1;
++ this->push_archive_event(k_attr_name, &value);
++ this->push_archive_event(k_no_detect_attr_name, &value);
++ }
++
++ void push_user()
++ {
++ value += 1;
++ std::vector<std::string> filter_names;
++ std::vector<double> filter_values;
++ this->push_event(k_attr_name, filter_names, filter_values, &value);
++ this->push_event(k_no_detect_attr_name, filter_names, filter_values, &value);
++ }
++
++ static void attribute_factory(std::vector<Tango::Attr *> &attrs)
++ {
++ using Attr = TangoTest::AutoAttr<&DuplicatePushedEvent::read_attr>;
++ Tango::UserDefaultAttrProp props;
++ props.set_abs_change("1");
++ props.set_archive_abs_change("1");
++
++ auto attr = new Attr(k_attr_name, Tango::DEV_LONG);
++ attr->set_default_properties(props);
++ attr->set_change_event(true, true);
++ attr->set_archive_event(true, true);
++ attrs.push_back(attr);
++
++ auto no_detect_attr = new Attr(k_no_detect_attr_name, Tango::DEV_LONG);
++ no_detect_attr->set_change_event(true, false);
++ no_detect_attr->set_archive_event(true, false);
++ attrs.push_back(no_detect_attr);
++ }
++
++ static void command_factory(std::vector<Tango::Command *> &cmds)
++ {
++ cmds.push_back(new TangoTest::AutoCommand<&DuplicatePushedEvent::push_change>("push_change"));
++ cmds.push_back(new TangoTest::AutoCommand<&DuplicatePushedEvent::push_archive>("push_archive"));
++ cmds.push_back(new TangoTest::AutoCommand<&DuplicatePushedEvent::push_user>("push_user_event"));
++ }
++
++ private:
++ Tango::DevLong value{0};
++};
++
++TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(DuplicatePushedEvent, 6)
++
++SCENARIO("Manually pushed events are not duplicated when old clients are connected")
++{
++ int idlver = GENERATE(TangoTest::idlversion(6));
++ GIVEN("a device proxy to a simple IDLv" << idlver << " device")
++ {
++ TangoTest::Context ctx{"duplicate_event", "DuplicatePushedEvent", idlver};
++ std::shared_ptr<Tango::DeviceProxy> device = ctx.get_proxy();
++
++ REQUIRE(idlver == device->get_idl_version());
++
++ std::string attr_name = GENERATE(k_attr_name, k_no_detect_attr_name);
++ Tango::EventType ev_type = GENERATE(Tango::CHANGE_EVENT, Tango::ARCHIVE_EVENT, Tango::USER_EVENT);
++
++ WHEN("some old clients subscribe to " << Tango::EventName[ev_type] << " events for " << attr_name)
++ {
++ std::unique_ptr<Tango::DeviceProxy> admin = ctx.get_admin_proxy();
++
++ simulate_old_clients_subscription(admin.get(), ev_type, idlver, device->dev_name(), attr_name);
++
++ AND_WHEN("we subscribe to " << Tango::EventName[ev_type] << " events for " << attr_name)
++ {
++ CallbackMockType callback;
++
++ device->subscribe_event(attr_name, ev_type, &callback);
++
++ auto maybe_initial_event = callback.pop_next_event();
++ REQUIRE(maybe_initial_event != std::nullopt);
++ maybe_initial_event = callback.pop_next_event();
++
++ AND_WHEN("the server sends an single event")
++ {
++ {
++ std::stringstream ss;
++ ss << "push_" << Tango::EventName[ev_type];
++ REQUIRE_NOTHROW(device->command_inout(ss.str()));
++ }
++
++ THEN("we only receive one event")
++ {
++ using TangoTest::AnyLikeContains;
++
++ auto maybe_event = callback.pop_next_event();
++ REQUIRE(maybe_event != std::nullopt);
++ REQUIRE(maybe_event->event == Tango::EventName[ev_type]);
++ REQUIRE_THAT(*maybe_event->attr_value, AnyLikeContains(static_cast<Tango::DevLong>(1)));
++
++ maybe_event = callback.pop_next_event();
++ REQUIRE(maybe_event == std::nullopt);
++ }
++ }
++ }
++ }
++ }
++}
++
++template <typename Base>
++class DuplicatePollingEvent : public Base
++{
++ public:
++ using Base::Base;
++
++ ~DuplicatePollingEvent() override { }
++
++ void init_device() override
++ {
++ value = 0;
++ }
++
++ void read_attr(Tango::Attribute &att) override
++ {
++ value += 1;
++ att.set_value(&value);
++ }
++
++ static void attribute_factory(std::vector<Tango::Attr *> &attrs)
++ {
++ using Attr = TangoTest::AutoAttr<&DuplicatePollingEvent::read_attr>;
++ Tango::UserDefaultAttrProp props;
++ {
++ std::stringstream ss;
++ ss << k_polling_period;
++ props.set_period(ss.str().c_str());
++ }
++ props.set_abs_change("1");
++ props.set_archive_abs_change("1");
++
++ auto attr = new Attr(k_attr_name, Tango::DEV_LONG);
++ attr->set_default_properties(props);
++ attr->set_polling_period(k_polling_period);
++ attrs.push_back(attr);
++ }
++
++ private:
++ Tango::DevLong value{0};
++};
++
++TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(DuplicatePollingEvent, 6)
++
++SCENARIO("Periodic events are not duplicated when old clients are connected")
++{
++ int idlver = GENERATE(TangoTest::idlversion(6));
++ GIVEN("a device proxy to a simple IDLv" << idlver << " device")
++ {
++ TangoTest::Context ctx{"duplicate_event", "DuplicatePollingEvent", idlver};
++ std::shared_ptr<Tango::DeviceProxy> device = ctx.get_proxy();
++
++ REQUIRE(idlver == device->get_idl_version());
++
++ Tango::EventType ev_type = GENERATE(Tango::CHANGE_EVENT, Tango::ARCHIVE_EVENT, Tango::PERIODIC_EVENT);
++
++ WHEN("some old clients subscribe to " << Tango::EventName[ev_type] << " events")
++ {
++ std::unique_ptr<Tango::DeviceProxy> admin = ctx.get_admin_proxy();
++
++ simulate_old_clients_subscription(admin.get(), ev_type, idlver, device->dev_name(), k_attr_name);
++
++ AND_WHEN("we subscribe to " << Tango::EventName[ev_type] << " events")
++ {
++ CallbackMockType callback;
++
++ device->subscribe_event(k_attr_name, ev_type, &callback);
++
++ // We only require one event for the initial subscription, we
++ // don't want to consume any events from the Zmq interface.
++ // Not quite sure why this matters for polling and not when
++ // pushing. I think the de-duplicate on the client side means
++ // we only see the duplicate event for the first events that are
++ // pushed.
++ auto maybe_initial_event = callback.pop_next_event();
++ REQUIRE(maybe_initial_event != std::nullopt);
++
++ AND_WHEN("we wait for three events")
++ {
++ // We wait for three events here as if there are duplicates
++ // two of these must have the same value, but we don't
++ // know which two.
++ auto maybe_event1 = callback.pop_next_event();
++ auto maybe_event2 = callback.pop_next_event();
++ auto maybe_event3 = callback.pop_next_event();
++
++ REQUIRE(maybe_event1 != std::nullopt);
++ REQUIRE(maybe_event2 != std::nullopt);
++ REQUIRE(maybe_event3 != std::nullopt);
++
++ THEN("the events have different values")
++ {
++ Tango::DevLong value1;
++ *maybe_event1->attr_value >> value1;
++ Tango::DevLong value2;
++ *maybe_event2->attr_value >> value2;
++ Tango::DevLong value3;
++ *maybe_event3->attr_value >> value3;
++
++ REQUIRE(value1 != value2);
++ REQUIRE(value2 != value3);
++ }
++ }
++ }
++ }
++ }
++}
++
++template <typename Base>
++class DuplicateAttrConfEvent : public Base
++{
++ public:
++ using Base::Base;
++
++ ~DuplicateAttrConfEvent() override { }
++
++ void init_device() override
++ {
++ value = 0;
++ }
++
++ void read_attr(Tango::Attribute &att) override
++ {
++ value += 1;
++ att.set_value(&value);
++ }
++
++ static void attribute_factory(std::vector<Tango::Attr *> &attrs)
++ {
++ using Attr = TangoTest::AutoAttr<&DuplicateAttrConfEvent::read_attr>;
++ Tango::UserDefaultAttrProp props;
++ props.set_abs_change("1");
++
++ auto attr = new Attr(k_attr_name, Tango::DEV_LONG);
++ attr->set_default_properties(props);
++ attrs.push_back(attr);
++ }
++
++ private:
++ Tango::DevLong value{0};
++};
++
++TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(DuplicateAttrConfEvent, 6)
++
++SCENARIO("Attribute config events are not duplicated when old clients are connected")
++{
++ int idlver = GENERATE(TangoTest::idlversion(6));
++ GIVEN("a device proxy to a simple IDLv" << idlver << " device")
++ {
++ TangoTest::Context ctx{"duplicate_event", "DuplicateAttrConfEvent", idlver};
++ std::shared_ptr<Tango::DeviceProxy> device = ctx.get_proxy();
++
++ REQUIRE(idlver == device->get_idl_version());
++
++ Tango::EventType ev_type = Tango::ATTR_CONF_EVENT;
++
++ WHEN("some old clients subscribe to attr config")
++ {
++ std::unique_ptr<Tango::DeviceProxy> admin = ctx.get_admin_proxy();
++
++ simulate_old_clients_subscription(admin.get(), ev_type, idlver, device->dev_name(), k_attr_name);
++
++ AND_WHEN("we subscribe to attr config events")
++ {
++ TangoTest::CallbackMock<Tango::AttrConfEventData> callback;
++
++ device->subscribe_event(k_attr_name, ev_type, &callback);
++
++ auto maybe_initial_event = callback.pop_next_event();
++ REQUIRE(maybe_initial_event != std::nullopt);
++
++ maybe_initial_event = callback.pop_next_event();
++
++ AND_WHEN("we configure the attribute")
++ {
++ auto ai = device->attribute_query(k_attr_name);
++ ai.events.ch_event.abs_change = "Not specified";
++
++ Tango::AttributeInfoListEx ail;
++ ail.push_back(ai);
++ REQUIRE_NOTHROW(device->set_attribute_config(ail));
++
++ THEN("we only get a single attr conf event")
++ {
++ auto maybe_event = callback.pop_next_event();
++ REQUIRE(maybe_event != std::nullopt);
++ REQUIRE(maybe_event->event == Tango::EventName[ev_type]);
++ REQUIRE(maybe_event->attr_conf != nullptr);
++
++ maybe_event = callback.pop_next_event();
++ REQUIRE(maybe_event == std::nullopt);
++ }
++ }
++ }
++ }
++ }
++}
+Index: tango/lib/cpp/tests/CMakeLists.txt
+===================================================================
+--- tango.orig/lib/cpp/tests/CMakeLists.txt
++++ tango/lib/cpp/tests/CMakeLists.txt
+@@ -312,6 +312,7 @@ tango_catch2_tests_create(
+ catch2_attr_polling.cpp
+ catch2_cmd_polling.cpp
+ catch2_connection.cpp
++ catch2_event_old_client.cpp
+ catch2_event_reconnection.cpp
+ catch2_test_dtypes.cpp
+ catch2_dev_state.cpp
diff -Nru tango-10.0.2+dfsg1/debian/patches/0001-EventConsumerKeepAliveThread-send-actual-IDL-when-ex.patch tango-10.0.2+dfsg1/debian/patches/0001-EventConsumerKeepAliveThread-send-actual-IDL-when-ex.patch
--- tango-10.0.2+dfsg1/debian/patches/0001-EventConsumerKeepAliveThread-send-actual-IDL-when-ex.patch 1969-12-31 21:00:00.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/patches/0001-EventConsumerKeepAliveThread-send-actual-IDL-when-ex.patch 2025-10-27 10:47:24.000000000 -0300
@@ -0,0 +1,61 @@
+From f4ebed11ff59e7cca3ceced373e6f008959f669f Mon Sep 17 00:00:00 2001
+From: matveyev <yury.matveev at desy.de>
+Date: Mon, 11 Aug 2025 16:23:38 +0200
+Subject: [PATCH 1/2] EventConsumerKeepAliveThread: send actual IDL when
+ execute ZmqEventSubscriptionChange
+
+When we executed ZmqEventSubscriptionChange we sent as client IDL 0, which means for the server, "Guess my IDL", as it was introduced in 01ffb8f4.
+
+After IDL6 release, due to IDL6 devices subscribing to ...idl5_... events, they were re-subscribed as IDL5 devices.
+
+Now we send the actual maximum IDL, supported by the client as we do at the subscription process
+---
+ src/client/eventkeepalive.cpp | 18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+diff --git a/lib/cpp/src/client/eventkeepalive.cpp b/lib/cpp/src/client/eventkeepalive.cpp
+index 487cdddd3..824adb2e3 100644
+--- a/lib/cpp/src/client/eventkeepalive.cpp
++++ b/lib/cpp/src/client/eventkeepalive.cpp
+@@ -200,7 +200,11 @@ bool EventConsumerKeepAliveThread::reconnect_to_zmq_channel(const EvChanIte &ipo
+ subscriber_info.push_back(epos->second.obj_name);
+ subscriber_info.emplace_back("subscribe");
+ subscriber_info.push_back(epos->second.event_name);
+- subscriber_info.emplace_back("0");
++
++ // maximum IDL version which client supports
++ std::stringstream ss;
++ ss << DevVersion;
++ subscriber_info.push_back(ss.str());
+
+ subscriber_in << subscriber_info;
+
+@@ -1039,7 +1043,12 @@ void EventConsumerKeepAliveThread::confirm_subscription(ZmqEventConsumer *event_
+ subscriber_info.push_back(cmd_params[(loop * 3) + 1]);
+ subscriber_info.emplace_back("subscribe");
+ subscriber_info.push_back(cmd_params[(loop * 3) + 2]);
+- subscriber_info.emplace_back("0");
++
++ // maximum IDL version which client supports
++ std::stringstream ss;
++ ss << DevVersion;
++ subscriber_info.push_back(ss.str());
++
+ subscriber_in << subscriber_info;
+
+ try
+@@ -1470,7 +1479,10 @@ void EventConsumerKeepAliveThread::re_subscribe_after_reconnect(
+ subscriber_info.push_back(epos->second.event_name);
+ if(ipos->second.channel_type == ZMQ)
+ {
+- subscriber_info.emplace_back("0");
++ // maximum IDL version which client supports
++ std::stringstream ss;
++ ss << DevVersion;
++ subscriber_info.push_back(ss.str());
+ }
+ subscriber_in << subscriber_info;
+
+--
+2.39.5
+
diff -Nru tango-10.0.2+dfsg1/debian/patches/0002-Catch2-tests-add-Event-re-subscribes-with-the-same-I.patch tango-10.0.2+dfsg1/debian/patches/0002-Catch2-tests-add-Event-re-subscribes-with-the-same-I.patch
--- tango-10.0.2+dfsg1/debian/patches/0002-Catch2-tests-add-Event-re-subscribes-with-the-same-I.patch 1969-12-31 21:00:00.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/patches/0002-Catch2-tests-add-Event-re-subscribes-with-the-same-I.patch 2025-10-27 10:47:24.000000000 -0300
@@ -0,0 +1,213 @@
+From e4effb6d670aeb2a02e174691aef36ca821c530c Mon Sep 17 00:00:00 2001
+From: matveyev <yury.matveev at desy.de>
+Date: Mon, 11 Aug 2025 16:56:28 +0200
+Subject: [PATCH 2/2] Catch2 tests: add "Event re-subscribes with the same IDL"
+ test
+
+In test we check, that after server restart EventConsumerKeepAliveThread re-subscribe all events to the same maximum supported by client IDL
+
+Test does analysis of debug printouts, due to I have not found any other working solution to check, which IDL was requested
+---
+ tests/CMakeLists.txt | 1 +
+ tests/catch2_event_reconnection.cpp | 177 ++++++++++++++++++++++++++++
+ 2 files changed, 178 insertions(+)
+ create mode 100644 tests/catch2_event_reconnection.cpp
+
+diff --git a/lib/cpp/tests/CMakeLists.txt b/lib/cpp/tests/CMakeLists.txt
+index cf60db7f5..d40ef041b 100644
+--- a/lib/cpp/tests/CMakeLists.txt
++++ b/lib/cpp/tests/CMakeLists.txt
+@@ -312,6 +312,7 @@ tango_catch2_tests_create(
+ catch2_attr_polling.cpp
+ catch2_cmd_polling.cpp
+ catch2_connection.cpp
++ catch2_event_reconnection.cpp
+ catch2_test_dtypes.cpp
+ catch2_dev_state.cpp
+ catch2_internal_utils.cpp
+diff --git a/lib/cpp/tests/catch2_event_reconnection.cpp b/lib/cpp/tests/catch2_event_reconnection.cpp
+new file mode 100644
+index 000000000..d80064ff9
+--- /dev/null
++++ b/lib/cpp/tests/catch2_event_reconnection.cpp
+@@ -0,0 +1,177 @@
++#include "catch2_common.h"
++#include <regex>
++
++#include <tango/internal/utils.h>
++
++namespace
++{
++const std::string TestExceptReason = "Ahhh!";
++const Tango::DevShort k_inital_short = 5678;
++using CallbackMockType = TangoTest::CallbackMock<Tango::EventData>;
++
++} // anonymous namespace
++
++template <class Base>
++class SimpleEventDevice : public Base
++{
++ public:
++ using Base::Base;
++
++ void init_device() override { }
++
++ void push_change_event(Tango::DevString attr)
++ {
++ if(!strcmp(attr, "Short_attr"))
++ {
++ Base::push_change_event(attr, &short_value);
++ }
++ else
++ {
++ TANGO_THROW_EXCEPTION(TestExceptReason, "This is a test");
++ }
++ }
++
++ void read_attribute(Tango::Attribute &att)
++ {
++ if(att.get_name() == "Short_attr")
++ {
++ att.set_value(&short_value);
++ }
++ else
++ {
++ TANGO_THROW_EXCEPTION(TestExceptReason, "This is a test");
++ }
++ }
++
++ static void attribute_factory(std::vector<Tango::Attr *> &attrs)
++ {
++ auto short_attr = new TangoTest::AutoAttr<&SimpleEventDevice::read_attribute>("Short_attr", Tango::DEV_SHORT);
++ short_attr->set_change_event(true, false);
++ attrs.push_back(short_attr);
++ }
++
++ static void command_factory(std::vector<Tango::Command *> &cmds)
++ {
++ cmds.push_back(new TangoTest::AutoCommand<&SimpleEventDevice::push_change_event>("PushChangeEvent"));
++ }
++
++ private:
++ Tango::DevShort short_value{k_inital_short};
++};
++
++/* Returns true if all occurrences of
++ * Attribute::set_client_lib(N,change)
++ * in 'input' use the same N
++ */
++bool checkSameClientLib(const std::string &input)
++{
++ std::regex re(R"(Attribute::set_client_lib\(([0-9]+),change\))");
++ std::smatch match;
++ std::string::const_iterator it = input.cbegin();
++
++ bool seenFirst = false;
++ int firstValue = Tango::detail::INVALID_IDL_VERSION;
++
++ while(std::regex_search(it, input.cend(), match, re))
++ {
++ int val = parse_as<int>(match[1].str());
++
++ if(!seenFirst)
++ {
++ firstValue = val;
++ seenFirst = true;
++ }
++ else if(val != firstValue)
++ {
++ return false;
++ }
++
++ it = match.suffix().first;
++ }
++
++ if(!seenFirst)
++ {
++ return false;
++ }
++
++ return true;
++}
++
++TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(SimpleEventDevice, 4)
++
++SCENARIO("Event re-subscribes with the same IDL", "[slow]")
++{
++ const std::string log_path = TangoTest::get_current_log_file_path();
++
++ // the previous full contents across GENERATE
++ static std::string prev_contents;
++
++ int idlver = GENERATE(TangoTest::idlversion(4));
++ GIVEN("a device proxy to a simple IDLv" << idlver << " device")
++ {
++ TangoTest::Context ctx{"event_reconnection", "SimpleEventDevice", idlver};
++ std::shared_ptr<Tango::DeviceProxy> device = ctx.get_proxy();
++
++ REQUIRE(idlver == device->get_idl_version());
++
++ AND_GIVEN("an change event subscription to that attribute")
++ {
++ TangoTest::CallbackMock<Tango::EventData> callback;
++ auto event_id = device->subscribe_event("Short_attr", Tango::CHANGE_EVENT, &callback);
++
++ auto maybe_event = callback.pop_next_event();
++ REQUIRE(maybe_event != std::nullopt);
++
++ WHEN("when we restart server")
++ {
++ ctx.stop_server();
++ ctx.restart_server();
++
++ WHEN("an change event is generated after another error event")
++ {
++ using namespace Catch::Matchers;
++
++ auto maybe_event = callback.pop_next_event(std::chrono::seconds{20});
++
++ REQUIRE(maybe_event != std::nullopt);
++ REQUIRE(maybe_event->err);
++ REQUIRE(std::string(Tango::API_EventTimeout) == maybe_event->errors[0].reason.in());
++
++ maybe_event = callback.pop_next_event(std::chrono::seconds{20});
++
++ REQUIRE(maybe_event != std::nullopt);
++ REQUIRE(maybe_event->event == Tango::EventName[Tango::CHANGE_EVENT]);
++
++ THEN("an change event is generated after another error event")
++ {
++ std::string all_server_log = load_file(log_path);
++
++ std::string new_chunk;
++ if(all_server_log.size() > prev_contents.size())
++ {
++ new_chunk = all_server_log.substr(prev_contents.size());
++ }
++ else
++ {
++ // log was truncated or rotated, then treat entire file as new
++ new_chunk = all_server_log;
++ }
++ prev_contents = std::move(all_server_log);
++
++ // sanity-check: we should actually have something new
++ REQUIRE(!new_chunk.empty());
++
++ REQUIRE(checkSameClientLib(new_chunk));
++
++ std::regex re(R"(Attribute::set_client_lib\(([0-9]+),change\))");
++ std::smatch m;
++ REQUIRE(std::regex_search(new_chunk, m, re));
++ int found = parse_as<int>(m[1].str());
++ CHECK(found == idlver);
++ }
++ }
++ }
++ device->unsubscribe_event(event_id);
++ }
++ }
++}
+--
+2.39.5
+
diff -Nru tango-10.0.2+dfsg1/debian/patches/0002-DServer-zmq_event_subscription_changed-Map-client-id.patch tango-10.0.2+dfsg1/debian/patches/0002-DServer-zmq_event_subscription_changed-Map-client-id.patch
--- tango-10.0.2+dfsg1/debian/patches/0002-DServer-zmq_event_subscription_changed-Map-client-id.patch 1969-12-31 21:00:00.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/patches/0002-DServer-zmq_event_subscription_changed-Map-client-id.patch 2025-10-27 10:40:08.000000000 -0300
@@ -0,0 +1,113 @@
+From d95abde7a0fd89cb979dae9052e3f0e7cc2be128 Mon Sep 17 00:00:00 2001
+From: Thomas Ives <tri at observatorysciences.co.uk>
+Date: Mon, 1 Sep 2025 09:11:22 +0100
+Subject: [PATCH 2/5] DServer::zmq_event_subscription_changed: Map client idl
+ versions...
+
+... to event data versions.
+
+We didn't introduce new AttributeValue data structures with IDLv6, so we
+shouldn't being sending an event for each client version selected.
+Instead, we should be sending a ZMQ event for each type of data
+structure, one that each client can use.
+---
+ src/server/eventcmds.cpp | 76 +++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 75 insertions(+), 1 deletion(-)
+
+diff --git a/lib/cpp/src/server/eventcmds.cpp b/lib/cpp/src/server/eventcmds.cpp
+index 4f26dd8a8..af96f70b7 100644
+--- a/lib/cpp/src/server/eventcmds.cpp
++++ b/lib/cpp/src/server/eventcmds.cpp
+@@ -489,6 +489,79 @@ MulticastParameters
+ return result;
+ }
+
++namespace
++{
++int zmq_client_idl_to_event_data_version(int client_lib_version, const std::string &event_name)
++{
++ TANGO_ASSERT(client_lib_version > 0);
++ TANGO_ASSERT(client_lib_version <= DevVersion);
++
++ auto *tg = Tango::Util::instance(false);
++ EventType event_type;
++ tg->event_name_2_event_type(event_name, event_type);
++
++ // The (event_type, <ret>) pair corresponds to which element on
++ // SuppliedEventData is active when we call ZmqEventSupplier::push_event.
++
++ switch(event_type)
++ {
++ case CHANGE_EVENT:
++ [[fallthrough]];
++ case ARCHIVE_EVENT:
++ [[fallthrough]];
++ case USER_EVENT:
++ [[fallthrough]];
++ case PERIODIC_EVENT:
++ if(client_lib_version >= 5)
++ {
++ // AttributeValue_5
++ return 5;
++ }
++ else if(client_lib_version == 4)
++ {
++ // AttributeValue_4
++ return 4;
++ }
++ else
++ {
++ // AttributeValue_3
++ return 3;
++ }
++ case ATTR_CONF_EVENT:
++ if(client_lib_version >= 5)
++ {
++ // AttributeConfig_5
++ return 5;
++ }
++ else
++ {
++ // AttributeConfig_3
++ return 3;
++ }
++ case ALARM_EVENT:
++ TANGO_ASSERT(client_lib_version >= 6);
++ // AttributeValue_5
++ // TODO(porting to main): Update alarm event handling so that
++ // this can be 5 (#1531).
++ return 6;
++ case DATA_READY_EVENT:
++ // AttDataReady
++ return 1;
++ case INTERFACE_CHANGE_EVENT:
++ // DevIntrChange
++ return 1;
++ case PIPE_EVENT:
++ // DevPipeData
++ return 1;
++ default:
++ TANGO_ASSERT_ON_DEFAULT(event_type);
++ }
++
++ // unreachable
++ return 0;
++}
++} // namespace
++
+ void DServer::store_subscribed_client_info(DeviceImpl &device,
+ const std::string &object_name,
+ const std::string &event_name,
+@@ -947,7 +1020,8 @@ DevVarLongStringArray *DServer::zmq_event_subscription_change(const Tango::DevVa
+ // the command fails.
+ if(action == "subscribe")
+ {
+- store_subscribed_client_info(*dev, obj_name, event, client_release);
++ int event_data_version = zmq_client_idl_to_event_data_version(client_release, event);
++ store_subscribed_client_info(*dev, obj_name, event, event_data_version);
+ }
+
+ //
+--
+2.39.5
+
diff -Nru tango-10.0.2+dfsg1/debian/patches/0003-push_att_conf_events-Use-event-data-to-determine-ver.patch tango-10.0.2+dfsg1/debian/patches/0003-push_att_conf_events-Use-event-data-to-determine-ver.patch
--- tango-10.0.2+dfsg1/debian/patches/0003-push_att_conf_events-Use-event-data-to-determine-ver.patch 1969-12-31 21:00:00.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/patches/0003-push_att_conf_events-Use-event-data-to-determine-ver.patch 2025-10-27 10:40:08.000000000 -0300
@@ -0,0 +1,42 @@
+From ebb88b7bad674cc9515765d09752ea6a518a72c7 Mon Sep 17 00:00:00 2001
+From: Thomas Ives <tri at observatorysciences.co.uk>
+Date: Mon, 1 Sep 2025 09:21:17 +0100
+Subject: [PATCH 3/5] push_att_conf_events: Use event data to determine vers
+
+Do not need to consider the version of the device pushing the event when
+deciding if we need to add the idl5_ prefix to the event name. Instead,
+the thing that matters is who the event is aimed at. This can be
+determined from the SuppliedEventData based on which pointer is not
+nullptr.
+---
+ src/server/eventsupplier.cpp | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/lib/cpp/src/server/eventsupplier.cpp b/lib/cpp/src/server/eventsupplier.cpp
+index 696370dce..e66df4a1b 100644
+--- a/lib/cpp/src/server/eventsupplier.cpp
++++ b/lib/cpp/src/server/eventsupplier.cpp
+@@ -2475,7 +2475,19 @@ void EventSupplier::push_att_conf_events(DeviceImpl *device_impl,
+ // Called for AttributeConfig_3 or AttributeConfig_5 ?
+ //
+
+- const int vers = device_impl->get_dev_idl_version();
++ int vers;
++ if(attr_conf.attr_conf_5 != nullptr)
++ {
++ vers = 5;
++ }
++ else if(attr_conf.attr_conf_3 != nullptr)
++ {
++ vers = 3;
++ }
++ else
++ {
++ vers = 2;
++ }
+
+ //
+ // Return if there is no client or if the last client subscription is more than 10 mins ago
+--
+2.39.5
+
diff -Nru tango-10.0.2+dfsg1/debian/patches/0004-catch2_event_reconnection-Use-log-from-new-mapping-f.patch tango-10.0.2+dfsg1/debian/patches/0004-catch2_event_reconnection-Use-log-from-new-mapping-f.patch
--- tango-10.0.2+dfsg1/debian/patches/0004-catch2_event_reconnection-Use-log-from-new-mapping-f.patch 1969-12-31 21:00:00.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/patches/0004-catch2_event_reconnection-Use-log-from-new-mapping-f.patch 2025-10-27 10:47:24.000000000 -0300
@@ -0,0 +1,40 @@
+From e6d9b39cf04086eb5d4cd48297a8eb59cdf84a5b Mon Sep 17 00:00:00 2001
+From: Thomas Ives <tri at observatorysciences.co.uk>
+Date: Mon, 1 Sep 2025 09:38:05 +0100
+Subject: [PATCH 4/5] catch2_event_reconnection: Use log from new mapping
+ function
+
+---
+ src/server/eventcmds.cpp | 2 ++
+ tests/catch2_event_reconnection.cpp | 2 +-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/lib/cpp/src/server/eventcmds.cpp b/lib/cpp/src/server/eventcmds.cpp
+index af96f70b7..d6bb5cffc 100644
+--- a/lib/cpp/src/server/eventcmds.cpp
++++ b/lib/cpp/src/server/eventcmds.cpp
+@@ -496,6 +496,8 @@ int zmq_client_idl_to_event_data_version(int client_lib_version, const std::stri
+ TANGO_ASSERT(client_lib_version > 0);
+ TANGO_ASSERT(client_lib_version <= DevVersion);
+
++ TANGO_LOG_DEBUG << "zmq_client_idl_to_event_data_version(" << client_lib_version << "," << event_name << ")";
++
+ auto *tg = Tango::Util::instance(false);
+ EventType event_type;
+ tg->event_name_2_event_type(event_name, event_type);
+diff --git a/lib/cpp/tests/catch2_event_reconnection.cpp b/lib/cpp/tests/catch2_event_reconnection.cpp
+index d80064ff9..2bd0a78b6 100644
+--- a/lib/cpp/tests/catch2_event_reconnection.cpp
++++ b/lib/cpp/tests/catch2_event_reconnection.cpp
+@@ -163,7 +163,7 @@ SCENARIO("Event re-subscribes with the same IDL", "[slow]")
+
+ REQUIRE(checkSameClientLib(new_chunk));
+
+- std::regex re(R"(Attribute::set_client_lib\(([0-9]+),change\))");
++ std::regex re(R"(zmq_client_idl_to_event_data_version\(([0-9]+),change\))");
+ std::smatch m;
+ REQUIRE(std::regex_search(new_chunk, m, re));
+ int found = parse_as<int>(m[1].str());
+--
+2.39.5
+
diff -Nru tango-10.0.2+dfsg1/debian/patches/0005-Attribute-set_upd_properties-Handle-no-database-case.patch tango-10.0.2+dfsg1/debian/patches/0005-Attribute-set_upd_properties-Handle-no-database-case.patch
--- tango-10.0.2+dfsg1/debian/patches/0005-Attribute-set_upd_properties-Handle-no-database-case.patch 1969-12-31 21:00:00.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/patches/0005-Attribute-set_upd_properties-Handle-no-database-case.patch 2025-10-27 10:40:08.000000000 -0300
@@ -0,0 +1,106 @@
+From 530c3bbb89c0115d58ba7bcc4dd7d15d0182e259 Mon Sep 17 00:00:00 2001
+From: Thomas Braun <thomas.braun at byte-physics.de>
+Date: Thu, 1 May 2025 14:29:28 +0200
+Subject: [PATCH 5/5] Attribute::set_upd_properties: Handle no database case
+ gracefully
+
+When trying to set the attribute configuration without database, we used
+to crash inside Attribute::db_access as the returned pointer from get_database() was not valid.
+
+We now skip the database update similiar to what is done in
+DeviceImpl::set_attribute_config.
+---
+ src/include/tango/server/attribute_templ.h | 5 ++-
+ tests/CMakeLists.txt | 1 +
+ tests/catch2_attr_conf_event.cpp | 51 ++++++++++++++++++++++
+ 3 files changed, 56 insertions(+), 1 deletion(-)
+ create mode 100644 tests/catch2_attr_conf_event.cpp
+
+diff --git a/lib/cpp/src/include/tango/server/attribute_templ.h b/lib/cpp/src/include/tango/server/attribute_templ.h
+index b05843a26..e5e98888d 100644
+--- a/lib/cpp/src/include/tango/server/attribute_templ.h
++++ b/lib/cpp/src/include/tango/server/attribute_templ.h
+@@ -1357,7 +1357,10 @@ void Attribute::set_upd_properties(const T &conf, const std::string &dev_name, b
+
+ try
+ {
+- upd_database(v_db);
++ if(Tango::Util::instance()->use_db())
++ {
++ upd_database(v_db);
++ }
+ }
+ catch(DevFailed &)
+ {
+diff --git a/lib/cpp/tests/CMakeLists.txt b/lib/cpp/tests/CMakeLists.txt
+index 64b957559..83db6fe97 100644
+--- a/lib/cpp/tests/CMakeLists.txt
++++ b/lib/cpp/tests/CMakeLists.txt
+@@ -309,6 +309,7 @@ tango_catch2_tests_create(
+ catch2_alarm.cpp
+ catch2_attr_manip.cpp
+ catch2_attr_proxy.cpp
++ catch2_attr_conf_event.cpp
+ catch2_attr_polling.cpp
+ catch2_cmd_polling.cpp
+ catch2_connection.cpp
+diff --git a/lib/cpp/tests/catch2_attr_conf_event.cpp b/lib/cpp/tests/catch2_attr_conf_event.cpp
+new file mode 100644
+index 000000000..aea73316b
+--- /dev/null
++++ b/lib/cpp/tests/catch2_attr_conf_event.cpp
+@@ -0,0 +1,51 @@
++#include "catch2_common.h"
++
++static constexpr double k_initial_value{1.1234};
++
++template <class Base>
++class AttrConfEventData : public Base
++{
++ public:
++ using Base::Base;
++
++ void init_device() override { }
++
++ void read_attr(Tango::Attribute &att) override
++ {
++ att.set_value(&attr_dq_double);
++ }
++
++ static void attribute_factory(std::vector<Tango::Attr *> &attrs)
++ {
++ attrs.push_back(new TangoTest::AutoAttr<&AttrConfEventData::read_attr>("double_attr", Tango::DEV_DOUBLE));
++ }
++
++ private:
++ Tango::DevDouble attr_dq_double{k_initial_value};
++};
++
++TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(AttrConfEventData, 1)
++
++SCENARIO("Setting AttributeConfig works without database")
++{
++ int idlver = GENERATE(TangoTest::idlversion(1));
++ GIVEN("a device proxy to a simple IDLv" << idlver << " device")
++ {
++ TangoTest::Context ctx{"double_attr", "AttrConfEventData", idlver};
++ auto device = ctx.get_proxy();
++
++ REQUIRE(idlver == device->get_idl_version());
++
++ THEN("we can change the attribute configuration")
++ {
++ std::string attr{"double_attr"};
++ auto ai = device->attribute_query(attr);
++ ai.events.ch_event.abs_change = "33333";
++ ai.events.ch_event.rel_change = "99.99";
++
++ Tango::AttributeInfoListEx ail;
++ ail.push_back(ai);
++ REQUIRE_NOTHROW(device->set_attribute_config(ail));
++ }
++ }
++}
+--
+2.39.5
+
diff -Nru tango-10.0.2+dfsg1/debian/patches/series tango-10.0.2+dfsg1/debian/patches/series
--- tango-10.0.2+dfsg1/debian/patches/series 2025-02-23 22:10:39.000000000 -0300
+++ tango-10.0.2+dfsg1/debian/patches/series 2025-10-27 10:47:24.000000000 -0300
@@ -5,3 +5,12 @@
0001-Fix-timestamp-columns-in-Mariadb-10.11-and-newer.patch
1090173-sphinx-conf.patch
+
+#1118207
+0001-EventConsumerKeepAliveThread-send-actual-IDL-when-ex.patch
+0002-Catch2-tests-add-Event-re-subscribes-with-the-same-I.patch
+0001-catch2_event_old_client-Add-test-to-catch-duplicate-.patch
+0002-DServer-zmq_event_subscription_changed-Map-client-id.patch
+0003-push_att_conf_events-Use-event-data-to-determine-ver.patch
+0004-catch2_event_reconnection-Use-log-from-new-mapping-f.patch
+0005-Attribute-set_upd_properties-Handle-no-database-case.patch
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: not available
URL: <http://alioth-lists.debian.net/pipermail/debian-science-maintainers/attachments/20251028/3ea6b6a9/attachment-0001.sig>
More information about the debian-science-maintainers
mailing list