Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8333e65
[IMP] [ADD] vpc_odoo: entry menu item for vcp.odoo.module.version
legalsylvain Mar 1, 2026
93f6094
[FIX] vcp_odoo: set readonly on all 'vcp.odoo.xxx' model fields
legalsylvain Mar 1, 2026
ff51064
[REF] vcp_odoo: rename vcp.odoo.lib.python into vcp.odoo.python.libra…
legalsylvain Mar 1, 2026
e8e895c
[IMP] vcp_odoo: add two new menu entries for vcp.odoo.python.library …
legalsylvain Mar 1, 2026
a8081c4
[IMP] vcp_odoo: display on python.library and bin.package form views,…
legalsylvain Mar 1, 2026
7abefa1
[IMP] Allow to unlink modules
legalsylvain Mar 1, 2026
53fbb0a
[IMP] vcp_management: Add a menu entry for the vcp.request.label mode…
legalsylvain Mar 1, 2026
0b9664d
[IMP] vcp_management: add link from vcp.request to vcp.review
legalsylvain Mar 1, 2026
cdd28ff
[IMP] vcp_management: add link from vcp.request to vcp.comment
legalsylvain Mar 1, 2026
67e00ac
[IMP] vcp_*: get 'draft' state of pull requests. add a new 'status' f…
legalsylvain Mar 2, 2026
b3aae4a
[IMP] vcp_management: allow to delete pull requests
legalsylvain Mar 2, 2026
69af62c
[IMP] vcp_*: fetch html description
legalsylvain Mar 2, 2026
56d3070
[IMP] vcp_* : improve naming of field, renaming information_update in…
legalsylvain Mar 2, 2026
d5aabe0
[REF] vcp_* : harmonize actions. put a path in all actions. rename xm…
legalsylvain Mar 2, 2026
54d2275
[IMP] vcp_* : improve naming of field, renaming information_update in…
legalsylvain Mar 2, 2026
75e9fb8
[IMP] vcp_* : improve naming of field, renaming branch_update into sc…
legalsylvain Mar 2, 2026
befb1ed
[IMP] vcp_management: explicit limit in dron definition.
legalsylvain Mar 2, 2026
d465f9d
[IMP] vcp_management: add a filter to display only active requests. (…
legalsylvain Mar 2, 2026
51a93d8
[IMP] vpc_management: add vcp.repository.branch menu entry
legalsylvain Mar 2, 2026
d6aa041
[FIX] vcp_github: do not create a branch when fetching pull request, …
legalsylvain Mar 2, 2026
3520a5a
[FIX] vcp_management: tests now include parameter limit
legalsylvain Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions vcp_github/demo/demo_vcp_platform.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<field name="name">OCA</field>
<field name="host_id" ref="vcp_github_host" />
<field name="fetch_repository_branch_pattern">^\d+\.\d+$</field>
<field name="scheduled_information_update" eval="False" />
</record>
</odoo>
14 changes: 13 additions & 1 deletion vcp_github/models/vcp_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def _update_branches_github(self):
raise ValidationError(self.env._(f"Reset on {reset}")) from e

def _parse_github_pr(self, pr, client):
self.ensure_one()
origin_data = pr.as_dict()
comments_url = pr.comments_url
comments_req = client.session.get(comments_url)
Expand All @@ -71,12 +72,22 @@ def _parse_github_pr(self, pr, client):
reviews_url = reviews_req.links["next"]["url"]
reviews_req = client.session.get(reviews_url)
reviews += reviews_req.json()

branch_pattern = (
self.fetch_branch_pattern
or self.platform_id.fetch_repository_branch_pattern
or False
)
if branch_pattern and not re.match(branch_pattern, pr.base.ref):
branch_id = False
else:
branch_id = self.platform_id._get_branch(pr.base.ref)
return (
str(pr.id),
{
"user_id": self.platform_id.host_id._get_user(pr.user.login),
"repository_id": self.id,
"branch_id": self.platform_id._get_branch(pr.base.ref),
"branch_id": branch_id,
"organization_id": self.platform_id.host_id._get_organization(
pr.head.repo[0]
),
Expand All @@ -85,6 +96,7 @@ def _parse_github_pr(self, pr, client):
"name": pr.title,
"is_merged": any(label["name"] == "merged 🎉" for label in pr.labels)
or pr.is_merged(),
"is_draft": pr.draft,
"created_at": self.platform_id._parse_github_date(
origin_data["created_at"]
),
Expand Down
4 changes: 2 additions & 2 deletions vcp_github/tests/test_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ def setUpClass(cls):
"key_ids": [
Command.create({"name": "ghp_exampletoken1234567890abcdef"})
],
"default_update_repository_information": True,
"information_update": True,
"default_repository_scheduled_information_update": True,
"scheduled_information_update": True,
}
)

Expand Down
2 changes: 2 additions & 0 deletions vcp_management/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
"views/vcp_comment.xml",
"views/vcp_review.xml",
"views/vcp_request.xml",
"views/vcp_request_label.xml",
"views/vcp_repository.xml",
"views/vcp_repository_branch.xml",
"views/vcp_branch.xml",
"views/vcp_platform.xml",
"views/vcp_organization.xml",
Expand Down
6 changes: 3 additions & 3 deletions vcp_management/data/ir_cron.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<field name="name">VCP: Repository Update</field>
<field name="model_id" ref="model_vcp_repository" />
<field name="state">code</field>
<field name="code">model._cron_update_repositories()</field>
<field name="code">model._cron_update_repositories(limit=1)</field>
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="active">False</field>
Expand All @@ -24,7 +24,7 @@
<field name="name">VCP: Branch Update</field>
<field name="model_id" ref="model_vcp_repository" />
<field name="state">code</field>
<field name="code">model._cron_update_branches()</field>
<field name="code">model._cron_update_branches(limit=1)</field>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we set a limit by default, we may change the default interval type (like every 10 minutes of 30 minutes or 1 hours?) what do you think ? (same for other cron)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in that commit, I just "moved" the default. It was "invisible" and in the code, it is now visible and can be changed by UI, changing the "code" of the cron.

See : befb1ed#diff-b554761c27adb312a1ee5d6b213691f187a281132e08f4525ea9b16d7eb7ba1bL43-R43

About the value, I have absolutely no clue, and I think it depends on the context. I used the code to fetch all grap modules. (250, in custom repo). I had no problem, with the value !

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to use queue, we should set it in a glue module, it is not a required dependancy.

<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="active">False</field>
Expand All @@ -33,7 +33,7 @@
<field name="name">VCP: Branch Rule Process</field>
<field name="model_id" ref="model_vcp_repository_branch" />
<field name="state">code</field>
<field name="code">model._cron_process_branch_rules()</field>
<field name="code">model._cron_process_branch_rules(limit=10)</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="active">False</field>
Expand Down
1 change: 1 addition & 0 deletions vcp_management/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from . import vcp_repository
from . import vcp_repository_branch
from . import vcp_request
from . import vcp_request_label
from . import vcp_review
from . import vcp_comment
from . import res_partner
Expand Down
2 changes: 2 additions & 0 deletions vcp_management/models/vcp_comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class VcpComment(models.Model):
comodel_name="vcp.request",
string="Request",
readonly=True,
required=True,
ondelete="cascade",
)
_sql_constraints = [
("external_id_uniq", "unique(external_id)", "External ID must be unique.")
Expand Down
17 changes: 14 additions & 3 deletions vcp_management/models/vcp_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,19 @@ class VcpPlatform(models.Model):
inverse_name="platform_id",
)
repository_count = fields.Integer(compute="_compute_repository_count", store=True)
default_update_repository_information = fields.Boolean()
information_update = fields.Boolean()
default_repository_scheduled_information_update = fields.Boolean(
help="If checked, the cron that update repositories"
" will look for up to date information, for this repository.",
)
default_repository_scheduled_branch_update = fields.Boolean(
help="If checked, the cron that update repository branches"
" will look for up to date branches, for this repository.",
)
scheduled_information_update = fields.Boolean(
default=True,
help="If checked, the cron that update platform informations"
" will look for up to date information, for this platform.",
)
fetch_repository_fork = fields.Boolean(
help="If checked, all repositories will be fetched (sources and forks)."
" Otherwise, only sources repositories will be fetched"
Expand Down Expand Up @@ -98,7 +109,7 @@ def _get_git_url(self, repository):
return getattr(self, f"_get_git_url_{self.kind}")(repository)

def _cron_update_platforms(self):
for platform in self.search([("information_update", "=", True)]):
for platform in self.search([("scheduled_information_update", "=", True)]):
try:
platform.update_information()
except Exception as e:
Expand Down
40 changes: 30 additions & 10 deletions vcp_management/models/vcp_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,21 @@ class VcpRepository(models.Model):
request_count = fields.Integer(compute="_compute_request_count")
test_field = fields.Char() # TODO remove after testing
active = fields.Boolean(default=True, readonly=True)
information_update = fields.Boolean(
compute="_compute_information_update",
scheduled_information_update = fields.Boolean(
compute="_compute_scheduled_information_update",
store=True,
readonly=False,
help="If checked, the cron that update repository informations"
" will look for up to date information, for this repository."
" This update include the recovery of requests, comments and reviews.",
)
scheduled_branch_update = fields.Boolean(
compute="_compute_scheduled_branch_update",
store=True,
readonly=False,
help="If checked, the cron that update repository branches"
" will look for up to date branches, for this repository.",
)
branch_update = fields.Boolean(default=False)
branch_update_date = fields.Datetime(
readonly=True, required=True, default=fields.Datetime.now
)
Expand Down Expand Up @@ -73,10 +82,17 @@ def _get_git_url(self):
return self.platform_id._get_git_url(self)

@api.depends("platform_id")
def _compute_information_update(self):
def _compute_scheduled_information_update(self):
for record in self:
record.scheduled_information_update = (
record.platform_id.default_repository_scheduled_information_update
)

@api.depends("platform_id")
def _compute_scheduled_branch_update(self):
for record in self:
record.information_update = (
record.platform_id.default_update_repository_information
record.scheduled_branch_update = (
record.platform_id.default_repository_scheduled_branch_update
)

@api.depends("request_ids")
Expand Down Expand Up @@ -104,16 +120,20 @@ def update_information(self, update_interval_days=None):
update_interval_days=update_interval_days
)

def _cron_update_repositories(self, limit=1):
def _cron_update_repositories(self, limit):
repositories = self.search(
[("information_update", "=", True)], limit=limit, order="from_date ASC"
[("scheduled_information_update", "=", True)],
limit=limit,
order="from_date ASC",
)
for repository in repositories:
repository.update_information()

def _cron_update_branches(self, limit=1):
def _cron_update_branches(self, limit):
repositories = self.search(
[("branch_update", "=", True)], limit=limit, order="branch_update_date ASC"
[("scheduled_branch_update", "=", True)],
limit=limit,
order="branch_update_date ASC",
)
for repository in repositories:
repository.update_branches()
Expand Down
4 changes: 3 additions & 1 deletion vcp_management/models/vcp_repository_branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ class VcpRepositoryBranch(models.Model):
"vcp.branch",
string="Branch",
required=True,
readonly=True,
ondelete="cascade",
)
repository_id = fields.Many2one(
"vcp.repository",
required=True,
readonly=True,
ondelete="cascade",
)
platform_id = fields.Many2one(
Expand All @@ -40,7 +42,7 @@ class VcpRepositoryBranch(models.Model):
required=True,
)

def _cron_process_branch_rules(self, limit=10):
def _cron_process_branch_rules(self, limit):
branches = self.search([], limit=limit, order="update_rule_processing_date asc")
for branch in branches:
branch.process_rules()
Expand Down
59 changes: 45 additions & 14 deletions vcp_management/models/vcp_request.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# Copyright 2026 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models, tools
from odoo import api, fields, models

_STATUS_SELECTION = [
("draft", "Draft"),
("open", "Open"),
("merged", "Merged"),
("closed", "Closed"),
]


class VcpRequest(models.Model):
Expand Down Expand Up @@ -41,9 +48,27 @@ class VcpRequest(models.Model):
related="organization_id.partner_id",
string="Organization Partner",
)
review_ids = fields.One2many(
comodel_name="vcp.review",
string="Reviews",
readonly=True,
inverse_name="request_id",
)
review_count = fields.Integer(compute="_compute_review_count", store=True)
comment_ids = fields.One2many(
comodel_name="vcp.comment",
string="Comments",
readonly=True,
inverse_name="request_id",
)
comment_count = fields.Integer(compute="_compute_comment_count", store=True)
url = fields.Char(readonly=True)
state = fields.Char(readonly=True)
status = fields.Selection(
selection=_STATUS_SELECTION, compute="_compute_status", store=True
)
is_merged = fields.Boolean(readonly=True)
is_draft = fields.Boolean(readonly=True)
created_at = fields.Datetime(readonly=True)
updated_at = fields.Datetime(readonly=True)
closed_at = fields.Datetime(readonly=True)
Expand All @@ -63,18 +88,24 @@ class VcpRequest(models.Model):
("external_id_uniq", "unique(external_id)", "External ID must be unique.")
]

@api.depends("review_ids")
def _compute_review_count(self):
for record in self:
record.review_count = len(record.review_ids)

class VcpRequestLabel(models.Model):
_name = "vcp.request.label"
_description = "Vcp Request Label"

name = fields.Char(required=True)

_sql_constraints = [("name_uniq", "unique(name)", "Label name must be unique.")]
@api.depends("comment_ids")
def _compute_comment_count(self):
for record in self:
record.comment_count = len(record.comment_ids)

@tools.ormcache("name")
def _get_label(self, name):
label = self.search([("name", "=", name)], limit=1)
if not label:
label = self.sudo().create({"name": name})
return label.id
@api.depends("is_draft", "is_merged", "state")
def _compute_status(self):
for record in self:
if record.is_merged:
record.status = "merged"
elif record.closed_at:
record.status = "closed"
elif record.is_draft:
record.status = "draft"
else:
record.status = "open"
44 changes: 44 additions & 0 deletions vcp_management/models/vcp_request_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2026 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from random import randint

from odoo import _, api, fields, models, tools
from odoo.exceptions import UserError


class VcpRequestLabel(models.Model):
_name = "vcp.request.label"
_description = "Vcp Request Label"

name = fields.Char(required=True, readonly=True)

color = fields.Char(default=lambda x: x._default_color())

request_ids = fields.Many2many(
comodel_name="vcp.request",
string="Requests",
readonly=True,
)

_sql_constraints = [("name_uniq", "unique(name)", "Label name must be unique.")]

def _default_color(self):
return randint(1, 11)

@tools.ormcache("name")
def _get_label(self, name):
label = self.search([("name", "=", name)], limit=1)
if not label:
label = self.sudo().create({"name": name})
return label.id

@api.ondelete(at_uninstall=False)
def _check_requests(self):
if self.mapped("request_ids"):
raise UserError(
_(
"You can not delete labels that are related to Requests. "
"You should first delete the related requests."
)
)
2 changes: 2 additions & 0 deletions vcp_management/models/vcp_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class VcpReview(models.Model):
request_id = fields.Many2one(
"vcp.request",
readonly=True,
required=True,
ondelete="cascade",
)
organization_id = fields.Many2one(
related="request_id.organization_id",
Expand Down
4 changes: 4 additions & 0 deletions vcp_management/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ manage_repository_branch,Access Repository Branch,model_vcp_repository_branch,gr
access_rule_information,Access Repository Branch Rule information,model_vcp_rule_information,group_vcp_user,1,0,0,0
manage_rule_information,Access Repository Branch Rule information,model_vcp_rule_information,group_vcp_manager,1,1,1,1
access_request,Access Pull Requests,model_vcp_request,group_vcp_user,1,0,0,0
manage_request,Access Pull Requests,model_vcp_request,group_vcp_manager,1,1,1,1
access_request_label,Access Pull Requests Labels,model_vcp_request_label,group_vcp_user,1,0,0,0
manage_request_label,Access Pull Requests Labels,model_vcp_request_label,group_vcp_manager,1,1,1,1
access_review,Access Reviews,model_vcp_review,group_vcp_user,1,0,0,0
manage_review,Access Reviews,model_vcp_review,group_vcp_manager,1,1,1,1
access_comment,Access Comments,model_vcp_comment,group_vcp_user,1,0,0,0
manage_comment,Access Comments,model_vcp_comment,group_vcp_manager,1,1,1,1
access_vcp_host_type,Access Host Type,model_vcp_host_type,group_vcp_user,1,0,0,0
access_vcp_host,Access Hosts,model_vcp_host,group_vcp_user,1,0,0,0
manage_vcp_host,Nabage Hosts,model_vcp_host,group_vcp_manager,1,1,1,1
Expand Down
Loading