1
0
mirror of https://github.com/openSUSE/osc.git synced 2026-03-08 01:56:15 +01:00

Merge pull request #2054 from openSUSE/feature/staging-group-no-fork-flow

Feature/staging group no fork flow
This commit is contained in:
2026-03-04 09:29:55 +01:00
committed by GitHub
3 changed files with 81 additions and 11 deletions

View File

@@ -144,17 +144,36 @@ class StagingGroupCommand(osc.commandline_git.GitObsCommand):
raise gitea_api.GitObsRuntimeError(f"Pull request {owner}/{repo}#{number} doesn't have any submodules changed.")
if not args.target:
fork_owner = args.fork_owner if args.fork_owner else user_obj.login
target_repo_full_name = f"{target_owner}/{target_repo}".lower()
# determine fork_owner and fork_repo for pull request creation
has_push_access = False
user_repos = gitea_api.Repo.list_my_repos(self.gitea_conn)
for repo in user_repos:
repo_name = repo._data["full_name"]
if target_repo_full_name == repo_name.lower() and repo.can_push:
has_push_access = True
print(f"You have push access to the target repository {target_owner}/{target_repo}, the pull request will be created from a branch in the target repository.")
break
if has_push_access and not args.fork_owner:
fork_owner = target_owner
fork_repo = target_repo
else:
fork_owner = args.fork_owner if args.fork_owner else user_obj.login
fork_repo = None
forks = gitea_api.Fork.list(self.gitea_conn, target_owner, target_repo)
for repo in forks:
if repo.owner.lower() == fork_owner.lower():
fork_repo = repo.repo
break
if not fork_repo:
raise gitea_api.GitObsRuntimeError(f"Cannot find a matching fork of {target_owner}/{target_repo} for user {fork_owner}")
# dates in ISO 8601 format cannot be part of a valid branch name, we need a custom format
fork_branch = args.fork_branch if args.fork_branch else f"for/{target_branch}/group-{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}"
fork_repo = None
forks = gitea_api.Fork.list(self.gitea_conn, target_owner, target_repo)
for repo in forks:
if repo.owner.lower() == fork_owner.lower():
fork_repo = repo.repo
if not fork_repo:
raise gitea_api.GitObsRuntimeError(f"Cannot find a matching fork of {target_owner}/{target_repo}")
clone_dir = gitea_api.Repo.clone(
self.gitea_conn,
@@ -205,18 +224,43 @@ class StagingGroupCommand(osc.commandline_git.GitObsCommand):
target = gitea_api.StagingPullRequestWrapper(self.gitea_conn, target_owner, target_repo, target_number, topdir=temp_dir, cache_directory=cache_dir)
target.clone()
has_push_access = False
if target.pr_obj.head_can_push:
print(f"You have push access to the head repository of the target pull request {target_owner}/{target_repo}#{target_number}, the pull request will be updated by pushing to the head branch.")
has_push_access = True
if target.pr_obj._data['head']['repo']['fork'] and has_push_access:
# if the head repo is a fork and we have push access to it, we can push directly to the head branch
target.git._run_git(["remote", "set-url", "fork", target.pr_obj._data['head']['repo']['ssh_url']])
# locally merge package pull requests to the target project pull request (don't change anything on server yet)
updated_packages = []
for owner, repo, number in args.pr_list:
pr = pr_map[(owner.lower(), repo.lower(), number)]
target.merge(pr)
for (pkg_owner, pkg_repo, pkg_number), pr_obj in pr.package_pr_map.items():
updated_packages.append(os.path.basename(pr.submodules_by_owner_repo[pkg_owner.lower(), pkg_repo.lower()]["path"]))
if target.pr_obj._data['head']['repo']['fork']:
remote="fork"
else:
remote="origin"
# push to git repo associated with the target pull request
target.git.push(remote="fork", branch=f"pull/{target.pr_obj.number}:{target.pr_obj.head_branch}")
print(f"Pushing changes to {remote} on pull/{target.pr_obj.number}:{target.pr_obj.head_branch}")
target.git.push(remote=remote, branch=f"pull/{target.pr_obj.number}:{target.pr_obj.head_branch}")
# update target pull request
if args.target:
# we keep only the first ``max_packages``, because the title might get too long quite easily
max_packages = 5
updated_packages_str = ", ".join(sorted(updated_packages)[:max_packages])
if len(updated_packages) > max_packages:
updated_packages_str += f" + {len(updated_packages) - max_packages} more"
title = args.title if args.title else f"{target.pr_obj.title}, {updated_packages_str}"
# if args.target is not set, we've created a new pull request with all 'PR:' references included
# if args.target is set (which is the case here), we need to update the description with the newly added 'PR:' references
target.pr_obj.set(self.gitea_conn, target_owner, target_repo, target_number, description=target.pr_obj.body)
target.pr_obj.set(self.gitea_conn, target_owner, target_repo, target_number, title=title, description=target.pr_obj.body)
for owner, repo, number in args.pr_list:
pr = pr_map[(owner.lower(), repo.lower(), number)]

View File

@@ -255,6 +255,15 @@ class PullRequest(GiteaModel):
return None
return self._data["head"]["repo"]["ssh_url"]
@property
def head_can_push(self) -> bool:
if not self.is_pull_request:
return False
if self._data["head"]["repo"] is None:
return False
repo_obj = Repo(self._data["head"]["repo"])
return repo_obj.can_push
@property
def merge_commit(self) -> Optional[str]:
if not self.is_pull_request:

View File

@@ -51,6 +51,10 @@ class Repo(GiteaModel):
def default_branch(self) -> str:
return self._data["default_branch"]
@property
def can_push(self) -> bool:
return self._data["permissions"]["push"] or self._data["permissions"]["admin"]
@classmethod
def split_id(cls, repo_id: str) -> Tuple[str, str]:
"""
@@ -284,13 +288,26 @@ class Repo(GiteaModel):
"""
List repos owned by an organization.
:param conn: Gitea ``Connection`` instance.
"""
url = conn.makeurl("orgs", owner, "repos")
obj_list = []
for response in conn.request_all_pages("GET", url):
obj_list.extend([cls(i, response=response) for i in response.json()])
return obj_list
@classmethod
def list_my_repos(cls, conn: Connection) -> List["Repo"]:
"""
List repos owned by a user.
:param conn: Gitea ``Connection`` instance.
"""
q = {
# XXX: limit works in range 1..50, setting it any higher doesn't help, we need to handle paginated results
"limit": 10**6,
}
url = conn.makeurl("orgs", owner, "repos", query=q)
url = conn.makeurl("user", "repos", query=q)
obj_list = []
for response in conn.request_all_pages("GET", url):
obj_list.extend([cls(i, response=response) for i in response.json()])