Skip to content
Open
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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ oca-update-pre-commit-excluded-addons = "tools.update_pre_commit_excluded_addons
oca-fix-manifest-website = "tools.fix_manifest_website:main"
oca-configure-travis= "tools.configure_travis:main"
oca-copier-update = "tools.copier_update:main"
oca-create-branch-from-previous = "tools.create_branch_from_previous:main"

[tool.hatch.build.targets.wheel]
packages = ["tools"]
Expand Down
50 changes: 50 additions & 0 deletions tests/test_mark_modules_uninstallable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import textwrap

from tools.manifest import mark_modules_uninstallable, mark_manifest_uninstallable


def test_mark_module_uninstallable(tmp_path):
(tmp_path / "mod1").mkdir()
(tmp_path / "mod1" / "__manifest__.py").write_text("""{'name': 'mod1'}""")
mark_modules_uninstallable(tmp_path)
assert (tmp_path / "mod1" / "__manifest__.py").read_text() == (
"""{'name': 'mod1',\n 'installable': False,\n}\n"""
)


def test_mark_module_uninstallable_key_exists(tmp_path):
(tmp_path / "mod1").mkdir()
(tmp_path / "mod1" / "__manifest__.py").write_text(
"""{'name': 'mod1', "installable": True}"""
)
mark_modules_uninstallable(tmp_path)
assert (tmp_path / "mod1" / "__manifest__.py").read_text() == (
"""{'name': 'mod1', "installable": False}"""
)


def test_mark_module_uninstallable_curly_braces(tmp_path):
assert mark_manifest_uninstallable(
textwrap.dedent(
"""\
{
'name': 'mod1',
'external_dependencies': {
'python': ['some_package'],
},
'license': 'AGPL-3',
}
"""
)
) == textwrap.dedent(
"""\
{
'name': 'mod1',
'external_dependencies': {
'python': ['some_package'],
},
'license': 'AGPL-3',
'installable': False,
}
"""
)
125 changes: 125 additions & 0 deletions tools/create_branch_from_previous.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import subprocess
from pathlib import Path

import click

from .manifest import mark_modules_uninstallable


@click.command()
@click.option("--odoo-version", required=True)
@click.option("--new-branch-name")
@click.option(
"--addons-dir",
type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
default=".",
)
@click.option(
"--data",
multiple=True,
help="Additional copier data, as key=value",
)
def main(
odoo_version: str,
new_branch_name: str | None,
addons_dir: Path,
data: list[str],
) -> None:
"""Create a new branch off an existing branch and set all addons installable=False.

To use it, go to a git clone of a repo and checkout the branch you want to start from.
"""
if not new_branch_name:
new_branch_name = odoo_version
result = subprocess.run(
["git", "rev-parse", "--abbrev-ref", "HEAD"],
check=True,
text=True,
capture_output=True,
)
previous_branch = result.stdout.strip()
subprocess.run(
[
"git",
"checkout",
"-b",
new_branch_name,
],
check=True,
)
result = subprocess.run(
[
"copier",
"recopy", # override local changes when creating a new branch
"--trust",
"--defaults",
"--overwrite",
f"--data=odoo_version={odoo_version}",
*(f"--data={d}" for d in data),
],
)
if result.returncode != 0:
raise SystemExit("copier update failed, please fix manually")
result = subprocess.run(
[
"git",
"diff",
"--diff-filter=U",
"--quiet",
],
)
if result.returncode != 0:
raise SystemExit(
"There are merge conflicts after copier update, please fix manually"
)
mark_modules_uninstallable(addons_dir)
if Path(".pre-commit-config.yaml").exists():
# First run pre-commit on .pre-commit-config.yaml, to exclude
# addons that are not installable.
subprocess.run(
[
"pre-commit",
"run",
"--files",
".pre-commit-config.yaml",
],
check=False,
)
# Run pre-commit once to let it apply auto fixes.
subprocess.run(
[
"pre-commit",
"run",
"--all-files",
],
check=False,
)
# Run pre-commit a second time to check that everything is green.
result = subprocess.run(
[
"pre-commit",
"run",
"--all-files",
],
check=False,
)
if result.returncode != 0:
raise SystemExit("pre-commit failed, please fix manually")
subprocess.run(
[
"git",
"add",
".",
],
check=True,
)
subprocess.run(
[
"git",
"commit",
"-m",
f"[MIG] Create {new_branch_name} from {previous_branch} "
f"for Odoo {odoo_version}",
],
check=True,
)
20 changes: 20 additions & 0 deletions tools/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import ast
import os
import re
from pathlib import Path

MANIFEST_NAMES = ("__manifest__.py", "__openerp__.py", "__terp__.py")

Expand Down Expand Up @@ -41,3 +43,21 @@ def find_addons(addons_dir, installable_only=True):
if installable_only and not manifest.get("installable", True):
continue
yield addon_name, addon_dir, manifest


def mark_manifest_uninstallable(manifest_text: str) -> str:
manifest = ast.literal_eval(manifest_text)
if "installable" not in manifest:
src = r",?\s*}\s*$"
dest = ",\n 'installable': False,\n}\n"
else:
src = "[\"']installable[\"']: *True"
dest = '"installable": False'
return re.sub(src, dest, manifest_text, re.DOTALL)


def mark_modules_uninstallable(addons_dir: Path) -> None:
for manifest_path in addons_dir.glob("*/__manifest__.py"):
manifest_text = manifest_path.read_text(encoding="utf-8")
new_manifest_text = mark_manifest_uninstallable(manifest_text)
manifest_path.write_text(new_manifest_text, encoding="utf-8")