Skip to content

Commit 2323f6f

Browse files
authored
Merge pull request #300 from acsone/label-pr-modified-addons
Label PRs with modified addons
2 parents da2357d + b6e7d32 commit 2323f6f

11 files changed

Lines changed: 334 additions & 3 deletions

environment.sample

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ ODOO_PASSWORD=
3434
# Number of days before the proposal can be marked as "Approved"
3535
#MIN_PR_AGE=5
3636

37+
# Color of the github label that contains the name of the module
38+
#MODULE_LABEL_COLOR=#ffc
39+
3740
# Coma separated list of task to run
3841
# By default all configured tasks are run.
3942
# Available tasks:

src/oca_github_bot/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def func_wrapper(*args, **kwargs):
5959
# Available tasks:
6060
# delete_branch,tag_approved,tag_ready_to_merge,gen_addons_table,
6161
# gen_addons_readme,gen_addons_icon,setuptools_odoo,merge_bot,tag_needs_review,
62-
# migration_issue_bot,whool_init,gen_metapackage
62+
# migration_issue_bot,whool_init,gen_metapackage,label_modified_addons
6363
BOT_TASKS = os.environ.get("BOT_TASKS", "all").split(",")
6464

6565
BOT_TASKS_DISABLED = os.environ.get("BOT_TASKS_DISABLED", "").split(",")
@@ -101,6 +101,8 @@ def func_wrapper(*args, **kwargs):
101101
APPROVALS_REQUIRED = int(os.environ.get("APPROVALS_REQUIRED", "2"))
102102
MIN_PR_AGE = int(os.environ.get("MIN_PR_AGE", "5"))
103103

104+
MODULE_LABEL_COLOR = os.environ.get("MODULE_LABEL_COLOR", "#ffc")
105+
104106
dist_publisher = MultiDistPublisher()
105107
SIMPLE_INDEX_ROOT = os.environ.get("SIMPLE_INDEX_ROOT")
106108
if SIMPLE_INDEX_ROOT:

src/oca_github_bot/tasks/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from . import (
55
heartbeat,
6+
label_modified_addons,
67
main_branch_bot,
78
mention_maintainer,
89
migration_issue_bot,
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright (c) ACSONE SA/NV 2024
2+
# Distributed under the MIT License (http://opensource.org/licenses/MIT).
3+
4+
from .. import github
5+
from ..config import MODULE_LABEL_COLOR, switchable
6+
from ..manifest import git_modified_addons
7+
from ..process import check_call
8+
from ..queue import task
9+
from ..utils import compute_module_label_name
10+
from ..version_branch import is_main_branch_bot_branch
11+
12+
13+
def _label_modified_addons(gh, org, repo, pr, dry_run):
14+
gh_repo = gh.repository(org, repo)
15+
gh_pr = gh.pull_request(org, repo, pr)
16+
target_branch = gh_pr.base.ref
17+
pr_branch = f"tmp-pr-{pr}"
18+
with github.temporary_clone(org, repo, target_branch) as clone_dir:
19+
check_call(
20+
["git", "fetch", "origin", f"pull/{pr}/head:{pr_branch}"],
21+
cwd=clone_dir,
22+
)
23+
check_call(["git", "checkout", pr_branch], cwd=clone_dir)
24+
modified_addons, _ = git_modified_addons(clone_dir, target_branch)
25+
if not modified_addons:
26+
return
27+
gh_issue = github.gh_call(gh_pr.issue)
28+
repo_label_names = [label.name for label in gh_repo.labels()]
29+
issue_label_names = [label.name for label in gh_issue.labels()]
30+
31+
new_labels = set()
32+
for modified_addon in modified_addons:
33+
label_name = compute_module_label_name(modified_addon)
34+
# We create label at repo level, because it is possible to
35+
# to set description in create_label() function
36+
# (and not in issue.add_labels())
37+
if label_name not in repo_label_names and not dry_run:
38+
github.gh_call(
39+
gh_repo.create_label,
40+
name=label_name,
41+
description=f"Module {modified_addon}",
42+
color=MODULE_LABEL_COLOR.replace("#", ""),
43+
)
44+
new_labels.add(label_name)
45+
46+
if is_main_branch_bot_branch(target_branch):
47+
new_labels.add(f"series:{target_branch}")
48+
new_labels |= {
49+
x
50+
for x in issue_label_names
51+
if not (x.startswith("mod:") or x.startswith("series:"))
52+
}
53+
54+
if not dry_run and new_labels != set(issue_label_names):
55+
github.gh_call(gh_issue.replace_labels, list(new_labels))
56+
57+
58+
@task()
59+
@switchable("label_modified_addons")
60+
def label_modified_addons(org, repo, pr, dry_run=False):
61+
with github.login() as gh:
62+
_label_modified_addons(gh, org, repo, pr, dry_run)

src/oca_github_bot/utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
# Copyright (c) ACSONE SA/NV 2021
22
# Distributed under the MIT License (http://opensource.org/licenses/MIT).
33

4+
import hashlib
45
import re
56
import shlex
67
import time
78
from collections.abc import Sequence
89

910
from . import config
1011

12+
# Max size allowed by github for label name
13+
_MAX_LABEL_SIZE = 50
14+
# Size of the hash, added at the end of the label name
15+
# if module name is too long
16+
_HASH_SIZE = 5
17+
1118

1219
def hide_secrets(s: str) -> str:
1320
# TODO do we want to hide other secrets ?
@@ -33,3 +40,24 @@ def retry_on_exception(
3340

3441
def cmd_to_str(cmd: Sequence[str]) -> str:
3542
return shlex.join(str(c) for c in cmd)
43+
44+
45+
def compute_module_label_name(module_name: str) -> str:
46+
"""To avoid error if label name is too long
47+
we cut big label, and finish by a hash of the module name.
48+
(The full module name will be present in the description).
49+
Short module name exemple :
50+
- module : 'web_responsive'
51+
- label : 'mod:web_responsive'
52+
Long module name exemple :
53+
- module : 'account_invoice_supplierinfo_update_triple_discount'
54+
- label : 'mod:account_invoice_supplierinfo_update_trip bf3f3'
55+
"""
56+
label_name = f"mod:{module_name}"
57+
if len(label_name) > _MAX_LABEL_SIZE:
58+
module_hash = hashlib.sha256(bytes(module_name, "utf-8")).hexdigest()
59+
label_name = (
60+
f"{label_name[:(_MAX_LABEL_SIZE - (_HASH_SIZE + 1))]}"
61+
f" {module_hash[:_HASH_SIZE]}"
62+
)
63+
return label_name

src/oca_github_bot/version_branch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def is_main_branch_bot_branch(branch_name):
3333

3434

3535
def is_protected_branch(branch_name):
36-
if branch_name == "master":
36+
if branch_name in ("master", "main"):
3737
return True
3838
return bool(ODOO_VERSION_RE.match(branch_name))
3939

src/oca_github_bot/webhooks/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
on_command,
66
on_pr_close_delete_branch,
77
on_pr_green_label_needs_review,
8+
on_pr_label_modified_addons,
89
on_pr_open_label_new_contributor,
910
on_pr_open_mention_maintainer,
1011
on_pr_review,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright (c) ACSONE SA/NV 2024
2+
# Distributed under the MIT License (http://opensource.org/licenses/MIT).
3+
4+
import logging
5+
6+
from ..router import router
7+
from ..tasks.label_modified_addons import label_modified_addons
8+
9+
_logger = logging.getLogger(__name__)
10+
11+
12+
@router.register("pull_request", action="opened")
13+
@router.register("pull_request", action="reopened")
14+
@router.register("pull_request", action="synchronize")
15+
async def on_pr_label_modified_addons(event, *args, **kwargs):
16+
"""
17+
Whenever a PR is opened, add labels based on modified addons.
18+
"""
19+
org, repo = event.data["repository"]["full_name"].split("/")
20+
pr = event.data["pull_request"]["number"]
21+
label_modified_addons.delay(org, repo, pr)

0 commit comments

Comments
 (0)