From 7d9b22a75b502db7e052dff9492e61498a163e40 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Aug 20 2025 13:48:18 +0000
Subject: [PATCH 1/3] koji --single


---

diff --git a/builder/kojid b/builder/kojid
index 779fcd1..9941815 100755
--- a/builder/kojid
+++ b/builder/kojid
@@ -145,6 +145,11 @@ def main(options, session):
             logger.info('Loading plugin: %s' % name)
             tm.scanPlugin(pt.load(name))
 
+    if options.single:
+        # force all task handlers to run in Foreground
+        for key in tm.handlers:
+            tm.handlers[key].Foreground = True
+
     def shutdown(*args):
         raise SystemExit
 
@@ -189,6 +194,9 @@ def main(options, session):
                 # from getting overloaded.
                 logger.debug('Sleeping for %s', options.sleeptime)
                 time.sleep(options.sleeptime)
+            elif options.single:
+                # exit after first task
+                break
         except (SystemExit, KeyboardInterrupt):
             logger.warning("Exiting")
             break
@@ -6752,6 +6760,8 @@ def get_options():
     parser.add_option("-f", "--fg", dest="daemon",
                       action="store_false", default=True,
                       help="run in foreground")
+    parser.add_option("--single", action="store_true",
+                      help="run a single task in foreground, for debugging")
     parser.add_option("--force-lock", action="store_true", default=False,
                       help="force lock for exclusive session")
     parser.add_option("-v", "--verbose", action="store_true", default=False,
@@ -6789,6 +6799,10 @@ def get_options():
         # not reached
         assert False  # pragma: no cover
 
+    # --single implies --fg
+    if options.single:
+        options.daemon = False
+
     # load local config
     config = koji.read_config_files(options.configFile, raw=True)
     for x in config.sections():

From e7bb3ed14b5d11f62a1ed19c126b8b75d7270f2a Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Aug 20 2025 13:48:18 +0000
Subject: [PATCH 2/3] handle foreground override in TaskManager


---

diff --git a/builder/kojid b/builder/kojid
index 9941815..2f7f948 100755
--- a/builder/kojid
+++ b/builder/kojid
@@ -145,11 +145,6 @@ def main(options, session):
             logger.info('Loading plugin: %s' % name)
             tm.scanPlugin(pt.load(name))
 
-    if options.single:
-        # force all task handlers to run in Foreground
-        for key in tm.handlers:
-            tm.handlers[key].Foreground = True
-
     def shutdown(*args):
         raise SystemExit
 
diff --git a/koji/daemon.py b/koji/daemon.py
index 5d2eeaf..16c3420 100644
--- a/koji/daemon.py
+++ b/koji/daemon.py
@@ -1379,7 +1379,7 @@ class TaskManager(object):
                 return False
             # otherwise...
             raise
-        if handler.Foreground:
+        if handler.Foreground or getattr(self.options, "single"):
             self.logger.info("running task in foreground")
             handler.setManager(self)
             self.runTask(handler)

From 1ecba244e6f6fb983556cf541d6076f8fde77bbd Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Aug 20 2025 13:48:18 +0000
Subject: [PATCH 3/3] fix unit tests


---

diff --git a/tests/test_builder/test_get_next_task.py b/tests/test_builder/test_get_next_task.py
index 7ef97d9..d9264bd 100644
--- a/tests/test_builder/test_get_next_task.py
+++ b/tests/test_builder/test_get_next_task.py
@@ -13,6 +13,7 @@ class TestGetNextTask(unittest.TestCase):
 
     def setUp(self):
         self.options = mock.MagicMock()
+        self.options.single = False
         self.session = mock.MagicMock()
         self.tm = koji.daemon.TaskManager(self.options, self.session)
         self.tm.readyForTask = mock.MagicMock()
@@ -65,6 +66,7 @@ class TestTakeTask(unittest.TestCase):
 
     def setUp(self):
         self.options = mock.MagicMock()
+        self.options.single = False
         self.session = mock.MagicMock()
         self.tm = koji.daemon.TaskManager(self.options, self.session)
         self.tm.readyForTask = mock.MagicMock()
diff --git a/tests/test_builder/test_main.py b/tests/test_builder/test_main.py
index eb87964..f687ea0 100644
--- a/tests/test_builder/test_main.py
+++ b/tests/test_builder/test_main.py
@@ -21,6 +21,7 @@ class TestMain(unittest.TestCase):
         # set up task handler
         self.session = mock.MagicMock()
         self.options = mock.MagicMock()
+        self.options.single = False
         self.options.plugin = []
         self.options.sleeptime = 1
         self.options.pluginpath = ''

