From d1fa9eb66841e2e48a975ea371cbc7b7c111072c Mon Sep 17 00:00:00 2001 From: Jussi Vatjus-Anttila Date: Fri, 17 Jan 2025 14:02:06 +0200 Subject: [PATCH] Add group syntax logging for CI group large json prints in GitHub CI --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/jupe/py-lockable?shareId=XXXX-XXXX-XXXX-XXXX). --- lockable/lockable.py | 10 ++++++++++ lockable/provider.py | 8 ++++---- tests/test_Lockable.py | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/lockable/lockable.py b/lockable/lockable.py index caeeb38..5d6c465 100644 --- a/lockable/lockable.py +++ b/lockable/lockable.py @@ -30,6 +30,16 @@ def invariant(true, message): raise ResourceNotFound(message) +def log_with_group(logger, title, message): + """ Log message with group syntax if running in GitHub Actions """ + if os.getenv('CI') == 'true' and os.getenv('GITHUB_ACTIONS') == 'true': + print(f"::group::{title}") + logger.info(message) + print("::endgroup::") + else: + logger.info(message) + + class Lockable: """ Base class for Lockable. It handle low-level functionality. diff --git a/lockable/provider.py b/lockable/provider.py index 40ff8c2..1f6882e 100644 --- a/lockable/provider.py +++ b/lockable/provider.py @@ -6,6 +6,7 @@ from typing import List from pydash import filter_, count_by +from lockable.lockable import log_with_group MODULE_LOGGER = logging.getLogger(__name__) @@ -36,9 +37,8 @@ def set_resources_list(self, resources_list: list): assert isinstance(resources_list, list), 'resources_list is not an list' Provider._validate_json(resources_list) self._resources = resources_list - MODULE_LOGGER.debug('Resources loaded: ') - for resource in self._resources: - MODULE_LOGGER.debug(json.dumps(resource)) + + log_with_group(MODULE_LOGGER, 'Resources loaded:', json.dumps(self._resources, indent=2)) @staticmethod def _validate_json(data: List[dict]): @@ -50,5 +50,5 @@ def _validate_json(data: List[dict]): duplicates = filter_(counts.keys(), lambda key: counts[key] > 1) if duplicates: - MODULE_LOGGER.warning('Duplicates: %s', duplicates) + log_with_group(MODULE_LOGGER, f'Duplicates: {duplicates}') raise ValueError(f"Invalid json, duplicate ids in {duplicates}") diff --git a/tests/test_Lockable.py b/tests/test_Lockable.py index 0aacd3d..97acbcd 100644 --- a/tests/test_Lockable.py +++ b/tests/test_Lockable.py @@ -8,8 +8,10 @@ from contextlib import contextmanager from tempfile import TemporaryDirectory from unittest import TestCase +from unittest.mock import patch +from io import StringIO -from lockable.lockable import Lockable, ResourceNotFound, Allocation +from lockable.lockable import Lockable, ResourceNotFound, Allocation, log_with_group @contextmanager @@ -280,3 +282,34 @@ def test_lock_many_existing_allocation(self): self.assertTrue(end - start < 2 and end - start > 1) self.assertTrue(os.path.exists(os.path.join(tmpdirname, 'a.pid'))) self.assertFalse(os.path.exists(os.path.join(tmpdirname, 'b.pid'))) + + def test_log_with_group(self): + logger = logging.getLogger('test_logger') + logger.setLevel(logging.INFO) + log_output = [] + + def mock_info(message): + log_output.append(message) + + logger.info = mock_info + + os.environ['CI'] = 'true' + os.environ['GITHUB_ACTIONS'] = 'true' + + with patch('sys.stdout', new=StringIO()) as fake_out: + log_with_group(logger, "Test Group", "Test message") + self.assertIn("::group::Test Group", fake_out.getvalue()) + self.assertIn("::endgroup::", fake_out.getvalue()) + + self.assertEqual(log_output, ["Test message"]) + + os.environ['CI'] = 'false' + os.environ['GITHUB_ACTIONS'] = 'false' + + log_output.clear() + with patch('sys.stdout', new=StringIO()) as fake_out: + log_with_group(logger, "Test Group", "Test message") + self.assertNotIn("::group::Test Group", fake_out.getvalue()) + self.assertNotIn("::endgroup::", fake_out.getvalue()) + + self.assertEqual(log_output, ["Test message"])