From fe1d1c867320eb9cb2499d595866dbaeb47a5feb Mon Sep 17 00:00:00 2001 From: Laurent Desausoi Date: Tue, 27 Aug 2024 09:12:25 +0000 Subject: [PATCH] [FIX] base: prevent rtlcss compilation errors Steps to reproduce: - Include in your assets a CSS file with invalid formatting. - Translate your website in RTL language. > The website will never load and you will be left with a blank page. Cause of the issue: `rtlcss` never exit with a returncode, consequently our error management there is useless. As from `rtlcss` 4.1.0 [1], an error code is returned but only when using a CSS file. In our case, Odoo pass the CSS payload via the `stdin`. A PR [2] has been opened on `rtlcss` to also exit with a return code in this scenario (and log details to `stderr`). In the meantime and also for earlier versions, the error management had to be slightly adjusted. As we cannot deduce any informations from the return code (and the `stderr` is completely empty), we can exploit the fact that, in case of errors, `rtlcss` doesn't output anything to `stdout`. [1]: https://github.com/MohammadYounes/rtlcss/commit/4e625458cb236a4a21cb6d5e54283a9d5244fdc8 [2]: https://github.com/MohammadYounes/rtlcss/pull/342 X-original-commit: 52ebdb54c02fe9e0746cb5107a07f2939b7b23c5 --- odoo/addons/base/models/assetsbundle.py | 12 +++++++----- odoo/addons/test_assetsbundle/__manifest__.py | 3 +++ .../static/invalid_src/css/invalid_css.css | 8 ++++++++ .../test_assetsbundle/tests/test_assetsbundle.py | 11 +++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 odoo/addons/test_assetsbundle/static/invalid_src/css/invalid_css.css diff --git a/odoo/addons/base/models/assetsbundle.py b/odoo/addons/base/models/assetsbundle.py index 29268a8450348..1a833b977029b 100644 --- a/odoo/addons/base/models/assetsbundle.py +++ b/odoo/addons/base/models/assetsbundle.py @@ -678,16 +678,18 @@ def run_rtlcss(self, source): self.css_errors.append(msg) return '' - result = rtlcss.communicate(input=source.encode('utf-8')) - if rtlcss.returncode: - cmd_output = ''.join(misc.ustr(result)) - if not cmd_output: + stdout, stderr = rtlcss.communicate(input=source.encode('utf-8')) + if rtlcss.returncode or (source and not stdout): + cmd_output = ''.join(misc.ustr(stderr)) + if not cmd_output and rtlcss.returncode: cmd_output = "Process exited with return code %d\n" % rtlcss.returncode + elif not cmd_output: + cmd_output = "rtlcss: error processing payload\n" error = self.get_rtlcss_error(cmd_output, source=source) _logger.warning(error) self.css_errors.append(error) return '' - rtlcss_result = result[0].strip().decode('utf8') + rtlcss_result = stdout.strip().decode('utf8') return rtlcss_result def get_preprocessor_error(self, stderr, source=None): diff --git a/odoo/addons/test_assetsbundle/__manifest__.py b/odoo/addons/test_assetsbundle/__manifest__.py index dc9dabc166be0..a6997880cf12b 100644 --- a/odoo/addons/test_assetsbundle/__manifest__.py +++ b/odoo/addons/test_assetsbundle/__manifest__.py @@ -59,6 +59,9 @@ ('include', 'test_assetsbundle.manifest4'), ], 'test_assetsbundle.manifest_multi_module1': [], + 'test_assetsbundle.broken_css': [ + 'test_assetsbundle/static/invalid_src/css/invalid_css.css', + ], 'test_assetsbundle.lazy_test_component': [ 'test_assetsbundle/static/tests/lazy_test_component/**/*', ], diff --git a/odoo/addons/test_assetsbundle/static/invalid_src/css/invalid_css.css b/odoo/addons/test_assetsbundle/static/invalid_src/css/invalid_css.css new file mode 100644 index 0000000000000..0327eb7d73fac --- /dev/null +++ b/odoo/addons/test_assetsbundle/static/invalid_src/css/invalid_css.css @@ -0,0 +1,8 @@ +.rule1 { + color: black, +} + +.rule2 { + color: yellow; !important; +} + diff --git a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py index 0f00d331dd533..3d76573eee652 100644 --- a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py +++ b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py @@ -415,10 +415,21 @@ def test_15_rtl_css_generation(self): # trigger the first generation and, thus, the first save in database self.bundle.css() + # there should be no compilation errors + self.assertEqual(len(self.bundle.css_errors), 0) + # there should be one attachment associated to this bundle self.assertEqual(len(self._any_ira_for_bundle('min.css', rtl=True)), 1) self.assertEqual(len(self.bundle.get_attachments('min.css')), 1) + def test_15_rtl_invalid_css_generation(self): + """ Checks that erroneous css cannot be compiled by rtlcss and that errors are registered """ + self.bundle = self._get_asset('test_assetsbundle.broken_css', rtl=True) + with mute_logger('odoo.addons.base.models.assetsbundle'): + self.bundle.css() + self.assertEqual(len(self.bundle.css_errors), 1) + self.assertIn('rtlcss: error processing payload', self.bundle.css_errors[0]) + def test_16_ltr_and_rtl_css_access(self): """ Checks that the bundle's cache is working, i.e. that the bundle creates only one ir.attachment record when rendered multiple times for rtl direction also check we have two css bundles,