[Concept,16/24] pickman: Add a way to update a gitlab merge-request

Message ID 20251217022823.392557-17-sjg@u-boot.org
State New
Headers
Series pickman: Refine the feature set |

Commit Message

Simon Glass Dec. 17, 2025, 2:28 a.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add function to update a merge request's description via the GitLab API.
This is used to update the MR with the agent's conversation log after
processing review comments.

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 tools/pickman/ftest.py      | 42 +++++++++++++++++++++++++++++++++++++
 tools/pickman/gitlab_api.py | 37 ++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+)
  

Patch

diff --git a/tools/pickman/ftest.py b/tools/pickman/ftest.py
index ae4ceaceaaa..ec9e7e91d65 100644
--- a/tools/pickman/ftest.py
+++ b/tools/pickman/ftest.py
@@ -1345,6 +1345,48 @@  class TestCheckAvailable(unittest.TestCase):
             self.assertTrue(result)
 
 
+class TestUpdateMrDescription(unittest.TestCase):
+    """Tests for update_mr_description function."""
+
+    @mock.patch.object(gitlab_api, 'get_remote_url')
+    @mock.patch.object(gitlab_api, 'get_token')
+    @mock.patch.object(gitlab_api, 'AVAILABLE', True)
+    def test_update_mr_description_success(self, mock_token, mock_url):
+        """Test successful MR description update."""
+        mock_token.return_value = 'test-token'
+        mock_url.return_value = 'git@gitlab.com:group/project.git'
+
+        mock_mr = mock.MagicMock()
+        mock_project = mock.MagicMock()
+        mock_project.mergerequests.get.return_value = mock_mr
+
+        with mock.patch('gitlab.Gitlab') as mock_gitlab:
+            mock_gitlab.return_value.projects.get.return_value = mock_project
+
+            result = gitlab_api.update_mr_description('origin', 123,
+                                                      'New description')
+
+            self.assertTrue(result)
+            self.assertEqual(mock_mr.description, 'New description')
+            mock_mr.save.assert_called_once()
+
+    @mock.patch.object(gitlab_api, 'AVAILABLE', False)
+    def test_update_mr_description_not_available(self):
+        """Test update_mr_description when gitlab not available."""
+        with terminal.capture():
+            result = gitlab_api.update_mr_description('origin', 123, 'desc')
+        self.assertFalse(result)
+
+    @mock.patch.object(gitlab_api, 'get_token')
+    @mock.patch.object(gitlab_api, 'AVAILABLE', True)
+    def test_update_mr_description_no_token(self, mock_token):
+        """Test update_mr_description when no token set."""
+        mock_token.return_value = None
+        with terminal.capture():
+            result = gitlab_api.update_mr_description('origin', 123, 'desc')
+        self.assertFalse(result)
+
+
 class TestParseApplyWithPush(unittest.TestCase):
     """Tests for apply command with push options."""
 
diff --git a/tools/pickman/gitlab_api.py b/tools/pickman/gitlab_api.py
index d0128e13f80..508168aa75c 100644
--- a/tools/pickman/gitlab_api.py
+++ b/tools/pickman/gitlab_api.py
@@ -320,6 +320,43 @@  def reply_to_mr(remote, mr_iid, message):
         return False
 
 
+def update_mr_description(remote, mr_iid, desc):
+    """Update a merge request's description
+
+    Args:
+        remote (str): Remote name
+        mr_iid (int): Merge request IID
+        desc (str): New description
+
+    Returns:
+        bool: True on success
+    """
+    if not check_available():
+        return False
+
+    token = get_token()
+    if not token:
+        tout.error('GITLAB_TOKEN environment variable not set')
+        return False
+
+    remote_url = get_remote_url(remote)
+    host, proj_path = parse_url(remote_url)
+
+    if not host or not proj_path:
+        return False
+
+    try:
+        glab = gitlab.Gitlab(f'https://{host}', private_token=token)
+        project = glab.projects.get(proj_path)
+        merge_req = project.mergerequests.get(mr_iid)
+        merge_req.description = desc
+        merge_req.save()
+        return True
+    except gitlab.exceptions.GitlabError as exc:
+        tout.error(f'GitLab API error: {exc}')
+        return False
+
+
 def push_and_create_mr(remote, branch, target, title, desc=''):
     """Push a branch and create a merge request