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(+)
@@ -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."""
@@ -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