-
Notifications
You must be signed in to change notification settings - Fork 189
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
795 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
Feature: `git-obs pr` command | ||
|
||
|
||
Background: | ||
Given I set working directory to "{context.osc.temp}" | ||
And I execute git-obs with args "repo fork pool test-GitPkgA" | ||
And I execute git-obs with args "repo clone Admin test-GitPkgA --no-ssh-strict-host-key-checking" | ||
And I set working directory to "{context.osc.temp}/test-GitPkgA" | ||
And I execute "sed -i 's@^\(Version.*\)@\1.1@' *.spec" | ||
And I execute "git commit -m 'Change version' -a" | ||
And I execute "git push" | ||
And I execute git-obs with args "pr create --title 'Change version' --description='some text'" | ||
|
||
|
||
@destructive | ||
Scenario: List pull requests | ||
When I execute git-obs with args "pr list pool test-GitPkgA" | ||
Then the exit code is 0 | ||
And stdout matches | ||
""" | ||
ID : pool/test-GitPkgA#1 | ||
URL : http://localhost:{context.podman.container.ports[gitea_http]}/pool/test-GitPkgA/pulls/1 | ||
Title : Change version | ||
State : open | ||
Draft : no | ||
Merged : no | ||
Author : Admin \(admin@example.com\) | ||
Source : Admin/test-GitPkgA, branch: factory, commit: .* | ||
Description : some text | ||
""" | ||
And stderr is | ||
""" | ||
Using the following Gitea settings: | ||
* Config path: {context.git_obs.config} | ||
* Login (name of the entry in the config file): admin | ||
* URL: http://localhost:{context.podman.container.ports[gitea_http]} | ||
* User: Admin | ||
Total entries: 1 | ||
""" | ||
|
||
|
||
@destructive | ||
Scenario: Search pull requests | ||
When I execute git-obs with args "pr search" | ||
Then the exit code is 0 | ||
And stdout matches | ||
""" | ||
ID : pool/test-GitPkgA#1 | ||
URL : http://localhost:{context.podman.container.ports[gitea_http]}/pool/test-GitPkgA/pulls/1 | ||
Title : Change version | ||
State : open | ||
Author : Admin \(admin@example.com\) | ||
Description : some text | ||
""" | ||
And stderr is | ||
""" | ||
Using the following Gitea settings: | ||
* Config path: {context.git_obs.config} | ||
* Login (name of the entry in the config file): admin | ||
* URL: http://localhost:{context.podman.container.ports[gitea_http]} | ||
* User: Admin | ||
Total entries: 1 | ||
""" | ||
|
||
|
||
@destructive | ||
Scenario: Get a pull request | ||
When I execute git-obs with args "pr get pool/test-GitPkgA#1" | ||
Then the exit code is 0 | ||
And stdout matches | ||
""" | ||
ID : pool/test-GitPkgA#1 | ||
URL : http://localhost:{context.podman.container.ports[gitea_http]}/pool/test-GitPkgA/pulls/1 | ||
Title : Change version | ||
State : open | ||
Draft : no | ||
Merged : no | ||
Author : Admin \(admin@example.com\) | ||
Source : Admin/test-GitPkgA, branch: factory, commit: .* | ||
Description : some text | ||
""" | ||
And stderr is | ||
""" | ||
Using the following Gitea settings: | ||
* Config path: {context.git_obs.config} | ||
* Login (name of the entry in the config file): admin | ||
* URL: http://localhost:{context.podman.container.ports[gitea_http]} | ||
* User: Admin | ||
Total entries: 1 | ||
""" | ||
|
||
|
||
@destructive | ||
Scenario: Get a pull request that doesn't exist | ||
When I execute git-obs with args "pr get does-not/exist#1" | ||
Then the exit code is 1 | ||
And stdout matches | ||
""" | ||
""" | ||
And stderr is | ||
""" | ||
Using the following Gitea settings: | ||
* Config path: {context.git_obs.config} | ||
* Login (name of the entry in the config file): admin | ||
* URL: http://localhost:{context.podman.container.ports[gitea_http]} | ||
* User: Admin | ||
Total entries: 0 | ||
ERROR: Couldn't retrieve the following pull requests: does-not/exist#1 | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import osc.commandline_git | ||
|
||
|
||
# we decided not to use the command name 'pull' because that could be confused | ||
# with the completely unrelated 'git pull' command | ||
|
||
|
||
class PullRequestCommand(osc.commandline_git.GitObsCommand): | ||
""" | ||
Manage pull requests | ||
""" | ||
|
||
name = "pr" | ||
|
||
def init_arguments(self): | ||
pass | ||
|
||
def run(self, args): | ||
self.parser.print_help() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
import os | ||
import re | ||
import subprocess | ||
import sys | ||
from typing import Optional | ||
|
||
import osc.commandline_git | ||
|
||
|
||
def get_editor() -> str: | ||
import shutil | ||
|
||
editor = os.getenv("EDITOR", None) | ||
if editor: | ||
candidates = [editor] | ||
else: | ||
candidates = ["vim", "vi"] | ||
|
||
editor_path = None | ||
for i in candidates: | ||
editor_path = shutil.which(i) | ||
if editor_path: | ||
break | ||
|
||
if not editor_path: | ||
raise RuntimeError(f"Unable to start editor '{candidates[0]}'") | ||
|
||
return editor_path | ||
|
||
|
||
def run_editor(file_path: str): | ||
cmd = [get_editor(), file_path] | ||
subprocess.run(cmd) | ||
|
||
|
||
def edit_message(template: Optional[str] = None) -> str: | ||
import tempfile | ||
|
||
with tempfile.NamedTemporaryFile(mode="w+", encoding="utf-8", prefix="git_obs_message_") as f: | ||
if template: | ||
f.write(template) | ||
f.flush() | ||
|
||
run_editor(f.name) | ||
|
||
f.seek(0) | ||
return f.read() | ||
|
||
|
||
NEW_PULL_REQUEST_TEMPLATE = """ | ||
# title | ||
{title} | ||
# description | ||
{description} | ||
# | ||
# Please enter pull request title and description in the following format: | ||
# <title> | ||
# <blank line> | ||
# <description> | ||
# | ||
# Lines starting with '#' will be ignored, and an empty message aborts the operation. | ||
# | ||
# Creating {source_owner}/{source_repo}#{source_branch} -> {target_owner}/{target_repo}#{target_branch} | ||
# | ||
""".lstrip() | ||
|
||
|
||
class PullRequestCreateCommand(osc.commandline_git.GitObsCommand): | ||
""" | ||
Create a pull request | ||
""" | ||
|
||
name = "create" | ||
parent = "PullRequestCommand" | ||
|
||
def init_arguments(self): | ||
self.add_argument( | ||
"--title", | ||
metavar="TEXT", | ||
help="Pull request title", | ||
) | ||
self.add_argument( | ||
"--description", | ||
metavar="TEXT", | ||
help="Pull request description (body)", | ||
) | ||
self.add_argument( | ||
"--source-owner", | ||
metavar="OWNER", | ||
help="Owner of the source repo (default: derived from remote URL in local git repo)", | ||
) | ||
self.add_argument( | ||
"--source-repo", | ||
metavar="REPO", | ||
help="Name of the source repo (default: derived from remote URL in local git repo)", | ||
) | ||
self.add_argument( | ||
"--source-branch", | ||
metavar="BRANCH", | ||
help="Source branch (default: the current branch in local git repo)", | ||
) | ||
self.add_argument( | ||
"--target-branch", | ||
metavar="BRANCH", | ||
help="Target branch (default: derived from the current branch in local git repo)", | ||
) | ||
|
||
def run(self, args): | ||
from osc import gitea_api | ||
|
||
# the source args are optional, but if one of them is used, the others must be used too | ||
source_args = (args.source_owner, args.source_repo, args.source_branch) | ||
if sum((int(i is not None) for i in source_args)) not in (0, len(source_args)): | ||
self.parser.error("All of the following options must be used together: --source-owner, --source-repo, --source-branch") | ||
|
||
self.print_gitea_settings() | ||
|
||
use_local_git = args.source_owner is None | ||
|
||
if use_local_git: | ||
# local git repo | ||
git = gitea_api.Git(".") | ||
local_owner, local_repo = git.get_owner_repo() | ||
local_branch = git.current_branch | ||
local_rev = git.get_branch_head(local_branch) | ||
|
||
# remote git repo - source | ||
if use_local_git: | ||
source_owner = local_owner | ||
source_repo = local_repo | ||
source_branch = local_branch | ||
else: | ||
source_owner = args.source_owner | ||
source_repo = args.source_repo | ||
source_branch = args.source_branch | ||
source_repo_data = gitea_api.Repo.get(self.gitea_conn, source_owner, source_repo).json() | ||
source_branch_data = gitea_api.Branch.get(self.gitea_conn, source_owner, source_repo, source_branch).json() | ||
source_rev = source_branch_data["commit"]["id"] | ||
|
||
# remote git repo - target | ||
target_owner, target_repo = source_repo_data["parent"]["full_name"].split("/") | ||
|
||
if source_branch.startswith("for/"): | ||
# source branch name format: for/<target-branch>/<what-the-branch-name-would-normally-be> | ||
target_branch = source_branch.split("/")[1] | ||
else: | ||
target_branch = source_branch | ||
|
||
target_branch_data = gitea_api.Branch.get(self.gitea_conn, target_owner, target_repo, target_branch).json() | ||
target_rev = target_branch_data["commit"]["id"] | ||
|
||
print("Creating a pull request ...", file=sys.stderr) | ||
if use_local_git: | ||
print(f" * Local git: branch: {local_branch}, rev: {local_rev}", file=sys.stderr) | ||
print(f" * Source: {source_owner}/{source_repo}, branch: {source_branch}, rev: {source_rev}", file=sys.stderr) | ||
print(f" * Target: {target_owner}/{target_repo}, branch: {target_branch}, rev: {target_rev}", file=sys.stderr) | ||
|
||
if use_local_git and local_rev != source_rev: | ||
from osc.output import tty | ||
print(f"{tty.colorize('ERROR', 'red,bold')}: Local commit doesn't correspond with the latest commit in the remote source branch") | ||
sys.exit(1) | ||
|
||
if source_rev == target_rev: | ||
from osc.output import tty | ||
print(f"{tty.colorize('ERROR', 'red,bold')}: Source and target are identical, make and push changes to the remote source repo first") | ||
sys.exit(1) | ||
|
||
title = args.title or "" | ||
description = args.description or "" | ||
|
||
if not title or not description: | ||
# TODO: add list of commits and list of changed files to the template; requires local git repo | ||
message = edit_message(template=NEW_PULL_REQUEST_TEMPLATE.format(**locals())) | ||
|
||
# remove comments | ||
message = "\n".join([i for i in message.splitlines() if not i.startswith("#")]) | ||
|
||
# strip leading and trailing spaces | ||
message = message.strip() | ||
|
||
if not message: | ||
raise RuntimeError("Aborting operation due to empty title and description.") | ||
|
||
parts = re.split(r"\n\n", message, 1) | ||
if len(parts) == 1: | ||
# empty description | ||
title = parts[0] | ||
description = "" | ||
else: | ||
title = parts[0] | ||
description = parts[1] | ||
|
||
title = title.strip() | ||
description = description.strip() | ||
|
||
pull = gitea_api.PullRequest.create( | ||
self.gitea_conn, | ||
target_owner=target_owner, | ||
target_repo=target_repo, | ||
target_branch=target_branch, | ||
source_owner=source_owner, | ||
# source_repo is not required because the information lives in Gitea database | ||
source_branch=source_branch, | ||
title=title, | ||
description=description, | ||
).json() | ||
|
||
print("", file=sys.stderr) | ||
print("Pull request created:", file=sys.stderr) | ||
print(gitea_api.PullRequest.to_human_readable_string(pull)) |
Oops, something went wrong.