Skip to content

Commit c926865

Browse files
committed
Add gitea_api.StagingPullRequestWrapper class for handling staging
1 parent b29bfde commit c926865

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

osc/gitea_api/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@
2020
from .pr_review import PullRequestReview
2121
from .repo import Repo
2222
from .ssh_key import SSHKey
23+
from .staging import StagingPullRequestWrapper
2324
from .tardiff import TarDiff
2425
from .user import User

osc/gitea_api/staging.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import os
2+
3+
4+
class StagingPullRequestWrapper:
5+
BACKLOG_LABEL = "staging/Backlog"
6+
INPROGRESS_LABEL = "staging/In Progress"
7+
8+
def __init__(self, conn, owner: str, repo: str, number: int, *, topdir: str):
9+
from . import PullRequest
10+
11+
self.conn = conn
12+
self.owner = owner
13+
self.repo = repo
14+
self.number = number
15+
self._topdir = topdir
16+
17+
self.pr_obj = PullRequest.get(conn, owner, repo, number)
18+
self.git = None
19+
self.submodules_by_owner_repo = {}
20+
self.package_pr_map = {}
21+
22+
self.base_git = None
23+
self.base_submodules_by_owner_repo = {}
24+
25+
def clone(self):
26+
from . import Git
27+
from . import Repo
28+
29+
path = os.path.join(self._topdir, f"{self.owner}_{self.repo}_{self.number}")
30+
Repo.clone_or_update(
31+
self.conn,
32+
self.owner,
33+
self.repo,
34+
pr_number=self.number,
35+
commit=self.pr_obj.head_commit,
36+
directory=path,
37+
ssh_private_key_path=self.conn.login.ssh_key,
38+
)
39+
self.git = Git(path)
40+
# git fetch --all to have all commits from all remotes available
41+
self.git.fetch()
42+
43+
submodules = self.git.get_submodules()
44+
self.submodules_by_owner_repo = dict([((i["owner"], i["repo"]), i) for i in submodules.values()])
45+
46+
for pkg_owner, pkg_repo, pkg_number in self.pr_obj.parse_pr_references():
47+
pkg_pr_obj = self.__class__(self.conn, pkg_owner, pkg_repo, pkg_number, topdir=self._topdir)
48+
self.package_pr_map[(pkg_owner, pkg_repo, pkg_number)] = pkg_pr_obj
49+
# FIXME: doesn't work when the commits are padded with zeros
50+
# assert self.submodules_by_owner_repo[(pkg_owner, pkg_repo)]["commit"] == pkg_pr_obj.pr_obj.head_commit
51+
52+
def clone_base(self):
53+
from . import Git
54+
from . import Repo
55+
56+
path = os.path.join(self._topdir, f"{self.owner}_{self.repo}_{self.number}_base")
57+
Repo.clone_or_update(
58+
self.conn,
59+
self.owner,
60+
self.repo,
61+
branch=self.pr_obj.base_branch,
62+
commit=self.pr_obj.base_commit,
63+
directory=path,
64+
ssh_private_key_path=self.conn.login.ssh_key,
65+
)
66+
self.base_git = Git(path)
67+
# git fetch --all to have all commits from all remotes available
68+
self.base_git.fetch()
69+
70+
submodules = self.base_git.get_submodules()
71+
self.base_submodules_by_owner_repo = dict([((i["owner"], i["repo"]), i) for i in submodules.values()])
72+
73+
def merge(self, other):
74+
"""
75+
Merge ``other`` pull request into ``self`` by MOVING all ``PR: <pr_id>`` references.
76+
It is crucial to remove the references from the self.package_pr_maporiginal ``other`` request, otherwise the bots might get confused and close the pull request with reparented package pull request.
77+
"""
78+
from . import Git
79+
from . import PullRequest
80+
81+
self.pr_obj._data["body"] = PullRequest.add_pr_references(self.pr_obj.body, other.package_pr_map.keys())
82+
other.pr_obj._data["body"] = PullRequest.remove_pr_references(other.pr_obj.body, other.package_pr_map.keys())
83+
84+
submodule_paths = []
85+
86+
for pkg_owner, pkg_repo, pkg_number in other.package_pr_map:
87+
other_submodule = other.submodules_by_owner_repo[(pkg_owner, pkg_repo)]
88+
self_submodule = self.submodules_by_owner_repo.get((pkg_owner, pkg_repo), None)
89+
90+
if self_submodule:
91+
# use an existing path if the submodule already exists
92+
submodule_path = self_submodule["path"]
93+
submodule_branch = self_submodule["branch"]
94+
else:
95+
# use submodule path from the ``other`` pull request
96+
submodule_path = other_submodule["path"]
97+
submodule_branch = other_submodule["branch"]
98+
99+
# add a submodule if missing
100+
if not self_submodule:
101+
self.git._run_git(["submodule", "add", "-b", submodule_branch, f"../../{pkg_owner}/{pkg_repo}", submodule_path])
102+
103+
# init the submodule
104+
self.git._run_git(["submodule", "update", "--init", submodule_path])
105+
106+
submodule_git = Git(os.path.join(self.git.abspath, submodule_path))
107+
submodule_git.fetch()
108+
submodule_git._run_git(["fetch", "origin", f"pull/{pkg_number}/head:{submodule_branch}", "--force", "--update-head-ok"])
109+
submodule_paths.append(submodule_path)
110+
111+
self.git.add(submodule_paths)
112+
self.git.commit(f"Merge package submodules from {other.pr_obj.base_owner}/{other.pr_obj.base_repo}!{other.pr_obj.number}")
113+
114+
def remove(self, package_pr):
115+
"""
116+
Remove a package submodule from this project.
117+
"""
118+
from . import Git
119+
from . import GitObsRuntimeError
120+
from . import PullRequest
121+
122+
self.pr_obj._data["body"] = PullRequest.remove_pr_references(self.pr_obj.body, [(package_pr.owner, package_pr.repo, package_pr.number)])
123+
124+
submodule = self.submodules_by_owner_repo.get((package_pr.owner, package_pr.repo), None)
125+
base_submodule = self.base_submodules_by_owner_repo.get((package_pr.owner, package_pr.repo), None)
126+
127+
if not submodule:
128+
raise GitObsRuntimeError(f"Unable to find a submodule for pull request {package_pr.owner}/{package_pr.repo}!{package_pr.number}")
129+
130+
submodule_path = submodule["path"]
131+
132+
if base_submodule:
133+
# we're reverting the submodule to an older commit
134+
self.git._run_git(["submodule", "update", "--init", "--remote", submodule_path])
135+
submodule_git = Git(os.path.join(self.git.abspath, submodule_path))
136+
submodule_git.fetch()
137+
submodule_git.reset(commit=base_submodule["commit"], hard=True)
138+
self.git.add([submodule_path])
139+
else:
140+
# we're removing the submodule completely
141+
self.git._run_git(["rm", submodule["path"]])
142+
143+
self.git.commit(f"Remove package pull request {package_pr.pr_obj.base_owner}/{package_pr.pr_obj.base_repo}!{package_pr.pr_obj.number}")

0 commit comments

Comments
 (0)