diff --git a/github_connector/README.rst b/github_connector/README.rst new file mode 100644 index 00000000..0e4cbcd0 --- /dev/null +++ b/github_connector/README.rst @@ -0,0 +1,394 @@ +================ +Github Connector +================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:5ece01ee45fb93aeefdf08b73b75977b4a0dee9ea4be2d45adaf4e9f3272a6f2 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Finterface--github-lightgray.png?logo=github + :target: https://github.com/OCA/interface-github/tree/18.0/github_connector + :alt: OCA/interface-github +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/interface-github-18-0/interface-github-18-0-github_connector + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/interface-github&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows you to: + +- Fetch into Odoo social information from Github (Organizations, Teams, + Users) +- Fetch into Odoo Code structure information from Github (Repositories, + Branches) +- Download source code from Github +- Analyze repository code from rules previously created + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +To install this addon, you need to install some python dependencies + +.. code:: bash + + sudo pip install PyGitHub + sudo pip install pygount + sudo pip install pathspec + sudo pip install GitPython + +Analysis source code is generated by +https://github.com/roskakori/pygount + +Configuration +============= + +Once installed, you have to: + +1. Open your odoo.conf file and add extra settings to mention Github + credentials, and the local path where the source code will be + downloaded: + + - ``source_code_local_path = /workspace/source_code/`` + +Note: you can define the route as environment variable using the key +SOURCECODE_LOCAL_PATH + +Note: make sure that Odoo process has read / write access on that folder + + - ``github_token = your_github_access_token`` + +Note: The login/password auth has been deprecated by GitHub. +https://docs.github.com/en/rest/overview/other-authentication-methods#via-username-and-password + +1. Go to 'Settings' / 'Technical' / 'Parameters' / 'System Parameters' + and define the following values: + + 1. ``github.max_try``: number of call to the API before an error is + raised. The more unstable/slow your connection, the higher should + be this value + 2. ``git.partial_commit_during_analysis``: Set to ``True`` if you + want to commit the result of the analysis in the database after + each repository analysis. We recommend to set to ``True`` when you + perform the initial download (potentially with a lot of + repositories) in order to reduce the size of the transaction + + |image| + +2. Go to your(s) user(s) form to add them in the new 'Connector Github + Manager' groups. The members of this group will have the possibility + to run Github synchronization. + +Technical Information +--------------------- + +This module provides 4 crons that you can enable: + +- Synchronize All Organizations and Teams (``cron_update_organization``) +- Synchronize Branches List for All repositories + (``cron_update_branch_list``) +- Download Source Code for All Github Branches (``cron_download_code``) +- Analyze Source Code for All Github Branches (``cron_analyze_code``) + +.. |image| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_settings.png + +Usage +===== + +Initial upload from Github +-------------------------- + +To fetch information from Github, you have to: + +1. go to 'Github' / 'Settings' / 'Sync Object' + +2. Select the object type you want to synchronize and its Gthub name + + |image| + +3. Once done for your organization(s), go to 'Github' / 'Github + Commnunity' / 'Organizations' + + |image1| + +4. Optionally, once organization is created, you can create series for + your projects. Go to 'Github' / 'Organizations' / click on your + organization / 'Organization Series' Tabs + + |image2| + +Select branches to download +--------------------------- + +This setting will prevent to download undesired branches, downloading +only main branches (releases): + +1. In the 'Settings' tab, set repositories you don't want to download + (or repositories you want to download). If 'Specific repositories' is + set, 'Ignored Repositories' value is ignored. + +2. In the 'Settings' tab, set the URL of the 'External Services' you use + for Continuous Integration and Coverage. + + |image3| + +3. Once done, click on buttons 'Syncs', to synchronize repositories, + teams and members. (This process can take a while depending of your + size) + + |image4| + +Team / members synchronization +------------------------------ + +You can synchronize members teams: + +1. Go to 'Teams' / tree view / 'Actions' / 'Update from Github'. + + |image5| + +2. In each team, you can see the members list and the role of the + members + + |image6| + +3. In each team, you can see the repositories list but not the + permissions of the team. (See 'Known Issues' Section) + + |image7| + +Repositories synchronization +---------------------------- + +You can synchronize the branches of your repositories: + +1. Go to 'Repositories' / tree view / 'Actions' / 'Update from Github' + + |image8| + +2. In each repository, you can see the main branches list and the size + of code source. + + |image9| + +Fetching the source code +------------------------ + +Finally, you can download locally the source code of all your branches: + +1. Go to 'Repository Branches' / tree view / 'Actions' / 'Download and + Analyse Source Code'. + + |image10| + +2. In the tree view you can update manually source code or refresh + analysis. + + |image11| + +Analysis source code +-------------------- + +It's possible to create custom analysis rules that relate to a GitHub +organization, GitHub repository and/or GitHub repository branch to +analyze code. + +1. Go to 'Settings' / 'Analysis rule groups' and create records that + allow to organize the rules +2. Go to 'Settings' / 'Analysis rules' and create rules, for example: + +.. + + 1. Name: All code, Group: General, Paths: ``*`` + 2. Name: Python code, Group: General, Paths: ``*.py`` + 3. Name: Xml code, Group: General, Paths: ``*.xml`` + 4. Name: Repository 1, Group: Custom, Paths: /path/ + +Note: Paths field in 'Analysis rule' allow to put multiple paths for +line, path format is according to +https://git-scm.com/docs/gitignore#_pattern_format + +1. Go to *GitHub > GitHub Community > Organizations* and define Analysis + rules (optional) +2. Go to *GitHub > GitHub Repository > Repositories* and define Analysis + rules (optional) +3. Go to *GitHub > GitHub Repository > Repository Branches* and define + Analysis rules (optional) + +Analysis source code is executed when 'Update Source Code Analysis' +button in some 'Repository Branch', get all the Analysis rules +(Repository + Organization) and analyze code and generate info about it +Other option to Analysis source code is in cron called 'Analyze Source +Code for All Github Branches' + +You can see in 'Repository Branch' / 'Code Analysis' the info obtained +from analysis rules. + +Data creation in Github +----------------------- + +You have the possibility to creates two items in Github directly from +Odoo + +1. Teams: + + 1. Go to 'Settings' / 'Create Team in Github'. + 2. Set the information and click on Create in Github. + 3. Odoo will try to create the team. If access right and datas are + correct, the creation will be done directly in Github + 4. Later on, a synchronization will be performed, to create the + according team in the Odoo instance. + + |image12| + +2. Repositories: + + 1. Go to 'Settings' / 'Create Team in Github'. + 2. Set the information and click on Create in Github. + + |image13| + +Note +---- + +Analysis in this module is basic: for the time being, it just gives +branches size. + +Nevertheless, you can develop an extra Odoo Custom module to extend +analysis function and get extra statistics, depending on your needs. + +In that way, you can see the module githubconnector_odoo, if your +repositories contain Odoo modules. + +Reporting +--------- + +This module provides several reports + +**Branches by Serie** + +|image14| + +**Sizes by Serie** + +|image15| + +**Repository branch analysis rule** + +|image16| + +.. |image| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/sync_organization.png +.. |image1| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_organization_kanban.png +.. |image2| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_organization_series.png +.. |image3| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_organization_external_services.png +.. |image4| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_organization_sync_buttons.png +.. |image5| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_team_kanban.png +.. |image6| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_team_partner_kanban.png +.. |image7| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_team_repository_kanban.png +.. |image8| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_repository_kanban.png +.. |image9| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_repository_branch_kanban.png +.. |image10| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/wizard_download_analyze.png +.. |image11| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_repository_branch_list.png +.. |image12| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/wizard_create_team.png +.. |image13| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/wizard_create_repository.png +.. |image14| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/reporting_branches_by_serie.png +.. |image15| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/reporting_sizes_by_serie.png +.. |image16| image:: https://raw.githubusercontent.com/OCA/interface-github/18.0/github_connector/static/description/github_repository_branch_rule_info_report.png + +Known issues / Roadmap +====================== + +- For the time being, Github API doesn't provide some informations that + are available by the classic UI, that includes: + + 1. team hierarchy: the field is present in the model + githubteam.parent_id, but unused. + +- Possible improvements: + + 1. Create a new module githubconnector_website, that could display + teams / repositories / branches informations for non logged users. + 2. Analyze commits (author, quantity by series, etc...): this feature + has been partially implemented in a V8.0 PR. + 3. Synchronize Pull Request, Issues, Comments: this feature has been + partially implemented in a V8.0 PR. + +- Refactor the github connector: + + A python library called PyGitHub is available. It could be interesting + to use it, instead of using custom code. However, this lib doesn't + provide good access to child object, generating for the time being, + unnecessary API calls. For example, updating a repository should call + before a call to the parent organization (The current module is so + faster). + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* GRAP +* Akretion +* Tecnativa + +Contributors +------------ + +- Sylvain LE GAL (https://twitter.com/legalsylvain) +- Sébastien BEAU (sebastien.beau@akretion.com) +- Benoît GUILLOT (benoit.guillot@akretion.com) +- Enrique Martín (enriquemartin@digital5.es) +- `Tecnativa `__:", + + - Pedro M. Baeza + - Vicent Cubells + - Alexandre Díaz + - Ernesto Tejeda + - Carlos Roca + - Víctor Martínez + - João Marques + +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/interface-github `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/github_connector/__init__.py b/github_connector/__init__.py new file mode 100644 index 00000000..cf6083cf --- /dev/null +++ b/github_connector/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import report +from . import wizards diff --git a/github_connector/__manifest__.py b/github_connector/__manifest__.py new file mode 100644 index 00000000..acbd90fe --- /dev/null +++ b/github_connector/__manifest__.py @@ -0,0 +1,48 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# @author: Sébastien BEAU +# Copyright 2019 Tecnativa - Cristina Martin R. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Github Connector", + "summary": "Synchronize information from Github repositories", + "version": "18.1.0", + "category": "Connector", + "license": "AGPL-3", + "author": "Odoo Community Association (OCA), GRAP, Akretion, Tecnativa", + "website": "https://github.com/OCA/interface-github", + "depends": ["base", "web"], + "data": [ + "security/ir_model_category.xml", + "security/res_groups.xml", + "security/ir.model.access.csv", + "data/ir_config_parameter.xml", + "data/ir_cron.xml", + "wizards/view_wizard_load_github_model.xml", + "views/view_reporting.xml", + "views/view_github_team_partner.xml", + "views/view_github_team_repository.xml", + "views/action.xml", + "views/view_res_partner.xml", + "views/view_github_analysis_rule.xml", + "views/view_github_analysis_rule_group.xml", + "views/view_github_organization.xml", + "views/view_github_repository.xml", + "views/view_github_repository_branch.xml", + "views/view_github_team.xml", + "views/menu.xml", + "report/github_repository_branch_rule_info_report_view.xml", + "wizards/view_wizard_create_team.xml", + "wizards/view_wizard_create_repository.xml", + ], + "demo": [ + "demo/github_analysis_rule_group_demo.xml", + "demo/github_analysis_rule_demo.xml", + "demo/github_organization.xml", + "demo/github_organization_serie.xml", + ], + "installable": True, + "external_dependencies": { + "python": ["GitPython", "pygount", "pathspec", "PyGithub"] + }, +} diff --git a/github_connector/data/ir_config_parameter.xml b/github_connector/data/ir_config_parameter.xml new file mode 100644 index 00000000..9013e390 --- /dev/null +++ b/github_connector/data/ir_config_parameter.xml @@ -0,0 +1,16 @@ + + + + + github.max_try + 5 + + + git.partial_commit_during_analysis + True + + diff --git a/github_connector/data/ir_cron.xml b/github_connector/data/ir_cron.xml new file mode 100644 index 00000000..ee3c7197 --- /dev/null +++ b/github_connector/data/ir_cron.xml @@ -0,0 +1,48 @@ + + + + + Synchronize All Organizations and Teams + 1 + + + days + code + + model.cron_update_organization_team() + + + Synchronize Branches List for All Repositories + 1 + + + days + code + + model.cron_update_branch_list() + + + Download Source Code for All Github Branches + 1 + + + days + code + + model.cron_download_all() + + + Analyze Source Code for All Github Branches + 1 + + + days + code + + model.cron_analyze_all() + + diff --git a/github_connector/demo/github_analysis_rule_demo.xml b/github_connector/demo/github_analysis_rule_demo.xml new file mode 100644 index 00000000..244d4b1f --- /dev/null +++ b/github_connector/demo/github_analysis_rule_demo.xml @@ -0,0 +1,39 @@ + + + + + Python files + + + *.py + !/setup + !.* + + + + XML files + + + *.xml + !/setup + !.* + + + + JS files + + + *.js + !/setup + !.* + + + + Test files + + /tests/*.py + + diff --git a/github_connector/demo/github_analysis_rule_group_demo.xml b/github_connector/demo/github_analysis_rule_group_demo.xml new file mode 100644 index 00000000..c4723242 --- /dev/null +++ b/github_connector/demo/github_analysis_rule_group_demo.xml @@ -0,0 +1,10 @@ + + + + + Odoo modules + + diff --git a/github_connector/demo/github_organization.xml b/github_connector/demo/github_organization.xml new file mode 100644 index 00000000..5ba610e4 --- /dev/null +++ b/github_connector/demo/github_organization.xml @@ -0,0 +1,32 @@ + + + + + Odoo Community Association + OCA + 7600578 + https://codecov.io/gh/{organization_name}/{repository_name}/branch/{branch_name} + https://travis-ci.org/{organization_name}/{repository_name} + + + diff --git a/github_connector/demo/github_organization_serie.xml b/github_connector/demo/github_organization_serie.xml new file mode 100644 index 00000000..9db7cb96 --- /dev/null +++ b/github_connector/demo/github_organization_serie.xml @@ -0,0 +1,58 @@ + + + + + 2 + 6.0 + + + + 3 + 6.1 + + + + 4 + 7.0 + + + + 5 + 8.0 + + + + 6 + 9.0 + + + + 7 + 10.0 + + + + 8 + 11.0 + + + + 9 + 12.0 + + + + 10 + 13.0 + + + + 11 + 14.0 + + + diff --git a/github_connector/i18n/es.po b/github_connector/i18n/es.po new file mode 100644 index 00000000..134b7fe3 --- /dev/null +++ b/github_connector/i18n/es.po @@ -0,0 +1,1463 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * github_connector +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-08-18 06:55+0000\n" +"PO-Revision-Date: 2023-08-18 08:56+0200\n" +"Last-Translator: Víctor Martínez \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Poedit 3.0.1\n" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_repository_branch_rule_info +msgid " Github Repository Branch Rule Info" +msgstr " Información sobre la rama del repositorio Github" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_tree +msgid "# Branches" +msgstr "# Ramas" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__code_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__code_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__code_count +msgid "# Code" +msgstr "# Código" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__documentation_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__documentation_count +msgid "# Doc." +msgstr "# Doc." + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__documentation_count +msgid "# Documentation" +msgstr "# Documentación" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__empty_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__empty_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__empty_count +msgid "# Empty" +msgstr "# Vacío" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__string_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__string_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__string_count +msgid "# String" +msgstr "# Cadena" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__total_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__total_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__total_count +msgid "# Total" +msgstr "# Total" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_kanban +msgid "" +"\n" +" \n" +" " +msgstr "" +"\n" +" \n" +" " + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_kanban +msgid "" +"\n" +" \n" +" " +msgstr "" +"\n" +" \n" +" " + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_kanban +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_kanban +msgid "" +"\n" +" \n" +" " +msgstr "" +"\n" +" \n" +" " + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "" +" Members\n" +" Member" +msgstr "" +" Miembros\n" +" Miembro" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +msgid "" +" Branches\n" +" Branch" +msgstr "" +" Ramas\n" +" Rama" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +msgid "" +" Teams\n" +" Team" +msgstr "" +" Equipos\n" +" Equipo" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "" +" Repositories\n" +" Repository" +msgstr "" +" Repositorios\n" +" Repositorio" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "" +" Teams\n" +" Team" +msgstr "" +" Equipos\n" +" Equipo" + +#. module: github_connector +#: code:addons/github_connector/models/res_partner.py:0 +#, python-format +msgid "A company ('%s') can not have a Github login associated." +msgstr "Una compañía ('%s') no puede tener un login de Github asociado." + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_repository__permission__admin +msgid "Admin" +msgstr "Administrador" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__analysis_rule_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__analysis_rule_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__analysis_rule_id +msgid "Analysis Rule" +msgstr "Regla de análisis" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__analysis_rule_ids +#: model:ir.model.fields,field_description:github_connector.field_github_repository__analysis_rule_ids +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__analysis_rule_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__analysis_rule_ids +msgid "Analysis Rules" +msgstr "Reglas de análisis" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__analysis_rule_info_ids +msgid "Analysis Rules (info)" +msgstr "Reglas de análisis (información)" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_analysis_rule +msgid "Analysis rule" +msgstr "Regla de análisis" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_analysis_rule_group +msgid "Analysis rule group" +msgstr "Grupo de regla de análisis" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github_analysis_rule_group +msgid "Analysis rule groups" +msgstr "Grupos de reglas de análisis" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github_analysis_rule +msgid "Analysis rules" +msgstr "Reglas de análisis" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Analyze Source Code" +msgstr "Analizar el código fuente" + +#. module: github_connector +#: model:ir.actions.server,name:github_connector.cron_analyze_code_ir_actions_server +#: model:ir.cron,cron_name:github_connector.cron_analyze_code +#: model:ir.cron,name:github_connector.cron_analyze_code +msgid "Analyze Source Code for All Github Branches" +msgstr "Analizar el código fuente de todas las ramas de Github" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_repository_branch__state__analyzed +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "Analyzed" +msgstr "Analizado" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_partner_from_team_kanban +msgid "Avatar" +msgstr "Avatar" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__repository_branch_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__repository_branch_ids +msgid "Branches" +msgstr "Ramas" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +msgid "Branches Sync." +msgstr "Sincronización de ramas" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_branch_by_serie +#: model:ir.ui.menu,name:github_connector.menu_branch_by_serie +msgid "Branches by Serie" +msgstr "Ramas por Serie" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__ci_url +msgid "CI URL" +msgstr "CI URL" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__ci_url_pattern +msgid "CI URL Pattern" +msgstr "Patrón CI URL" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_load_github_model_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_res_partner__is_bot_account +#: model:ir.model.fields,help:github_connector.field_res_users__is_bot_account +msgid "Check this box if this account is a bot or similar." +msgstr "Marque esta casilla si esta cuenta es un bot o similar." + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team__privacy__closed +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_create_team__privacy__closed +msgid "Closed" +msgstr "Cerrado" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +msgid "Code Analysis" +msgstr "Análisis de código" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__color +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__color +msgid "Color Index" +msgstr "Índice de color" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__complete_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__complete_name +#: model:ir.model.fields,field_description:github_connector.field_github_team__complete_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__complete_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__complete_name +msgid "Complete Name" +msgstr "Nombre completo" + +#. module: github_connector +#: model:res.groups,name:github_connector.group_github_connector_manager +msgid "Connector Github Manager" +msgstr "Conector Responsable Github" + +#. module: github_connector +#: model:res.groups,name:github_connector.group_github_connector_user +msgid "Connector Github User" +msgstr "Conector Usuario Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_res_partner +msgid "Contact" +msgstr "Contacto" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__context_search_default_partner_id +msgid "Context Search Default Partner" +msgstr "Búsqueda de contexto por defecto del contacto" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__context_search_default_repository_id +msgid "Context Search Default Repository" +msgstr "Búsqueda de contexto por defecto del repositorio" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__context_search_default_team_id +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__context_search_default_team_id +msgid "Context Search Default Team" +msgstr "Búsqueda de contexto por defecto del equipo" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__coverage_url +msgid "Coverage URL" +msgstr "URL de cobertura" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__coverage_url_pattern +msgid "Coverage URL Pattern" +msgstr "Patrón URL de cobertura" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_create_date +msgid "Create Date on Github" +msgstr "Fecha de creación en Github" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_wizard_create_repository +msgid "Create Repo in Github" +msgstr "Repositorio creado en Github" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_wizard_create_repository +msgid "Create Repo in Github Wizard" +msgstr "Crear repositorio en el asistente de Github" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_wizard_create_team +msgid "Create Team in Github" +msgstr "Equipo creado en Github" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_wizard_create_team +msgid "Create Team in Github Wizard" +msgstr "Crear equipo en el asistente de Github" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_team_form +msgid "Create in Github" +msgstr "Creado en Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_organization__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__create_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__create_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__create_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_analysis_rule__paths +#: model:ir.model.fields,help:github_connector.field_github_repository_branch_rule_info_report__paths +msgid "Define with pathspec especification" +msgstr "Definir con especificación de pathspec" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__description +#: model:ir.model.fields,field_description:github_connector.field_github_repository__description +#: model:ir.model.fields,field_description:github_connector.field_github_team__description +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__description +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__description +msgid "Description" +msgstr "Descripción" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_organization__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_team__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__display_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__display_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__display_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__display_name +msgid "Display Name" +msgstr "Nombre a mostrar" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Download Source Code" +msgstr "Descargar código fuente" + +#. module: github_connector +#: model:ir.actions.server,name:github_connector.cron_download_code_ir_actions_server +#: model:ir.cron,cron_name:github_connector.cron_download_code +#: model:ir.cron,name:github_connector.cron_download_code +msgid "Download Source Code for All Github Branches" +msgstr "Descargar código fuente de todas las ramas de Github" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "Duplicate object with Github login %s" +msgstr "Objeto duplicado con Github login %s" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__email +msgid "Email" +msgstr "Email" + +#. module: github_connector +#: code:addons/github_connector/models/github_repository_branch.py:0 +#, python-format +msgid "" +"Error when trying to create the folder %s\n" +" Please check Odoo Access Rights." +msgstr "" +"Error al intentar crear la carpeta %s\n" +" Por favor revise los Derechos de Acceso de Odoo." + +#. module: github_connector +#: code:addons/github_connector/models/github_repository_branch.py:0 +#, python-format +msgid "" +"Error when trying to create the main folder %(path)s\n" +" Please check Odoo Access Rights.\n" +" %(error)s" +msgstr "" +"Error al intentar crear la carpeta principal %(path)s\n" +" Por favor, compruebe los derechos de acceso de Odoo.\n" +" %(error)s" + +#. module: github_connector +#: code:addons/github_connector/models/github_repository_branch.py:0 +#, python-format +msgid "" +"Error when updating the branch %(branch)s in the local folder %(path)s.\n" +"Deleting the local folder and trying again." +msgstr "" +"Error al actualizar la rama %(branch)s en la carpeta local %(path)s.\n" +"Borrando la carpeta local e intentándolo de nuevo." + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "External Services" +msgstr "Servicios externos" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "" +"Feature not Implemented : Please define 'get_github_base_obj_for_creation' " +"function in child model." +msgstr "" +"Característica no implementada : Defina la función " +"'get_github_base_obj_for_creation' en el modelo hijo." + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "" +"Feature not Implemented : Please define 'github_login_field' function in " +"child model." +msgstr "" +"Característica no implementada : Defina la función 'github_login_field' en " +"el modelo hijo." + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Full Sync" +msgstr "Sincronización completa" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Github" +msgstr "Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_analysis_rule +msgid "Github Analysis Rule" +msgstr "Regla de análisis de Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_analysis_rule_group +msgid "Github Analysis Rule Group" +msgstr "Grupo de regla de análisis de Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_analysis_rule_info_mixin +msgid "Github Analysis Rule Mixin" +msgstr "Mixin de reglas de análisis de Github" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github_community +msgid "Github Community" +msgstr "Comunidad Github" + +#. module: github_connector +#: model:ir.module.category,name:github_connector.module_category_github_connector +msgid "Github Connector" +msgstr "Conector Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_id_external +msgid "Github Id" +msgstr "Id Github" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_search +msgid "Github Login" +msgstr "Acceso a Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__name +msgid "Github Name" +msgstr "Nombre Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_organization_serie +msgid "Github Organization Serie" +msgstr "Serie Organización Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_repository +#: model:ir.ui.menu,name:github_connector.menu_github_repository_root +msgid "Github Repository" +msgstr "Repositorio Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_repository_branch +msgid "Github Repository Branch" +msgstr "Rama de repositorio Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_repository_branch_rule_info_report +msgid "Github Repository Branch Rule Info Report" +msgstr "Informe de regla de rama de repositorio de Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_team +msgid "Github Team" +msgstr "Equipo Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_team_partner +msgid "Github Team Partner" +msgstr "Contacto de equipo Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_team_repository +msgid "Github Team Repository" +msgstr "Repositorio de equipo Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_name +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_name +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_name +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_name +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_name +msgid "Github Technical Name" +msgstr "Nombre técnico Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__github_type +msgid "Github Type Name" +msgstr "Tipo de nombre Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_url +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_url +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_url +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_url +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_url +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_url +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_url +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_url +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_url +msgid "Github URL" +msgstr "URL Github" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_abstract_github_model +msgid "Github abstract model" +msgstr "Github modelo abstracto" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_organization +msgid "Github organization" +msgstr "Organización Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__group_id +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__group_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__group_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__group_id +msgid "Group" +msgstr "Grupo" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_search +msgid "Group By" +msgstr "Grupo por" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_search +msgid "Has Github Account" +msgstr "Tiene cuenta Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__id +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__id +#: model:ir.model.fields,field_description:github_connector.field_github_organization__id +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__id +#: model:ir.model.fields,field_description:github_connector.field_github_repository__id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__id +#: model:ir.model.fields,field_description:github_connector.field_github_team__id +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__id +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__id +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__id +msgid "ID" +msgstr "ID" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_repository__inhibit_inherited_rules +#: model:ir.model.fields,help:github_connector.field_wizard_create_repository__inhibit_inherited_rules +msgid "" +"If checked, the analysis rules to be used will be only those set here.\n" +" If unchecked, the analysis rules to be used will be those set in the " +"organization and those set here." +msgstr "" +"Si está marcada, las reglas de análisis que se utilizarán serán sólo las " +"definidas aquí.\n" +" Si no está marcada, las reglas de análisis que se utilizarán serán las " +"definidas en la organización y las definidas aquí." + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_repository_branch__inhibit_inherited_rules +msgid "" +"If checked, the analysis rules to be used will be only those set here.\n" +" If unchecked, the analysis rules to be used will be those set in the " +"organization, repository and those set here." +msgstr "" +"Si está marcada, las reglas de análisis que se utilizarán serán sólo las " +"definidas aquí.\n" +" Si no está marcada, las reglas de análisis que se utilizarán serán las " +"definidas en la organización, el repositorio y las definidas aquí." + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_repository__is_ignored +#: model:ir.model.fields,help:github_connector.field_wizard_create_repository__is_ignored +msgid "" +"If checked, the branches will not be synchronized, and the code source will " +"this way not be downloaded and analyzed. To ignore a repository, go to the " +"organization and add the file 'Ignored Repositories'." +msgstr "" +"Si está marcada, las ramas no se sincronizarán, y el código fuente no se " +"descargará ni analizará. Para ignorar un repositorio, vaya a la organización " +"y añada el archivo 'Repositorios ignorados'." + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__ignored_repository_names +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Ignored Repositories" +msgstr "Repositorios ignorados" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__image +msgid "Image" +msgstr "Imagen" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__inhibit_inherited_rules +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__inhibit_inherited_rules +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__inhibit_inherited_rules +msgid "Inhibit inherited rules" +msgstr "Inhibir reglas heredadas" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "Invalid name '%s' provided" +msgstr "Nombre no válido '%s' proporcionado" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_res_partner__is_bot_account +#: model:ir.model.fields,field_description:github_connector.field_res_users__is_bot_account +msgid "Is Bot Github Account" +msgstr "Es Cuenta Bot de Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__is_ignored +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__is_ignored +msgid "Is Ignored" +msgstr "Es ignorado" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__last_analyze_date +msgid "Last Analyze Date" +msgstr "Última fecha de análisis" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__last_download_date +msgid "Last Download Date" +msgstr "Última fecha de descarga" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_organization____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_repository____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_team____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository____last_update +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository____last_update +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team____last_update +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_last_sync_date +msgid "Last Sync Date with Github" +msgstr "Última fecha de sincronización con Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_organization__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__write_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__write_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__write_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_write_date +msgid "Last Write Date on Github" +msgstr "Última fecha de escritura en Github" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Light Sync" +msgstr "Sincronización básica" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_load_github_model_form +msgid "Load From Github" +msgstr "Cargar desde Github" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_wizard_load_github_model +msgid "Load from Github Wizard" +msgstr "Cargar desde el asistente de Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__local_path +msgid "Local Path" +msgstr "Ruta local" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__location +msgid "Location" +msgstr "Ubicación" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_partner__role__maintainer +msgid "Maintainer" +msgstr "Mantenedor" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "Maximum attempts reached." +msgstr "Intentos máximos alcanzados." + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_kanban +msgid "Mb" +msgstr "Mb" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__partner_id +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_partner__role__member +msgid "Member" +msgstr "Miembro" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_team_partner_from_team +#: model:ir.model.fields,field_description:github_connector.field_github_organization__member_ids +#: model:ir.model.fields,field_description:github_connector.field_github_team__partner_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__partner_ids +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +msgid "Members" +msgstr "Miembros" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +msgid "Members Sync." +msgstr "Sincronización de miembros" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__name +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__name +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__name +#: model:ir.model.fields,field_description:github_connector.field_github_repository__name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__name +#: model:ir.model.fields,field_description:github_connector.field_github_team__name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__name +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "Name" +msgstr "Nombre" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_search +msgid "Name or Description" +msgstr "Nombre o descripción" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_search +msgid "Name or Github Name" +msgstr "Nombre o nombre de Github" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__repository_branch_qty +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__repository_branch_qty +msgid "Number of Branches" +msgstr "Número de ramas" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__member_qty +#: model:ir.model.fields,field_description:github_connector.field_github_team__partner_qty +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__partner_qty +msgid "Number of Members" +msgstr "Número de miembros" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_res_partner__organization_qty +#: model:ir.model.fields,field_description:github_connector.field_res_users__organization_qty +msgid "Number of Organizations" +msgstr "Número de organizaciones" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__repository_qty +#: model:ir.model.fields,field_description:github_connector.field_github_team__repository_qty +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__repository_qty +msgid "Number of Repositories" +msgstr "Número de repositorios" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__organization_serie_qty +msgid "Number of Series" +msgstr "Número de series" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__team_qty +#: model:ir.model.fields,field_description:github_connector.field_github_repository__team_qty +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_team_qty +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_team_qty +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__team_qty +msgid "Number of Teams" +msgstr "Número de equipos" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__organization_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository__organization_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__organization_id +#: model:ir.model.fields,field_description:github_connector.field_github_team__organization_id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__organization_id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__organization_id +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_load_github_model__github_type__organization +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_search +msgid "Organization" +msgstr "Organización" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__name +msgid "Organization Name" +msgstr "Nombre de organización" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__organization_serie_id +msgid "Organization Serie" +msgstr "Serie de organización" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__organization_serie_ids +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Organization Series" +msgstr "Series de organización" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__organization_serie_id +msgid "Organization serie" +msgstr "Serie de organización" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_organization +#: model:ir.model.fields,field_description:github_connector.field_res_partner__organization_ids +#: model:ir.model.fields,field_description:github_connector.field_res_users__organization_ids +#: model:ir.ui.menu,name:github_connector.menu_github_organization +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Organizations" +msgstr "Organizaciones" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team__parent_id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__parent_id +msgid "Parent Team" +msgstr "Equipo padre" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__paths +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__paths +msgid "Paths" +msgstr "Rutas" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__permission +msgid "Permission" +msgstr "Permiso" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "" +"Please add the 'github_token' in Odoo configuration file or as the 'github." +"access_token' configuration parameter." +msgstr "" +"Por favor añade el 'github_token' en el archivo de configuración de Odoo o " +"como parámetro de configuración 'github.access_token'." + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team__privacy +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__privacy +msgid "Privacy" +msgstr "Privacidad" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_repository__permission__read +msgid "Read" +msgstr "Leer" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_reporting +msgid "Reports" +msgstr "Informes" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_repository +#: model:ir.actions.act_window,name:github_connector.action_github_team_repository_from_team +#: model:ir.model.fields,field_description:github_connector.field_github_organization__repository_ids +#: model:ir.model.fields,field_description:github_connector.field_github_team__repository_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__repository_ids +#: model:ir.ui.menu,name:github_connector.menu_github_repository +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +msgid "Repositories" +msgstr "Repositorios" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Repositories Sync." +msgstr "Sincronización de repositorios" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__repository_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__repository_id +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__repository_id +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_load_github_model__github_type__repository +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "Repository" +msgstr "Repositorio" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__repository_branch_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__repository_branch_id +msgid "Repository Branch" +msgstr "Rama de repositorio" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_repository_branch +#: model:ir.ui.menu,name:github_connector.menu_github_repository_branch +msgid "Repository Branches" +msgstr "Ramas de repositorio" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +msgid "Repository Sync." +msgstr "Sincronización de repositorio" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_repository_branch_rule_info_report +#: model:ir.ui.menu,name:github_connector.menu_action_github_repository_branch_rule_info_report +#: model_terms:ir.ui.view,arch_db:github_connector.action_github_repository_branch_rule_info_report_graph +#: model_terms:ir.ui.view,arch_db:github_connector.action_github_repository_branch_rule_info_report_pivot +msgid "Repository branch analysis rule" +msgstr "Regla de análisis de la rama del repositorio" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__role +msgid "Role" +msgstr "Rol" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__scanned_files +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__scanned_files +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__scanned_files +msgid "Scanned Files" +msgstr "Archivos escaneados" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team__privacy__secret +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_create_team__privacy__secret +msgid "Secret" +msgstr "Secreto" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__sequence +msgid "Sequence" +msgstr "Secuencia" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__sequence_serie +msgid "Sequence Serie" +msgstr "Seria de secuencia" + +#. module: github_connector +#: model:ir.model.constraint,message:github_connector.constraint_github_organization_serie_sequence_organization_uniq +msgid "Sequence serie must be unique by organization." +msgstr "La serie de secuencias debe ser única por organización." + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "Serie" +msgstr "Serie" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_organization__ignored_repository_names +msgid "" +"Set here repository names you want to ignore. One repository per line. If " +"set, the repositories will be created, but branches synchronization and " +"source code download will be disabled. Exemple:\n" +"purchase-workflow\n" +"OCB\n" +"OpenUpgrade\n" +msgstr "" +"Establezca aquí los nombres de repositorios que desea ignorar. Un " +"repositorio por línea. Si se establece, los repositorios se crearán, pero la " +"sincronización de ramas y la descarga del código fuente se desactivarán. " +"Ejemplo:\n" +"purchase-workflow\n" +"OCB\n" +"OpenUpgrade\n" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github_settings +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Settings" +msgstr "Ajustes" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__size +msgid "Size (Byte) " +msgstr "Tamaño (Byte) " + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__mb_size +msgid "Size (Megabyte)" +msgstr "Tamaño (Megabyte)" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_size_by_serie +#: model:ir.ui.menu,name:github_connector.menu_size_by_serie +msgid "Sizes by Serie" +msgstr "Tamaños por serie" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__state +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "State" +msgstr "Estado" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_wizard_load_github_model +msgid "Sync Object" +msgstr "Sincronización de objeto" + +#. module: github_connector +#: model:ir.actions.server,name:github_connector.cron_update_organization_ir_actions_server +#: model:ir.cron,cron_name:github_connector.cron_update_organization +#: model:ir.cron,name:github_connector.cron_update_organization +msgid "Synchronize All Organizations and Teams" +msgstr "Sincronizar todas las organizaciones y equipos" + +#. module: github_connector +#: model:ir.actions.server,name:github_connector.cron_update_branch_list_ir_actions_server +#: model:ir.cron,cron_name:github_connector.cron_update_branch_list +#: model:ir.cron,name:github_connector.cron_update_branch_list +msgid "Synchronize Branches List for All Repositories" +msgstr "Sincronizar el listado de ramas de todos los repositorios" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_team_repository_from_repository +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__team_id +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__team_id +msgid "Team" +msgstr "Equipo" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__wizard_partner_ids +msgid "Team Members" +msgstr "Miembros del equipo" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__wizard_repository_ids +msgid "Team Repositories" +msgstr "Repositorios del equipo" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_team +#: model:ir.actions.act_window,name:github_connector.action_github_team_partner_from_partner +#: model:ir.model.fields,field_description:github_connector.field_github_organization__team_ids +#: model:ir.model.fields,field_description:github_connector.field_github_repository__team_ids +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_team_partner_ids +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_team_partner_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__team_ids +#: model:ir.ui.menu,name:github_connector.menu_github_team +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Teams" +msgstr "Equipos" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Teams Sync." +msgstr "Sincronización de equipos" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_analysis_rule_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Technical Settings" +msgstr "Configuración técnica" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_team__privacy +#: model:ir.model.fields,help:github_connector.field_wizard_create_team__privacy +msgid "" +"The level of privacy this team should have. Can be one of:\n" +"* secret - only visible to organization owners and members of this team.\n" +"* closed - visible to all members of this organization." +msgstr "" +"El nivel de privacidad que debe tener este equipo. Puede ser uno de los " +"siguientes:\n" +"* secreto - sólo visible para los propietarios de la organización y los " +"miembros de este equipo.\n" +"* cerrado: visible para todos los miembros de la organización." + +#. module: github_connector +#: code:addons/github_connector/models/github_organization.py:0 +#, python-format +msgid "" +"The provided Github Token must have admin read:org permissions to the " +"organization '%s'" +msgstr "" +"El token de Github proporcionado debe tener permisos admin read:org para la " +"organización '%s'." + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_repository_branch__state__to_analyze +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "To Analyze" +msgstr "A analizar" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_repository_branch__state__to_download +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "To Download" +msgstr "A descargar" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Total" +msgstr "Total" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total code" +msgstr "Código total" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total documentation" +msgstr "Total documentación" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total empty" +msgstr "Total vacío" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total scanned" +msgstr "Total escaneado" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total string" +msgstr "Total cadena" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total total" +msgstr "Total total" + +#. module: github_connector +#: model:ir.model.constraint,message:github_connector.constraint_res_partner_github_login_uniq +msgid "Two different partners cannot have the same Github Login" +msgstr "Dos contactos diferentes no pueden tener el mismo Login de Github" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_repository__permission__undefined +msgid "Undefined" +msgstr "Indefinido" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__child_update +msgid "Update Child Objects" +msgstr "Actualizar objetos hijos" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Update Source Code" +msgstr "Actualizar código fuente" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Update Source Code Analyzis" +msgstr "Actualizar el análisis del código fuente" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_load_github_model__github_type__user +msgid "User" +msgstr "Usuario" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_res_partner +#: model:ir.ui.menu,name:github_connector.menu_res_partner +msgid "Users" +msgstr "Usuarios" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__website +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__website +msgid "Website" +msgstr "Sitio web" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__website_url +msgid "Website Url" +msgstr "Url sitio web" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_wizard_create_repository +msgid "Wizard Create Repository" +msgstr "Asistente de creación de repositorio" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_wizard_create_team +msgid "Wizard Create Team" +msgstr "Asistente de creación de equipo" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_wizard_load_github_model +msgid "Wizard Load Github Model" +msgstr "Asistente de carga del modelo Github" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_repository__permission__write +msgid "Write" +msgstr "Escribir" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_analysis_rule_form +msgid "You can see reference documentation and some examples in:" +msgstr "Puede consultar la documentación de referencia y algunos ejemplos en:" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_analysis_rule_form +msgid "https://git-scm.com/docs/gitignore#_pattern_format" +msgstr "https://git-scm.com/docs/gitignore#_pattern_format" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_load_github_model_form +msgid "or" +msgstr "o" + +#. module: github_connector +#: code:addons/github_connector/models/github_repository_branch.py:0 +#, python-format +msgid "source_code_local_path should be defined in your configuration file" +msgstr "source_code_local_path debe definirse en su archivo de configuración" diff --git a/github_connector/i18n/github_connector.pot b/github_connector/i18n/github_connector.pot new file mode 100644 index 00000000..eccb1f96 --- /dev/null +++ b/github_connector/i18n/github_connector.pot @@ -0,0 +1,1372 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * github_connector +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_repository_branch_rule_info +msgid " Github Repository Branch Rule Info" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_tree +msgid "# Branches" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__code_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__code_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__code_count +msgid "# Code" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__documentation_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__documentation_count +msgid "# Doc." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__documentation_count +msgid "# Documentation" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__empty_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__empty_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__empty_count +msgid "# Empty" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__string_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__string_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__string_count +msgid "# String" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__total_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__total_count +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__total_count +msgid "# Total" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_kanban +msgid "" +"\n" +" \n" +" " +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_kanban +msgid "" +"\n" +" \n" +" " +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_kanban +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_kanban +msgid "" +"\n" +" \n" +" " +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "" +" Members\n" +" Member" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +msgid "" +" Branches\n" +" Branch" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +msgid "" +" Teams\n" +" Team" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "" +" Repositories\n" +" Repository" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "" +" Teams\n" +" Team" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/res_partner.py:0 +#, python-format +msgid "A company ('%s') can not have a Github login associated." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_repository__permission__admin +msgid "Admin" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__analysis_rule_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__analysis_rule_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__analysis_rule_id +msgid "Analysis Rule" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__analysis_rule_ids +#: model:ir.model.fields,field_description:github_connector.field_github_repository__analysis_rule_ids +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__analysis_rule_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__analysis_rule_ids +msgid "Analysis Rules" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__analysis_rule_info_ids +msgid "Analysis Rules (info)" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_analysis_rule +msgid "Analysis rule" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_analysis_rule_group +msgid "Analysis rule group" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github_analysis_rule_group +msgid "Analysis rule groups" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github_analysis_rule +msgid "Analysis rules" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Analyze Source Code" +msgstr "" + +#. module: github_connector +#: model:ir.actions.server,name:github_connector.cron_analyze_code_ir_actions_server +#: model:ir.cron,cron_name:github_connector.cron_analyze_code +#: model:ir.cron,name:github_connector.cron_analyze_code +msgid "Analyze Source Code for All Github Branches" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_repository_branch__state__analyzed +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "Analyzed" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_partner_from_team_kanban +msgid "Avatar" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__repository_branch_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__repository_branch_ids +msgid "Branches" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +msgid "Branches Sync." +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_branch_by_serie +#: model:ir.ui.menu,name:github_connector.menu_branch_by_serie +msgid "Branches by Serie" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__ci_url +msgid "CI URL" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__ci_url_pattern +msgid "CI URL Pattern" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_load_github_model_form +msgid "Cancel" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_res_partner__is_bot_account +#: model:ir.model.fields,help:github_connector.field_res_users__is_bot_account +msgid "Check this box if this account is a bot or similar." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team__privacy__closed +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_create_team__privacy__closed +msgid "Closed" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +msgid "Code Analysis" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__color +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__color +msgid "Color Index" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__complete_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__complete_name +#: model:ir.model.fields,field_description:github_connector.field_github_team__complete_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__complete_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__complete_name +msgid "Complete Name" +msgstr "" + +#. module: github_connector +#: model:res.groups,name:github_connector.group_github_connector_manager +msgid "Connector Github Manager" +msgstr "" + +#. module: github_connector +#: model:res.groups,name:github_connector.group_github_connector_user +msgid "Connector Github User" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_res_partner +msgid "Contact" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__context_search_default_partner_id +msgid "Context Search Default Partner" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__context_search_default_repository_id +msgid "Context Search Default Repository" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__context_search_default_team_id +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__context_search_default_team_id +msgid "Context Search Default Team" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__coverage_url +msgid "Coverage URL" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__coverage_url_pattern +msgid "Coverage URL Pattern" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_create_date +msgid "Create Date on Github" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_wizard_create_repository +msgid "Create Repo in Github" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_wizard_create_repository +msgid "Create Repo in Github Wizard" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_wizard_create_team +msgid "Create Team in Github" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_wizard_create_team +msgid "Create Team in Github Wizard" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_team_form +msgid "Create in Github" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_organization__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__create_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__create_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__create_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__create_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__create_uid +msgid "Created by" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__create_date +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__create_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__create_date +msgid "Created on" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_analysis_rule__paths +#: model:ir.model.fields,help:github_connector.field_github_repository_branch_rule_info_report__paths +msgid "Define with pathspec especification" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__description +#: model:ir.model.fields,field_description:github_connector.field_github_repository__description +#: model:ir.model.fields,field_description:github_connector.field_github_team__description +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__description +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__description +msgid "Description" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_organization__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_team__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__display_name +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__display_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__display_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__display_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__display_name +msgid "Display Name" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Download Source Code" +msgstr "" + +#. module: github_connector +#: model:ir.actions.server,name:github_connector.cron_download_code_ir_actions_server +#: model:ir.cron,cron_name:github_connector.cron_download_code +#: model:ir.cron,name:github_connector.cron_download_code +msgid "Download Source Code for All Github Branches" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "Duplicate object with Github login %s" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__email +msgid "Email" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/github_repository_branch.py:0 +#, python-format +msgid "" +"Error when trying to create the folder %s\n" +" Please check Odoo Access Rights." +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/github_repository_branch.py:0 +#, python-format +msgid "" +"Error when trying to create the main folder %(path)s\n" +" Please check Odoo Access Rights.\n" +" %(error)s" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/github_repository_branch.py:0 +#, python-format +msgid "" +"Error when updating the branch %(branch)s in the local folder %(path)s.\n" +"Deleting the local folder and trying again." +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "External Services" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "" +"Feature not Implemented : Please define 'get_github_base_obj_for_creation' " +"function in child model." +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "" +"Feature not Implemented : Please define 'github_login_field' function in " +"child model." +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Full Sync" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Github" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_analysis_rule +msgid "Github Analysis Rule" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_analysis_rule_group +msgid "Github Analysis Rule Group" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_analysis_rule_info_mixin +msgid "Github Analysis Rule Mixin" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github_community +msgid "Github Community" +msgstr "" + +#. module: github_connector +#: model:ir.module.category,name:github_connector.module_category_github_connector +msgid "Github Connector" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_id_external +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_id_external +msgid "Github Id" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_search +msgid "Github Login" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__name +msgid "Github Name" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_organization_serie +msgid "Github Organization Serie" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_repository +#: model:ir.ui.menu,name:github_connector.menu_github_repository_root +msgid "Github Repository" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_repository_branch +msgid "Github Repository Branch" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_repository_branch_rule_info_report +msgid "Github Repository Branch Rule Info Report" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_team +msgid "Github Team" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_team_partner +msgid "Github Team Partner" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_team_repository +msgid "Github Team Repository" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_name +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_name +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_name +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_name +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_name +msgid "Github Technical Name" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__github_type +msgid "Github Type Name" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_url +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_url +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_url +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_url +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_url +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_url +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_url +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_url +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_url +msgid "Github URL" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_abstract_github_model +msgid "Github abstract model" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_github_organization +msgid "Github organization" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__group_id +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__group_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__group_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__group_id +msgid "Group" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_search +msgid "Group By" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_search +msgid "Has Github Account" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__id +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__id +#: model:ir.model.fields,field_description:github_connector.field_github_organization__id +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__id +#: model:ir.model.fields,field_description:github_connector.field_github_repository__id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__id +#: model:ir.model.fields,field_description:github_connector.field_github_team__id +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__id +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__id +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__id +msgid "ID" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_repository__inhibit_inherited_rules +#: model:ir.model.fields,help:github_connector.field_wizard_create_repository__inhibit_inherited_rules +msgid "" +"If checked, the analysis rules to be used will be only those set here.\n" +" If unchecked, the analysis rules to be used will be those set in the organization and those set here." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_repository_branch__inhibit_inherited_rules +msgid "" +"If checked, the analysis rules to be used will be only those set here.\n" +" If unchecked, the analysis rules to be used will be those set in the organization, repository and those set here." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_repository__is_ignored +#: model:ir.model.fields,help:github_connector.field_wizard_create_repository__is_ignored +msgid "" +"If checked, the branches will not be synchronized, and the code source will " +"this way not be downloaded and analyzed. To ignore a repository, go to the " +"organization and add the file 'Ignored Repositories'." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__ignored_repository_names +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Ignored Repositories" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__image +msgid "Image" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__inhibit_inherited_rules +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__inhibit_inherited_rules +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__inhibit_inherited_rules +msgid "Inhibit inherited rules" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "Invalid name '%s' provided" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_res_partner__is_bot_account +#: model:ir.model.fields,field_description:github_connector.field_res_users__is_bot_account +msgid "Is Bot Github Account" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__is_ignored +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__is_ignored +msgid "Is Ignored" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__last_analyze_date +msgid "Last Analyze Date" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__last_download_date +msgid "Last Download Date" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_organization____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_repository____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_team____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner____last_update +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository____last_update +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository____last_update +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team____last_update +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model____last_update +msgid "Last Modified on" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_last_sync_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_last_sync_date +msgid "Last Sync Date with Github" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_organization__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__write_uid +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__write_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__write_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__write_uid +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__write_date +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__write_date +msgid "Last Updated on" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_abstract_github_model__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_github_organization__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_github_team__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__github_write_date +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__github_write_date +msgid "Last Write Date on Github" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Light Sync" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_load_github_model_form +msgid "Load From Github" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_wizard_load_github_model +msgid "Load from Github Wizard" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__local_path +msgid "Local Path" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__location +msgid "Location" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_partner__role__maintainer +msgid "Maintainer" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "Maximum attempts reached." +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_kanban +msgid "Mb" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__partner_id +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_partner__role__member +msgid "Member" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_team_partner_from_team +#: model:ir.model.fields,field_description:github_connector.field_github_organization__member_ids +#: model:ir.model.fields,field_description:github_connector.field_github_team__partner_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__partner_ids +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +msgid "Members" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +msgid "Members Sync." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__name +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_group__name +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__name +#: model:ir.model.fields,field_description:github_connector.field_github_repository__name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__name +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__name +#: model:ir.model.fields,field_description:github_connector.field_github_team__name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__name +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__name +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "Name" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_search +msgid "Name or Description" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_search +msgid "Name or Github Name" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__repository_branch_qty +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__repository_branch_qty +msgid "Number of Branches" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__member_qty +#: model:ir.model.fields,field_description:github_connector.field_github_team__partner_qty +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__partner_qty +msgid "Number of Members" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_res_partner__organization_qty +#: model:ir.model.fields,field_description:github_connector.field_res_users__organization_qty +msgid "Number of Organizations" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__repository_qty +#: model:ir.model.fields,field_description:github_connector.field_github_team__repository_qty +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__repository_qty +msgid "Number of Repositories" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__organization_serie_qty +msgid "Number of Series" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__team_qty +#: model:ir.model.fields,field_description:github_connector.field_github_repository__team_qty +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_team_qty +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_team_qty +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__team_qty +msgid "Number of Teams" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__organization_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository__organization_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__organization_id +#: model:ir.model.fields,field_description:github_connector.field_github_team__organization_id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__organization_id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__organization_id +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_load_github_model__github_type__organization +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_search +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_search +msgid "Organization" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__name +msgid "Organization Name" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__organization_serie_id +msgid "Organization Serie" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__organization_serie_ids +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Organization Series" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__organization_serie_id +msgid "Organization serie" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_organization +#: model:ir.model.fields,field_description:github_connector.field_res_partner__organization_ids +#: model:ir.model.fields,field_description:github_connector.field_res_users__organization_ids +#: model:ir.ui.menu,name:github_connector.menu_github_organization +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Organizations" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team__parent_id +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__parent_id +msgid "Parent Team" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule__paths +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__paths +msgid "Paths" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__permission +msgid "Permission" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/abstract_github_model.py:0 +#, python-format +msgid "" +"Please add the 'github_token' in Odoo configuration file or as the " +"'github.access_token' configuration parameter." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team__privacy +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__privacy +msgid "Privacy" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_repository__permission__read +msgid "Read" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_reporting +msgid "Reports" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_repository +#: model:ir.actions.act_window,name:github_connector.action_github_team_repository_from_team +#: model:ir.model.fields,field_description:github_connector.field_github_organization__repository_ids +#: model:ir.model.fields,field_description:github_connector.field_github_team__repository_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__repository_ids +#: model:ir.ui.menu,name:github_connector.menu_github_repository +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +msgid "Repositories" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Repositories Sync." +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__repository_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__repository_id +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__repository_id +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_load_github_model__github_type__repository +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "Repository" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__repository_branch_id +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__repository_branch_id +msgid "Repository Branch" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_repository_branch +#: model:ir.ui.menu,name:github_connector.menu_github_repository_branch +msgid "Repository Branches" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_team_form +msgid "Repository Sync." +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_repository_branch_rule_info_report +#: model:ir.ui.menu,name:github_connector.menu_action_github_repository_branch_rule_info_report +#: model_terms:ir.ui.view,arch_db:github_connector.action_github_repository_branch_rule_info_report_graph +#: model_terms:ir.ui.view,arch_db:github_connector.action_github_repository_branch_rule_info_report_pivot +msgid "Repository branch analysis rule" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__role +msgid "Role" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_analysis_rule_info_mixin__scanned_files +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info__scanned_files +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch_rule_info_report__scanned_files +msgid "Scanned Files" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team__privacy__secret +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_create_team__privacy__secret +msgid "Secret" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization_serie__sequence +msgid "Sequence" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__sequence_serie +msgid "Sequence Serie" +msgstr "" + +#. module: github_connector +#: model:ir.model.constraint,message:github_connector.constraint_github_organization_serie_sequence_organization_uniq +msgid "Sequence serie must be unique by organization." +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "Serie" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_organization__ignored_repository_names +msgid "" +"Set here repository names you want to ignore. One repository per line. If set, the repositories will be created, but branches synchronization and source code download will be disabled. Exemple:\n" +"purchase-workflow\n" +"OCB\n" +"OpenUpgrade\n" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_github_settings +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Settings" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__size +msgid "Size (Byte) " +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__mb_size +msgid "Size (Megabyte)" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_size_by_serie +#: model:ir.ui.menu,name:github_connector.menu_size_by_serie +msgid "Sizes by Serie" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository_branch__state +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "State" +msgstr "" + +#. module: github_connector +#: model:ir.ui.menu,name:github_connector.menu_wizard_load_github_model +msgid "Sync Object" +msgstr "" + +#. module: github_connector +#: model:ir.actions.server,name:github_connector.cron_update_organization_ir_actions_server +#: model:ir.cron,cron_name:github_connector.cron_update_organization +#: model:ir.cron,name:github_connector.cron_update_organization +msgid "Synchronize All Organizations and Teams" +msgstr "" + +#. module: github_connector +#: model:ir.actions.server,name:github_connector.cron_update_branch_list_ir_actions_server +#: model:ir.cron,cron_name:github_connector.cron_update_branch_list +#: model:ir.cron,name:github_connector.cron_update_branch_list +msgid "Synchronize Branches List for All Repositories" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_team_repository_from_repository +#: model:ir.model.fields,field_description:github_connector.field_github_team_partner__team_id +#: model:ir.model.fields,field_description:github_connector.field_github_team_repository__team_id +msgid "Team" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__wizard_partner_ids +msgid "Team Members" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_team__wizard_repository_ids +msgid "Team Repositories" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_github_team +#: model:ir.actions.act_window,name:github_connector.action_github_team_partner_from_partner +#: model:ir.model.fields,field_description:github_connector.field_github_organization__team_ids +#: model:ir.model.fields,field_description:github_connector.field_github_repository__team_ids +#: model:ir.model.fields,field_description:github_connector.field_res_partner__github_team_partner_ids +#: model:ir.model.fields,field_description:github_connector.field_res_users__github_team_partner_ids +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__team_ids +#: model:ir.ui.menu,name:github_connector.menu_github_team +#: model_terms:ir.ui.view,arch_db:github_connector.view_res_partner_form +msgid "Teams" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_organization_form +msgid "Teams Sync." +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_analysis_rule_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Technical Settings" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,help:github_connector.field_github_team__privacy +#: model:ir.model.fields,help:github_connector.field_wizard_create_team__privacy +msgid "" +"The level of privacy this team should have. Can be one of:\n" +"* secret - only visible to organization owners and members of this team.\n" +"* closed - visible to all members of this organization." +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/github_organization.py:0 +#, python-format +msgid "" +"The provided Github Token must have admin read:org permissions to the " +"organization '%s'" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_repository_branch__state__to_analyze +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "To Analyze" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_repository_branch__state__to_download +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_search +msgid "To Download" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Total" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total code" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total documentation" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total empty" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total scanned" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total string" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +msgid "Total total" +msgstr "" + +#. module: github_connector +#: model:ir.model.constraint,message:github_connector.constraint_res_partner_github_login_uniq +msgid "Two different partners cannot have the same Github Login" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_repository__permission__undefined +msgid "Undefined" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_wizard_load_github_model__child_update +msgid "Update Child Objects" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Update Source Code" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_repository_branch_tree +msgid "Update Source Code Analyzis" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__wizard_load_github_model__github_type__user +msgid "User" +msgstr "" + +#. module: github_connector +#: model:ir.actions.act_window,name:github_connector.action_res_partner +#: model:ir.ui.menu,name:github_connector.menu_res_partner +msgid "Users" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_repository__website +#: model:ir.model.fields,field_description:github_connector.field_wizard_create_repository__website +msgid "Website" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields,field_description:github_connector.field_github_organization__website_url +msgid "Website Url" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_wizard_create_repository +msgid "Wizard Create Repository" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_wizard_create_team +msgid "Wizard Create Team" +msgstr "" + +#. module: github_connector +#: model:ir.model,name:github_connector.model_wizard_load_github_model +msgid "Wizard Load Github Model" +msgstr "" + +#. module: github_connector +#: model:ir.model.fields.selection,name:github_connector.selection__github_team_repository__permission__write +msgid "Write" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_analysis_rule_form +msgid "You can see reference documentation and some examples in:" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_github_analysis_rule_form +msgid "https://git-scm.com/docs/gitignore#_pattern_format" +msgstr "" + +#. module: github_connector +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_repository_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_create_team_form +#: model_terms:ir.ui.view,arch_db:github_connector.view_wizard_load_github_model_form +msgid "or" +msgstr "" + +#. module: github_connector +#: code:addons/github_connector/models/github_repository_branch.py:0 +#, python-format +msgid "source_code_local_path should be defined in your configuration file" +msgstr "" diff --git a/github_connector/models/__init__.py b/github_connector/models/__init__.py new file mode 100644 index 00000000..db322cdb --- /dev/null +++ b/github_connector/models/__init__.py @@ -0,0 +1,14 @@ +from . import abstract_github_model + +from . import res_partner + +from . import github_analysis_rule +from . import github_analysis_rule_group +from . import github_analysis_rule_info +from . import github_organization +from . import github_organization_serie +from . import github_repository +from . import github_repository_branch +from . import github_team +from . import github_team_partner +from . import github_team_repository diff --git a/github_connector/models/abstract_github_model.py b/github_connector/models/abstract_github_model.py new file mode 100644 index 00000000..6563d266 --- /dev/null +++ b/github_connector/models/abstract_github_model.py @@ -0,0 +1,327 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# Copyright 2021 Tecnativa - João Marques +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import base64 +import logging +from datetime import datetime +from urllib.request import urlopen + +import pytz +from github import Auth, Github # pylint: disable=missing-manifest-dependency +from github.GithubException import ( # pylint: disable=missing-manifest-dependency + UnknownObjectException, +) + +from odoo import _, api, fields, models, tools +from odoo.exceptions import UserError + +_logger = logging.getLogger(__name__) + +_GITHUB_URL = "https://github.com/" + + +class AbstractGithubModel(models.AbstractModel): + """ + This abstract model is used to share all features related to github model. + Note that some fields and function have to be defined in the inherited + model. + """ + + _name = "abstract.github.model" + _description = "Github abstract model" + _github_login_field = None + _need_individual_call = False + _field_list_prevent_overwrite = [] + + github_id_external = fields.Char(string="Github Id", readonly=True, index=True) + + github_name = fields.Char(string="Github Technical Name", readonly=True, index=True) + + github_url = fields.Char(string="Github URL", readonly=True) + + github_create_date = fields.Datetime(string="Create Date on Github", readonly=True) + + github_write_date = fields.Datetime( + string="Last Write Date on Github", readonly=True + ) + + github_last_sync_date = fields.Datetime( + string="Last Sync Date with Github", readonly=True + ) + + # Overloadable Section + def github_login_field(self): + if self._github_login_field is None: + raise UserError( + _( + "Feature not Implemented : Please define 'github_login_field'" + " function in child model." + ) + ) + else: + return self._github_login_field + + @api.model + def get_conversion_dict(self): + """ + Prepare function that builds a map from Github fields to Odoo fields + + :return: Dictionary {odoo_field: github_field} + """ + return { + "github_id_external": "id", + "github_url": "html_url", + "github_name": self.github_login_field(), + "github_create_date": "created_at", + "github_write_date": "updated_at", + } + + def process_timezone_fields(self, res): + for k, v in res.items(): + if self._fields[k].type == "datetime": + if isinstance(v, str): + res[k] = datetime.strptime(v, "%Y-%m-%dT%H:%M:%SZ") + elif isinstance(v, datetime) and v.tzinfo: + res[k] = v.astimezone(pytz.utc).replace(tzinfo=None) + + @api.model + def get_odoo_data_from_github(self, data): + """Prepare function that maps data from a Github object to a dictionary of + values ready to create an Odoo record + """ + map_dict = self.get_conversion_dict() + res = {} + for k, v in map_dict.items(): + if hasattr(self, k) and hasattr(data, v): + res.update({k: getattr(data, v)}) + res.update({"github_last_sync_date": fields.Datetime.now()}) + # res.update({"github_type": type(data).__name__}) + self.process_timezone_fields(res) + return res + + def get_github_base_obj_for_creation(self): + """Should return the base object required to create the given item + in Github. + Usefull only if your model implements creation in github + """ + self.ensure_one() + raise UserError( + _( + "Feature not Implemented : Please define" + " 'get_github_base_obj_for_creation' function in child model." + ) + ) + + def full_update(self): + """Override this function in models that inherit this abstract + to mention which items should be synchronized from github when the + user clicks the 'Full Update' Button""" + + def _hook_after_github_creation(self): + """Hook that will be called after a creation in github. + Override this function to add custom logic for after creation.""" + + # Custom Public Function + @api.model + def get_from_id_or_create(self, gh_data=None, data=None, extra_data=None): + """Search if an Odoo object related to the Github data exists in database. + If it does, it returns the object. Otherwise, it creates the new object. + + :param gh_data: Github object the will be used to get/create Odoo record + :param data: dict with github 'id' and 'url' keys (deprecated) + :param extra_data: dict with extra data to be put into the Odoo record + :return: The searched or created object + + :Example: + + >>> self.env['github_organization'].get_from_id_or_create( + data={'id': 7600578, 'url': 'https://api.github.com/orgs/OCA'}) + """ + extra_data = extra_data or {} + if gh_data and not data: + # Get a dictionary of data corresponding to the Github object + data = self.get_odoo_data_from_github(gh_data) + # We try to search object by id + existing_object = None + if "github_id_external" in data: + existing_object = self.with_context(active_test=False).search( + [("github_id_external", "=", data["github_id_external"])] + ) + if existing_object: + return existing_object + # We try to search the object by name (instead of id) + if self._github_login_field and self._github_login_field in data: + existing_object = self.with_context(active_test=False).search( + [("github_name", "=", data[self._github_login_field])] + ) + if len(existing_object) == 1: + # Update the existing object + existing_object.github_id_external = ( + data["github_id_external"] + if "github_id_external" in data + else existing_object.find_related_github_object().id + ) + _logger.info( + "Existing object %s#%d with Github name '%s' has been" + " updated from a '%s' object with unique Github id %s", + self._name, + existing_object.id, + data[self._github_login_field], + type(data).__name__, + existing_object.github_id_external, + ) + return existing_object + elif len(existing_object) > 1: + raise UserError( + _("Duplicate object with Github login %s") + % (data[self._github_login_field],) + ) + # if self._need_individual_call: + # github_connector = self.get_github_connector(self.github_type()) + # data = github_connector.get_by_url(data["url"], "get") + # Create a new object otherwise + # The _create function already parses the Github object to obtain the full + # Odoo dictionary, so we need to pass the full gh_data object. + return self._create_from_github_data(data, extra_data) + + @api.model + def create_from_name(self, name): + """Call Github API, using a URL using github name. Load data and + Create Odoo object accordingly, if the odoo object doesn't exist. + + :param name: the github name to load + :return: The created object + + :Example: + + >>> self.env['github_organization'].create_from_name('OCA') + >>> self.env['github_repository'].create_from_name('OCA/web') + """ + gh_api = self.get_github_connector() + p_name = name.rstrip("/").lstrip( + "/" + ) # strip any / in the beggining of end just in case + try: + if "/" in p_name: + # It's a repo + gh_obj = gh_api.get_repo(p_name) + else: + try: + # Try to get an organization 1st. + # An organization is always an user, but a user is not necessarily an org. + gh_obj = gh_api.get_organization(p_name) + except UnknownObjectException: + # Try to get an user. + gh_obj = gh_api.get_user(p_name) + except UnknownObjectException: + raise UserError(_("Invalid name '%s' provided") % name) from None + res = self.get_odoo_data_from_github(gh_obj) + # search if ID doesn't exist in database + current_object = self.with_context(active_test=False).search( + [("github_id_external", "=", res["github_id_external"])] + ) + if current_object: + return current_object + # Create the object + return self._create_from_github_data(res) + + def button_update_from_github_light(self): + return self.update_from_github(child_update=False) + + def button_update_from_github_full(self): + return self.update_from_github(child_update=True) + + @api.model + def find_related_github_object(self, obj_id=None): + """Query Github API to find the related object + + This function should be overwritten in the child classes. + """ + + def update_from_github(self, child_update): + """Call Github API, using a URL using github id. Load data and + update Odoo object accordingly, if the odoo object is obsolete. + (Based on last write dates) + + :param child_update: set to True if you want to reload childs + Objects linked to this object. (like members for teams) + """ + for item in self: + gh_obj = item.find_related_github_object() + data = self.get_odoo_data_from_github(gh_obj) + item._update_from_github_data(data) + if child_update: + self.full_update() + + def get_base64_image_from_github(self, url): + max_try = int( + self.sudo().env["ir.config_parameter"].get_param("github.max_try") + ) + for _i in range(max_try): + try: + stream = urlopen(url, timeout=10).read() + break + except Exception as err: + _logger.warning("URL Call Error. %s" % (err.__str__())) + else: + raise UserError(_("Maximum attempts reached.")) + return base64.standard_b64encode(stream) + + # Custom Private Function + @api.model + def _create_from_github_data(self, data, extra_data=None): + extra_data = extra_data and extra_data or {} + data.update(extra_data) + return self.create(data) + + def _update_from_github_data(self, data): + for item in self: + # Optimization. Due to the fact that github datas rarely change, + # and that there a lot of related / computed fields invalidation + # process, we realize a write only if data changed + to_write = {} + for k, v in data.items(): + if hasattr(item[k], "ids"): + to_compare = item[k].ids + else: + to_compare = item[k] + # do not overwrite existing values for some given fields + if to_compare != v and ( + k not in self._field_list_prevent_overwrite or to_compare is False + ): + to_write[k] = v + if to_write: + item.write(to_write) + + def get_github_connector(self): + ICP = self.env["ir.config_parameter"] + token = tools.config.get("github_token") or ICP.get_param( + "github.access_token", default="" + ) + if not token: + raise UserError( + _( + "Please add the 'github_token' in Odoo configuration file" + " or as the 'github.access_token' configuration parameter." + ) + ) + return Github(auth=Auth.Token(token)) + + def create_in_github(self): + """Create an object in Github through the API + + As this depends on each object, it should be overwritten in each + implementation class + """ + + def get_action(self): + self.ensure_one() + return { + "type": "ir.actions.act_window", + "name": self.name, + "view_mode": "form", + "res_model": self._name, + "res_id": self.id, + } diff --git a/github_connector/models/github_analysis_rule.py b/github_connector/models/github_analysis_rule.py new file mode 100644 index 00000000..e21c07a3 --- /dev/null +++ b/github_connector/models/github_analysis_rule.py @@ -0,0 +1,46 @@ +# Copyright 2020 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import pathspec +from pygount import SourceAnalysis + +from odoo import fields, models + + +class GithubAnalysisRule(models.Model): + _name = "github.analysis.rule" + _description = "Github Analysis Rule" + + name = fields.Char(required=True) + group_id = fields.Many2one( + string="Group", comodel_name="github.analysis.rule.group", required=True + ) + """ + Example paths: https://git-scm.com/docs/gitignore#_pattern_format + """ + paths = fields.Text( + help="Define with pathspec especification", + default="*", + required=True, + ) + + def _set_spec(self, lines): + return pathspec.PathSpec.from_lines("gitwildmatch", lines) + + def _get_matches(self, path): + """ + Get all matches from rule paths (multiple per line allow in rule) + in a local path + """ + return self._set_spec(self.paths.splitlines()).match_tree(path) + + def _analysis_file(self, path): + file_res = SourceAnalysis.from_file(path, "") + return { + "path": file_res._path, + "language": file_res._language, + "code": file_res._code, + "documentation": file_res._documentation, + "empty": file_res._empty, + "string": file_res._string, + } diff --git a/github_connector/models/github_analysis_rule_group.py b/github_connector/models/github_analysis_rule_group.py new file mode 100644 index 00000000..cec4e0b5 --- /dev/null +++ b/github_connector/models/github_analysis_rule_group.py @@ -0,0 +1,11 @@ +# Copyright 2020 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class GithubAnalysisRule(models.Model): + _name = "github.analysis.rule.group" + _description = "Github Analysis Rule Group" + + name = fields.Char(required=True) diff --git a/github_connector/models/github_analysis_rule_info.py b/github_connector/models/github_analysis_rule_info.py new file mode 100644 index 00000000..faf2e67b --- /dev/null +++ b/github_connector/models/github_analysis_rule_info.py @@ -0,0 +1,36 @@ +# Copyright 2020 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class GithubAnalysisRuleInfoMixin(models.AbstractModel): + _name = "github.analysis.rule.info.mixin" + _description = "Github Analysis Rule Mixin" + + analysis_rule_id = fields.Many2one( + string="Analysis Rule", + comodel_name="github.analysis.rule", + ondelete="cascade", + ) + group_id = fields.Many2one( + string="Group", related="analysis_rule_id.group_id", readonly=True + ) + code_count = fields.Integer(string="# Code") + documentation_count = fields.Integer(string="# Doc.") + empty_count = fields.Integer(string="# Empty") + string_count = fields.Integer(string="# String") + total_count = fields.Integer( + string="# Total", store=True, compute="_compute_total_count" + ) + scanned_files = fields.Integer() + + @api.depends("code_count", "documentation_count", "empty_count", "string_count") + def _compute_total_count(self): + for item in self: + item.total_count = ( + item.code_count + + item.documentation_count + + item.empty_count + + item.string_count + ) diff --git a/github_connector/models/github_organization.py b/github_connector/models/github_organization.py new file mode 100644 index 00000000..a39bfe02 --- /dev/null +++ b/github_connector/models/github_organization.py @@ -0,0 +1,254 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# Copyright 2020 Tecnativa - Víctor Martínez +# Copyright 2021 Tecnativa - João Marques +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +# pylint: disable=missing-manifest-dependency +from github.GithubException import GithubException + +from odoo import _, api, exceptions, fields, models + + +class GithubOrganization(models.Model): + _name = "github.organization" + _inherit = ["abstract.github.model"] + _order = "name" + _description = "Github organization" + + _github_login_field = "login" + + # Columns Section + name = fields.Char(string="Organization Name", required=True, readonly=True) + + image = fields.Image(readonly=True) + + description = fields.Char(readonly=True) + + email = fields.Char(readonly=True) + + website_url = fields.Char(readonly=True) + + location = fields.Char(readonly=True) + + ignored_repository_names = fields.Text( + string="Ignored Repositories", + help="Set here repository names" + " you want to ignore. One repository per line." + " If set, the repositories will be created, but branches" + " synchronization and source code download will be disabled." + " Exemple:\n" + "purchase-workflow\nOCB\nOpenUpgrade\n", + ) + + member_ids = fields.Many2many( + string="Members", + comodel_name="res.partner", + relation="github_organization_partner_rel", + column1="organization_id", + column2="partner_id", + readonly=True, + ) + + member_qty = fields.Integer( + string="Number of Members", compute="_compute_member_qty", store=True + ) + + repository_ids = fields.One2many( + string="Repositories", + comodel_name="github.repository", + inverse_name="organization_id", + readonly=True, + ) + + repository_qty = fields.Integer( + string="Number of Repositories", compute="_compute_repository_qty", store=True + ) + + team_ids = fields.One2many( + string="Teams", + comodel_name="github.team", + inverse_name="organization_id", + readonly=True, + ) + + team_qty = fields.Integer( + string="Number of Teams", compute="_compute_team_qty", store=True + ) + + organization_serie_ids = fields.One2many( + string="Organization Series", + comodel_name="github.organization.serie", + inverse_name="organization_id", + ) + + organization_serie_qty = fields.Integer( + string="Number of Series", store=True, compute="_compute_organization_serie_qty" + ) + + coverage_url_pattern = fields.Char(string="Coverage URL Pattern") + + ci_url_pattern = fields.Char(string="CI URL Pattern") + + analysis_rule_ids = fields.Many2many( + string="Analysis Rules", comodel_name="github.analysis.rule" + ) + + # Overloadable Section + @api.model + def get_conversion_dict(self): + res = super().get_conversion_dict() + res.update( + { + "name": "name", + "description": "description", + "location": "location", + "email": "email", + "website_url": "blog", + } + ) + return res + + @api.model + def get_odoo_data_from_github(self, gh_data): + res = super().get_odoo_data_from_github(gh_data) + if hasattr(gh_data, "avatar_url"): + res.update({"image": self.get_base64_image_from_github(gh_data.avatar_url)}) + return res + + def full_update(self): + self.button_sync_member() + self.button_sync_repository() + self.button_sync_team() + + @api.model + def cron_update_organization_team(self): + organizations = self.search([]) + organizations.full_update() + organizations.mapped("team_ids").full_update() + return True + + # Compute Section + @api.depends("member_ids", "member_ids.organization_ids") + def _compute_member_qty(self): + for organization in self: + organization.member_qty = len(organization.member_ids) + + @api.depends("repository_ids.organization_id") + def _compute_repository_qty(self): + data = self.env["github.repository"].read_group( + [("organization_id", "in", self.ids)], + ["organization_id"], + ["organization_id"], + ) + mapping = { + data["organization_id"][0]: data["organization_id_count"] for data in data + } + for item in self: + item.repository_qty = mapping.get(item.id, 0) + + @api.depends("team_ids.organization_id") + def _compute_team_qty(self): + data = self.env["github.team"].read_group( + [("organization_id", "in", self.ids)], + ["organization_id"], + ["organization_id"], + ) + mapping = { + data["organization_id"][0]: data["organization_id_count"] for data in data + } + for item in self: + item.team_qty = mapping.get(item.id, 0) + + @api.depends("organization_serie_ids.organization_id") + def _compute_organization_serie_qty(self): + data = self.env["github.organization.serie"].read_group( + [("organization_id", "in", self.ids)], + ["organization_id"], + ["organization_id"], + ) + mapping = { + data["organization_id"][0]: data["organization_id_count"] for data in data + } + for item in self: + item.organization_serie_qty = mapping.get(item.id, 0) + + def find_related_github_object(self, obj_id=None): + """Query Github API to find the related object""" + gh_api = self.get_github_connector() + return gh_api.get_organization(obj_id or self.github_name) + + # Action section + def button_sync_member(self): + gh_org = self.find_related_github_object() + partner_obj = self.env["res.partner"] + for organization in self: + member_ids = [] + for gh_member in gh_org.get_members(): + partner = partner_obj.get_from_id_or_create(gh_data=gh_member) + member_ids.append(partner.id) + organization.member_ids = member_ids + + def button_sync_repository(self): + gh_org = self.find_related_github_object() + repository_obj = self.env["github.repository"] + for organization in self: + repository_ids = [] + for gh_repo in gh_org.get_repos(): + repository = repository_obj.with_context( + github_organization_id=organization.id + ).get_from_id_or_create(gh_data=gh_repo) + repository_ids.append(repository.id) + organization.repository_ids = repository_ids + + def button_sync_team(self): + gh_org = self.find_related_github_object() + team_obj = self.env["github.team"] + for organization in self: + try: + team_ids = [] + for gh_team in gh_org.get_teams(): + team = team_obj.get_from_id_or_create( + gh_data=gh_team, extra_data={"organization_id": organization.id} + ) + team_ids.append(team.id) + organization.team_ids = team_ids + except GithubException as e: + if e.status == 403: + raise exceptions.AccessError( + _( + "The provided Github Token must have admin read:org" + " permissions to the organization '%s'" + ) + % self.name + ) from None + + def action_github_repository(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_github_repository" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_organization_id"] = self.id + return action + + def action_github_team(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_github_team" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_organization_id"] = self.id + return action + + def action_res_partner(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_res_partner" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_organization_ids"] = self.id + return action diff --git a/github_connector/models/github_organization_serie.py b/github_connector/models/github_organization_serie.py new file mode 100644 index 00000000..799a1719 --- /dev/null +++ b/github_connector/models/github_organization_serie.py @@ -0,0 +1,31 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class GithubOrganizationSerie(models.Model): + _name = "github.organization.serie" + _description = "Github Organization Serie" + _order = "sequence, name" + + # Columns Section + name = fields.Char(required=True) + + sequence = fields.Integer(required=True) + + organization_id = fields.Many2one( + comodel_name="github.organization", + string="Organization", + ondelete="cascade", + required=True, + ) + + _sql_constraints = [ + ( + "sequence_organization_uniq", + "unique(organization_id, sequence)", + "Sequence serie must be unique by organization.", + ) + ] diff --git a/github_connector/models/github_repository.py b/github_connector/models/github_repository.py new file mode 100644 index 00000000..b2ef7211 --- /dev/null +++ b/github_connector/models/github_repository.py @@ -0,0 +1,236 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# Copyright 2021 Tecnativa - João Marques +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + + +class GithubRepository(models.Model): + _name = "github.repository" + _inherit = ["abstract.github.model"] + _order = "organization_id, name" + _description = "Github Repository" + + _github_login_field = "full_name" + + # Column Section + organization_id = fields.Many2one( + comodel_name="github.organization", + string="Organization", + required=True, + index=True, + readonly=True, + ondelete="cascade", + ) + + name = fields.Char(index=True, required=True, readonly=True) + + complete_name = fields.Char( + readonly=True, + compute="_compute_complete_name", + store=True, + ) + + description = fields.Char(readonly=True) + + website = fields.Char(readonly=True) + + repository_branch_ids = fields.One2many( + comodel_name="github.repository.branch", + inverse_name="repository_id", + string="Branches", + readonly=True, + ) + + repository_branch_qty = fields.Integer( + string="Number of Branches", + compute="_compute_repository_branch_qty", + store=True, + ) + + team_ids = fields.One2many( + string="Teams", + comodel_name="github.team.repository", + inverse_name="repository_id", + readonly=True, + ) + + team_qty = fields.Integer( + string="Number of Teams", compute="_compute_team_qty", store=True + ) + + is_ignored = fields.Boolean( + compute="_compute_ignore", + help="If checked, the branches will not be synchronized, and the" + " code source will this way not be downloaded and analyzed. To ignore" + " a repository, go to the organization and add the file" + " 'Ignored Repositories'.", + ) + + color = fields.Integer(string="Color Index", compute="_compute_ignore") + inhibit_inherited_rules = fields.Boolean( + string="Inhibit inherited rules", + default=False, + help="If checked, the analysis rules to be used will be only those set here." + "\n If unchecked, the analysis rules to be used will be those set in the" + " organization and those set here.", + ) + analysis_rule_ids = fields.Many2many( + string="Analysis Rules", comodel_name="github.analysis.rule" + ) + + # Compute Section + @api.depends("organization_id.ignored_repository_names") + def _compute_ignore(self): + for repository in self: + ignored_txt = repository.organization_id.ignored_repository_names + repository.is_ignored = ( + ignored_txt and repository.name in ignored_txt.split("\n") + ) + repository.color = repository.is_ignored and 1 or 0 + + @api.depends("team_ids") + def _compute_team_qty(self): + data = self.env["github.team.repository"].read_group( + [("repository_id", "in", self.ids)], ["repository_id"], ["repository_id"] + ) + mapping = { + data["repository_id"][0]: data["repository_id_count"] for data in data + } + for item in self: + item.team_qty = mapping.get(item.id, 0) + + @api.depends("name", "organization_id.github_name") + def _compute_complete_name(self): + for repository in self: + repository.complete_name = "%(login)s/%(rep_name)s" % ( + { + "login": repository.organization_id.github_name, + "rep_name": repository.name or "", + } + ) + + @api.depends("repository_branch_ids.repository_id") + def _compute_repository_branch_qty(self): + data = self.env["github.repository.branch"].read_group( + [("repository_id", "in", self.ids)], ["repository_id"], ["repository_id"] + ) + mapping = { + data["repository_id"][0]: data["repository_id_count"] for data in data + } + for item in self: + item.repository_branch_qty = mapping.get(item.id, 0) + + # Overloadable Section + @api.model + def get_conversion_dict(self): + res = super().get_conversion_dict() + res.update( + { + "name": "name", + "description": "description", + "website": "homepage", + } + ) + return res + + @api.model + def get_odoo_data_from_github(self, gh_data): + res = super().get_odoo_data_from_github(gh_data) + org_id = self.env.context.get("github_organization_id", None) + if not org_id: + # Fetch current organization object + organization_obj = self.env["github.organization"] + organization = organization_obj.get_from_id_or_create(gh_data=gh_data.owner) + org_id = organization.id + res.update({"organization_id": org_id}) + return res + + def find_related_github_object(self, obj_id=None): + """Query Github API to find the related object""" + gh_api = self.get_github_connector() + return gh_api.get_repo(int(obj_id or self.github_id_external)) + + def get_github_base_obj_for_creation(self): + self.ensure_one() + gh_api = self.get_github_connector() + return gh_api.get_organization(self.organization_id.github_name) + + def _get_analysis_rules(self): + if self.inhibit_inherited_rules: + return self.analysis_rule_ids + return self.organization_id.analysis_rule_ids + self.analysis_rule_ids + + def create_in_github(self): + """Create an object in Github through the API""" + self.ensure_one() + # Create in Github + gh_base_obj = self.get_github_base_obj_for_creation() + gh_repo = gh_base_obj.create_repo( + name=self.name, description=self.description or "", homepage=self.website + ) + # Create in Odoo with the returned data and update object + data = self.get_odoo_data_from_github(gh_repo) + new_item = self._create_from_github_data(data) + new_item.full_update() + new_item._hook_after_github_creation() + return new_item + + def full_update(self): + self.button_sync_branch() + + @api.model + def cron_update_branch_list(self): + branches = self.search([]) + branches.button_sync_branch() + return True + + def button_sync_branch(self): + branch_obj = self.env["github.repository.branch"] + for repository in self.filtered(lambda r: not r.is_ignored): + gh_repo = repository.find_related_github_object() + branch_ids = [] + correct_series = repository.organization_id.organization_serie_ids.mapped( + "name" + ) + for gh_branch in gh_repo.get_branches(): + if gh_branch.name in correct_series: + # We don't use get_from_id_or_create because repository + # branches does not have any ids. (very basic object in the + # Github API) + branch = branch_obj.create_or_update_from_name( + repository.id, gh_branch.name + ) + branch_ids.append(branch.id) + else: + _logger.warning( + "the branch '%s'/'%s' has been ignored.", + repository.name, + gh_branch.name, + ) + repository.repository_branch_ids = [(6, 0, branch_ids)] + + def action_github_team_repository_from_repository(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_github_team_repository_from_repository" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_repository_id"] = self.id + return action + + def action_github_repository_branch(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_github_repository_branch" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_repository_id"] = self.id + return action diff --git a/github_connector/models/github_repository_branch.py b/github_connector/models/github_repository_branch.py new file mode 100644 index 00000000..8d7504ad --- /dev/null +++ b/github_connector/models/github_repository_branch.py @@ -0,0 +1,422 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# Copyright 2020 Tecnativa - Víctor Martínez +# Copyright 2021 Tecnativa - João Marques +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +import os +import shutil +from datetime import datetime +from subprocess import check_output + +from odoo import _, addons, api, exceptions, fields, models, tools +from odoo.tools.safe_eval import safe_eval + +_logger = logging.getLogger(__name__) + +try: + from git import Repo +except ImportError: + _logger.debug("Cannot import 'git' python library.") + + +class GithubRepository(models.Model): + _name = "github.repository.branch" + _inherit = ["abstract.github.model"] + _order = "repository_id, sequence_serie" + _description = "Github Repository Branch" + + _github_login_field = False + + _SELECTION_STATE = [ + ("to_download", "To Download"), + ("to_analyze", "To Analyze"), + ("analyzed", "Analyzed"), + ] + + # Column Section + name = fields.Char(readonly=True, index=True) + + size = fields.Integer(string="Size (Byte) ", readonly=True) + + mb_size = fields.Float( + string="Size (Megabyte)", store=True, compute="_compute_mb_size" + ) + + complete_name = fields.Char(store=True, compute="_compute_complete_name") + + repository_id = fields.Many2one( + comodel_name="github.repository", + string="Repository", + required=True, + index=True, + readonly=True, + ondelete="cascade", + ) + + organization_id = fields.Many2one( + comodel_name="github.organization", + string="Organization", + related="repository_id.organization_id", + store=True, + readonly=True, + ) + + organization_serie_id = fields.Many2one( + comodel_name="github.organization.serie", + string="Organization Serie", + store=True, + compute="_compute_organization_serie_id", + ) + + sequence_serie = fields.Integer( + string="Sequence Serie", store=True, related="organization_serie_id.sequence" + ) + + local_path = fields.Char(compute="_compute_local_path") + + state = fields.Selection(selection=_SELECTION_STATE, default="to_download") + + last_download_date = fields.Datetime() + + last_analyze_date = fields.Datetime() + + coverage_url = fields.Char( + string="Coverage URL", store=True, compute="_compute_coverage_url" + ) + + ci_url = fields.Char(string="CI URL", store=True, compute="_compute_ci_url") + + github_url = fields.Char( + string="Github URL", store=True, compute="_compute_github_url" + ) + inhibit_inherited_rules = fields.Boolean( + string="Inhibit inherited rules", + default=False, + help="If checked, the analysis rules to be used will be only those set here." + "\n If unchecked, the analysis rules to be used will be those set in the" + " organization, repository and those set here.", + ) + analysis_rule_ids = fields.Many2many( + string="Analysis Rules", comodel_name="github.analysis.rule" + ) + analysis_rule_info_ids = fields.One2many( + string="Analysis Rules (info)", + comodel_name="github.repository.branch.rule.info", + inverse_name="repository_branch_id", + ) + + # Init Section + def __init__(self, *args, **kwargs): + source_path = self._get_source_path() + if source_path and not os.path.exists(source_path): + try: + os.makedirs(source_path) + except Exception as e: + _logger.error( + _( + "Error when trying to create the main folder %(path)s\n" + " Please check Odoo Access Rights.\n %(error)s" + ) + % { + "path": source_path, + "error": e, + } + ) + if source_path and source_path not in addons.__path__: + addons.__path__.append(source_path) + super().__init__(*args, **kwargs) + + def _get_source_path(self): + return tools.config.get("source_code_local_path", "") or os.environ.get( + "SOURCE_CODE_LOCAL_PATH", "" + ) + + # Action Section + def button_download_code(self): + return self._download_code() + + def button_analyze_code(self): + return self._analyze_code() + + @api.model + def cron_download_all(self): + branches = self.search([]) + branches._download_code() + return True + + @api.model + def cron_analyze_all(self): + branches = self.search([("state", "=", "to_analyze")]) + branches._analyze_code() + return True + + # Custom + def create_or_update_from_name(self, repository_id, name): + branch = self.search( + [("name", "=", name), ("repository_id", "=", repository_id)] + ) + if not branch: + branch = self.create({"name": name, "repository_id": repository_id}) + return branch + + def _download_code(self): + for branch in self: + repository = branch.repository_id + gh_repo = repository.find_related_github_object() + if not os.path.exists(branch.local_path): + _logger.info("Cloning new repository into %s ..." % branch.local_path) + # Cloning the repository + try: + os.makedirs(branch.local_path) + except Exception: + raise exceptions.UserError( + _( + "Error when trying to create the folder %s\n" + " Please check Odoo Access Rights." + ) + % (branch.local_path) + ) from None + command = ("git clone %s -b %s %s") % ( + gh_repo.clone_url, + branch.name, + branch.local_path, + ) + os.system(command) + branch.write( + {"last_download_date": datetime.today(), "state": "to_analyze"} + ) + else: + # Update repository + _logger.info("Pulling existing repository %s ..." % branch.local_path) + try: + res = check_output( + ["git", "pull", "origin", branch.name], cwd=branch.local_path + ) + vals = {"last_download_date": datetime.today()} + if b"up to date" not in res: + vals["state"] = "to_analyze" + branch.write(vals) + except Exception: + # Trying to clean the local folder + _logger.warning( + _( + "Error when updating the branch %(branch)s in the local " + "folder %(path)s.\nDeleting the local folder and trying" + " again." + ) + % { + "branch": branch.name, + "path": branch.local_path, + } + ) + try: + shutil.rmtree(branch.local_path) + except Exception: + _logger.error( + "Error deleting the branch %s in the local folder " + "%s. You need to check manually what is happening " + "there." + ) + else: + branch._download_code() + return True + + def _get_analyzable_files(self, existing_folder): + res = [] + for root, _dirs, files in os.walk(existing_folder): + if "/.git" not in root: + for fic in files: + if fic != ".gitignore": + res.append(os.path.join(root, fic)) + return res + + def _get_analysis_rules(self): + if self.inhibit_inherited_rules: + return self.analysis_rule_ids + return self.repository_id._get_analysis_rules() + self.analysis_rule_ids + + def set_analysis_rule_info(self): + for rule_id in self._get_analysis_rules(): + self._process_analysis_rule_info(rule_id) + + def _process_analysis_rule_info(self, rule_id): + """Process to specific rule (Create or update info record).""" + analysis_rule_item = self.analysis_rule_info_ids.filtered( + lambda x: x.analysis_rule_id == rule_id + ) + vals = self._prepare_analysis_rule_info_vals(rule_id) + if analysis_rule_item: + self.analysis_rule_info_ids = [(1, analysis_rule_item.id, vals)] + else: + self.analysis_rule_info_ids = [(0, 0, vals)] + + def analyze_code_one(self): + """Overload Me in custom Module that manage Source Code analysis.""" + self.ensure_one() + path = self.local_path + self.set_analysis_rule_info() + # Compute Files Sizes + size = 0 + for file_path in self._get_analyzable_files(path): + try: + size += os.path.getsize(file_path) + except Exception: + _logger.warning("Warning : unable to eval the size of '%s'.", file_path) + try: + Repo(path) + except Exception: + # If it's not a correct repository, we flag the branch + # to be downloaded again + self.state = "to_download" + return {"size": 0} + return {"size": size} + + def _analyze_code(self): + partial_commit = safe_eval( + self.sudo() + .env["ir.config_parameter"] + .get_param("git.partial_commit_during_analysis") + ) + for branch in self: + path = branch.local_path + if not os.path.exists(path): + _logger.warning("Warning Folder %s not found: Analysis skipped.", path) + else: + _logger.info("Analyzing Source Code in %s ...", path) + try: + vals = branch.analyze_code_one() + vals.update( + {"last_analyze_date": datetime.today(), "state": "analyzed"} + ) + # Mark the branch as analyzed + branch.write(vals) + if partial_commit: + self._cr.commit() # pylint: disable=invalid-commit + except Exception as e: + _logger.warning( + "Cannot analyze branch %s so skipping it, error " "is: %s", + branch.name, + e, + ) + return True + + def _prepare_analysis_rule_info_vals(self, analysis_rule_id): + """Prepare info vals""" + res = self._operation_analysis_rule_id(analysis_rule_id) + return { + "analysis_rule_id": analysis_rule_id.id, + "repository_branch_id": self.id, + "code_count": res["code"], + "documentation_count": res["documentation"], + "empty_count": res["empty"], + "string_count": res["string"], + "scanned_files": len(res["paths"]), + } + + def _operation_analysis_rule_id(self, analysis_rule_id): + """This function allow to override with other addons that need + to change this analysis + """ + res = { + "paths": [], + "code": 0, + "documentation": 0, + "empty": 0, + "string": 0, + } + for match in analysis_rule_id._get_matches(self.local_path): + res_file = analysis_rule_id._analysis_file(self.local_path + "/" + match) + res["paths"].append(res_file["path"]) + # define values + for key in ("code", "documentation", "empty", "string"): + res[key] += res_file[key] + return res + + # Compute Section + @api.depends("name", "repository_id.name") + def _compute_complete_name(self): + for branch in self: + branch.complete_name = branch.repository_id.name + "/" + branch.name + + @api.depends("size") + def _compute_mb_size(self): + for branch in self: + branch.mb_size = float(branch.size) / (1024**2) + + @api.depends("organization_id", "name") + def _compute_organization_serie_id(self): + for branch in self: + for serie in branch.organization_id.organization_serie_ids: + if serie.name == branch.name: + branch.organization_serie_id = serie + + @api.depends("complete_name") + def _compute_local_path(self): + source_path = self._get_source_path() + if not source_path and not tools.config["test_enable"]: + raise exceptions.UserError( + _( + "source_code_local_path should be defined in your " + " configuration file" + ) + ) + for branch in self: + branch.local_path = os.path.join( + source_path, branch.organization_id.github_name, branch.complete_name + ) + + @api.depends( + "name", + "repository_id.name", + "organization_id.github_name", + "organization_id.coverage_url_pattern", + ) + def _compute_coverage_url(self): + for branch in self: + if not branch.organization_id.coverage_url_pattern: + branch.coverage_url = "" + else: + # This is done because if not, black format the line in a wrong + # way + org_id = branch.organization_id + branch.coverage_url = org_id.coverage_url_pattern.format( + organization_name=org_id.github_name, + repository_name=branch.repository_id.name, + branch_name=branch.name, + ) + + @api.depends( + "name", + "repository_id.name", + "organization_id.github_name", + "organization_id.ci_url_pattern", + ) + def _compute_ci_url(self): + for branch in self: + if not branch.organization_id.ci_url_pattern: + branch.ci_url = "" + continue + branch.ci_url = branch.organization_id.ci_url_pattern.format( + organization_name=branch.organization_id.github_name, + repository_name=branch.repository_id.name, + branch_name=branch.name, + ) + + @api.depends("name", "repository_id.complete_name") + def _compute_github_url(self): + for branch in self: + branch.github_url = f"{branch.repository_id.github_url}/tree/{branch.name}" + + +class GithubRepositoryBranchRuleInfo(models.TransientModel): + _inherit = "github.analysis.rule.info.mixin" + _name = "github.repository.branch.rule.info" + _description = " Github Repository Branch Rule Info" + + repository_branch_id = fields.Many2one( + string="Repository Branch", + comodel_name="github.repository.branch", + ondelete="cascade", + ) diff --git a/github_connector/models/github_team.py b/github_connector/models/github_team.py new file mode 100644 index 00000000..f6d22996 --- /dev/null +++ b/github_connector/models/github_team.py @@ -0,0 +1,218 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# Copyright 2021 Tecnativa - João Marques +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class GithubTeam(models.Model): + _name = "github.team" + _inherit = ["abstract.github.model"] + _order = "name" + _description = "Github Team" + + _github_login_field = "slug" + + _PRIVACY_SELECTION = [("secret", "Secret"), ("closed", "Closed")] + + # Column Section + organization_id = fields.Many2one( + comodel_name="github.organization", + string="Organization", + required=True, + index=True, + readonly=True, + ondelete="cascade", + ) + + name = fields.Char(index=True, required=True, readonly=True) + + privacy = fields.Selection( + selection=_PRIVACY_SELECTION, + readonly=True, + default="secret", + help="The level of privacy this team should have. Can be one of:\n" + "* secret - only visible to organization owners and members of" + " this team.\n" + "* closed - visible to all members of this organization.", + ) + + parent_id = fields.Many2one( + string="Parent Team", readonly=True, comodel_name="github.team" + ) + + partner_ids = fields.One2many( + string="Members", + comodel_name="github.team.partner", + inverse_name="team_id", + readonly=True, + ) + + partner_qty = fields.Integer( + string="Number of Members", compute="_compute_partner_qty", store=True + ) + + repository_ids = fields.One2many( + string="Repositories", + comodel_name="github.team.repository", + inverse_name="team_id", + readonly=True, + ) + + repository_qty = fields.Integer( + string="Number of Repositories", compute="_compute_repository_qty", store=True + ) + + description = fields.Char(readonly=True) + + complete_name = fields.Char( + readonly=True, + compute="_compute_complete_name", + store=True, + ) + + github_url = fields.Char( + string="Github URL", compute="_compute_github_url", readonly=True + ) + + # Compute Section + @api.depends("github_name", "organization_id.github_name") + def _compute_github_url(self): + for team in self: + team.github_url = ( + f"https://github.com/orgs/{team.organization_id.github_name}/" + f"teams/{team.github_name}" + ) + + @api.depends("name", "organization_id.github_name") + def _compute_complete_name(self): + for team in self: + team.complete_name = ( + f"{team.organization_id.github_name}/{team.github_name}" + ) + + @api.depends("partner_ids") + def _compute_partner_qty(self): + data = self.env["github.team.partner"].read_group( + [("team_id", "in", self.ids)], ["team_id"], ["team_id"] + ) + mapping = {data["team_id"][0]: data["team_id_count"] for data in data} + for item in self: + item.partner_qty = mapping.get(item.id, 0) + + @api.depends("repository_ids") + def _compute_repository_qty(self): + data = self.env["github.team.repository"].read_group( + [("team_id", "in", self.ids)], ["team_id"], ["team_id"] + ) + mapping = {data["team_id"][0]: data["team_id_count"] for data in data} + for item in self: + item.repository_qty = mapping.get(item.id, 0) + + # Overloadable Section + @api.model + def get_conversion_dict(self): + res = super().get_conversion_dict() + res.update({"name": "name", "description": "description", "privacy": "privacy"}) + return res + + @api.model + def get_odoo_data_from_github(self, gh_data): + organization_obj = self.env["github.organization"] + res = super().get_odoo_data_from_github(gh_data) + if gh_data.organization: + organization_id = organization_obj.get_from_id_or_create( + gh_data=gh_data.organization + ).id + else: + organization_id = False + res.update({"organization_id": organization_id}) + return res + + def get_github_base_obj_for_creation(self): + self.ensure_one() + gh_api = self.get_github_connector() + return gh_api.get_organization(self.organization_id.github_name) + + def create_in_github(self): + """Create an object in Github through the API""" + self.ensure_one() + # Create in Github + gh_base_obj = self.get_github_base_obj_for_creation() + gh_team = gh_base_obj.create_team( + name=self.name, description=self.description or "", privacy=self.privacy + ) + # Create in Odoo with the returned data and update object + data = self.get_odoo_data_from_github(gh_team) + new_item = self._create_from_github_data(data) + new_item.full_update() + new_item._hook_after_github_creation() + return new_item + + def full_update(self): + self.button_sync_member() + self.button_sync_repository() + + def find_related_github_object(self, obj_id=None): + """Query Github API to find the related object""" + self.get_github_connector() + return self.organization_id.find_related_github_object().get_team( + int(obj_id or self.github_id_external) + ) + + # Action Section + def button_sync_member(self): + partner_obj = self.env["res.partner"] + gh_team = self.find_related_github_object() + for team in self: + partner_data = [] + # Fetching the role after getting each user requires more API calls for + # each user, so we fetch the users in 2 steps, one for each role + for gh_user in gh_team.get_members(role="member"): + partner = partner_obj.get_from_id_or_create(gh_data=gh_user) + partner_data.append({"partner_id": partner.id, "role": "member"}) + for gh_user in gh_team.get_members(role="maintainer"): + partner = partner_obj.get_from_id_or_create(gh_data=gh_user) + partner_data.append({"partner_id": partner.id, "role": "maintainer"}) + team.partner_ids = [(2, x.id, False) for x in team.partner_ids] + team.partner_ids = [(0, False, x) for x in partner_data] + + def button_sync_repository(self): + repository_obj = self.env["github.repository"] + gh_team = self.find_related_github_object() + for team in self: + repository_data = [] + for gh_repo in gh_team.get_repos(): + repository = repository_obj.get_from_id_or_create(gh_data=gh_repo) + if gh_repo.permissions.admin: + permission = "admin" + elif gh_repo.permissions.push: + permission = "write" + else: + permission = "read" + repository_data.append( + {"repository_id": repository.id, "permission": permission} + ) + team.repository_ids = [(2, x.id, False) for x in team.repository_ids] + team.repository_ids = [(0, False, x) for x in repository_data] + + def action_github_team_partner_from_team(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_github_team_partner_from_team" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_team_id"] = self.id + return action + + def action_github_team_repository_from_team(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_github_team_repository_from_team" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_team_id"] = self.id + return action diff --git a/github_connector/models/github_team_partner.py b/github_connector/models/github_team_partner.py new file mode 100644 index 00000000..fb1ce90a --- /dev/null +++ b/github_connector/models/github_team_partner.py @@ -0,0 +1,54 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class GithubTeamPartner(models.Model): + _name = "github.team.partner" + _description = "Github Team Partner" + _order = "team_id, partner_id" + + _ROLE_SELECTION = [("member", "Member"), ("maintainer", "Maintainer")] + + # Column Section + team_id = fields.Many2one( + comodel_name="github.team", + string="Team", + required=True, + index=True, + readonly=True, + ondelete="cascade", + ) + + partner_id = fields.Many2one( + comodel_name="res.partner", + string="Member", + required=True, + index=True, + readonly=True, + ondelete="cascade", + ) + + role = fields.Selection(selection=_ROLE_SELECTION, required=True, readonly=True) + + context_search_default_team_id = fields.Integer( + compute="_compute_context_search_default" + ) + context_search_default_partner_id = fields.Integer( + compute="_compute_context_search_default" + ) + + @api.depends_context("search_default_team_id", "search_default_partner_id") + def _compute_context_search_default(self): + """Compute the context value for the search terms + into helper fields for the view + """ + for record in self: + record.context_search_default_team_id = self.env.context.get( + "search_default_team_id", False + ) + record.context_search_default_partner_id = self.env.context.get( + "search_default_partner_id", False + ) diff --git a/github_connector/models/github_team_repository.py b/github_connector/models/github_team_repository.py new file mode 100644 index 00000000..32bcd375 --- /dev/null +++ b/github_connector/models/github_team_repository.py @@ -0,0 +1,63 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class GithubTeamRepository(models.Model): + _name = "github.team.repository" + _description = "Github Team Repository" + _order = "team_id, repository_id" + + _PERMISSION_SELECTION = [ + ("undefined", "Undefined"), + ("read", "Read"), + ("write", "Write"), + ("admin", "Admin"), + ] + + # Column Section + team_id = fields.Many2one( + comodel_name="github.team", + string="Team", + required=True, + index=True, + readonly=True, + ondelete="cascade", + ) + + repository_id = fields.Many2one( + comodel_name="github.repository", + string="Repository", + required=True, + index=True, + readonly=True, + ondelete="cascade", + ) + + permission = fields.Selection( + selection=_PERMISSION_SELECTION, + required=True, + readonly=True, + ) + + context_search_default_team_id = fields.Integer( + compute="_compute_context_search_default" + ) + context_search_default_repository_id = fields.Integer( + compute="_compute_context_search_default" + ) + + @api.depends_context("search_default_team_id", "search_default_repository_id") + def _compute_context_search_default(self): + """Compute the context value for the search terms + into helper fields for the view + """ + for record in self: + record.context_search_default_team_id = self.env.context.get( + "search_default_team_id", False + ) + record.context_search_default_repository_id = self.env.context.get( + "search_default_repository_id", False + ) diff --git a/github_connector/models/res_partner.py b/github_connector/models/res_partner.py new file mode 100644 index 00000000..3b604542 --- /dev/null +++ b/github_connector/models/res_partner.py @@ -0,0 +1,129 @@ +# Copyright (C) 2016-Today: Odoo Community Association (OCA) +# Copyright 2021 Tecnativa - João Marques +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +# pylint: disable=missing-manifest-dependency +from github.GithubException import UnknownObjectException + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class ResPartner(models.Model): + _name = "res.partner" + _inherit = ["res.partner", "abstract.github.model"] + + _github_login_field = "login" + _need_individual_call = True + _field_list_prevent_overwrite = ["name", "website", "email", "image_1920"] + + # Column Section + is_bot_account = fields.Boolean( + string="Is Bot Github Account", + help="Check this box if this " "account is a bot or similar.", + ) + + github_team_partner_ids = fields.One2many( + string="Teams", + comodel_name="github.team.partner", + inverse_name="partner_id", + readonly=True, + ) + + github_team_qty = fields.Integer( + string="Number of Teams", compute="_compute_github_team_qty", store=True + ) + + organization_ids = fields.Many2many( + string="Organizations", + comodel_name="github.organization", + relation="github_organization_partner_rel", + column1="partner_id", + column2="organization_id", + readonly=True, + ) + + organization_qty = fields.Integer( + string="Number of Organizations", + compute="_compute_organization_qty", + store=True, + ) + + # Constraints Section + _sql_constraints = [ + ( + "github_login_uniq", + "unique(github_name)", + "Two different partners cannot have the same Github Login", + ) + ] + + @api.constrains("github_name", "is_company") + def _check_login_company(self): + for partner in self: + if partner.is_company and partner.github_name: + raise UserError( + _("A company ('%s') can not have a Github login" " associated.") + % partner.name + ) + + # Compute Section + @api.depends("organization_ids", "organization_ids.member_ids") + def _compute_organization_qty(self): + for partner in self: + partner.organization_qty = len(partner.organization_ids) + + @api.depends("github_team_partner_ids") + def _compute_github_team_qty(self): + data = self.env["github.team.partner"].read_group( + [("partner_id", "in", self.ids)], ["partner_id"], ["partner_id"] + ) + mapping = {data["partner_id"][0]: data["partner_id_count"] for data in data} + for item in self: + item.github_team_qty = mapping.get(item.id, 0) + + # Custom Section + @api.model + def get_conversion_dict(self): + res = super().get_conversion_dict() + res.update({"website": "blog", "email": "email"}) + return res + + @api.model + def get_odoo_data_from_github(self, gh_data): + res = super().get_odoo_data_from_github(gh_data) + res.update({"name": gh_data.name or "%s (Github)" % gh_data.login}) + if hasattr(gh_data, "avatar_url"): + res.update( + {"image_1920": self.get_base64_image_from_github(gh_data.avatar_url)} + ) + return res + + def find_related_github_object(self, obj_id=None): + """Query Github API to find the related object""" + gh_api = self.get_github_connector() + try: + return gh_api.get_user_by_id(int(obj_id or self.github_id_external)) + except UnknownObjectException: + return gh_api.get_user(self.github_name) + + def action_github_organization(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_github_organization" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_member_ids"] = self.id + return action + + def action_github_team_partner_from_partner(self): + self.ensure_one() + action = self.env["ir.actions.act_window"]._for_xml_id( + "github_connector.action_github_team_partner_from_partner" + ) + action["context"] = dict(self.env.context) + action["context"].pop("group_by", None) + action["context"]["search_default_partner_id"] = self.id + return action diff --git a/github_connector/pyproject.toml b/github_connector/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/github_connector/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/github_connector/readme/CONFIGURE.md b/github_connector/readme/CONFIGURE.md new file mode 100644 index 00000000..97196952 --- /dev/null +++ b/github_connector/readme/CONFIGURE.md @@ -0,0 +1,44 @@ +Once installed, you have to: + +1. Open your odoo.conf file and add extra settings to mention Github + credentials, and the local path where the source code will be + downloaded: + - `source_code_local_path = /workspace/source_code/` + +Note: you can define the route as environment variable using the key +SOURCECODE_LOCAL_PATH + +Note: make sure that Odoo process has read / write access on that folder + +> - `github_token = your_github_access_token` + +Note: The login/password auth has been deprecated by GitHub. + + +1. Go to 'Settings' / 'Technical' / 'Parameters' / 'System Parameters' + and define the following values: + + 1. `github.max_try`: number of call to the API before an error is + raised. The more unstable/slow your connection, the higher + should be this value + 2. `git.partial_commit_during_analysis`: Set to `True` if you want + to commit the result of the analysis in the database after each + repository analysis. We recommend to set to `True` when you + perform the initial download (potentially with a lot of + repositories) in order to reduce the size of the transaction + + ![image](../static/description/github_settings.png) + +2. Go to your(s) user(s) form to add them in the new 'Connector Github + Manager' groups. The members of this group will have the possibility + to run Github synchronization. + +## Technical Information + +This module provides 4 crons that you can enable: + +- Synchronize All Organizations and Teams (`cron_update_organization`) +- Synchronize Branches List for All repositories + (`cron_update_branch_list`) +- Download Source Code for All Github Branches (`cron_download_code`) +- Analyze Source Code for All Github Branches (`cron_analyze_code`) diff --git a/github_connector/readme/CONTRIBUTORS.md b/github_connector/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..4f853d0c --- /dev/null +++ b/github_connector/readme/CONTRIBUTORS.md @@ -0,0 +1,12 @@ +- Sylvain LE GAL () +- Sébastien BEAU () +- Benoît GUILLOT () +- Enrique Martín () +- [Tecnativa](https://www.tecnativa.com):", + - Pedro M. Baeza + - Vicent Cubells + - Alexandre Díaz + - Ernesto Tejeda + - Carlos Roca + - Víctor Martínez + - João Marques diff --git a/github_connector/readme/DESCRIPTION.md b/github_connector/readme/DESCRIPTION.md new file mode 100644 index 00000000..dd7ab77f --- /dev/null +++ b/github_connector/readme/DESCRIPTION.md @@ -0,0 +1,8 @@ +This module allows you to: + +- Fetch into Odoo social information from Github (Organizations, Teams, + Users) +- Fetch into Odoo Code structure information from Github (Repositories, + Branches) +- Download source code from Github +- Analyze repository code from rules previously created diff --git a/github_connector/readme/INSTALL.md b/github_connector/readme/INSTALL.md new file mode 100644 index 00000000..b51dd110 --- /dev/null +++ b/github_connector/readme/INSTALL.md @@ -0,0 +1,11 @@ +To install this addon, you need to install some python dependencies + +``` bash +sudo pip install PyGitHub +sudo pip install pygount +sudo pip install pathspec +sudo pip install GitPython +``` + +Analysis source code is generated by + diff --git a/github_connector/readme/ROADMAP.md b/github_connector/readme/ROADMAP.md new file mode 100644 index 00000000..13701567 --- /dev/null +++ b/github_connector/readme/ROADMAP.md @@ -0,0 +1,23 @@ +- For the time being, Github API doesn't provide some informations that + are available by the classic UI, that includes: + + 1. team hierarchy: the field is present in the model + githubteam.parent_id, but unused. + +- Possible improvements: + + 1. Create a new module githubconnector_website, that could display + teams / repositories / branches informations for non logged users. + 2. Analyze commits (author, quantity by series, etc...): this feature + has been partially implemented in a V8.0 PR. + 3. Synchronize Pull Request, Issues, Comments: this feature has been + partially implemented in a V8.0 PR. + +- Refactor the github connector: + + A python library called PyGitHub is available. It could be interesting + to use it, instead of using custom code. However, this lib doesn't + provide good access to child object, generating for the time being, + unnecessary API calls. For example, updating a repository should call + before a call to the parent organization (The current module is so + faster). diff --git a/github_connector/readme/USAGE.md b/github_connector/readme/USAGE.md new file mode 100644 index 00000000..dc4699da --- /dev/null +++ b/github_connector/readme/USAGE.md @@ -0,0 +1,170 @@ +## Initial upload from Github + +To fetch information from Github, you have to: + +1. go to 'Github' / 'Settings' / 'Sync Object' + +2. Select the object type you want to synchronize and its Gthub name + + ![image](../static/description/sync_organization.png) + +3. Once done for your organization(s), go to 'Github' / 'Github + Commnunity' / 'Organizations' + + ![image](../static/description/github_organization_kanban.png) + +4. Optionally, once organization is created, you can create series for + your projects. Go to 'Github' / 'Organizations' / click on your + organization / 'Organization Series' Tabs + + ![image](../static/description/github_organization_series.png) + +## Select branches to download + +This setting will prevent to download undesired branches, downloading +only main branches (releases): + +1. In the 'Settings' tab, set repositories you don't want to download + (or repositories you want to download). If 'Specific repositories' + is set, 'Ignored Repositories' value is ignored. + +2. In the 'Settings' tab, set the URL of the 'External Services' you + use for Continuous Integration and Coverage. + + ![image](../static/description/github_organization_external_services.png) + +3. Once done, click on buttons 'Syncs', to synchronize repositories, + teams and members. (This process can take a while depending of your + size) + + ![image](../static/description/github_organization_sync_buttons.png) + +## Team / members synchronization + +You can synchronize members teams: + +1. Go to 'Teams' / tree view / 'Actions' / 'Update from Github'. + + ![image](../static/description/github_team_kanban.png) + +2. In each team, you can see the members list and the role of the + members + + ![image](../static/description/github_team_partner_kanban.png) + +3. In each team, you can see the repositories list but not the + permissions of the team. (See 'Known Issues' Section) + + ![image](../static/description/github_team_repository_kanban.png) + +## Repositories synchronization + +You can synchronize the branches of your repositories: + +1. Go to 'Repositories' / tree view / 'Actions' / 'Update from Github' + + ![image](../static/description/github_repository_kanban.png) + +2. In each repository, you can see the main branches list and the size + of code source. + + ![image](../static/description/github_repository_branch_kanban.png) + +## Fetching the source code + +Finally, you can download locally the source code of all your branches: + +1. Go to 'Repository Branches' / tree view / 'Actions' / 'Download and + Analyse Source Code'. + + ![image](../static/description/wizard_download_analyze.png) + +2. In the tree view you can update manually source code or refresh + analysis. + + ![image](../static/description/github_repository_branch_list.png) + +## Analysis source code + +It's possible to create custom analysis rules that relate to a GitHub +organization, GitHub repository and/or GitHub repository branch to +analyze code. + +1. Go to 'Settings' / 'Analysis rule groups' and create records that + allow to organize the rules +2. Go to 'Settings' / 'Analysis rules' and create rules, for example: + +> 1. Name: All code, Group: General, Paths: `*` +> 2. Name: Python code, Group: General, Paths: `*.py` +> 3. Name: Xml code, Group: General, Paths: `*.xml` +> 4. Name: Repository 1, Group: Custom, Paths: /path/ + +Note: Paths field in 'Analysis rule' allow to put multiple paths for +line, path format is according to + + +1. Go to *GitHub \> GitHub Community \> Organizations* and define + Analysis rules (optional) +2. Go to *GitHub \> GitHub Repository \> Repositories* and define + Analysis rules (optional) +3. Go to *GitHub \> GitHub Repository \> Repository Branches* and + define Analysis rules (optional) + +Analysis source code is executed when 'Update Source Code Analysis' +button in some 'Repository Branch', get all the Analysis rules +(Repository + Organization) and analyze code and generate info about it +Other option to Analysis source code is in cron called 'Analyze Source +Code for All Github Branches' + +You can see in 'Repository Branch' / 'Code Analysis' the info obtained +from analysis rules. + +## Data creation in Github + +You have the possibility to creates two items in Github directly from +Odoo + +1. Teams: + + 1. Go to 'Settings' / 'Create Team in Github'. + 2. Set the information and click on Create in Github. + 3. Odoo will try to create the team. If access right and datas are + correct, the creation will be done directly in Github + 4. Later on, a synchronization will be performed, to create the + according team in the Odoo instance. + + ![image](../static/description/wizard_create_team.png) + +2. Repositories: + + 1. Go to 'Settings' / 'Create Team in Github'. + 2. Set the information and click on Create in Github. + + ![image](../static/description/wizard_create_repository.png) + +## Note + +Analysis in this module is basic: for the time being, it just gives +branches size. + +Nevertheless, you can develop an extra Odoo Custom module to extend +analysis function and get extra statistics, depending on your needs. + +In that way, you can see the module githubconnector_odoo, if your +repositories contain Odoo modules. + +## Reporting + +This module provides several reports + +**Branches by Serie** + +![image](../static/description/reporting_branches_by_serie.png) + +**Sizes by Serie** + +![image](../static/description/reporting_sizes_by_serie.png) + +**Repository branch analysis rule** + +![image](../static/description/github_repository_branch_rule_info_report.png) diff --git a/github_connector/report/__init__.py b/github_connector/report/__init__.py new file mode 100644 index 00000000..1d7ac39c --- /dev/null +++ b/github_connector/report/__init__.py @@ -0,0 +1 @@ +from . import github_repository_branch_rule_info_report diff --git a/github_connector/report/github_repository_branch_rule_info_report.py b/github_connector/report/github_repository_branch_rule_info_report.py new file mode 100644 index 00000000..35eddb85 --- /dev/null +++ b/github_connector/report/github_repository_branch_rule_info_report.py @@ -0,0 +1,89 @@ +# Copyright 2020 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models, tools + + +class GithubRepositoryBranchRuleInfoReport(models.Model): + _inherit = "github.analysis.rule" + _name = "github.repository.branch.rule.info.report" + _description = "Github Repository Branch Rule Info Report" + _auto = False + + analysis_rule_id = fields.Many2one( + string="Analysis Rule", + comodel_name="github.analysis.rule", + ) + group_id = fields.Many2one( + string="Group", related="analysis_rule_id.group_id", readonly=True + ) + repository_branch_id = fields.Many2one( + string="Repository Branch", + comodel_name="github.repository.branch", + ) + repository_id = fields.Many2one( + string="Repository", + comodel_name="github.repository", + ) + organization_serie_id = fields.Many2one( + string="Organization serie", + comodel_name="github.organization.serie", + ) + code_count = fields.Integer(string="# Code") + documentation_count = fields.Integer(string="# Documentation") + empty_count = fields.Integer(string="# Empty") + string_count = fields.Integer(string="# String") + total_count = fields.Integer(string="# Total") + scanned_files = fields.Integer() + + def _query(self, with_clause="", fields=None, groupby="", from_clause=""): + if fields is None: + fields = {} + with_ = ("WITH %s" % with_clause) if with_clause else "" + + select_ = """ + min(grbri.id) as id, + gar.id as analysis_rule_id, + garg.id as group_id, + grb.id as repository_branch_id, + gr.id as repository_id, + gos.id as organization_serie_id, + sum(grbri.code_count) as code_count, + sum(grbri.documentation_count) as documentation_count, + sum(grbri.string_count) as string_count, + sum(grbri.empty_count) as empty_count, + sum(grbri.total_count) as total_count, + sum(grbri.scanned_files) as scanned_files + """ + + from_ = ( + """ + github_repository_branch_rule_info as grbri + left join github_analysis_rule as gar on grbri.analysis_rule_id = gar.id + left join github_analysis_rule_group as garg on gar.group_id = garg.id + left join github_repository_branch as grb + on grbri.repository_branch_id = grb.id + left join github_organization_serie as gos + on grb.organization_serie_id = gos.id + left join github_repository as gr on grb.repository_id = gr.id + %s + """ + % from_clause + ) + + groupby_ = """ + gar.id, + garg.id, + grb.id, + gr.id, + gos.id %s + """ % (groupby) + + return f"{with_} (SELECT {select_} FROM {from_} WHERE grbri.id > 0 GROUP BY {groupby_})" + + def init(self): + tools.drop_view_if_exists(self.env.cr, self._table) + # pylint: disable=E8103 + self.env.cr.execute( + f"""CREATE or REPLACE VIEW {self._table} as ({self._query()})""" + ) diff --git a/github_connector/report/github_repository_branch_rule_info_report_view.xml b/github_connector/report/github_repository_branch_rule_info_report_view.xml new file mode 100644 index 00000000..6ba61694 --- /dev/null +++ b/github_connector/report/github_repository_branch_rule_info_report_view.xml @@ -0,0 +1,46 @@ + + + + + github.repository.branch.rule.info.report + + + + + + + + + github.repository.branch.rule.info.report + + + + + + + + + Repository branch analysis rule + github.repository.branch.rule.info.report + pivot,graph + + + diff --git a/github_connector/security/ir.model.access.csv b/github_connector/security/ir.model.access.csv new file mode 100644 index 00000000..d5b09dcd --- /dev/null +++ b/github_connector/security/ir.model.access.csv @@ -0,0 +1,28 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_github_analysis_rule_reader,github_analysis_rule reader,model_github_analysis_rule,,1,0,0,0 +access_github_analysis_rule_manager,github_analysis_rule manager,model_github_analysis_rule,github_connector.group_github_connector_manager,1,1,1,1 +access_github_analysis_rule_group_reader,github_analysis_rule_group reader,model_github_analysis_rule_group,,1,0,0,0 +access_github_analysis_rule_group_manager,github_analysis_rule_group manager,model_github_analysis_rule_group,github_connector.group_github_connector_manager,1,1,1,1 +access_github_organization_reader,github_organization reader,model_github_organization,,1,0,0,0 +access_github_organization_manager,github_organization manager,model_github_organization,github_connector.group_github_connector_manager,1,1,1,1 +access_github_organization_serie_reader,github_organization_serie reader,model_github_organization_serie,,1,0,0,0 +access_github_organization_serie_manager,github_organization_serie manager,model_github_organization_serie,github_connector.group_github_connector_manager,1,1,1,1 +access_github_repository_branch_rule_info_report_reader,github_repository_branch_rule_info_report reader,model_github_repository_branch_rule_info_report,,1,0,0,0 +access_github_repository_reader,github_repository reader,model_github_repository,,1,0,0,0 +access_github_repository_manager,github_repository manager,model_github_repository,github_connector.group_github_connector_manager,1,1,1,1 +access_github_repository_branch_reader,github_repository_branch reader,model_github_repository_branch,,1,0,0,0 +access_github_repository_branch_manager,github_repository_branch manager,model_github_repository_branch,github_connector.group_github_connector_manager,1,1,1,1 +access_github_team_reader,github_team reader,model_github_team,,1,0,0,0 +access_github_team_manager,github_team manager,model_github_team,github_connector.group_github_connector_manager,1,1,1,1 +access_github_team_partner_reader,github_team partner reader,model_github_team_partner,,1,0,0,0 +access_github_team_partner_manager,github_team partner manager,model_github_team_partner,github_connector.group_github_connector_manager,1,1,1,1 +access_github_team_repository_reader,github_team repository reader,model_github_team_repository,,1,0,0,0 +access_github_team_repository_manager,github_team repository manager,model_github_team_repository,github_connector.group_github_connector_manager,1,1,1,1 +access_github_repository_branch_rule_info_reader,access_github_repository_branch_rule_info_reader,model_github_repository_branch_rule_info,base.group_user,1,0,0,0 +access_github_repository_branch_rule_info_manager,access_github_repository_branch_rule_info_manager,model_github_repository_branch_rule_info,github_connector.group_github_connector_manager,1,1,1,1 +access_wizard_load_github_model_reader,access_wizard_load_github_model,model_wizard_load_github_model,base.group_user,1,0,0,0 +access_wizard_load_github_model_manager,access_wizard_load_github_model_manager,model_wizard_load_github_model,github_connector.group_github_connector_manager,1,1,1,1 +access_wizard_create_repository_reader,access_wizard_create_repository,model_wizard_create_repository,base.group_user,1,0,0,0 +access_wizard_create_repository_manager,access_wizard_create_repository_manager,model_wizard_create_repository,github_connector.group_github_connector_manager,1,1,1,1 +access_wizard_create_team_reader,access_wizard_create_team_reader,model_wizard_create_team,base.group_user,1,0,0,0 +access_wizard_create_team_manager,access_wizard_create_team_manager,model_wizard_create_team,github_connector.group_github_connector_manager,1,1,1,1 diff --git a/github_connector/security/ir_model_category.xml b/github_connector/security/ir_model_category.xml new file mode 100644 index 00000000..5e06ad6e --- /dev/null +++ b/github_connector/security/ir_model_category.xml @@ -0,0 +1,12 @@ + + + + + Github Connector + 20 + + diff --git a/github_connector/security/res_groups.xml b/github_connector/security/res_groups.xml new file mode 100644 index 00000000..3a0b44c3 --- /dev/null +++ b/github_connector/security/res_groups.xml @@ -0,0 +1,27 @@ + + + + + + + + Connector Github User + + + + Connector Github Manager + + + + + diff --git a/github_connector/static/description/github_organization_external_services.png b/github_connector/static/description/github_organization_external_services.png new file mode 100644 index 00000000..b92f8552 Binary files /dev/null and b/github_connector/static/description/github_organization_external_services.png differ diff --git a/github_connector/static/description/github_organization_kanban.png b/github_connector/static/description/github_organization_kanban.png new file mode 100644 index 00000000..21a0d4ac Binary files /dev/null and b/github_connector/static/description/github_organization_kanban.png differ diff --git a/github_connector/static/description/github_organization_series.png b/github_connector/static/description/github_organization_series.png new file mode 100644 index 00000000..ef815903 Binary files /dev/null and b/github_connector/static/description/github_organization_series.png differ diff --git a/github_connector/static/description/github_organization_sync_buttons.png b/github_connector/static/description/github_organization_sync_buttons.png new file mode 100644 index 00000000..3d6504a7 Binary files /dev/null and b/github_connector/static/description/github_organization_sync_buttons.png differ diff --git a/github_connector/static/description/github_repository_branch_kanban.png b/github_connector/static/description/github_repository_branch_kanban.png new file mode 100644 index 00000000..d77aa3a4 Binary files /dev/null and b/github_connector/static/description/github_repository_branch_kanban.png differ diff --git a/github_connector/static/description/github_repository_branch_list.png b/github_connector/static/description/github_repository_branch_list.png new file mode 100644 index 00000000..8971ff63 Binary files /dev/null and b/github_connector/static/description/github_repository_branch_list.png differ diff --git a/github_connector/static/description/github_repository_branch_rule_info_report.png b/github_connector/static/description/github_repository_branch_rule_info_report.png new file mode 100644 index 00000000..da943a61 Binary files /dev/null and b/github_connector/static/description/github_repository_branch_rule_info_report.png differ diff --git a/github_connector/static/description/github_repository_kanban.png b/github_connector/static/description/github_repository_kanban.png new file mode 100644 index 00000000..9d2a5918 Binary files /dev/null and b/github_connector/static/description/github_repository_kanban.png differ diff --git a/github_connector/static/description/github_settings.png b/github_connector/static/description/github_settings.png new file mode 100644 index 00000000..e2c92d76 Binary files /dev/null and b/github_connector/static/description/github_settings.png differ diff --git a/github_connector/static/description/github_team_kanban.png b/github_connector/static/description/github_team_kanban.png new file mode 100644 index 00000000..ed6a1e0a Binary files /dev/null and b/github_connector/static/description/github_team_kanban.png differ diff --git a/github_connector/static/description/github_team_partner_kanban.png b/github_connector/static/description/github_team_partner_kanban.png new file mode 100644 index 00000000..2c44ddd8 Binary files /dev/null and b/github_connector/static/description/github_team_partner_kanban.png differ diff --git a/github_connector/static/description/github_team_repository_kanban.png b/github_connector/static/description/github_team_repository_kanban.png new file mode 100644 index 00000000..303a051d Binary files /dev/null and b/github_connector/static/description/github_team_repository_kanban.png differ diff --git a/github_connector/static/description/icon.png b/github_connector/static/description/icon.png new file mode 100644 index 00000000..b8d31321 Binary files /dev/null and b/github_connector/static/description/icon.png differ diff --git a/github_connector/static/description/icon.svg b/github_connector/static/description/icon.svg new file mode 100644 index 00000000..86d303f4 --- /dev/null +++ b/github_connector/static/description/icon.svg @@ -0,0 +1,132 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/github_connector/static/description/index.html b/github_connector/static/description/index.html new file mode 100644 index 00000000..fb7d838a --- /dev/null +++ b/github_connector/static/description/index.html @@ -0,0 +1,704 @@ + + + + + + +Github Connector + + + +
+

Github Connector

+ + +

Beta License: AGPL-3 OCA/interface-github Translate me on Weblate Try me on Runboat

+

This module allows you to:

+
    +
  • Fetch into Odoo social information from Github (Organizations, Teams, Users)
  • +
  • Fetch into Odoo Code structure information from Github (Repositories, +Branches)
  • +
  • Download source code from Github
  • +
  • Analyze repository code from rules previously created
  • +
+

Table of contents

+ +
+

Installation

+

To install this addon, you need to install some python dependencies

+
+sudo pip install PyGitHub
+sudo pip install pygount
+sudo pip install pathspec
+sudo pip install GitPython
+
+

Analysis source code is generated by https://github.com/roskakori/pygount

+
+
+

Configuration

+

Once installed, you have to:

+
    +
  1. Open your odoo.conf file and add extra settings to mention Github +credentials, and the local path where the source code will be downloaded:
      +
    • source_code_local_path = /workspace/source_code/
    • +
    +
  2. +
+

Note: you can define the route as environment variable using the key +SOURCE_CODE_LOCAL_PATH

+

Note: make sure that Odoo process has read / write access on that folder

+
+
    +
  • github_token = your_github_access_token
  • +
+
+

Note: The login/password auth has been deprecated by GitHub. +https://docs.github.com/en/rest/overview/other-authentication-methods#via-username-and-password

+
    +
  1. Go to ‘Settings’ / ‘Technical’ / ‘Parameters’ / ‘System Parameters’ +and define the following values:

    +
      +
    1. github.max_try: number of call to the API before an error +is raised. The more unstable/slow your connection, the higher should be +this value
    2. +
    3. git.partial_commit_during_analysis: Set to True if you want to +commit the result of the analysis in the database after each repository +analysis. We recommend to set to True when you perform the initial +download (potentially with a lot of repositories) in order to reduce the +size of the transaction
    4. +
    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_settings.png +
  2. +
  3. Go to your(s) user(s) form to add them in the new ‘Connector Github Manager’ +groups. The members of this group will have the possibility to run Github +synchronization.

    +
  4. +
+
+

Technical Information

+

This module provides 4 crons that you can enable:

+
    +
  • Synchronize All Organizations and Teams (cron_update_organization)
  • +
  • Synchronize Branches List for All repositories (cron_update_branch_list)
  • +
  • Download Source Code for All Github Branches (cron_download_code)
  • +
  • Analyze Source Code for All Github Branches (cron_analyze_code)
  • +
+
+
+
+

Usage

+
+

Initial upload from Github

+

To fetch information from Github, you have to:

+
    +
  1. go to ‘Github’ / ‘Settings’ / ‘Sync Object’

    +
  2. +
  3. Select the object type you want to synchronize and its Gthub name

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/sync_organization.png +
  4. +
  5. Once done for your organization(s), go to ‘Github’ / ‘Github Commnunity’ / +‘Organizations’

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_organization_kanban.png +
  6. +
  7. Optionally, once organization is created, you can create series for your +projects. Go to ‘Github’ / ‘Organizations’ / click on your organization / +‘Organization Series’ Tabs

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_organization_series.png +
  8. +
+
+
+

Select branches to download

+

This setting will prevent to download undesired branches, downloading only +main branches (releases):

+
    +
  1. In the ‘Settings’ tab, set repositories you don’t want to download +(or repositories you want to download). If ‘Specific repositories’ is set, +‘Ignored Repositories’ value is ignored.

    +
  2. +
  3. In the ‘Settings’ tab, set the URL of the ‘External Services’ you use +for Continuous Integration and Coverage.

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_organization_external_services.png +
  4. +
  5. Once done, click on buttons ‘Syncs’, to synchronize repositories, teams and +members. (This process can take a while depending of your size)

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_organization_sync_buttons.png +
  6. +
+
+
+

Team / members synchronization

+

You can synchronize members teams:

+
    +
  1. Go to ‘Teams’ / tree view / ‘Actions’ / ‘Update from Github’.

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_team_kanban.png +
  2. +
  3. In each team, you can see the members list and the role of the members

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_team_partner_kanban.png +
  4. +
  5. In each team, you can see the repositories list but not the permissions of the +team. (See ‘Known Issues’ Section)

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_team_repository_kanban.png +
  6. +
+
+
+

Repositories synchronization

+

You can synchronize the branches of your repositories:

+
    +
  1. Go to ‘Repositories’ / +tree view / ‘Actions’ / ‘Update from Github’

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_repository_kanban.png +
  2. +
  3. In each repository, you can see the main branches list and the size of code +source.

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_repository_branch_kanban.png +
  4. +
+
+
+

Fetching the source code

+

Finally, you can download locally the source code of all your branches:

+
    +
  1. Go to ‘Repository Branches’ / tree view / ‘Actions’ / ‘Download and Analyse Source Code’.

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/wizard_download_analyze.png +
  2. +
  3. In the tree view you can update manually source code or refresh analysis.

    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_repository_branch_list.png +
  4. +
+
+
+

Analysis source code

+

It’s possible to create custom analysis rules that relate to a GitHub organization, GitHub repository and/or GitHub repository branch to analyze code.

+
    +
  1. Go to ‘Settings’ / ‘Analysis rule groups’ and create records that allow to organize the rules
  2. +
  3. Go to ‘Settings’ / ‘Analysis rules’ and create rules, for example:
  4. +
+
+
    +
  1. Name: All code, Group: General, Paths: *
  2. +
  3. Name: Python code, Group: General, Paths: *.py
  4. +
  5. Name: Xml code, Group: General, Paths: *.xml
  6. +
  7. Name: Repository 1, Group: Custom, Paths: /path/
  8. +
+
+

Note: Paths field in ‘Analysis rule’ allow to put multiple paths for line, path format is according to https://git-scm.com/docs/gitignore#_pattern_format

+
    +
  1. Go to GitHub > GitHub Community > Organizations and define Analysis rules (optional)
  2. +
  3. Go to GitHub > GitHub Repository > Repositories and define Analysis rules (optional)
  4. +
  5. Go to GitHub > GitHub Repository > Repository Branches and define Analysis rules (optional)
  6. +
+

Analysis source code is executed when ‘Update Source Code Analysis’ button in some ‘Repository Branch’, get all the Analysis rules (Repository + Organization) and analyze code and generate info about it +Other option to Analysis source code is in cron called ‘Analyze Source Code for All Github Branches’

+

You can see in ‘Repository Branch’ / ‘Code Analysis’ the info obtained from analysis rules.

+
+
+

Data creation in Github

+

You have the possibility to creates two items in Github directly from Odoo

+
    +
  1. Teams:

    +
      +
    1. Go to ‘Settings’ / ‘Create Team in Github’.
    2. +
    3. Set the information and click on Create in Github.
    4. +
    5. Odoo will try to create the team. If access right and datas are correct, +the creation will be done directly in Github
    6. +
    7. Later on, a synchronization will be performed, to create the according +team in the Odoo instance.
    8. +
    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/wizard_create_team.png +
  2. +
  3. Repositories:

    +
      +
    1. Go to ‘Settings’ / ‘Create Team in Github’.
    2. +
    3. Set the information and click on Create in Github.
    4. +
    +https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/wizard_create_repository.png +
  4. +
+
+
+

Note

+

Analysis in this module is basic: for the time being, it just gives branches +size.

+

Nevertheless, you can develop an extra Odoo Custom module to extend analysis +function and get extra statistics, depending on your needs.

+

In that way, you can see the module github_connector_odoo, if your repositories +contain Odoo modules.

+
+
+

Reporting

+

This module provides several reports

+

Branches by Serie

+https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/reporting_branches_by_serie.png +

Sizes by Serie

+https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/reporting_sizes_by_serie.png +

Repository branch analysis rule

+https://raw.githubusercontent.com/OCA/interface-github/16.0/github_connector/static/description/github_repository_branch_rule_info_report.png +
+
+
+

Known issues / Roadmap

+
    +
  • For the time being, Github API doesn’t provide some informations that are +available by the classic UI, that includes:

    +
      +
    1. team hierarchy: the field is present in the model github_team.parent_id, +but unused.
    2. +
    +
  • +
  • Possible improvements:

    +
      +
    1. Create a new module github_connector_website, that could display +teams / repositories / branches informations for non logged users.
    2. +
    3. Analyze commits (author, quantity by series, etc…): +this feature has been partially implemented in a V8.0 PR.
    4. +
    5. Synchronize Pull Request, Issues, Comments: +this feature has been partially implemented in a V8.0 PR.
    6. +
    +
  • +
  • Refactor the github connector:

    +

    A python library called PyGitHub is available. It could be interesting +to use it, instead of using custom code. However, this lib doesn’t provide +good access to child object, generating for the time being, unnecessary +API calls. For example, updating a repository should call before a call to +the parent organization (The current module is so faster).

    +
  • +
+
+
+

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 to smash it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • GRAP
  • +
  • Akretion
  • +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

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/interface-github project on GitHub.

+

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

+
+
+
+ + diff --git a/github_connector/static/description/reporting_branches_by_serie.png b/github_connector/static/description/reporting_branches_by_serie.png new file mode 100644 index 00000000..c57c6ed4 Binary files /dev/null and b/github_connector/static/description/reporting_branches_by_serie.png differ diff --git a/github_connector/static/description/reporting_sizes_by_serie.png b/github_connector/static/description/reporting_sizes_by_serie.png new file mode 100644 index 00000000..e27faae6 Binary files /dev/null and b/github_connector/static/description/reporting_sizes_by_serie.png differ diff --git a/github_connector/static/description/sync_organization.png b/github_connector/static/description/sync_organization.png new file mode 100644 index 00000000..69339f3d Binary files /dev/null and b/github_connector/static/description/sync_organization.png differ diff --git a/github_connector/static/description/wizard_create_repository.png b/github_connector/static/description/wizard_create_repository.png new file mode 100644 index 00000000..bd6f609f Binary files /dev/null and b/github_connector/static/description/wizard_create_repository.png differ diff --git a/github_connector/static/description/wizard_create_team.png b/github_connector/static/description/wizard_create_team.png new file mode 100644 index 00000000..a810ad0a Binary files /dev/null and b/github_connector/static/description/wizard_create_team.png differ diff --git a/github_connector/static/description/wizard_download_analyze.png b/github_connector/static/description/wizard_download_analyze.png new file mode 100644 index 00000000..a8b27e9e Binary files /dev/null and b/github_connector/static/description/wizard_download_analyze.png differ diff --git a/github_connector/static/src/img/default_github_organization.png b/github_connector/static/src/img/default_github_organization.png new file mode 100644 index 00000000..9187bcdd Binary files /dev/null and b/github_connector/static/src/img/default_github_organization.png differ diff --git a/github_connector/tests/__init__.py b/github_connector/tests/__init__.py new file mode 100644 index 00000000..d62e4b0b --- /dev/null +++ b/github_connector/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2021-2022 Tecnativa - Víctor Martínez +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +from . import test_github_connector +from . import test_github_analysis_rule diff --git a/github_connector/tests/common.py b/github_connector/tests/common.py new file mode 100644 index 00000000..58e0ecb0 --- /dev/null +++ b/github_connector/tests/common.py @@ -0,0 +1,61 @@ +# Copyright 2020-2022 Tecnativa - Víctor Martínez +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +import responses + +from odoo.tests.common import TransactionCase + + +class TestGithubConnectorCommon(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.info_keys = [ + "code_count", + "documentation_count", + "empty_count", + "string_count", + "scanned_files", + ] + cls.model_gos = cls.env["github.organization.serie"] + cls.model_gr = cls.env["github.repository"] + cls.model_grb = cls.env["github.repository.branch"] + cls.oca = cls.env.ref("github_connector.oca_organization") + cls.serie_13 = cls.env.ref("github_connector.oca_organization_serie_13") + cls.repository_ocb = cls.model_gr.create( + { + "name": "OCB", + "organization_id": cls.oca.id, + "github_id_external": 20558462, + } + ) + cls.repository_interface_github = cls.model_gr.create( + { + "name": "interface-github", + "organization_id": cls.oca.id, + "github_id_external": 70173147, + } + ) + # repository branch + cls.repository_ocb_13 = cls.model_grb.create( + { + "name": cls.serie_13.name, + "organization_id": cls.oca.id, + "repository_id": cls.repository_ocb.id, + "organization_serie_id": cls.serie_13.id, + } + ) + cls.repository_interface_github_13 = cls.model_grb.create( + { + "name": cls.serie_13.name, + "organization_id": cls.oca.id, + "repository_id": cls.repository_interface_github.id, + "organization_serie_id": cls.serie_13.id, + } + ) + cls.env["ir.config_parameter"].set_param("github.access_token", "test") + + @responses.activate + def _download_and_analyze(self, repo_branch): + if repo_branch.state == "to_download": + repo_branch._download_code() + repo_branch.analyze_code_one() diff --git a/github_connector/tests/res/github_repo_20558462_response.json b/github_connector/tests/res/github_repo_20558462_response.json new file mode 100644 index 00000000..5c9f20a3 --- /dev/null +++ b/github_connector/tests/res/github_repo_20558462_response.json @@ -0,0 +1,338 @@ +{ + "id": 20558462, + "node_id": "MDEwOlJlcG9zaXRvcnkyMDU1ODQ2Mg==", + "name": "OCB", + "full_name": "OCA/OCB", + "private": false, + "owner": { + "login": "OCA", + "id": 7600578, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2MDA1Nzg=", + "avatar_url": "https://avatars.githubusercontent.com/u/7600578?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/OCA", + "html_url": "https://github.com/OCA", + "followers_url": "https://api.github.com/users/OCA/followers", + "following_url": "https://api.github.com/users/OCA/following{/other_user}", + "gists_url": "https://api.github.com/users/OCA/gists{/gist_id}", + "starred_url": "https://api.github.com/users/OCA/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/OCA/subscriptions", + "organizations_url": "https://api.github.com/users/OCA/orgs", + "repos_url": "https://api.github.com/users/OCA/repos", + "events_url": "https://api.github.com/users/OCA/events{/privacy}", + "received_events_url": "https://api.github.com/users/OCA/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/OCA/OCB", + "description": "Odoo Community Backports", + "fork": true, + "url": "https://api.github.com/repos/OCA/OCB", + "forks_url": "https://api.github.com/repos/OCA/OCB/forks", + "keys_url": "https://api.github.com/repos/OCA/OCB/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/OCA/OCB/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/OCA/OCB/teams", + "hooks_url": "https://api.github.com/repos/OCA/OCB/hooks", + "issue_events_url": "https://api.github.com/repos/OCA/OCB/issues/events{/number}", + "events_url": "https://api.github.com/repos/OCA/OCB/events", + "assignees_url": "https://api.github.com/repos/OCA/OCB/assignees{/user}", + "branches_url": "https://api.github.com/repos/OCA/OCB/branches{/branch}", + "tags_url": "https://api.github.com/repos/OCA/OCB/tags", + "blobs_url": "https://api.github.com/repos/OCA/OCB/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/OCA/OCB/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/OCA/OCB/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/OCA/OCB/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/OCA/OCB/statuses/{sha}", + "languages_url": "https://api.github.com/repos/OCA/OCB/languages", + "stargazers_url": "https://api.github.com/repos/OCA/OCB/stargazers", + "contributors_url": "https://api.github.com/repos/OCA/OCB/contributors", + "subscribers_url": "https://api.github.com/repos/OCA/OCB/subscribers", + "subscription_url": "https://api.github.com/repos/OCA/OCB/subscription", + "commits_url": "https://api.github.com/repos/OCA/OCB/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/OCA/OCB/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/OCA/OCB/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/OCA/OCB/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/OCA/OCB/contents/{+path}", + "compare_url": "https://api.github.com/repos/OCA/OCB/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/OCA/OCB/merges", + "archive_url": "https://api.github.com/repos/OCA/OCB/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/OCA/OCB/downloads", + "issues_url": "https://api.github.com/repos/OCA/OCB/issues{/number}", + "pulls_url": "https://api.github.com/repos/OCA/OCB/pulls{/number}", + "milestones_url": "https://api.github.com/repos/OCA/OCB/milestones{/number}", + "notifications_url": "https://api.github.com/repos/OCA/OCB/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/OCA/OCB/labels{/name}", + "releases_url": "https://api.github.com/repos/OCA/OCB/releases{/id}", + "deployments_url": "https://api.github.com/repos/OCA/OCB/deployments", + "created_at": "2014-06-06T09:33:16Z", + "updated_at": "2022-07-11T17:24:33Z", + "pushed_at": "2022-07-19T22:38:25Z", + "git_url": "git://github.com/OCA/OCB.git", + "ssh_url": "git@github.com:OCA/OCB.git", + "clone_url": "https://github.com/OCA/OCB.git", + "svn_url": "https://github.com/OCA/OCB", + "homepage": "http://odoo-community.org", + "size": 4097132, + "stargazers_count": 261, + "watchers_count": 261, + "language": "JavaScript", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 334, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 173, + "license": { + "key": "other", + "name": "Other", + "spdx_id": "NOASSERTION", + "url": null, + "node_id": "MDc6TGljZW5zZTA=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [], + "visibility": "public", + "forks": 334, + "open_issues": 173, + "watchers": 261, + "default_branch": "14.0", + "temp_clone_token": null, + "organization": { + "login": "OCA", + "id": 7600578, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2MDA1Nzg=", + "avatar_url": "https://avatars.githubusercontent.com/u/7600578?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/OCA", + "html_url": "https://github.com/OCA", + "followers_url": "https://api.github.com/users/OCA/followers", + "following_url": "https://api.github.com/users/OCA/following{/other_user}", + "gists_url": "https://api.github.com/users/OCA/gists{/gist_id}", + "starred_url": "https://api.github.com/users/OCA/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/OCA/subscriptions", + "organizations_url": "https://api.github.com/users/OCA/orgs", + "repos_url": "https://api.github.com/users/OCA/repos", + "events_url": "https://api.github.com/users/OCA/events{/privacy}", + "received_events_url": "https://api.github.com/users/OCA/received_events", + "type": "Organization", + "site_admin": false + }, + "parent": { + "id": 19745004, + "node_id": "MDEwOlJlcG9zaXRvcnkxOTc0NTAwNA==", + "name": "odoo", + "full_name": "odoo/odoo", + "private": false, + "owner": { + "login": "odoo", + "id": 6368483, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjYzNjg0ODM=", + "avatar_url": "https://avatars.githubusercontent.com/u/6368483?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/odoo", + "html_url": "https://github.com/odoo", + "followers_url": "https://api.github.com/users/odoo/followers", + "following_url": "https://api.github.com/users/odoo/following{/other_user}", + "gists_url": "https://api.github.com/users/odoo/gists{/gist_id}", + "starred_url": "https://api.github.com/users/odoo/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/odoo/subscriptions", + "organizations_url": "https://api.github.com/users/odoo/orgs", + "repos_url": "https://api.github.com/users/odoo/repos", + "events_url": "https://api.github.com/users/odoo/events{/privacy}", + "received_events_url": "https://api.github.com/users/odoo/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/odoo/odoo", + "description": "Odoo. Open Source Apps To Grow Your Business.", + "fork": false, + "url": "https://api.github.com/repos/odoo/odoo", + "forks_url": "https://api.github.com/repos/odoo/odoo/forks", + "keys_url": "https://api.github.com/repos/odoo/odoo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/odoo/odoo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/odoo/odoo/teams", + "hooks_url": "https://api.github.com/repos/odoo/odoo/hooks", + "issue_events_url": "https://api.github.com/repos/odoo/odoo/issues/events{/number}", + "events_url": "https://api.github.com/repos/odoo/odoo/events", + "assignees_url": "https://api.github.com/repos/odoo/odoo/assignees{/user}", + "branches_url": "https://api.github.com/repos/odoo/odoo/branches{/branch}", + "tags_url": "https://api.github.com/repos/odoo/odoo/tags", + "blobs_url": "https://api.github.com/repos/odoo/odoo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/odoo/odoo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/odoo/odoo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/odoo/odoo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/odoo/odoo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/odoo/odoo/languages", + "stargazers_url": "https://api.github.com/repos/odoo/odoo/stargazers", + "contributors_url": "https://api.github.com/repos/odoo/odoo/contributors", + "subscribers_url": "https://api.github.com/repos/odoo/odoo/subscribers", + "subscription_url": "https://api.github.com/repos/odoo/odoo/subscription", + "commits_url": "https://api.github.com/repos/odoo/odoo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/odoo/odoo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/odoo/odoo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/odoo/odoo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/odoo/odoo/contents/{+path}", + "compare_url": "https://api.github.com/repos/odoo/odoo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/odoo/odoo/merges", + "archive_url": "https://api.github.com/repos/odoo/odoo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/odoo/odoo/downloads", + "issues_url": "https://api.github.com/repos/odoo/odoo/issues{/number}", + "pulls_url": "https://api.github.com/repos/odoo/odoo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/odoo/odoo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/odoo/odoo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/odoo/odoo/labels{/name}", + "releases_url": "https://api.github.com/repos/odoo/odoo/releases{/id}", + "deployments_url": "https://api.github.com/repos/odoo/odoo/deployments", + "created_at": "2014-05-13T15:38:58Z", + "updated_at": "2022-07-20T08:12:51Z", + "pushed_at": "2022-07-20T10:32:52Z", + "git_url": "git://github.com/odoo/odoo.git", + "ssh_url": "git@github.com:odoo/odoo.git", + "clone_url": "https://github.com/odoo/odoo.git", + "svn_url": "https://github.com/odoo/odoo", + "homepage": "https://www.odoo.com", + "size": 4637751, + "stargazers_count": 25671, + "watchers_count": 25671, + "language": "JavaScript", + "has_issues": true, + "has_projects": false, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 16544, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 5463, + "license": { + "key": "other", + "name": "Other", + "spdx_id": "NOASSERTION", + "url": null, + "node_id": "MDc6TGljZW5zZTA=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": ["apps", "business", "erp", "management", "odoo", "odoo-apps", "python"], + "visibility": "public", + "forks": 16544, + "open_issues": 5463, + "watchers": 25671, + "default_branch": "15.0" + }, + "source": { + "id": 19745004, + "node_id": "MDEwOlJlcG9zaXRvcnkxOTc0NTAwNA==", + "name": "odoo", + "full_name": "odoo/odoo", + "private": false, + "owner": { + "login": "odoo", + "id": 6368483, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjYzNjg0ODM=", + "avatar_url": "https://avatars.githubusercontent.com/u/6368483?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/odoo", + "html_url": "https://github.com/odoo", + "followers_url": "https://api.github.com/users/odoo/followers", + "following_url": "https://api.github.com/users/odoo/following{/other_user}", + "gists_url": "https://api.github.com/users/odoo/gists{/gist_id}", + "starred_url": "https://api.github.com/users/odoo/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/odoo/subscriptions", + "organizations_url": "https://api.github.com/users/odoo/orgs", + "repos_url": "https://api.github.com/users/odoo/repos", + "events_url": "https://api.github.com/users/odoo/events{/privacy}", + "received_events_url": "https://api.github.com/users/odoo/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/odoo/odoo", + "description": "Odoo. Open Source Apps To Grow Your Business.", + "fork": false, + "url": "https://api.github.com/repos/odoo/odoo", + "forks_url": "https://api.github.com/repos/odoo/odoo/forks", + "keys_url": "https://api.github.com/repos/odoo/odoo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/odoo/odoo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/odoo/odoo/teams", + "hooks_url": "https://api.github.com/repos/odoo/odoo/hooks", + "issue_events_url": "https://api.github.com/repos/odoo/odoo/issues/events{/number}", + "events_url": "https://api.github.com/repos/odoo/odoo/events", + "assignees_url": "https://api.github.com/repos/odoo/odoo/assignees{/user}", + "branches_url": "https://api.github.com/repos/odoo/odoo/branches{/branch}", + "tags_url": "https://api.github.com/repos/odoo/odoo/tags", + "blobs_url": "https://api.github.com/repos/odoo/odoo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/odoo/odoo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/odoo/odoo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/odoo/odoo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/odoo/odoo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/odoo/odoo/languages", + "stargazers_url": "https://api.github.com/repos/odoo/odoo/stargazers", + "contributors_url": "https://api.github.com/repos/odoo/odoo/contributors", + "subscribers_url": "https://api.github.com/repos/odoo/odoo/subscribers", + "subscription_url": "https://api.github.com/repos/odoo/odoo/subscription", + "commits_url": "https://api.github.com/repos/odoo/odoo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/odoo/odoo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/odoo/odoo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/odoo/odoo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/odoo/odoo/contents/{+path}", + "compare_url": "https://api.github.com/repos/odoo/odoo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/odoo/odoo/merges", + "archive_url": "https://api.github.com/repos/odoo/odoo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/odoo/odoo/downloads", + "issues_url": "https://api.github.com/repos/odoo/odoo/issues{/number}", + "pulls_url": "https://api.github.com/repos/odoo/odoo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/odoo/odoo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/odoo/odoo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/odoo/odoo/labels{/name}", + "releases_url": "https://api.github.com/repos/odoo/odoo/releases{/id}", + "deployments_url": "https://api.github.com/repos/odoo/odoo/deployments", + "created_at": "2014-05-13T15:38:58Z", + "updated_at": "2022-07-20T08:12:51Z", + "pushed_at": "2022-07-20T10:32:52Z", + "git_url": "git://github.com/odoo/odoo.git", + "ssh_url": "git@github.com:odoo/odoo.git", + "clone_url": "https://github.com/odoo/odoo.git", + "svn_url": "https://github.com/odoo/odoo", + "homepage": "https://www.odoo.com", + "size": 4637751, + "stargazers_count": 25671, + "watchers_count": 25671, + "language": "JavaScript", + "has_issues": true, + "has_projects": false, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 16544, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 5463, + "license": { + "key": "other", + "name": "Other", + "spdx_id": "NOASSERTION", + "url": null, + "node_id": "MDc6TGljZW5zZTA=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": ["apps", "business", "erp", "management", "odoo", "odoo-apps", "python"], + "visibility": "public", + "forks": 16544, + "open_issues": 5463, + "watchers": 25671, + "default_branch": "15.0" + }, + "network_count": 16544, + "subscribers_count": 127 +} diff --git a/github_connector/tests/res/github_repo_70173147_response.json b/github_connector/tests/res/github_repo_70173147_response.json new file mode 100644 index 00000000..933e79e8 --- /dev/null +++ b/github_connector/tests/res/github_repo_70173147_response.json @@ -0,0 +1,128 @@ +{ + "id": 70173147, + "node_id": "MDEwOlJlcG9zaXRvcnk3MDE3MzE0Nw==", + "name": "interface-github", + "full_name": "OCA/interface-github", + "private": false, + "owner": { + "login": "OCA", + "id": 7600578, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2MDA1Nzg=", + "avatar_url": "https://avatars.githubusercontent.com/u/7600578?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/OCA", + "html_url": "https://github.com/OCA", + "followers_url": "https://api.github.com/users/OCA/followers", + "following_url": "https://api.github.com/users/OCA/following{/other_user}", + "gists_url": "https://api.github.com/users/OCA/gists{/gist_id}", + "starred_url": "https://api.github.com/users/OCA/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/OCA/subscriptions", + "organizations_url": "https://api.github.com/users/OCA/orgs", + "repos_url": "https://api.github.com/users/OCA/repos", + "events_url": "https://api.github.com/users/OCA/events{/privacy}", + "received_events_url": "https://api.github.com/users/OCA/received_events", + "type": "Organization", + "site_admin": false + }, + "html_url": "https://github.com/OCA/interface-github", + "description": "Tools to interact with github from Odoo", + "fork": false, + "url": "https://api.github.com/repos/OCA/interface-github", + "forks_url": "https://api.github.com/repos/OCA/interface-github/forks", + "keys_url": "https://api.github.com/repos/OCA/interface-github/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/OCA/interface-github/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/OCA/interface-github/teams", + "hooks_url": "https://api.github.com/repos/OCA/interface-github/hooks", + "issue_events_url": "https://api.github.com/repos/OCA/interface-github/issues/events{/number}", + "events_url": "https://api.github.com/repos/OCA/interface-github/events", + "assignees_url": "https://api.github.com/repos/OCA/interface-github/assignees{/user}", + "branches_url": "https://api.github.com/repos/OCA/interface-github/branches{/branch}", + "tags_url": "https://api.github.com/repos/OCA/interface-github/tags", + "blobs_url": "https://api.github.com/repos/OCA/interface-github/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/OCA/interface-github/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/OCA/interface-github/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/OCA/interface-github/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/OCA/interface-github/statuses/{sha}", + "languages_url": "https://api.github.com/repos/OCA/interface-github/languages", + "stargazers_url": "https://api.github.com/repos/OCA/interface-github/stargazers", + "contributors_url": "https://api.github.com/repos/OCA/interface-github/contributors", + "subscribers_url": "https://api.github.com/repos/OCA/interface-github/subscribers", + "subscription_url": "https://api.github.com/repos/OCA/interface-github/subscription", + "commits_url": "https://api.github.com/repos/OCA/interface-github/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/OCA/interface-github/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/OCA/interface-github/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/OCA/interface-github/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/OCA/interface-github/contents/{+path}", + "compare_url": "https://api.github.com/repos/OCA/interface-github/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/OCA/interface-github/merges", + "archive_url": "https://api.github.com/repos/OCA/interface-github/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/OCA/interface-github/downloads", + "issues_url": "https://api.github.com/repos/OCA/interface-github/issues{/number}", + "pulls_url": "https://api.github.com/repos/OCA/interface-github/pulls{/number}", + "milestones_url": "https://api.github.com/repos/OCA/interface-github/milestones{/number}", + "notifications_url": "https://api.github.com/repos/OCA/interface-github/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/OCA/interface-github/labels{/name}", + "releases_url": "https://api.github.com/repos/OCA/interface-github/releases{/id}", + "deployments_url": "https://api.github.com/repos/OCA/interface-github/deployments", + "created_at": "2016-10-06T16:42:24Z", + "updated_at": "2022-06-22T12:50:39Z", + "pushed_at": "2022-07-20T10:34:42Z", + "git_url": "git://github.com/OCA/interface-github.git", + "ssh_url": "git@github.com:OCA/interface-github.git", + "clone_url": "https://github.com/OCA/interface-github.git", + "svn_url": "https://github.com/OCA/interface-github", + "homepage": null, + "size": 1573, + "stargazers_count": 39, + "watchers_count": 39, + "language": "Python", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": false, + "forks_count": 64, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 11, + "license": { + "key": "agpl-3.0", + "name": "GNU Affero General Public License v3.0", + "spdx_id": "AGPL-3.0", + "url": "https://api.github.com/licenses/agpl-3.0", + "node_id": "MDc6TGljZW5zZTE=" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "topics": [], + "visibility": "public", + "forks": 64, + "open_issues": 11, + "watchers": 39, + "default_branch": "13.0", + "temp_clone_token": null, + "organization": { + "login": "OCA", + "id": 7600578, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2MDA1Nzg=", + "avatar_url": "https://avatars.githubusercontent.com/u/7600578?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/OCA", + "html_url": "https://github.com/OCA", + "followers_url": "https://api.github.com/users/OCA/followers", + "following_url": "https://api.github.com/users/OCA/following{/other_user}", + "gists_url": "https://api.github.com/users/OCA/gists{/gist_id}", + "starred_url": "https://api.github.com/users/OCA/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/OCA/subscriptions", + "organizations_url": "https://api.github.com/users/OCA/orgs", + "repos_url": "https://api.github.com/users/OCA/repos", + "events_url": "https://api.github.com/users/OCA/events{/privacy}", + "received_events_url": "https://api.github.com/users/OCA/received_events", + "type": "Organization", + "site_admin": false + }, + "network_count": 64, + "subscribers_count": 31 +} diff --git a/github_connector/tests/res/github_search_issues_response.json b/github_connector/tests/res/github_search_issues_response.json new file mode 100644 index 00000000..508b4e6f --- /dev/null +++ b/github_connector/tests/res/github_search_issues_response.json @@ -0,0 +1,100 @@ +{ + "total_count": 280, + "incomplete_results": false, + "items": [ + { + "url": "https://api.github.com/repos/batterseapower/pinyin-toolkit/issues/132", + "repository_url": "https://api.github.com/repos/batterseapower/pinyin-toolkit", + "labels_url": "https://api.github.com/repos/batterseapower/pinyin-toolkit/issues/132/labels{/name}", + "comments_url": "https://api.github.com/repos/batterseapower/pinyin-toolkit/issues/132/comments", + "events_url": "https://api.github.com/repos/batterseapower/pinyin-toolkit/issues/132/events", + "html_url": "https://github.com/batterseapower/pinyin-toolkit/issues/132", + "id": 35802, + "node_id": "MDU6SXNzdWUzNTgwMg==", + "number": 132, + "title": "Line Number Indexes Beyond 20 Not Displayed", + "user": { + "login": "Nick3C", + "id": 90254, + "node_id": "MDQ6VXNlcjkwMjU0", + "avatar_url": "https://secure.gravatar.com/avatar/934442aadfe3b2f4630510de416c5718?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png", + "gravatar_id": "", + "url": "https://api.github.com/users/Nick3C", + "html_url": "https://github.com/Nick3C", + "followers_url": "https://api.github.com/users/Nick3C/followers", + "following_url": "https://api.github.com/users/Nick3C/following{/other_user}", + "gists_url": "https://api.github.com/users/Nick3C/gists{/gist_id}", + "starred_url": "https://api.github.com/users/Nick3C/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/Nick3C/subscriptions", + "organizations_url": "https://api.github.com/users/Nick3C/orgs", + "repos_url": "https://api.github.com/users/Nick3C/repos", + "events_url": "https://api.github.com/users/Nick3C/events{/privacy}", + "received_events_url": "https://api.github.com/users/Nick3C/received_events", + "type": "User", + "site_admin": true + }, + "labels": [ + { + "id": 4, + "node_id": "MDU6TGFiZWw0", + "url": "https://api.github.com/repos/batterseapower/pinyin-toolkit/labels/bug", + "name": "bug", + "color": "ff0000" + } + ], + "state": "open", + "assignee": null, + "milestone": { + "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1", + "html_url": "https://github.com/octocat/Hello-World/milestones/v1.0", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/milestones/1/labels", + "id": 1002604, + "node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA==", + "number": 1, + "state": "open", + "title": "v1.0", + "description": "Tracking milestone for version 1.0", + "creator": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "open_issues": 4, + "closed_issues": 8, + "created_at": "2011-04-10T20:09:31Z", + "updated_at": "2014-03-03T18:58:10Z", + "closed_at": "2013-02-12T13:22:01Z", + "due_on": "2012-10-09T23:39:01Z" + }, + "comments": 15, + "created_at": "2009-07-12T20:10:41Z", + "updated_at": "2009-07-19T09:23:43Z", + "closed_at": null, + "pull_request": { + "url": "https://api/github.com/repos/octocat/Hello-World/pull/1347", + "html_url": "https://github.com/octocat/Hello-World/pull/1347", + "diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff", + "patch_url": "https://api.github.com/repos/octocat/Hello-World/pulls/1347" + }, + "body": "...", + "score": 1, + "locked": true, + "author_association": "COLLABORATOR" + } + ] +} diff --git a/github_connector/tests/res/github_user_OCA-git-bot_response.json b/github_connector/tests/res/github_user_OCA-git-bot_response.json new file mode 100644 index 00000000..0eb0bf23 --- /dev/null +++ b/github_connector/tests/res/github_user_OCA-git-bot_response.json @@ -0,0 +1,34 @@ +{ + "login": "OCA-git-bot", + "id": 8723280, + "node_id": "MDQ6VXNlcjg3MjMyODA=", + "avatar_url": "https://avatars.githubusercontent.com/u/8723280?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/OCA-git-bot", + "html_url": "https://github.com/OCA-git-bot", + "followers_url": "https://api.github.com/users/OCA-git-bot/followers", + "following_url": "https://api.github.com/users/OCA-git-bot/following{/other_user}", + "gists_url": "https://api.github.com/users/OCA-git-bot/gists{/gist_id}", + "starred_url": "https://api.github.com/users/OCA-git-bot/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/OCA-git-bot/subscriptions", + "organizations_url": "https://api.github.com/users/OCA-git-bot/orgs", + "repos_url": "https://api.github.com/users/OCA-git-bot/repos", + "events_url": "https://api.github.com/users/OCA-git-bot/events{/privacy}", + "received_events_url": "https://api.github.com/users/OCA-git-bot/received_events", + "type": "User", + "site_admin": false, + "name": "OCA Bot", + "company": "OCA", + "blog": "", + "location": null, + "email": null, + "hireable": null, + "bio": null, + "twitter_username": null, + "public_repos": 1, + "public_gists": 0, + "followers": 24, + "following": 0, + "created_at": "2014-09-10T13:03:33Z", + "updated_at": "2021-05-15T09:42:06Z" +} diff --git a/github_connector/tests/test_github_analysis_rule.py b/github_connector/tests/test_github_analysis_rule.py new file mode 100644 index 00000000..9a8ad37d --- /dev/null +++ b/github_connector/tests/test_github_analysis_rule.py @@ -0,0 +1,172 @@ +# Copyright 2020-2022 Tecnativa - Víctor Martínez +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +import json + +import responses + +from odoo.modules.module import get_resource_path + +from .common import TestGithubConnectorCommon + + +class TestGithubConnectorAnalysisRuleBase(TestGithubConnectorCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.rule_group = cls.env.ref( + "github_connector.github_analysis_rule_group_1_demo" + ) + cls.rule_python = cls.env.ref("github_connector.github_analysis_rule_python") + cls.rule_xml = cls.env.ref("github_connector.github_analysis_rule_xml") + cls.rule_js = cls.env.ref("github_connector.github_analysis_rule_js") + cls.rule_test = cls.env.ref("github_connector.github_analysis_rule_test") + cls.rule_ocb = cls.env["github.analysis.rule"].create( + { + "name": "OCB files (.py + .xml)", + "group_id": cls.rule_group.id, + "paths": """ + *.py + *.xml + !/.*/ + """, + } + ) + cls.rule_custom = cls.env["github.analysis.rule"].create( + { + "name": "Custom", + "group_id": cls.rule_group.id, + "paths": """/custom/*.py""", + } + ) + cls.oca.write( + { + "analysis_rule_ids": [ + (6, 0, [cls.rule_python.id, cls.rule_xml.id, cls.rule_js.id]) + ] + } + ) + cls.repository_ocb.write({"analysis_rule_ids": [(6, 0, cls.rule_ocb.ids)]}) + cls.repository_interface_github_13.write( + {"analysis_rule_ids": [(6, 0, cls.rule_custom.ids)]} + ) + cls.repo_branch_item = cls.repository_interface_github_13 + # Create appropriate responses for the API calls + cls._set_github_responses(cls) + + def _set_github_responses(self): + for github_id in self.oca.mapped("repository_ids.github_id_external"): + with open( + get_resource_path( + "github_connector", + "tests", + "res", + "github_repo_%s_response.json" % github_id, + ) + ) as jsonfile: + responses.add( + responses.GET, + "https://api.github.com:443/repositories/%s" % github_id, + json=json.loads(jsonfile.read()), + status=200, + ) + + +class TestGithubConnectorAnalysisRule(TestGithubConnectorAnalysisRuleBase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._download_and_analyze(cls, cls.repo_branch_item) + + def test_analysis_rule_info(self): + self.assertEqual(len(self.oca.analysis_rule_ids), 3) + self.assertEqual(len(self.repository_ocb.analysis_rule_ids), 1) + self.assertEqual(len(self.repo_branch_item.analysis_rule_info_ids), 4) + + def test_inhibit_analysis_rule_info_01(self): + # Rules available from repository branch + rules = self.repo_branch_item._get_analysis_rules() + self.assertIn(self.rule_custom, rules) + self.assertNotIn(self.rule_ocb, rules) + self.assertIn(self.rule_python, rules) + self.assertIn(self.rule_xml, rules) + self.assertIn(self.rule_js, rules) + # Rules from OCB repository + rules = self.repository_ocb._get_analysis_rules() + self.assertNotIn(self.rule_custom, rules) + self.assertIn(self.rule_ocb, rules) + self.assertIn(self.rule_python, rules) + self.assertIn(self.rule_xml, rules) + self.assertIn(self.rule_js, rules) + # Rules from interface-gitbub repository + rules = self.repository_interface_github._get_analysis_rules() + self.assertNotIn(self.rule_custom, rules) + self.assertNotIn(self.rule_ocb, rules) + self.assertIn(self.rule_python, rules) + self.assertIn(self.rule_xml, rules) + self.assertIn(self.rule_js, rules) + + def test_inhibit_analysis_rule_info_02(self): + # Only repository branch rule available + self.repo_branch_item.inhibit_inherited_rules = True + rules = self.repo_branch_item._get_analysis_rules() + self.assertIn(self.rule_custom, rules) + self.assertNotIn(self.rule_ocb, rules) + self.assertNotIn(self.rule_python, rules) + self.assertNotIn(self.rule_xml, rules) + self.assertNotIn(self.rule_js, rules) + # Remove rule from repository branch + self.repo_branch_item.analysis_rule_ids = [(5, 0, 0)] + rules = self.repo_branch_item._get_analysis_rules() + self.assertNotIn(self.rule_custom, rules) + self.assertNotIn(self.rule_ocb, rules) + self.assertNotIn(self.rule_python, rules) + self.assertNotIn(self.rule_xml, rules) + self.assertNotIn(self.rule_js, rules) + + def test_inhibit_analysis_rule_info_03(self): + # Only repository rules available + self.repo_branch_item.analysis_rule_ids = [(5, 0, 0)] + self.repository_interface_github.write( + { + "inhibit_inherited_rules": True, + "analysis_rule_ids": [(6, 0, self.rule_custom.ids)], + } + ) + rules = self.repo_branch_item._get_analysis_rules() + self.assertIn(self.rule_custom, rules) + self.assertNotIn(self.rule_ocb, rules) + self.assertNotIn(self.rule_python, rules) + self.assertNotIn(self.rule_xml, rules) + self.assertNotIn(self.rule_js, rules) + # Remove rules from repository branch + self.repository_interface_github.analysis_rule_ids = [(5, 0, 0)] + rules = self.repo_branch_item._get_analysis_rules() + self.assertNotIn(self.rule_custom, rules) + self.assertNotIn(self.rule_ocb, rules) + self.assertNotIn(self.rule_python, rules) + self.assertNotIn(self.rule_xml, rules) + self.assertNotIn(self.rule_js, rules) + + def test_analysis_rule_info_python(self): + rule_info = self.repo_branch_item.analysis_rule_info_ids.filtered( + lambda x: x.analysis_rule_id.id == self.rule_python.id + ) + for info_key in self.info_keys: + self.assertGreater(rule_info[info_key], 0) + + def test_analysis_rule_info_xml(self): + rule_info = self.repo_branch_item.analysis_rule_info_ids.filtered( + lambda x: x.analysis_rule_id.id == self.rule_xml.id + ) + for info_key in self.info_keys: + if info_key in ("empty_count", "string_count"): + self.assertEqual(rule_info[info_key], 0) + else: + self.assertGreater(rule_info[info_key], 0) + + def test_analysis_rule_info_js(self): + rule_info = self.repo_branch_item.analysis_rule_info_ids.filtered( + lambda x: x.analysis_rule_id.id == self.rule_js.id + ) + for info_key in self.info_keys: + self.assertEqual(rule_info[info_key], 0) diff --git a/github_connector/tests/test_github_connector.py b/github_connector/tests/test_github_connector.py new file mode 100644 index 00000000..32517bb2 --- /dev/null +++ b/github_connector/tests/test_github_connector.py @@ -0,0 +1,60 @@ +# Copyright 2021-2022 Tecnativa - Víctor Martínez +# Copyright 2021 Tecnativa - João Marques +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import json + +import responses + +from odoo.modules.module import get_resource_path + +from .common import TestGithubConnectorCommon + + +class TestGithubConnector(TestGithubConnectorCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Create appropriate responses for the API calls + with open( + get_resource_path( + "github_connector", + "tests", + "res", + "github_user_OCA-git-bot_response.json", + ) + ) as jsonfile: + cls.user_data = json.loads(jsonfile.read()) + responses.add( + responses.GET, + "https://api.github.com:443/users/OCA-git-bot", + json=cls.user_data, + status=200, + ) + responses.add( + responses.GET, + "https://api.github.com:443/user/8723280", + json=cls.user_data, + status=200, + ) + responses.add( + responses.GET, + "https://api.github.com:443/orgs/OCA-git-bot", + json={ + "message": "Not Found", + }, + status=404, + ) + + @responses.activate + def test_partner_get_from_id_or_create(self): + partner_model = self.env["res.partner"] + partner = partner_model.create_from_name("OCA-git-bot") + self.assertEqual(partner.github_name, "OCA-git-bot") + # Check create process not really create new record + res = partner_model.get_from_id_or_create(data={"login": "OCA-git-bot"}) + self.assertEqual(partner.id, res.id) + # Try to archive record and try to create again + partner.active = False + res = partner_model.get_from_id_or_create(data={"login": "OCA-git-bot"}) + self.assertEqual(partner.id, res.id) diff --git a/github_connector/views/action.xml b/github_connector/views/action.xml new file mode 100644 index 00000000..ac82517e --- /dev/null +++ b/github_connector/views/action.xml @@ -0,0 +1,128 @@ + + + + + + Organizations + ir.actions.act_window + github.organization + kanban,list,form + + + Users + ir.actions.act_window + res.partner + kanban,list,form + {'search_default_has_github_account':1} + + + Teams + ir.actions.act_window + github.team + kanban,list,form + + + Teams + ir.actions.act_window + github.team.partner + + kanban,list,form + + + Members + ir.actions.act_window + github.team.partner + + kanban,list,form + + + Team + ir.actions.act_window + github.team.repository + + kanban,list,form + + + Repositories + ir.actions.act_window + github.team.repository + + kanban,list,form + + + + Repositories + ir.actions.act_window + github.repository + kanban,list,form + + + Repository Branches + ir.actions.act_window + github.repository.branch + kanban,list,form + + + + Load from Github Wizard + ir.actions.act_window + wizard.load.github.model + form + new + + + Create Team in Github Wizard + ir.actions.act_window + wizard.create.team + form + new + + + Create Repo in Github Wizard + ir.actions.act_window + wizard.create.repository + form + new + + + Analysis rule + github.analysis.rule + list,form + + + Analysis rule group + github.analysis.rule.group + list,form + + + + Branches by Serie + ir.actions.act_window + github.repository.branch + graph,pivot + {'search_default_group_by_organization_serie_id': True} + + + + Sizes by Serie + ir.actions.act_window + github.repository.branch + graph,pivot + {'search_default_group_by_organization_serie_id': True} + + + diff --git a/github_connector/views/menu.xml b/github_connector/views/menu.xml new file mode 100644 index 00000000..223d8f75 --- /dev/null +++ b/github_connector/views/menu.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/github_connector/views/view_github_analysis_rule.xml b/github_connector/views/view_github_analysis_rule.xml new file mode 100644 index 00000000..ac9c2830 --- /dev/null +++ b/github_connector/views/view_github_analysis_rule.xml @@ -0,0 +1,51 @@ + + + + + github.analysis.rule + + + + + + + + + github.analysis.rule + +
+ +
+

+ +

+
+ + + + + + + + + + + +
+
+
+
+
diff --git a/github_connector/views/view_github_analysis_rule_group.xml b/github_connector/views/view_github_analysis_rule_group.xml new file mode 100644 index 00000000..8e2d570d --- /dev/null +++ b/github_connector/views/view_github_analysis_rule_group.xml @@ -0,0 +1,29 @@ + + + + + github.analysis.rule.group + + + + + + + + github.analysis.rule.group + +
+ +
+

+ +

+
+
+
+
+
+
diff --git a/github_connector/views/view_github_organization.xml b/github_connector/views/view_github_organization.xml new file mode 100644 index 00000000..e8ac3e52 --- /dev/null +++ b/github_connector/views/view_github_organization.xml @@ -0,0 +1,273 @@ + + + + github.organization + + + + + + + + github.organization + + + + + + + + + + + + github.organization + +
+
+
+ +
+ + + +
+ +
+

+ +

+

+ +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

+ +

+

+ +

+
+ + + + + + + + + + + + + + + + + +
+

+ +

+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +