Skip to content

Commit 8d1d270

Browse files
[ADD] ocabot migration issue maintainer
New ocabot command for maintaining migration issues: ``/ocabot migration``, followed by the module name, performing the following: * Look for an issue in that repository with the name "Migration to version ``{version}``", where ``{version}`` is the name of the target branch. * Add or edit a line in that issue, linking the module to the pull request (PR) and the author of it. * TODO: When the PR is merged, the line gets ticked. * Put the milestone corresponding to the target branch in the PR. Co-authored-by: Pedro M. Baeza <[email protected]>
1 parent a1c656d commit 8d1d270

File tree

7 files changed

+255
-1
lines changed

7 files changed

+255
-1
lines changed

Diff for: README.rst

+11
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ can be used to ask the bot to do the following:
8989

9090
* rebase the PR on the target branch
9191

92+
``/ocabot migration``, followed by the module name, performing the following:
93+
94+
* Look for an issue in that repository with the name "Migration to version
95+
``{version}``", where ``{version}`` is the name of the target branch.
96+
* Add or edit a line in that issue, linking the module to the pull request
97+
(PR) and the author of it.
98+
* TODO: When the PR is merged, the line gets ticked.
99+
* Put the milestone corresponding to the target branch in the PR.
100+
92101
TODO (help wanted)
93102
------------------
94103

@@ -215,6 +224,8 @@ Contributors
215224
* Jose Angel Fentanez <[email protected]>
216225
* Simone Rubino <[email protected]>
217226
* Sylvain Le Gal (https://twitter.com/legalsylvain)
227+
* Tecnativa - Pedro M. Baeza
228+
* Tecnativa - Víctor Martínez
218229

219230
Maintainers
220231
===========

Diff for: src/oca_github_bot/commands.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import re
55

6-
from .tasks import merge_bot, rebase_bot
6+
from .tasks import merge_bot, rebase_bot, migration_issue_bot
77

88
BOT_COMMAND_RE = re.compile(
99
# Do not start with > (Github comment), not consuming it
@@ -61,6 +61,8 @@ def create(cls, name, options):
6161
return BotCommandMerge(name, options)
6262
elif name == "rebase":
6363
return BotCommandRebase(name, options)
64+
elif name == "migration":
65+
return BotCommandMigrationIssue(name, options)
6466
else:
6567
raise InvalidCommandError(name)
6668

@@ -100,6 +102,19 @@ def parse_options(self, options):
100102

101103
def delay(self, org, repo, pr, username, dry_run=False):
102104
rebase_bot.rebase_bot_start.delay(org, repo, pr, username, dry_run=False)
105+
class BotCommandMigrationIssue(BotCommand):
106+
module = None # mandatory str: module name
107+
108+
def parse_options(self, options):
109+
if len(options) == 1:
110+
self.module = options[0]
111+
else:
112+
raise InvalidOptionsError(self.name, options)
113+
114+
def delay(self, org, repo, pr, username, dry_run=False):
115+
migration_issue_bot.migration_issue_start.delay(
116+
org, repo, pr, username, module=self.module, dry_run=dry_run
117+
)
103118

104119

105120
def parse_commands(text):

Diff for: src/oca_github_bot/config.py

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ def func_wrapper(*args, **kwargs):
5454

5555
DRY_RUN = os.environ.get("DRY_RUN", "").lower() in ("1", "true", "yes")
5656

57+
# Coma separated list of task to run
58+
# By default all configured tasks are run.
59+
# Available tasks:
60+
# delete_branch,tag_approved,tag_ready_to_merge,gen_addons_table,
61+
# gen_addons_readme,gen_addons_icon,setuptools_odoo,merge_bot,tag_needs_review,
62+
# migration_issue_bot
5763
BOT_TASKS = os.environ.get("BOT_TASKS", "all").split(",")
5864

5965
BOT_TASKS_DISABLED = os.environ.get("BOT_TASKS_DISABLED", "").split(",")

Diff for: src/oca_github_bot/tasks/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
heartbeat,
66
main_branch_bot,
77
mention_maintainer,
8+
migration_issue_bot,
89
tag_approved,
910
tag_needs_review,
1011
tag_ready_to_merge,

Diff for: src/oca_github_bot/tasks/migration_issue_bot.py

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Copyright 2019 Tecnativa - Pedro M. Baeza
2+
# Copyright 2021 Tecnativa - Víctor Martínez
3+
# Distributed under the MIT License (http://opensource.org/licenses/MIT).
4+
5+
import re
6+
7+
from .. import github
8+
from ..config import switchable
9+
from ..process import check_call
10+
from ..queue import getLogger, task
11+
from .merge_bot import _user_can_merge
12+
13+
_logger = getLogger(__name__)
14+
15+
16+
def _create_or_find_branch_milestone(repo, branch):
17+
for milestone in repo.milestones():
18+
if milestone.title == branch:
19+
return milestone
20+
return repo.create_milestone(branch)
21+
22+
23+
def _find_issue(repo, milestone, target_branch):
24+
issue_title = f"Migration to version {target_branch}"
25+
issue = False
26+
for i in repo.issues(milestone=milestone.number):
27+
if i.title == issue_title:
28+
issue = i
29+
break
30+
return issue
31+
32+
33+
def _set_lines_issue(gh_pr, issue, module):
34+
lines = []
35+
added = False
36+
module_list = False
37+
new_line = f"- [ ] {module} - By @{gh_pr.user.login} - #{gh_pr.number}"
38+
for line in issue.body.split("\n"):
39+
if line.startswith(f"- [ ] {module}"):
40+
lines.append(new_line)
41+
continue
42+
elif not added:
43+
splits = re.split(r"- \[ \] ([0-9a-zA-Z_]*)", line)
44+
if len(splits) >= 2:
45+
# Flag for detecting if we have passed already module list
46+
module_list = True
47+
line_module = splits[1]
48+
if line_module > module:
49+
lines.append(new_line)
50+
added = True
51+
elif module_list:
52+
lines.append(new_line)
53+
added = True
54+
lines.append(line)
55+
return lines
56+
57+
58+
@task()
59+
@switchable("migration_issue_bot")
60+
def migration_issue_start(org, repo, pr, username, module=None, dry_run=False):
61+
with github.login() as gh:
62+
gh_pr = gh.pull_request(org, repo, pr)
63+
target_branch = gh_pr.base.ref
64+
pr_branch = f"tmp-pr-{pr}"
65+
try:
66+
with github.temporary_clone(org, repo, target_branch) as clone_dir:
67+
# Create merge bot branch from PR and rebase it on target branch
68+
# This only serves for checking permissions
69+
check_call(
70+
["git", "fetch", "origin", f"pull/{pr}/head:{pr_branch}"],
71+
cwd=clone_dir,
72+
)
73+
check_call(["git", "checkout", pr_branch], cwd=clone_dir)
74+
if not _user_can_merge(
75+
gh, org, repo, username, clone_dir, target_branch
76+
):
77+
github.gh_call(
78+
gh_pr.create_comment,
79+
f"Sorry @{username} you are not allowed to mark the addon to"
80+
f"be migrated.\n\n"
81+
f"To do so you must either have push permissions on "
82+
f"the repository, or be a declared maintainer of all "
83+
f"modified addons.\n\n"
84+
f"If you wish to adopt an addon and become it's "
85+
f"[maintainer]"
86+
f"(https://odoo-community.org/page/maintainer-role), "
87+
f"open a pull request to add "
88+
f"your GitHub login to the `maintainers` key of its "
89+
f"manifest.",
90+
)
91+
return
92+
# Assign milestone to PR
93+
milestone = _create_or_find_branch_milestone(repo, target_branch)
94+
gh_pr.issue().edit(milestone=milestone.number)
95+
# Find issue
96+
issue = _find_issue(repo, milestone, target_branch)
97+
if not issue:
98+
issue_title = f"Migration to version {target_branch}"
99+
github.gh_call(
100+
gh_pr.create_comment,
101+
f"There's no issue in this repo with the title '{issue_title}' "
102+
f"and the milestone {target_branch}, so not possible to add "
103+
f"the comment.",
104+
)
105+
return
106+
# Change issue to add the PR in the module list
107+
lines = _set_lines_issue(gh_pr, issue, module)
108+
issue.edit(body="\n".join(lines))
109+
except Exception as e:
110+
github.gh_call(
111+
gh_pr.create_comment,
112+
f"@{username} The migration issue commenter process could not "
113+
f"start, because of exception {e}.",
114+
)
115+
raise
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- application/vnd.github.v3.full+json
7+
Accept-Charset:
8+
- utf-8
9+
Accept-Encoding:
10+
- gzip, deflate
11+
Connection:
12+
- keep-alive
13+
Content-Type:
14+
- application/json
15+
User-Agent:
16+
- github3.py/1.3.0
17+
authorization:
18+
- DUMMY
19+
method: GET
20+
uri: https://api.github.com/repos/OCA/contract
21+
response:
22+
body:
23+
string: '{"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}'
24+
headers:
25+
Access-Control-Allow-Origin:
26+
- '*'
27+
Access-Control-Expose-Headers:
28+
- ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining,
29+
X-RateLimit-Used, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
30+
X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
31+
Content-Length:
32+
- '80'
33+
Content-Security-Policy:
34+
- default-src 'none'
35+
Content-Type:
36+
- application/json; charset=utf-8
37+
Date:
38+
- Mon, 22 Feb 2021 16:13:08 GMT
39+
Referrer-Policy:
40+
- origin-when-cross-origin, strict-origin-when-cross-origin
41+
Server:
42+
- GitHub.com
43+
Strict-Transport-Security:
44+
- max-age=31536000; includeSubdomains; preload
45+
Vary:
46+
- Accept-Encoding, Accept, X-Requested-With
47+
X-Content-Type-Options:
48+
- nosniff
49+
X-Frame-Options:
50+
- deny
51+
X-GitHub-Media-Type:
52+
- github.v3; param=full; format=json
53+
X-GitHub-Request-Id:
54+
- 1ACE:CF7C:5A9C753:64D7CA5:6033D814
55+
X-RateLimit-Limit:
56+
- '60'
57+
X-RateLimit-Remaining:
58+
- '59'
59+
X-RateLimit-Reset:
60+
- '1614013988'
61+
X-XSS-Protection:
62+
- 1; mode=block
63+
x-ratelimit-used:
64+
- '1'
65+
status:
66+
code: 401
67+
message: ''
68+
version: 1

Diff for: tests/test_migration_issue_bot.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright 2021 Tecnativa - Víctor Martínez
2+
# Distributed under the MIT License (http://opensource.org/licenses/MIT).
3+
4+
import pytest
5+
6+
from oca_github_bot.tasks.migration_issue_bot import (
7+
_create_or_find_branch_milestone,
8+
_find_issue,
9+
_set_lines_issue,
10+
)
11+
12+
13+
def _get_repository(gh, org, repo):
14+
return gh.repository(org, repo)
15+
16+
17+
@pytest.mark.vcr()
18+
def test_create_or_find_branch_milestone(gh):
19+
repo = _get_repository(gh, "OCA", "contract")
20+
milestone = _create_or_find_branch_milestone(repo, "8.0")
21+
assert milestone.name == "8.0"
22+
23+
24+
@pytest.mark.vcr()
25+
def test_find_issue(gh):
26+
repo = _get_repository(gh, "OCA", "contract")
27+
milestone = _create_or_find_branch_milestone(repo, "8.0")
28+
issue = _find_issue(repo, milestone, "14.0")
29+
assert issue.name == "Migration to version 14.0"
30+
31+
32+
@pytest.mark.vcr()
33+
def test_set_lines_issue(gh):
34+
repo = _get_repository(gh, "OCA", "contract")
35+
milestone = _create_or_find_branch_milestone(repo, "13.0")
36+
issue = _find_issue(repo, milestone, "14.0")
37+
lines = _set_lines_issue(gh, issue, "contract")
38+
assert len(lines) > 0

0 commit comments

Comments
 (0)