Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
61 changes: 61 additions & 0 deletions odoo_project_changelog/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
=========================
Odoo Project - Changelogs
=========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:caf51f538da8b936e047ed66d1dce665e597514cd6e5da23c75d763fef84f5a3
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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-camptocamp%2Fodoo--repository-lightgray.png?logo=github
:target: https://github.com/camptocamp/odoo-repository/tree/16.0/odoo_project_changelog
:alt: camptocamp/odoo-repository

|badge1| |badge2| |badge3|

This module allows to generate CHANGELOGs for repositories used within a project.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/camptocamp/odoo-repository/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 <https://github.com/camptocamp/odoo-repository/issues/new?body=module:%20odoo_project_changelog%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
~~~~~~~

* Camptocamp

Contributors
~~~~~~~~~~~~

* Camptocamp
* Sébastien Alix <sebastien.alix@camptocamp.com>

Maintainers
~~~~~~~~~~~

This module is part of the `camptocamp/odoo-repository <https://github.com/camptocamp/odoo-repository/tree/16.0/odoo_project_changelog>`_ project on GitHub.

You are welcome to contribute.
1 change: 1 addition & 0 deletions odoo_project_changelog/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
22 changes: 22 additions & 0 deletions odoo_project_changelog/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
{
"name": "Odoo Project - Changelogs",
"summary": "Generate Changelogs from repositories for installed modules.",
"version": "16.0.1.0.0",
"category": "Tools",
"author": "Camptocamp, Odoo Community Association (OCA)",
"website": "https://github.com/camptocamp/odoo-repository",
"data": [
"security/ir.model.access.csv",
"data/queue_job.xml",
"views/odoo_project.xml",
"report/ir_actions_report.xml",
"report/odoo_project_changelog.xml",
],
"installable": True,
"depends": [
"odoo_project",
],
"license": "AGPL-3",
}
31 changes: 31 additions & 0 deletions odoo_project_changelog/data/queue_job.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 Camptocamp SA
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo noupdate="1">

<record id="queue_job_channel_odoo_project_changelog" model="queue.job.channel">
<field name="name">odoo_project_changelog</field>
<field name="parent_id" ref="queue_job.channel_root" />
</record>

<record
id="queue_job_function_odoo_project_repository_generate_changelog"
model="queue.job.function"
>
<field name="model_id" ref="model_odoo_project_repository" />
<field name="method">_generate_changelog</field>
<field name="channel_id" ref="queue_job_channel_odoo_project_changelog" />
<field name="retry_pattern" eval="{1: 1, 5: 5, 10: 10, 15: 30}" />
</record>

<record
id="queue_job_function_odoo_project_generate_changelog_report"
model="queue.job.function"
>
<field name="model_id" ref="model_odoo_project" />
<field name="method">_generate_changelog_report</field>
<field name="channel_id" ref="queue_job_channel_odoo_project_changelog" />
<field name="retry_pattern" eval="{1: 1, 5: 5, 10: 10, 15: 30}" />
</record>

</odoo>
2 changes: 2 additions & 0 deletions odoo_project_changelog/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import odoo_project_repository
from . import odoo_project
85 changes: 85 additions & 0 deletions odoo_project_changelog/models/odoo_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from urllib.parse import urljoin

from odoo import fields, models

from odoo.addons.queue_job.delay import chain
from odoo.addons.queue_job.job import identity_exact


class OdooProject(models.Model):
_inherit = "odoo.project"

used_repository_ids = fields.One2many(
comodel_name="odoo.project.repository",
inverse_name="odoo_project_id",
string="Used Repositories",
context={"active_test": False},
)
changelog_enabled_repository_ids = fields.One2many(
comodel_name="odoo.project.repository",
inverse_name="odoo_project_id",
string="Enabled Repositories for CHANGELOG",
)
changelog_state = fields.Selection(
selection=[
("none", "None"),
("in_progress", "In progress"),
("done", "Done"),
],
default="none",
copy=False,
)
changelog_url = fields.Char(compute="_compute_changelog_url")

def _compute_changelog_url(self):
for rec in self:
rec.changelog_url = urljoin(
rec.get_base_url() + "/",
f"report/html/odoo_project_changelog.report_changelog/{rec.id}",
)

def action_generate_changelog(self):
self.ensure_one()
self.changelog_state = "in_progress"
self.used_repository_ids.changelog = False
jobs = self._create_jobs()
chain(*jobs).delay()

def action_open_changelog(self):
return {
"type": "ir.actions.act_url",
"url": self.changelog_url,
"target": "_new",
"target_type": "public",
}

def _create_jobs(self):
self.ensure_one()
jobs = []
# Spawn jobs generating a changelog for each repository
for repo in self.used_repository_ids:
if not repo.active:
continue
delayable = repo.delayable(
description=(
f"Collect CHANGELOG data for {self.display_name}, "
f"repository {repo.repository_branch_id.display_name}"
),
identity_key=identity_exact,
)
job = delayable._generate_changelog()
jobs.append(job)
# Spawn job updating the CHANGELOG state to done
delayable = self.delayable(
description=(f"Set CHANGELOG as ready for {self.display_name}"),
identity_key=identity_exact,
)
job = delayable._set_changelog_done()
jobs.append(job)
return jobs

def _set_changelog_done(self):
self.changelog_state = "done"
87 changes: 87 additions & 0 deletions odoo_project_changelog/models/odoo_project_repository.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Copyright 2024 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)

from odoo import fields, models

from ..utils.scanner import ChangelogScannerOdooEnv


class OdooProjectRepository(models.Model):
_name = "odoo.project.repository"
_description = "Repository used in a project"

odoo_project_id = fields.Many2one(
comodel_name="odoo.project",
ondelete="cascade",
string="Project",
required=True,
index=True,
readonly=True,
)
repository_branch_id = fields.Many2one(
comodel_name="odoo.repository.branch",
ondelete="cascade",
string="Repository Branch",
required=True,
index=True,
)
deployed_commit = fields.Char(help="The changelog is generated from this commit.")
target_commit = fields.Char(
help=(
"Changelog is generated until this commit. "
"If not set, the latest commit of the branch is used."
)
)
active = fields.Boolean(string="Include", default=True)
changelog = fields.Serialized()

def _prepare_changelog_scanner_parameters(self):
ir_config = self.env["ir.config_parameter"]
odoo_repository = self.repository_branch_id.repository_id
repositories_path = ir_config.get_param(odoo_repository._repositories_path_key)
return {
"org": odoo_repository.org_id.name,
"name": odoo_repository.name,
"clone_url": odoo_repository.clone_url,
"odoo_project_repository_id": self.id,
"repositories_path": repositories_path,
"repo_type": odoo_repository.repo_type,
"ssh_key": odoo_repository.ssh_key_id.private_key,
"token": odoo_repository._get_token(),
"env": self.env,
}

def _generate_changelog(self):
self.ensure_one()
params = self._prepare_changelog_scanner_parameters()
scanner = ChangelogScannerOdooEnv(**params)
scanner.scan()

def push_changelog(self, changelog):
"""Store the changelog. Called by the scanner."""
self.ensure_one()
self.changelog = changelog
self.target_commit = self.changelog["target_commit"]

def _get_report_data(self):
"""Return data used by the CHANGELOG report."""
self.ensure_one()
project_module_model = self.env["odoo.project.module"]
if not self.changelog.get("modules"):
return {"categories": {}, "count": 0}
# Collect all related categories and sort them by name
project_module_ids = [
int(project_module_id) for project_module_id in self.changelog["modules"]
]
project_modules = project_module_model.browse(project_module_ids).exists()
categories = project_modules.category_id.sorted(
key=lambda o: (o.name or "").lower() # Case insensitive
)
data = {"categories": {categ: {} for categ in categories}}
data["categories"][self.env["odoo.module.category"]] = {}
data["count"] = len(self.changelog["modules"])
for project_module_id, module_data in self.changelog["modules"].items():
project_module = project_module_model.browse(int(project_module_id))
categ = project_module.category_id
data["categories"][categ][project_module] = module_data
return data
2 changes: 2 additions & 0 deletions odoo_project_changelog/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* Camptocamp
* Sébastien Alix <sebastien.alix@camptocamp.com>
1 change: 1 addition & 0 deletions odoo_project_changelog/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This module allows to generate CHANGELOGs for repositories used within a project.
17 changes: 17 additions & 0 deletions odoo_project_changelog/report/ir_actions_report.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 Camptocamp SA
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>

<record id="action_report_changelog" model="ir.actions.report">
<field name="name">CHANGELOG</field>
<field name="model">odoo.project</field>
<field name="report_type">qweb-html</field>
<field name="report_name">odoo_project_changelog.report_changelog</field>
<field name="report_file">odoo_project_changelog.report_changelog</field>
<field name="print_report_name">CHANGELOG</field>
<field name="binding_model_id" ref="model_odoo_project" />
<field name="binding_type">report</field>
</record>

</odoo>
79 changes: 79 additions & 0 deletions odoo_project_changelog/report/odoo_project_changelog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 Camptocamp SA
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>

<template id="report_changelog">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="web.internal_layout">
<t t-set="company" t-value="o" />
<div class="page">
<h1 class="mt-4">
<span t-field="o.name" /> - CHANGELOG
</h1>
<t t-foreach="o.used_repository_ids.filtered('active')" t-as="repo">
<t t-set="report_data" t-value="repo._get_report_data()" />
<h2 class="mt-4">
<span t-field="repo.repository_branch_id.display_name" /> (<t
t-esc="report_data['count']"
/> modules)
</h2>
<div style="padding-left: 2em;">
<p>From <code t-field="repo.deployed_commit" /> to <code
t-esc="repo.target_commit or 'latest'"
/></p>
<t t-foreach="report_data['categories']" t-as="categ">
<h3 class="mt-4">
<t t-set="anchor" t-value="'%s-%s' % (repo.id, categ.id or 0)" />
<a t-att-name="anchor" t-att-href="'#%s' % anchor"><span
t-field="categ.name"
/></a>
</h3>
<t
t-set="changelog_data"
t-value="report_data['categories'][categ]"
/>
<t t-foreach="changelog_data" t-as="module">
<details>
<summary>
<strong><span t-field="module.module_name" /> (<span
t-field="module.title"
/>)</strong> - <t
t-esc="len(changelog_data[module])"
/> change<t
t-esc="'s'"
t-if="len(changelog_data[module]) > 1"
/>
</summary>
<t t-set="changelog" t-value="changelog_data[module]" />
<ul>
<t t-foreach="changelog" t-as="commit">
<details>
<summary><span t-esc="commit['summary']" /></summary>
<div style="padding-left: 2em;">
<pre><t t-esc="commit['message']" /></pre>
<pre>Date: <span
t-esc="commit['authored_datetime']"
/></pre>
<t
t-set="commit_url"
t-value="repo.repository_branch_id.repository_id.repo_url + '/commit/' + commit['hexsha']"
/>
<a t-att-href="commit_url" target="blank_">Link</a>
</div>
</details>
</t>
</ul>
</details>
</t>
</t>
</div>
</t>
</div>
</t>
</t>
</t>
</template>

</odoo>
Loading