Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package openSUSE-release-tools for 
openSUSE:Factory checked in at 2026-11-10 14:44:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/openSUSE-release-tools (Old)
 and      /work/SRC/openSUSE:Factory/.openSUSE-release-tools.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "openSUSE-release-tools"

Tue Nov 10 14:44:29 2026 rev:561 rq:1396486 version:20261103.4d61b3c3

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/openSUSE-release-tools/openSUSE-release-tools.changes
    2026-10-20 12:41:59.771715021 +0200
+++ 
/work/SRC/openSUSE:Factory/.openSUSE-release-tools.new.3759290/openSUSE-release-tools.changes
       2026-11-10 14:46:06.021842578 +0100
@@ -1,0 +2,30 @@
+Tue Nov 03 09:56:06 UTC 2026 - [email protected]
+
+- Update to version 20261103.4d61b3c3:
+  * ttm: fix logic bugs in set_openqa_group_id
+  * ttm: throw an exeption if the openQA configuration is incomplete
+  * ttm: deprecate openqa_group setting
+  * ttm: use openqa_group_id for group lookup if it is set
+  * ttm: add job group lookup by id
+
+-------------------------------------------------------------------
+Thu Oct 29 18:51:14 UTC 2026 - [email protected]
+
+- Update to version 20261029.2ea606f7:
+  * gocd: monitors: SUSE.Repo.Monitor: exclude in-development projects
+  * gocd: rabbit-repoid: allow excluding projects
+  * obsrsync: allow specifying openQA configuration from env variables
+  * packaging: ensure we exclude obsrsync.py from the main package
+  * osrt-obsrsync: add packaging and example configuration files for obsrysnc
+  * obsrsync_trigger: add initial obsrsync trigger script that doesn't need 
RepoMonitor
+
+-------------------------------------------------------------------
+Thu Oct 29 17:28:32 UTC 2026 - [email protected]
+
+- Update to version 20261029.c705c4f0:
+  * Add also configuration for Package Hub 16.0
+  * Add Package Hub 16.1 for testing
+  * CI: update docker composer command
+  * Setup bot for Leap 16.1
+
+-------------------------------------------------------------------

Old:
----
  openSUSE-release-tools-20261012.83f7e5fa.obscpio

New:
----
  openSUSE-release-tools-20261103.4d61b3c3.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ openSUSE-release-tools.spec ++++++
--- /var/tmp/diff_new_pack.H17USM/_old  2026-11-10 14:46:07.367898969 +0100
+++ /var/tmp/diff_new_pack.H17USM/_new  2026-11-10 14:46:07.369899053 +0100
@@ -21,7 +21,7 @@
 %define announcer_filename factory-package-news
 %define services osrt-slsa.target [email protected] 
[email protected] [email protected] [email protected] 
[email protected] [email protected]
 Name:           openSUSE-release-tools
-Version:        20261012.83f7e5fa
+Version:        20261103.4d61b3c3
 Release:        0
 Summary:        Tools to aid in staging and release work for openSUSE/SUSE
 License:        GPL-2.0-or-later AND MIT
@@ -121,6 +121,20 @@
 %description scm
 VCS implementations used by scripts in %{name}.
 
+%package openqa-trigger-bots
+Summary:        openQA trigger bots
+Group:          Development/Tools/Other
+Requires:       osclib = %{version}
+Requires:       %{name} = %{version}
+Requires:       python3-pika
+Requires:       python3-openqa_client
+Requires:       python3-lxml
+BuildArch:      noarch
+
+%description openqa-trigger-bots
+Bots to trigger images syncing in openQA, and to get
+back feedback.
+
 %package check-bugowner
 Summary:        Check bugowner review bot
 Group:          Development/Tools/Other
@@ -289,6 +303,7 @@
 Requires:       openSUSE-release-tools-check-bugowner
 Requires:       openSUSE-release-tools-pkglistgen
 Requires:       openSUSE-release-tools-repo-checker
+Requires:       openSUSE-release-tools-openqa-trigger-bots
 %sysusers_requires
 Recommends:     logrotate
 BuildArch:      noarch
@@ -486,6 +501,7 @@
 %exclude %{_datadir}/%{source_dir}/suppkg_rebuild.py
 %exclude %{_datadir}/%{source_dir}/skippkg-finder.py
 %exclude %{_datadir}/%{source_dir}/slfo-packagelist-uploader.py
+%exclude %{_datadir}/%{source_dir}/obsrsync.py
 %exclude %{_datadir}/%{source_dir}/osclib
 %exclude %{_datadir}/%{source_dir}/osc-cycle.py
 %exclude %{_datadir}/%{source_dir}/osc-origin.py
@@ -522,6 +538,10 @@
 %{_bindir}/osrt-check_bugowner
 %{_datadir}/%{source_dir}/check_bugowner.py
 
+%files openqa-trigger-bots
+%{_bindir}/osrt-obsrsync
+%{_datadir}/%{source_dir}/obsrsync.py
+
 %files check-source
 %{_bindir}/osrt-check_source
 %if 0%{?suse_version} > 1500
@@ -548,12 +568,14 @@
 %{_sysconfdir}/openSUSE-release-tools/ibsapi
 %{_sysconfdir}/openSUSE-release-tools/osrt-check-bugowner-gitea.env.in
 %{_sysconfdir}/openSUSE-release-tools/osrt-git-installcheck.env.in
+%{_sysconfdir}/openSUSE-release-tools/osrt-obsrsync.env.in
 %{_sysusersdir}/%{name}.conf
 %{_sysusersdir}/%{name}-staging.conf
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
+%{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
@@ -565,6 +587,7 @@
 %dir %attr(750,osrt-slsa,osrt-slsa) %{_sharedstatedir}/osrt-slsa
 %dir %attr(750,osrt-slsa,osrt-slsa) %{_sharedstatedir}/osrt-slsa/pkglistgen
 %dir %attr(750,osrt-slsa,osrt-slsa) %{_sharedstatedir}/osrt-slsa/relpkggen
+%dir %attr(750,osrt-slsa,osrt-slsa) %{_sharedstatedir}/osrt-slsa/obsrsync
 %dir %attr(750,osrt-staging,osrt-staging) %{_sharedstatedir}/osrt-staging
 %dir %attr(750,osrt-staging,osrt-staging) 
%{_sharedstatedir}/osrt-staging/check-bugowner
 %dir %attr(750,osrt-staging,osrt-staging) 
%{_sharedstatedir}/osrt-staging/git-installcheck

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.H17USM/_old  2026-11-10 14:46:07.419901147 +0100
+++ /var/tmp/diff_new_pack.H17USM/_new  2026-11-10 14:46:07.422901273 +0100
@@ -1,7 +1,7 @@
 <servicedata>
   <service name="tar_scm">
     <param 
name="url">https://github.com/openSUSE/openSUSE-release-tools.git</param>
-    <param 
name="changesrevision">83f7e5fa518790dedaf6a790fcf474e239279349</param>
+    <param 
name="changesrevision">4d61b3c33b82d36a2d57458f7f92e079452b74c1</param>
   </service>
 </servicedata>
 

++++++ openSUSE-release-tools-20261012.83f7e5fa.obscpio -> 
openSUSE-release-tools-20261103.4d61b3c3.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/.github/workflows/ci-test.yml 
new/openSUSE-release-tools-20261103.4d61b3c3/.github/workflows/ci-test.yml
--- old/openSUSE-release-tools-20261012.83f7e5fa/.github/workflows/ci-test.yml  
2026-10-12 12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/.github/workflows/ci-test.yml  
2026-11-03 10:55:23.000000000 +0100
@@ -30,7 +30,7 @@
         sudo apt-get install -y docker-compose
 
     - name: Run tests
-      run: docker-compose -f dist/ci/docker-compose.yml run test
+      run: docker compose -f dist/ci/docker-compose.yml run test
       env:
         # Specifies how many jobs you would like to run in parallel,
         # used for partitioning
@@ -40,7 +40,7 @@
 
     - name: Stop containers
       if: always()
-      run: docker-compose -f dist/ci/docker-compose.yml down
+      run: docker compose -f dist/ci/docker-compose.yml down
 
   smoke-test:
     runs-on: ubuntu-latest
@@ -120,9 +120,9 @@
       run: sed -i -e "s,../..:,$PWD:," dist/ci/docker-compose.yml
 
     - name: Run Flaker
-      run: docker-compose -f dist/ci/docker-compose.yml run flaker
+      run: docker compose -f dist/ci/docker-compose.yml run flaker
 
     - name: Stop containers
       if: always()
-      run: docker-compose -f dist/ci/docker-compose.yml down
+      run: docker compose -f dist/ci/docker-compose.yml down
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openSUSE-release-tools-20261012.83f7e5fa/Makefile 
new/openSUSE-release-tools-20261103.4d61b3c3/Makefile
--- old/openSUSE-release-tools-20261012.83f7e5fa/Makefile       2026-10-12 
12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/Makefile       2026-11-03 
10:55:23.000000000 +0100
@@ -11,7 +11,7 @@
 all:
 
 install:
-       install -d -m 755 $(DESTDIR)$(bindir) $(DESTDIR)$(pkgdatadir) 
$(DESTDIR)$(unitdir) $(DESTDIR)$(oscplugindir) 
$(DESTDIR)$(sysconfdir)/$(package_name) 
$(DESTDIR)$(grafana_provisioning_dir)/dashboards 
$(DESTDIR)$(grafana_provisioning_dir)/datasources 
$(DESTDIR)$(logdir)/$(package_name) 
$(DESTDIR)$(varlibdir)/osrt-staging/check-bugowner 
$(DESTDIR)$(varlibdir)/osrt-staging/git-installcheck 
$(DESTDIR)$(varlibdir)/osrt-slsa/pkglistgen 
$(DESTDIR)$(varlibdir)/osrt-slsa/relpkggen
+       install -d -m 755 $(DESTDIR)$(bindir) $(DESTDIR)$(pkgdatadir) 
$(DESTDIR)$(unitdir) $(DESTDIR)$(oscplugindir) 
$(DESTDIR)$(sysconfdir)/$(package_name) 
$(DESTDIR)$(grafana_provisioning_dir)/dashboards 
$(DESTDIR)$(grafana_provisioning_dir)/datasources 
$(DESTDIR)$(logdir)/$(package_name) 
$(DESTDIR)$(varlibdir)/osrt-staging/check-bugowner 
$(DESTDIR)$(varlibdir)/osrt-staging/git-installcheck 
$(DESTDIR)$(varlibdir)/osrt-slsa/obsrsync 
$(DESTDIR)$(varlibdir)/osrt-slsa/pkglistgen 
$(DESTDIR)$(varlibdir)/osrt-slsa/relpkggen
        for i in $(pkgdata_SCRIPTS); do install -m 755 $$i 
$(DESTDIR)$(pkgdatadir); done
        chmod 644 $(DESTDIR)$(pkgdatadir)/osc-*.py
        for i in $(pkgdata_DATA); do cp -a $$i $(DESTDIR)$(pkgdatadir); done
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openSUSE-release-tools-20261012.83f7e5fa/README.md 
new/openSUSE-release-tools-20261103.4d61b3c3/README.md
--- old/openSUSE-release-tools-20261012.83f7e5fa/README.md      2026-10-12 
12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/README.md      2026-11-03 
10:55:23.000000000 +0100
@@ -62,7 +62,7 @@
 
 Using [Docker Compose](https://docs.docker.com/compose/), a containerized OBS 
can be started via one command. The default credentials are `Admin` and 
`opensuse` on [0.0.0.0:3000](http://0.0.0.0:3000). You can change the port by 
setting the environment variable `OSRT_EXPOSED_OBS_PORT`.
 
-    docker-compose -f dist/ci/docker-compose.yml up api
+    docker compose -f dist/ci/docker-compose.yml up api
 
 To make things easier, you can add an alias to refer to this instance (do not 
forget to adjust the port if you are using a different one):
 
@@ -91,16 +91,16 @@
     sed -i -e "s,../..:,$PWD:," dist/ci/docker-compose.yml
 
     # Run the linter
-    docker-compose -f dist/ci/docker-compose.yml run flaker
+    docker compose -f dist/ci/docker-compose.yml run flaker
 
     # Run the test full suite (it may take some time)...
-    docker-compose -f dist/ci/docker-compose.yml run test
+    docker compose -f dist/ci/docker-compose.yml run test
 
     # .. or just run a single test (i.e., the 'tests/util_tests.py')
-    docker-compose -f dist/ci/docker-compose.yml run test run_as_tester pytest 
tests/util_tests.py
+    docker compose -f dist/ci/docker-compose.yml run test run_as_tester pytest 
tests/util_tests.py
 
     # We are finished. Now you can shut the containers down.
-    docker-compose -f dist/ci/docker-compose.yml down
+    docker compose -f dist/ci/docker-compose.yml down
 
 The [docker-compose.yml](dist/ci/docker-compose.yml) mentions two container 
images that are built in the 
[openSUSE:Tools:Images](https://build.opensuse.org/project/show/openSUSE:Tools:Images)
 project:
 
@@ -115,15 +115,15 @@
 This section lists a few tricks to debug problems in the CI. You will use your 
local setup so, as a first step, you need to be able to run the tests as 
described in the previous section.
 To see the logs from all the containers, the following command can be executed:
 
-  docker-compose -f dist/ci/docker-compose.yml logs -f --tail=10
+  docker compose -f dist/ci/docker-compose.yml logs -f --tail=10
 
 You can run commands in any container by using the docker-compose `exec` 
command. For instance, you can connect to a container through a shell with the 
following command (in this case, it will connect to the container behind the 
`api` service):
 
-  docker-compose -f dist/ci/docker-compose.yml exec api sh
+  docker compose -f dist/ci/docker-compose.yml exec api sh
 
 Or you could check the API logs by issuing the following command:
 
-  docker-compose -f dist/ci/docker-compose.yml exec api sh -c 'tail -f 
/srv/www/obs/api/log/*.log'
+  docker compose -f dist/ci/docker-compose.yml exec api sh -c 'tail -f 
/srv/www/obs/api/log/*.log'
 
 To debug problems in the test suite or in the code, place a `breakpoint()` 
call and you will get access to Python's debugger.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/config/osrt-obsrsync.env.in 
new/openSUSE-release-tools-20261103.4d61b3c3/config/osrt-obsrsync.env.in
--- old/openSUSE-release-tools-20261012.83f7e5fa/config/osrt-obsrsync.env.in    
1970-01-01 01:00:00.000000000 +0100
+++ new/openSUSE-release-tools-20261103.4d61b3c3/config/osrt-obsrsync.env.in    
2026-11-03 10:55:23.000000000 +0100
@@ -0,0 +1,19 @@
+# Copy this file to /etc/default/osrt-obsrsync.env.<instance_name>
+# where <instance_name> is the name of the unit instance
+
+# API_URL: the Open Build Service apiurl
+API_URL="https://api.suse.de";
+
+# OPENQA_URL: the url of the local openQA instance
+OPENQA_URL="https://openqa.suse.de";
+
+# OSRT_NAMESPACES: the namespaces to look for
+OSRT_NAMESPACES="SUSE:SLFO:"
+
+# OSC_CONFIG: the path to the oscrc configuration file
+# to use to interact with the Open Build Service
+OSC_CONFIG="/etc/openSUSE-release-tools/oscrc.example"
+
+# OPENQA_CONFIG: the path to the openQA configuration
+# file to use to interact with openQA
+OPENQA_CONFIG="/etc/openSUSE-release-tools/openQA.example"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/dist/package/openSUSE-release-tools.spec
 
new/openSUSE-release-tools-20261103.4d61b3c3/dist/package/openSUSE-release-tools.spec
--- 
old/openSUSE-release-tools-20261012.83f7e5fa/dist/package/openSUSE-release-tools.spec
       2026-10-12 12:56:36.000000000 +0200
+++ 
new/openSUSE-release-tools-20261103.4d61b3c3/dist/package/openSUSE-release-tools.spec
       2026-11-03 10:55:23.000000000 +0100
@@ -121,6 +121,20 @@
 %description scm
 VCS implementations used by scripts in %{name}.
 
+%package openqa-trigger-bots
+Summary:        openQA trigger bots
+Group:          Development/Tools/Other
+Requires:       osclib = %{version}
+Requires:       %{name} = %{version}
+Requires:       python3-pika
+Requires:       python3-openqa_client
+Requires:       python3-lxml
+BuildArch:      noarch
+
+%description openqa-trigger-bots
+Bots to trigger images syncing in openQA, and to get
+back feedback.
+
 %package check-bugowner
 Summary:        Check bugowner review bot
 Group:          Development/Tools/Other
@@ -289,6 +303,7 @@
 Requires:       openSUSE-release-tools-check-bugowner
 Requires:       openSUSE-release-tools-pkglistgen
 Requires:       openSUSE-release-tools-repo-checker
+Requires:       openSUSE-release-tools-openqa-trigger-bots
 %sysusers_requires
 Recommends:     logrotate
 BuildArch:      noarch
@@ -486,6 +501,7 @@
 %exclude %{_datadir}/%{source_dir}/suppkg_rebuild.py
 %exclude %{_datadir}/%{source_dir}/skippkg-finder.py
 %exclude %{_datadir}/%{source_dir}/slfo-packagelist-uploader.py
+%exclude %{_datadir}/%{source_dir}/obsrsync.py
 %exclude %{_datadir}/%{source_dir}/osclib
 %exclude %{_datadir}/%{source_dir}/osc-cycle.py
 %exclude %{_datadir}/%{source_dir}/osc-origin.py
@@ -522,6 +538,10 @@
 %{_bindir}/osrt-check_bugowner
 %{_datadir}/%{source_dir}/check_bugowner.py
 
+%files openqa-trigger-bots
+%{_bindir}/osrt-obsrsync
+%{_datadir}/%{source_dir}/obsrsync.py
+
 %files check-source
 %{_bindir}/osrt-check_source
 %if 0%{?suse_version} > 1500
@@ -548,12 +568,14 @@
 %{_sysconfdir}/openSUSE-release-tools/ibsapi
 %{_sysconfdir}/openSUSE-release-tools/osrt-check-bugowner-gitea.env.in
 %{_sysconfdir}/openSUSE-release-tools/osrt-git-installcheck.env.in
+%{_sysconfdir}/openSUSE-release-tools/osrt-obsrsync.env.in
 %{_sysusersdir}/%{name}.conf
 %{_sysusersdir}/%{name}-staging.conf
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
+%{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
 %{_unitdir}/[email protected]
@@ -565,6 +587,7 @@
 %dir %attr(750,osrt-slsa,osrt-slsa) %{_sharedstatedir}/osrt-slsa
 %dir %attr(750,osrt-slsa,osrt-slsa) %{_sharedstatedir}/osrt-slsa/pkglistgen
 %dir %attr(750,osrt-slsa,osrt-slsa) %{_sharedstatedir}/osrt-slsa/relpkggen
+%dir %attr(750,osrt-slsa,osrt-slsa) %{_sharedstatedir}/osrt-slsa/obsrsync
 %dir %attr(750,osrt-staging,osrt-staging) %{_sharedstatedir}/osrt-staging
 %dir %attr(750,osrt-staging,osrt-staging) 
%{_sharedstatedir}/osrt-staging/check-bugowner
 %dir %attr(750,osrt-staging,osrt-staging) 
%{_sharedstatedir}/osrt-staging/git-installcheck
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/docs/totest.asciidoc 
new/openSUSE-release-tools-20261103.4d61b3c3/docs/totest.asciidoc
--- old/openSUSE-release-tools-20261012.83f7e5fa/docs/totest.asciidoc   
2026-10-12 12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/docs/totest.asciidoc   
2026-11-03 10:55:23.000000000 +0100
@@ -68,7 +68,8 @@
 -------------------------------------------------------------------------------
 $ cat <<EOF > myattribute
 jobs_num: 2
-openqa_group: My Product 66.6
+# openqa_group: My Product 66.6 has an id of 666. openqa_group is deprecated
+openqa_group_id: 666
 openqa_server: https://openqa.opensuse.org
 product_repo: images
 product_arch: x86_64
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/gocd/checkers.opensuse.gocd.yaml 
new/openSUSE-release-tools-20261103.4d61b3c3/gocd/checkers.opensuse.gocd.yaml
--- 
old/openSUSE-release-tools-20261012.83f7e5fa/gocd/checkers.opensuse.gocd.yaml   
    2026-10-12 12:56:36.000000000 +0200
+++ 
new/openSUSE-release-tools-20261103.4d61b3c3/gocd/checkers.opensuse.gocd.yaml   
    2026-11-03 10:55:23.000000000 +0100
@@ -143,7 +143,7 @@
           - staging-bot
         tasks:
           - script: ./origin-manager.py -A https://api.opensuse.org --debug 
review
-  openSUSE.QA.Leap16.Maint:
+  openSUSE.QA.Leap16.0.Maint:
     group: openSUSE.Checkers
     lock_behavior: unlockWhenFinished
     environment_variables:
@@ -164,6 +164,65 @@
           - script: ./git-openqa-maintenance.py --openqa-host 
https://openqa.opensuse.org --project openSUSE/Leap --branch leap-16.0
           - script: ./git-openqa-maintenance.py --openqa-host 
https://openqa.opensuse.org --project products/PackageHub --branch leap-16.0
           - script: ./git-openqa-maintenance.py --openqa-host 
https://openqa.opensuse.org --project openSUSE/LeapNonFree --branch 
leap-16.0-nonfree
+  openSUSE.QA.Leap16.1.Maint:
+    group: openSUSE.Checkers
+    lock_behavior: unlockWhenFinished
+    environment_variables:
+      OSC_CONFIG: /home/go/config/oscrc-openqa-maintenance
+      GITEA_TOKEN_FILE: /home/go/config/openqa-maintenance-botmaster-token
+    materials:
+      script:
+        git: https://github.com/openSUSE/openSUSE-release-tools.git
+    timer:
+      spec: 0 */10 * ? * *
+      only_on_changes: false
+    stages:
+    - Run:
+        approval: manual
+        resources:
+          - staging-bot
+        tasks:
+          - script: ./git-openqa-maintenance.py --openqa-host 
https://openqa.opensuse.org --project openSUSE/Leap --branch leap-16.1
+          - script: ./git-openqa-maintenance.py --openqa-host 
https://openqa.opensuse.org --project products/PackageHub --branch leap-16.1
+          - script: ./git-openqa-maintenance.py --openqa-host 
https://openqa.opensuse.org --project openSUSE/LeapNonFree --branch 
leap-16.1-nonfree
+  openSUSE.QA.PackageHub16.0.Maint:
+    group: openSUSE.Checkers
+    lock_behavior: unlockWhenFinished
+    environment_variables:
+      OSC_CONFIG: /home/go/config/oscrc-openqa-maintenance
+      GITEA_TOKEN_FILE: /home/go/config/openqa-maintenance-botmaster-token
+    materials:
+      script:
+        git: https://github.com/openSUSE/openSUSE-release-tools.git
+    timer:
+      spec: 0 */10 * ? * *
+      only_on_changes: false
+    stages:
+    - Run:
+        approval: manual
+        resources:
+          - staging-bot
+        tasks:
+          - script: ./git-openqa-maintenance.py --openqa-host 
https://openqa.suse.de --project products/PackageHub --branch leap-16.0
+  openSUSE.QA.PackageHub16.1.Maint:
+    group: openSUSE.Checkers
+    lock_behavior: unlockWhenFinished
+    environment_variables:
+      OSC_CONFIG: /home/go/config/oscrc-openqa-maintenance
+      GITEA_TOKEN_FILE: /home/go/config/openqa-maintenance-botmaster-token
+    materials:
+      script:
+        git: https://github.com/openSUSE/openSUSE-release-tools.git
+    timer:
+      spec: 0 */10 * ? * *
+      only_on_changes: false
+    stages:
+    - Run:
+        approval: manual
+        resources:
+          - staging-bot
+        tasks:
+          - script: ./git-openqa-maintenance.py --openqa-host 
https://openqa.suse.de --project products/PackageHub --branch leap-16.1
   openSUSE.Devel.Reminder:
     group: openSUSE.Checkers
     lock_behavior: unlockWhenFinished
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/gocd/monitors.gocd.yaml 
new/openSUSE-release-tools-20261103.4d61b3c3/gocd/monitors.gocd.yaml
--- old/openSUSE-release-tools-20261012.83f7e5fa/gocd/monitors.gocd.yaml        
2026-10-12 12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/gocd/monitors.gocd.yaml        
2026-11-03 10:55:23.000000000 +0100
@@ -89,7 +89,7 @@
                 git config --global user.email "[email protected]"
                 git config --global user.name "GoCD Repo Monitor"
                 cd repos
-                ../scripts/gocd/rabbit-repoid.py -A https://api.suse.de 
SUSE:SLE SUSE:ALP SUSE:SLFO Devel:YaST
+                ../scripts/gocd/rabbit-repoid.py -A https://api.suse.de 
SUSE:SLE SUSE:ALP SUSE:SLFO Devel:YaST --exclude SUSE:SLE-15-SP7:Update:QR:TEST 
--exclude SUSE:SLFO:Products:SLES:16.2:TEST
   openSUSE.Repo.Monitor:
     group: Monitors
     lock_behavior: unlockWhenFinished
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/gocd/rabbit-repoid.py 
new/openSUSE-release-tools-20261103.4d61b3c3/gocd/rabbit-repoid.py
--- old/openSUSE-release-tools-20261012.83f7e5fa/gocd/rabbit-repoid.py  
2026-10-12 12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/gocd/rabbit-repoid.py  
2026-11-03 10:55:23.000000000 +0100
@@ -18,11 +18,12 @@
 
 
 class Listener(PubSubConsumer):
-    def __init__(self, apiurl, amqp_prefix, namespaces):
+    def __init__(self, apiurl, amqp_prefix, namespaces, exclude=None):
         super(Listener, self).__init__(amqp_prefix, 
logging.getLogger(__name__))
         self.apiurl = apiurl
         self.amqp_prefix = amqp_prefix
         self.namespaces = namespaces
+        self.exclude = exclude or []
         # repos to check on startup
         self.repositories_to_check = []
         # repos to check periodically that in flux
@@ -68,7 +69,7 @@
         return ids
 
     def is_part_of_namespaces(self, project):
-        if ":PullRequest:" in project:
+        if ":PullRequest:" in project or project in self.exclude:
             return False
 
         for namespace in self.namespaces:
@@ -148,6 +149,8 @@
     parser.add_argument('--apiurl', '-A', type=str, help='API URL of OBS')
     parser.add_argument('-d', '--debug', action='store_true', default=False,
                         help='enable debug information')
+    parser.add_argument('--exclude', action='append', default=[],
+                        help='projects to exclude (can be specified multiple 
times)')
     parser.add_argument('namespaces', nargs='*', help='namespaces to wait for')
 
     args = parser.parse_args()
@@ -164,7 +167,7 @@
 
     logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
 
-    listener = Listener(apiurl, amqp_prefix, args.namespaces)
+    listener = Listener(apiurl, amqp_prefix, args.namespaces, 
exclude=args.exclude)
 
     try:
         listener.run(10800)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openSUSE-release-tools-20261012.83f7e5fa/obsrsync.py 
new/openSUSE-release-tools-20261103.4d61b3c3/obsrsync.py
--- old/openSUSE-release-tools-20261012.83f7e5fa/obsrsync.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/openSUSE-release-tools-20261103.4d61b3c3/obsrsync.py    2026-11-03 
10:55:23.000000000 +0100
@@ -0,0 +1,342 @@
+#!/usr/bin/python3
+
+import argparse
+import glob
+import json
+import logging
+import os
+import random
+import requests
+
+import configparser
+
+import osc
+from osc.core import http_GET, makeurl
+from osclib.core import target_archs
+from osclib.PubSubConsumer import PubSubConsumer
+from lxml import etree as ET
+from urllib.error import HTTPError
+
+from openqa_client.client import OpenQA_Client
+from openqa_client.exceptions import RequestError
+
+from urllib.parse import urlparse, urlunparse
+
+
+class Configurable_OpenQA_Client(OpenQA_Client):
+    # The whole __init__ block comes from openQA-python-client,
+    # with the single change of being able to load a custom configuration
+    # file rather than looking at the hardcoded paths.
+    # Once openQA-python-client gets the same feature, this whole class
+    # can be removed and OpenQA_Client can be used instead.
+    #
+    # openQA-python-client is licensed with the same license as OSRT,
+    # GPL2+, and is copyrighted to Red Hat.
+    # 
https://github.com/os-autoinst/openQA-python-client/blob/ed924a8921ecee6a843bd141faac6e41c4739e0a/src/openqa_client/client.py
+    def __init__(
+        self, server: str = "", scheme: str = "", retries: int = 5, wait: int 
= 10,
+        config_path: str = ""
+    ) -> None:
+        self.retries = retries
+        self.wait = wait
+        # Read in config files.
+        config = configparser.ConfigParser()
+        if config_path and os.path.exists(config_path):
+            config.read(config_path)
+        else:
+            paths = ("/etc/openqa", 
f"{os.path.expanduser('~')}/.config/openqa")
+            config.read(f"{path}/client.conf" for path in paths)
+
+        # If server not specified, default to the first one in the
+        # configuration file. If no configuration file, default to
+        # localhost. NOTE: this is different from the perl client, it
+        # *always* defaults to localhost.
+        if not server:
+            try:
+                server = config.sections()[0]
+            except (configparser.MissingSectionHeaderError, IndexError):
+                server = "localhost"
+
+        if server.startswith("http"):
+            # Handle entries like [http://foo] or [https://foo]. The,
+            # perl client does NOT handle these, so you shouldn't use
+            # them. This client started out supporting this, though,
+            # so it should continue to.
+            if not scheme:
+                scheme = urlparse(server).scheme
+            server = urlparse(server).netloc
+
+        if not scheme:
+            if server in ("localhost", "127.0.0.1", "::1"):
+                # Default to non-TLS for localhost; cert is unlikely to
+                # be valid for 'localhost' and there's no MITM...
+                scheme = "http"
+            else:
+                scheme = "https"
+
+        self.baseurl = urlunparse((scheme, server, "", "", "", ""))
+
+        # Get the API secrets from the config file.
+        try:
+            apikey = config.get(server, "key")
+            self.apisecret = config.get(server, "secret")
+        except configparser.Error:
+            try:
+                apikey = config.get(self.baseurl, "key")
+                self.apisecret = config.get(self.baseurl, "secret")
+            except configparser.Error:
+                raise Exception(f"No API key for {server}: only GET requests 
will be allowed")
+
+        # Create a Requests session and ensure some standard headers
+        # will be used for all requests run through the session.
+        self.session: requests.Session = requests.Session()
+        headers = {}
+        headers["Accept"] = "json"
+        if apikey:
+            headers["X-API-Key"] = apikey
+        self.session.headers.update(headers)
+
+
+class Listener(PubSubConsumer):
+    def __init__(
+        self, apiurl, amqp_prefix, namespaces, openqa_url, state_dir, 
dry_run=False
+    ):
+        super(Listener, self).__init__(amqp_prefix, 
logging.getLogger(__name__))
+        self.apiurl = apiurl
+        self.amqp_prefix = amqp_prefix
+        self.namespaces = namespaces
+        self.state_dir = state_dir
+        self.dry_run = dry_run
+        self.openqa = Configurable_OpenQA_Client(server=openqa_url, 
config_path=os.environ.get("OPENQA_CONFIG", ""))
+
+        self.repositories_to_check = []
+        self.repositories_to_monitor = set()
+
+        os.makedirs(self.state_dir, exist_ok=True)
+        self.interesting_repos = set()
+        self.refresh_interesting_repos()
+
+    def refresh_interesting_repos(self):
+        interesting = set()
+        try:
+            repo_list = self.openqa.openqa_request("GET", "obs_rsync")
+            for project, repository in repo_list:
+                interesting.add(f"{project}_-_{repository}")
+        except RequestError as e:
+            self.logger.warning(f"Could not fetch interesting repos from 
openQA: {e}")
+        else:
+            self.interesting_repos = interesting
+
+    def interval(self):
+        if len(self.repositories_to_check) or 
len(self.repositories_to_monitor):
+            return 5
+        return super(Listener, self).interval()
+
+    def still_alive(self):
+        super(Listener, self).still_alive()
+        self.refresh_interesting_repos()
+        self.check_some_repos()
+
+    def routing_keys(self):
+        return [self.amqp_prefix + ".obs.repo.build_finished"]
+
+    def check_arch(self, project, repository, architecture):
+        url = makeurl(
+            self.apiurl,
+            ["build", project, repository, architecture],
+            {"view": "status"},
+        )
+        root = ET.parse(http_GET(url)).getroot()
+        if root.get("code") == "finished":
+            buildid = root.find("buildid")
+            if buildid is not None:
+                return buildid.text
+
+        self.logger.info(
+            f"{project}/{repository}/{architecture}: code=%s dirty=%s"
+            % (root.get("code"), root.get("dirty", "false"))
+        )
+        if root.get("code") == "scheduling" or root.get("dirty", "false") == 
"true":
+            self.repositories_to_monitor.add(f"{project}/{repository}")
+
+    def check_all_archs(self, project, repository):
+        ids = {}
+        try:
+            archs = target_archs(self.apiurl, project, repository)
+        except HTTPError:
+            return None
+
+        for arch in archs:
+            repoid = self.check_arch(project, repository, arch)
+            if not repoid:
+                self.logger.info(f"{project}/{repository}/{arch} not yet done")
+                return None
+            ids[arch] = repoid
+
+        self.logger.info(f"All of {project}/{repository} finished")
+        return ids
+
+    def is_part_of_namespaces(self, project):
+        if ":PullRequest:" in project:
+            return False
+        for namespace in self.namespaces:
+            if project.startswith(namespace):
+                return True
+        return False
+
+    def start_consuming(self):
+        for namespace in self.namespaces:
+            for state_file in glob.glob(
+                os.path.join(self.state_dir, f"{namespace}*.json")
+            ):
+                state = os.path.basename(state_file).replace(".json", "")
+                project, repository = state.split("_-_")
+                if self.is_part_of_namespaces(project):
+                    self.repositories_to_check.append([project, repository])
+        self.check_some_repos()
+        super(Listener, self).start_consuming()
+
+    def check_some_repos(self):
+        count = 0
+        limit = 15
+        while len(self.repositories_to_check):
+            project, repository = self.repositories_to_check.pop()
+            self.logger.debug(f"Check repo {project}/{repository}")
+            self.update_repo(project, repository)
+            count += 1
+            if count >= limit:
+                return
+
+        repos = list(self.repositories_to_monitor)
+        random.shuffle(repos)
+        for entry in repos:
+            self.repositories_to_monitor.discard(entry)
+            project, repository = entry.split("/")
+            self.logger.debug(f"Recheck repo {project}/{repository}")
+            self.update_repo(project, repository)
+            count += 1
+            if count >= limit:
+                return
+
+    def update_repo(self, project, repository):
+        ids = self.check_all_archs(project, repository)
+        if not ids:
+            return
+
+        state_id = f"{project}_-_{repository}"
+        state_file = os.path.join(self.state_dir, f"{state_id}.json")
+        old_ids = {}
+
+        if os.path.exists(state_file):
+            with open(state_file, "r") as f:
+                old_ids = json.load(f)
+
+        if ids == old_ids:
+            self.logger.debug(f"{state_id} did not change")
+            return
+
+        if state_id in self.interesting_repos:
+            if self.dry_run:
+                self.logger.info(
+                    f"[DRY-RUN] Would trigger openQA obs_rsync for 
{project}/{repository}"
+                )
+            else:
+                try:
+                    self.logger.info(
+                        f"Triggering openQA obs_rsync for 
{project}/{repository}"
+                    )
+                    self.openqa.openqa_request(
+                        "PUT",
+                        f"obs_rsync/{project}/runs?repository={repository}",
+                        retries=0,
+                    )
+                except RequestError as e:
+                    self.logger.error(
+                        f"Got exception on syncing repository to openQA: {e}"
+                    )
+
+                    # We don't return here, as we would rather lose the
+                    # event than spamming openQA over and over. So after
+                    # logging the error, just go ahead and save the state
+                    # anyway.
+
+        if self.dry_run:
+            self.logger.info(f"[DRY-RUN] Would save new state to {state_file}")
+            return
+
+        with open(state_file, "w") as f:
+            json.dump(ids, f)
+
+    def on_message(self, unused_channel, method, properties, body):
+        self.logger.debug("on_message")
+        self.acknowledge_message(method.delivery_tag)
+        try:
+            body = json.loads(body)
+        except ValueError:
+            return
+
+        if method.routing_key.endswith(".obs.repo.build_finished"):
+            if not self.is_part_of_namespaces(body["project"]):
+                return
+            self.restart_timer()
+            self.logger.info(
+                f"Repo finished event: 
{body['project']}/{body['repo']}/{body['arch']}"
+            )
+            self.update_repo(body["project"], body["repo"])
+        else:
+            self.logger.warning(f"unknown rabbitmq message 
{method.routing_key}")
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(
+        description="Daemon to monitor OBS via RabbitMQ and trigger openQA 
obs_rsync"
+    )
+    parser.add_argument(
+        "--apiurl", "-A", type=str, required=True, help="API URL of OBS"
+    )
+    parser.add_argument("--openqa", type=str, required=True, help="OpenQA URL")
+    parser.add_argument(
+        "--state-dir",
+        type=str,
+        default="/var/lib/obs-openqa-sync",
+        help="Directory to keep track of build states locally",
+    )
+    parser.add_argument(
+        "--dry-run",
+        action="store_true",
+        default=False,
+        help="Print actions without triggering openQA or saving state",
+    )
+    parser.add_argument(
+        "--run-duration",
+        type=int,
+        default=10800,
+        help="Seconds to run before exiting. Set to 0 to run indefinitely 
(default: 10800)",
+    )
+    parser.add_argument(
+        "-d",
+        "--debug",
+        action="store_true",
+        default=False,
+        help="enable debug information",
+    )
+    parser.add_argument("namespaces", nargs="*", help="namespaces to wait for")
+
+    args = parser.parse_args()
+
+    osc.conf.get_config(override_apiurl=args.apiurl)
+    osc.conf.config["debug"] = args.debug
+
+    apiurl = osc.conf.config["apiurl"]
+    amqp_prefix = "suse" if apiurl.endswith("suse.de") else "opensuse"
+
+    logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
+
+    listener = Listener(
+        apiurl, amqp_prefix, args.namespaces, args.openqa, args.state_dir, 
args.dry_run
+    )
+
+    try:
+        listener.run(args.run_duration)
+    except KeyboardInterrupt:
+        listener.stop()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/systemd/[email protected] 
new/openSUSE-release-tools-20261103.4d61b3c3/systemd/[email protected]
--- old/openSUSE-release-tools-20261012.83f7e5fa/systemd/[email protected] 
1970-01-01 01:00:00.000000000 +0100
+++ new/openSUSE-release-tools-20261103.4d61b3c3/systemd/[email protected] 
2026-11-03 10:55:23.000000000 +0100
@@ -0,0 +1,20 @@
+[Unit]
+PartOf=osrt-slsa.target
+Description=service for osrt-obsrsync running for %i
+After=network-online.target
+StartLimitBurst=3
+StartLimitIntervalSec=60
+
+[Service]
+Type=simple
+Restart=always
+RestartSec=5
+User=osrt-slsa
+SyslogIdentifier=osrt-slsa
+EnvironmentFile=/etc/default/osrt-obsrsync.env.%i
+WorkingDirectory=/var/lib/osrt-slsa/obsrsync
+ExecStartPre=/bin/bash -xc '/usr/bin/systemctl is-active --quiet 
osrt-obsrsync@%i.service && exit 1 || exit 0'
+ExecStart=/usr/bin/osrt-obsrsync -A "${API_URL}" --openqa "${OPENQA_URL}" 
--state-dir /var/lib/osrt-slsa/obsrsync/%i $OSRT_NAMESPACES
+
+[Install]
+WantedBy=osrt-slsa.target
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/ttm/README.md 
new/openSUSE-release-tools-20261103.4d61b3c3/ttm/README.md
--- old/openSUSE-release-tools-20261012.83f7e5fa/ttm/README.md  2026-10-12 
12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/ttm/README.md  2026-11-03 
10:55:23.000000000 +0100
@@ -25,8 +25,9 @@
 arch: x86_64
 
 # openQA settings
-openqa_server: None # URL to the openQA server, e.g. 
"https://openqa.opensuse.org";
-openqa_group: None # Name of the job group in openQA, e.g. "openSUSE 
Tumbleweed"
+openqa_server: None # URL to the openQA server, e.g: 
https://openqa.opensuse.org
+openqa_group: None # [Deprecated] Name of the job group in openQA, e.g: 
openSUSE Tumbleweed
+openqa_group_id: None # id of the job group in openQA, e.g.: 1
 jobs_num: 42 # Minimum number of openQA jobs before publishing is possible
 
 # Global defaults for products
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/ttm/publisher.py 
new/openSUSE-release-tools-20261103.4d61b3c3/ttm/publisher.py
--- old/openSUSE-release-tools-20261012.83f7e5fa/ttm/publisher.py       
2026-10-12 12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/ttm/publisher.py       
2026-11-03 10:55:23.000000000 +0100
@@ -28,10 +28,18 @@
         ToTestManager.__init__(self, tool)
 
     def setup(self, project):
+        """Setup the ToTestPublisher with the given project.
+
+        When openQA is enabled but is missing a setting it will throw an 
exception.
+        """
         super(ToTestPublisher, self).setup(project)
-        self.openqa = OpenQA_Client(server=self.project.openqa_server)
         self.load_issues_to_ignore()
         self.seen_issues_updated = False
+        self.openqa = OpenQA_Client(server=self.project.openqa_server)
+        if not (self.project.openqa_server and (self.project.openqa_group_id 
or self.project.openqa_group)):
+            raise Exception('OSRT:ToTestManagerConfig: openQA configuration is 
incomplete')
+
+        self.set_openqa_group_id()
 
     def ignore_issue(self, ref, build_nr):
         if self.issues_to_ignore.get(ref) != build_nr:
@@ -236,20 +244,14 @@
             return
 
         self.update_status('published', current_snapshot)
-        group_id = self.openqa_group_id()
-        if not group_id:
-            return
-
-        self.add_published_tag(group_id, current_snapshot)
+        self.add_published_tag(current_snapshot)
 
     def find_openqa_results(self, snapshot):
-        """Return the openqa jobs of a given snapshot and filter out the
-        cloned jobs
-
+        """Return the openqa jobs of a given snapshot and filter out the 
cloned jobs
         """
 
-        url = makeurl(self.project.openqa_server,
-                      ['api', 'v1', 'jobs'], {'group': 
self.project.openqa_group, 'build': snapshot, 'latest': 1})
+        jobs_lookup = {'build': snapshot, 'latest': 1, 'groupid': 
self.project.openqa_group_id}
+        url = makeurl(self.project.openqa_server, ['api', 'v1', 'jobs'], 
jobs_lookup)
         f = self.api.retried_GET(url)
         jobs = []
         for job in json.load(f)['jobs']:
@@ -259,24 +261,39 @@
             jobs.append(job)
         return jobs
 
-    def add_published_tag(self, group_id, snapshot):
+    def add_published_tag(self, snapshot):
         if self.dryrun:
             return
 
         status_flag = 'published'
         data = {'text': f'tag:{snapshot}:{status_flag}:{status_flag}'}
-        self.openqa.openqa_request('POST', f'groups/{group_id}/comments', 
data=data)
+        self.openqa.openqa_request('POST', 
f'groups/{self.project.openqa_group_id}/comments', data=data)
 
-    def openqa_group_id(self):
-        url = makeurl(self.project.openqa_server,
-                      ['api', 'v1', 'job_groups'])
-        f = self.api.retried_GET(url)
-        job_groups = json.load(f)
-        for jg in job_groups:
-            if jg['name'] == self.project.openqa_group:
-                return jg['id']
+    def set_openqa_group_id(self):
+        """Ensures the group_id is set and exists in the openQA instance, 
throws otherwise"""
 
-        self.logger.debug('No openQA group id found for status comment update, 
ignoring')
+        def _get_group_id_by_name():
+            url = makeurl(self.project.openqa_server, ['api', 'v1', 
'job_groups'])
+            f = self.api.retried_GET(url)
+            job_groups = json.load(f)
+            for jg in job_groups:
+                if jg['name'] == self.project.openqa_group:
+                    return jg['id']
+
+            # Kill the run if the group doesn't exist
+            raise Exception('OSRT:ToTestManagerConfig: configured openQA job 
group was not found')
+
+        def _validate_group_by_id():
+            url = makeurl(self.project.openqa_server, ['api', 'v1', 
'job_groups', f"{self.project.openqa_group_id}"])
+            f = self.api.retried_GET(url)
+            return f.status == 200  # Let the HTTP exception kill the run if 
the group_id is invalid
+
+        if not self.project.openqa_group_id and self.project.openqa_group:
+            self.project.openqa_group_id = _get_group_id_by_name()
+            self.logger.warning("[DEPRECATED] openqa_group in 
OSRT:ToTestManagerConfig must be changed to"
+                                + f" openqa_group_id: 
'{self.project.openqa_group_id}'.")
+        else:
+            _validate_group_by_id()
 
     def load_issues_to_ignore(self):
         text = self.api.attribute_value_load('IgnoredIssues')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openSUSE-release-tools-20261012.83f7e5fa/ttm/totest.py 
new/openSUSE-release-tools-20261103.4d61b3c3/ttm/totest.py
--- old/openSUSE-release-tools-20261012.83f7e5fa/ttm/totest.py  2026-10-12 
12:56:36.000000000 +0200
+++ new/openSUSE-release-tools-20261103.4d61b3c3/ttm/totest.py  2026-11-03 
10:55:23.000000000 +0100
@@ -138,6 +138,7 @@
         self.take_source_from_product = False
         self.arch = 'x86_64'
         self.openqa_group = None
+        self.openqa_group_id = None
         self.openqa_server = None
         self.jobs_num = 42
         self.test_subproject = 'ToTest'

++++++ openSUSE-release-tools.obsinfo ++++++
--- /var/tmp/diff_new_pack.H17USM/_old  2026-11-10 14:46:08.177932904 +0100
+++ /var/tmp/diff_new_pack.H17USM/_new  2026-11-10 14:46:08.184933197 +0100
@@ -1,5 +1,5 @@
 name: openSUSE-release-tools
-version: 20261012.83f7e5fa
-mtime: 1791802596
-commit: 83f7e5fa518790dedaf6a790fcf474e239279349
+version: 20261103.4d61b3c3
+mtime: 1793699723
+commit: 4d61b3c33b82d36a2d57458f7f92e079452b74c1
 

Reply via email to