Skip to content

Commit

Permalink
Merge pull request #29 from jpower432/PSCE-192
Browse files Browse the repository at this point in the history
PSCE-192 - feat: adds support for GitLab as a Provider type
  • Loading branch information
Alex Flom committed Aug 3, 2023
2 parents c9d2b58 + 07cc533 commit 54ee49a
Show file tree
Hide file tree
Showing 9 changed files with 383 additions and 41 deletions.
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[flake8]
max-line-length=105
max-line-length=105
exclude=.venv*
50 changes: 49 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ python = '^3.8.1'
gitpython = "^3.1.31"
compliance-trestle = "^2.2.1"
github3-py = "^4.0.1"
python-gitlab = "^3.15.0"

[tool.poetry.group.dev.dependencies]
flake8 = "^6.0.0"
Expand All @@ -31,6 +32,7 @@ mypy = "^1.3.0"
isort = "^5.12.0"
safety = "^2.3.5"
bandit = "^1.7.5"
flake8-print = "^5.0.0"

[tool.poetry.group.tests.dependencies]
pytest = "^7.3.2"
Expand Down
9 changes: 6 additions & 3 deletions tests/trestlebot/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def test_with_target_branch(monkeypatch, valid_args_dict, capsys):
args_dict["target-branch"] = "main"
monkeypatch.setattr(sys, "argv", ["trestlebot", *args_dict_to_list(args_dict)])

# Patch is_github_actions since these tests will be running in
# GitHub Actions
with patch("trestlebot.cli.is_github_actions") as mock_check:
mock_check.return_value = False

Expand All @@ -107,9 +109,10 @@ def test_with_target_branch(monkeypatch, valid_args_dict, capsys):
captured = capsys.readouterr()

expected_string = (
"target-branch flag is set with an unsupported git provider. "
"If testing locally with the GitHub API, "
"set the GITHUB_ACTIONS environment variable to true."
"target-branch flag is set with an unset git provider. "
"To test locally, set the GITHUB_ACTIONS or GITLAB_CI environment variable."
)

assert expected_string in captured.err

mock_check.assert_called_once()
17 changes: 17 additions & 0 deletions tests/trestlebot/test_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"""Test for GitHub provider logic"""

from typing import Tuple
from unittest.mock import patch

import pytest
from git.repo import Repo
Expand Down Expand Up @@ -70,3 +71,19 @@ def test_parse_repository_with_incorrect_name() -> None:
match="https://notgithub.com/owner/repo.git is an invalid GitHub repo URL",
):
gh.parse_repository("https://notgithub.com/owner/repo.git")


def test_create_pull_request_invalid_repo() -> None:
"""Test triggering an error during pull request creation"""
gh = GitHub("fake")
with patch("github3.GitHub.repository") as mock_pull:
mock_pull.return_value = None

with pytest.raises(
GitProviderException,
match="Repository for owner/repo cannot be None",
):
gh.create_pull_request(
"owner", "repo", "main", "test", "My PR", "Has Changes"
)
mock_pull.assert_called_once()
129 changes: 129 additions & 0 deletions tests/trestlebot/test_gitlab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/python

# Copyright 2023 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""Test for GitLab provider logic"""

from typing import Callable, Tuple
from unittest.mock import patch

import pytest
from git.repo import Repo
from gitlab.exceptions import GitlabAuthenticationError, GitlabCreateError

from tests.testutils import clean
from trestlebot.gitlab import GitLab
from trestlebot.provider import GitProviderException


@pytest.mark.parametrize(
"repo_url",
[
"https://gitlab.com/owner/repo",
"https://gitlab.com/owner/repo.git",
"gitlab.com/owner/repo.git",
],
)
def test_parse_repository(repo_url: str) -> None:
"""Tests parsing valid GitLab repo urls"""
gl = GitLab("fake")

owner, repo_name = gl.parse_repository(repo_url)

assert owner == "owner"
assert repo_name == "repo"


@pytest.mark.parametrize(
"repo_url",
[
"https://mygitlab.com/owner/repo",
"https://mygitlab.com/owner/repo.git",
"mygitlab.com/owner/repo.git",
],
)
def test_parse_repository_with_server_url(repo_url: str) -> None:
"""Test an invalid url input"""
gl = GitLab("fake", "https://mygitlab.com")

owner, repo_name = gl.parse_repository(repo_url)

assert owner == "owner"
assert repo_name == "repo"


def test_parse_repository_integration(tmp_repo: Tuple[str, Repo]) -> None:
"""Tests integration with git remote get-url"""
repo_path, repo = tmp_repo

repo.create_remote("origin", url="gitlab.com/test/repo.git")

remote = repo.remote()

gl = GitLab("fake")

owner, repo_name = gl.parse_repository(remote.url)

assert owner == "test"
assert repo_name == "repo"

clean(repo_path, repo)


def test_parse_repository_with_incorrect_name() -> None:
"""Test an invalid url input"""
gl = GitLab("fake")
with pytest.raises(
GitProviderException,
match="https://notgitlab.com/owner/repo.git is an invalid repo URL",
):
gl.parse_repository("https://notgitlab.com/owner/repo.git")


def create_side_effect(name: str) -> None:
raise GitlabCreateError("example")


def auth_side_effect(name: str) -> None:
raise GitlabAuthenticationError("example")


@pytest.mark.parametrize(
"side_effect, msg",
[
(create_side_effect, "Failed to create merge request in .*: example"),
(
auth_side_effect,
"Authentication error during merge request creation in .*: example",
),
],
)
def test_create_pull_request_with_exceptions(
side_effect: Callable[[str], None], msg: str
) -> None:
"""Test triggering an error during pull request creation"""
gl = GitLab("fake")

with patch("gitlab.v4.objects.ProjectManager.get") as mock_get:
mock_get.side_effect = side_effect

with pytest.raises(
GitProviderException,
match=msg,
):
gl.create_pull_request(
"owner", "repo", "main", "test", "My PR", "Has Changes"
)
mock_get.assert_called_once()
39 changes: 18 additions & 21 deletions trestlebot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@

import argparse
import logging
import os
import sys
from typing import List, Optional

from trestlebot import bot, const, log
from trestlebot.github import GitHub
from trestlebot.github import GitHub, is_github_actions
from trestlebot.gitlab import GitLab, get_gitlab_root_url, is_gitlab_ci
from trestlebot.provider import GitProvider
from trestlebot.tasks.assemble_task import AssembleTask
from trestlebot.tasks.authored import types
Expand Down Expand Up @@ -229,19 +229,25 @@ def run() -> None:
logger.info("Regeneration task skipped")

if args.target_branch:
if not is_github_actions():
logger.error(
"target-branch flag is set with an unsupported git provider. "
"If testing locally with the GitHub API, set "
"the GITHUB_ACTIONS environment variable to true."
)
sys.exit(const.ERROR_EXIT_CODE)

if not args.with_token:
logger.error("with-token value cannot be empty")
sys.exit(const.ERROR_EXIT_CODE)

git_provider = GitHub(access_token=args.with_token.read().strip())
if is_github_actions():
git_provider = GitHub(access_token=args.with_token.read().strip())
elif is_gitlab_ci():
server_api_url = get_gitlab_root_url()
git_provider = GitLab(
api_token=args.with_token.read().strip(), server_url=server_api_url
)
else:
logger.error(
(
"target-branch flag is set with an unset git provider. "
"To test locally, set the GITHUB_ACTIONS or GITLAB_CI environment variable."
)
)
sys.exit(const.ERROR_EXIT_CODE)

exit_code: int = const.SUCCESS_EXIT_CODE

Expand All @@ -266,7 +272,7 @@ def run() -> None:

# Print the full commit sha
if commit_sha:
print(f"Commit Hash: {commit_sha}")
print(f"Commit Hash: {commit_sha}") # noqa

except Exception as e:
exit_code = handle_exception(e)
Expand All @@ -278,12 +284,3 @@ def comma_sep_to_list(string: str) -> List[str]:
"""Convert comma-sep string to list of strings and strip."""
string = string.strip() if string else ""
return list(map(str.strip, string.split(","))) if string else []


# GitHub ref:
# https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
def is_github_actions() -> bool:
var_value = os.getenv("GITHUB_ACTIONS")
if var_value and var_value.lower() in ["true", "1"]:
return True
return False
Loading

0 comments on commit 54ee49a

Please sign in to comment.