diff --git a/.travis.yml b/.travis.yml index eaa2aa0c..4f327717 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ env: matrix: - LINT_CHECK="1" - TESTS="1" ODOO_REPO="OCA/OCB" EXCLUDE="runbot_buildout" - - TESTS="1" ODOO_REPO="OCA/OCB" EXCLUDE="runbot_travis2docker" + - TESTS="1" ODOO_REPO="OCA/OCB" EXCLUDE="runbot_travis2docker,runbot_push_pot" before_install: # Fix https://github.com/travis-ci/travis-ci/issues/8982#issuecomment-354357640 diff --git a/runbot_push_pot/README.rst b/runbot_push_pot/README.rst new file mode 100644 index 00000000..85d8c556 --- /dev/null +++ b/runbot_push_pot/README.rst @@ -0,0 +1,102 @@ +=========================== +Export translatable strings +=========================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frunbot--addons-lightgray.png?logo=github + :target: https://github.com/OCA/runbot-addons/tree/11.0/runbot_push_pot + :alt: OCA/runbot-addons +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/runbot-addons-11-0/runbot-addons-11-0-runbot_push_pot + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/146/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module exports translations for your builds and pushes them back to your repository. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to: + +#. give the user running your runbot write access to the repositories in question +#. go to one of your stable branches and check the box ``Push Pot`` + +Usage +===== + +This module presupposes a directory structure like:: + + addon_name/i18n/addon_name.pot + +in the root of your repository. + +So if you don't have that, it won't work for you. + +Known issues / Roadmap +====================== + +* investigate if we can do the commit via the API using the exsiting git{hub,lab} token + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Therp BV + +Contributors +~~~~~~~~~~~~ + +* Holger Brunn + +Other credits +~~~~~~~~~~~~~ + +* Odoo Community Association: `Icon `_. + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/runbot-addons `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/runbot_push_pot/__init__.py b/runbot_push_pot/__init__.py new file mode 100644 index 00000000..c8c7f8b1 --- /dev/null +++ b/runbot_push_pot/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2019 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import models diff --git a/runbot_push_pot/__manifest__.py b/runbot_push_pot/__manifest__.py new file mode 100644 index 00000000..ce1faa3a --- /dev/null +++ b/runbot_push_pot/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2019 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "Export translatable strings", + "version": "11.0.1.0.0", + "author": "Therp BV,Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Runbot", + "summary": "Export & push .pot files to your repo", + "depends": [ + 'runbot', + ], + "data": [ + "views/runbot_branch.xml", + ], +} diff --git a/runbot_push_pot/models/__init__.py b/runbot_push_pot/models/__init__.py new file mode 100644 index 00000000..e2133e1d --- /dev/null +++ b/runbot_push_pot/models/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2019 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import runbot_build +from . import runbot_branch diff --git a/runbot_push_pot/models/runbot_branch.py b/runbot_push_pot/models/runbot_branch.py new file mode 100644 index 00000000..6ddcb820 --- /dev/null +++ b/runbot_push_pot/models/runbot_branch.py @@ -0,0 +1,13 @@ +# Copyright 2019 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import fields, models + + +class RunbotBranch(models.Model): + _inherit = 'runbot.branch' + + push_pot = fields.Boolean( + default=False, help='Enable this on your stable branch to have runbot ' + 'push new translatable strings to your repo. Note this will overwrite ' + 'your existing pot file', + ) diff --git a/runbot_push_pot/models/runbot_build.py b/runbot_push_pot/models/runbot_build.py new file mode 100644 index 00000000..14f80677 --- /dev/null +++ b/runbot_push_pot/models/runbot_build.py @@ -0,0 +1,86 @@ +# Copyright 2019 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +import multiprocessing +import subprocess +import tarfile +import tempfile +from odoo import models +from odoo.addons.runbot.common import lock + + +class RunbotBuild(models.Model): + _inherit = 'runbot.build' + + def _job_29_export_translations(self, build, lock_path, log_path): + if not build.branch_id.push_pot or build.build_type == 'rebuild': + return -2 + command, modules = build._cmd() + process = multiprocessing.Process( + target=self._export_translations.__func__, + args=( + None, + command, modules, build.repo_id.path, build.repo_id.name, + build.branch_id.branch_name, '%s-all' % build.dest, lock_path, + log_path, self.env['ir.config_parameter'].get_param( + 'runbot_push_pot.commit_message', + '[UPD] updated translations from runbot', + ), + ), + name='translation export for %s' % build, + ) + process.start() + return process.pid + + def _export_translations( + self, command, modules, repo_path, repo_url, branch_name, db_name, + lock_path, log_path, commit_message, + ): + """Do the actual translation export. Note you can't override this + because we don't use it as a class member""" + lock(lock_path) + with open(log_path, 'w') as log_file, tempfile.NamedTemporaryFile( + suffix='.tgz', delete=False, + ) as translation_file: + command.extend([ + '-d', db_name, + '--i18n-export', translation_file.name, + '--modules', modules, + '--stop-after-init', + ]) + subprocess.check_call( + command, stdout=log_file, stderr=log_file, + ) + translations = tarfile.open(translation_file.name) + with tempfile.TemporaryDirectory() as checkout: + subprocess.run( + ['git', 'clone', repo_path, checkout], + stdout=log_file, stderr=log_file, + ) + + def git(*cmd, **kwargs): + cmd = ('git', '-C', checkout) + cmd + return subprocess.run( + cmd, stdout=log_file, stderr=log_file, **kwargs + ) + + git('checkout', branch_name) + translations.extractall(checkout) + git('add', '.') + diff_lines = [ + line for line in + subprocess.run( + ['git', '-C', checkout, 'diff', 'HEAD', '-U0'], + capture_output=True + ).stdout.decode('utf8').split('\n') + if + # we want only lines from the diff where we actually have + # changes, and we want to ignore the date fields because + # they change for every export + (line.startswith('+') or line.startswith('-')) and not + (line.startswith('+++') or line.startswith('---')) and + 'POT-Creation-Date' not in line and + 'PO-Revision-Date' not in line + ] + if diff_lines: + git('commit', '-m', commit_message) + git('push', repo_url, branch_name) diff --git a/runbot_push_pot/readme/CONFIGURE.rst b/runbot_push_pot/readme/CONFIGURE.rst new file mode 100644 index 00000000..ed904163 --- /dev/null +++ b/runbot_push_pot/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +To configure this module, you need to: + +#. give the user running your runbot write access to the repositories in question +#. go to one of your stable branches and check the box ``Push Pot`` diff --git a/runbot_push_pot/readme/CONTRIBUTORS.rst b/runbot_push_pot/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..b120a956 --- /dev/null +++ b/runbot_push_pot/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Holger Brunn diff --git a/runbot_push_pot/readme/CREDITS.rst b/runbot_push_pot/readme/CREDITS.rst new file mode 100644 index 00000000..cc056a80 --- /dev/null +++ b/runbot_push_pot/readme/CREDITS.rst @@ -0,0 +1 @@ +* Odoo Community Association: `Icon `_. diff --git a/runbot_push_pot/readme/DESCRIPTION.rst b/runbot_push_pot/readme/DESCRIPTION.rst new file mode 100644 index 00000000..16419676 --- /dev/null +++ b/runbot_push_pot/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module exports translations for your builds and pushes them back to your repository. diff --git a/runbot_push_pot/readme/ROADMAP.rst b/runbot_push_pot/readme/ROADMAP.rst new file mode 100644 index 00000000..82b53343 --- /dev/null +++ b/runbot_push_pot/readme/ROADMAP.rst @@ -0,0 +1 @@ +* investigate if we can do the commit via the API using the exsiting git{hub,lab} token diff --git a/runbot_push_pot/readme/USAGE.rst b/runbot_push_pot/readme/USAGE.rst new file mode 100644 index 00000000..059c3626 --- /dev/null +++ b/runbot_push_pot/readme/USAGE.rst @@ -0,0 +1,7 @@ +This module presupposes a directory structure like:: + + addon_name/i18n/addon_name.pot + +in the root of your repository. + +So if you don't have that, it won't work for you. diff --git a/runbot_push_pot/static/description/icon.png b/runbot_push_pot/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/runbot_push_pot/static/description/icon.png differ diff --git a/runbot_push_pot/static/description/index.html b/runbot_push_pot/static/description/index.html new file mode 100644 index 00000000..9e20fac6 --- /dev/null +++ b/runbot_push_pot/static/description/index.html @@ -0,0 +1,452 @@ + + + + + + +Export translatable strings + + + +
+

Export translatable strings

+ + +

Beta License: AGPL-3 OCA/runbot-addons Translate me on Weblate Try me on Runbot

+

This module exports translations for your builds and pushes them back to your repository.

+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to:

+
    +
  1. give the user running your runbot write access to the repositories in question
  2. +
  3. go to one of your stable branches and check the box Push Pot
  4. +
+
+
+

Usage

+

This module presupposes a directory structure like:

+
+addon_name/i18n/addon_name.pot
+
+

in the root of your repository.

+

So if you don’t have that, it won’t work for you.

+
+
+

Known issues / Roadmap

+
    +
  • investigate if we can do the commit via the API using the exsiting git{hub,lab} token
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Therp BV
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+
    +
  • Odoo Community Association: Icon.
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/runbot-addons project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/runbot_push_pot/tests/__init__.py b/runbot_push_pot/tests/__init__.py new file mode 100644 index 00000000..138b7328 --- /dev/null +++ b/runbot_push_pot/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2019 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import test_runbot_push_translation diff --git a/runbot_push_pot/tests/test_runbot_push_translation.py b/runbot_push_pot/tests/test_runbot_push_translation.py new file mode 100644 index 00000000..c80fe4a2 --- /dev/null +++ b/runbot_push_pot/tests/test_runbot_push_translation.py @@ -0,0 +1,104 @@ +# Copyright 2019 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +import mock +import os +import shutil +import subprocess +import tempfile +import time +import odoo +from odoo.tests.common import TransactionCase + + +class TestRunbotPushTranslation(TransactionCase): + @classmethod + def setUpClass(cls): + """Create a demo git repo in the file system and add it to runbot""" + super().setUpClass() + + cls.git_dir = tempfile.mkdtemp() + + def _git(*args): + return subprocess.check_output( + ('git', '-C', cls.git_dir) + args, stderr=subprocess.STDOUT, + ) + + cls._git = lambda *args: _git(*args[1:]) + + module_dir = os.path.join(cls.git_dir, 'runbot_push_pot') + os.mkdir(module_dir) + with open( + os.path.join(module_dir, '__manifest__.py'), 'a' + ) as manifest: + manifest.write('{}') + with open( + os.path.join(module_dir, '__init__.py'), 'a' + ): + pass + + _git('init') + _git('checkout', '-b', '11.0') + _git('add', '.') + _git('commit', '-m', 'initial commit') + + # make the repo bare to allow pusing to it + cls.git_dir = os.path.join(cls.git_dir, '.git') + _git('config', 'core.bare', 'true') + + @classmethod + def tearDownClass(cls): + shutil.rmtree(cls.git_dir) + super().tearDownClass() + + def test_runbot_push_translation(self): + repo = self.env['runbot.repo'].create({ + 'name': self.git_dir, + }) + + repo._update(repo) + repo._scheduler() + build = self.env['runbot.build'].search([('repo_id', '=', repo.id)]) + build._checkout() + build.branch_id.write({'push_pot': True}) + + # we just need to intercept the call where our code would call the + # server to export our translations on the commandline + def export_translations(command, **kwargs): + with open(command[-4], 'wb') as translations: + odoo.tools.trans_export( + None, command[-2:], translations, 'tgz', + self.env.cr, + ) + + # as the module didn't have a pot file before, this will add it + with tempfile.TemporaryDirectory() as run_dir, mock.patch( + 'subprocess.check_call', side_effect=export_translations, + ): + pid = build._job_29_export_translations( + build, + os.path.join(run_dir, 'lock_file'), + os.path.join(run_dir, 'log_file'), + ) + os.waitpid(pid, 0) + + log = self._git('log').decode('utf8') + self.assertIn('[UPD] updated translations from runbot', log) + + # pull changes to runbot's checkout + repo._update(repo) + # sleep a while to be sure enough time passed to generate a + # different timestamp + time.sleep(1) + + # subsequent calls shouldn't change anything + with tempfile.TemporaryDirectory() as run_dir, mock.patch( + 'subprocess.check_call', side_effect=export_translations, + ): + pid = build._job_29_export_translations( + build, + os.path.join(run_dir, 'lock_file'), + os.path.join(run_dir, 'log_file'), + ) + os.waitpid(pid, 0) + + self.assertEqual(log, self._git('log').decode('utf8')) diff --git a/runbot_push_pot/views/runbot_branch.xml b/runbot_push_pot/views/runbot_branch.xml new file mode 100644 index 00000000..567463a1 --- /dev/null +++ b/runbot_push_pot/views/runbot_branch.xml @@ -0,0 +1,12 @@ + + + + runbot.branch + + + + + + + +