Skip to content

Commit

Permalink
v0.3.0 - fixes git & closes #33
Browse files Browse the repository at this point in the history
  • Loading branch information
Justintime50 committed Oct 22, 2020
1 parent ee18571 commit 180c162
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 47 deletions.
Binary file removed .coverage
Binary file not shown.
4 changes: 4 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[report]
exclude_lines =
if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ logs
*.egg-info
venv
htmlcov
.coverage
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ python:
- '3.6'
- '3.7'
- '3.8'
- '3.9'

install: pip install -e ."[dev]"

script:
- flake8 harvey/*.py
- flake8 test/*.py
- flake8 examples/*.py
- pytest --cov=harvey
- pytest --cov=harvey --cov-branch

deploy:
provider: pypi
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## v0.3.0 (2020-10-22)

* Refactored git logic into smaller units
* Added unit tests for `git` modules
* Changed `fast forward` git operation to `rebase`

## v0.2.0 (2020-09-18)

* Added testing framework and code coverage
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ lint:
test:
venv/bin/pytest

.PHONY: help install clean lint test
## coverage - Test the project and generate an HTML coverage report
coverage:
venv/bin/pytest --cov=harvey --cov-branch --cov-report=html

.PHONY: help install clean lint test coverage
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Because of the way Harvey was built with Docker (using sockets) this project tha

```bash
# Install Harvey
# TODO: Not yet available via PyPi
pip3 install harvey-ci

# Install locally
make install
Expand Down Expand Up @@ -147,6 +147,9 @@ make lint

# Run tests
make test

# Run test coverage
make coverage
```

## Resources
Expand Down
7 changes: 5 additions & 2 deletions harvey/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,13 @@ def retrieve_pipelines():
# def pull_project():
# """Pull/clone GitHub project"""
# data = json.loads(request.data)
# pull = harvey.Git.pull(data)
# pull = harvey.Git.pull_repo(data)
# response = str(pull)
# return response

def main():
API.run(host=HOST, port=PORT, debug=DEBUG)


if __name__ == '__main__':
API.run(host=HOST, port=PORT, debug=DEBUG)
main()
90 changes: 50 additions & 40 deletions harvey/git.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,63 @@
import os
import subprocess
from .globals import Global
from .utils import Utils
from harvey.globals import Global
from harvey.utils import Utils


class Git():
@classmethod
def pull(cls, webhook):
"""Clone/pull project using Git
def update_git_repo(cls, webhook):
"""Clone or pull repo using Git depending on if it exists or not
"""
project_path = os.path.join(
Global.PROJECTS_PATH, Global.repo_full_name(webhook)
)
if os.path.exists(project_path):
try:
final_output = subprocess.check_output(
f'git - C {project_path} pull - -ff-only origin master',
stdin=None,
stderr=None,
shell=True,
timeout=Global.GIT_TIMEOUT
)
print(final_output)
except subprocess.TimeoutExpired:
final_output = 'Error: Harvey timed out during git pull operation.' # noqa
print(final_output)
Utils.kill(final_output, webhook)
except subprocess.CalledProcessError:
final_output = f'\nError: Harvey could not pull {Global.repo_full_name(webhook)}.'
print(final_output)
Utils.kill(final_output, webhook)
output = cls.pull_repo(project_path, webhook)
else:
try:
final_output = subprocess.check_output(
f'git clone --depth=10 --branch=master {Global.repo_url(webhook)} \
{project_path}',
stdin=None,
stderr=None,
shell=True,
timeout=Global.GIT_TIMEOUT
)
print(final_output)
except subprocess.TimeoutExpired:
final_output = 'Error: Harvey timed out during git clone operation.'
print(final_output)
Utils.kill(final_output, webhook)
except subprocess.CalledProcessError:
final_output = f'\nError: Harvey could not clone {Global.repo_full_name(webhook)}.'
print(final_output)
Utils.kill(final_output, webhook)
output = cls.clone_repo(project_path, webhook)
return output.decode('UTF-8')

return final_output.decode('UTF-8')
@classmethod
def clone_repo(cls, project_path, webhook):
"""Clone a repo into the Harvey projects folder
"""
try:
final_output = subprocess.check_output(
f'git -C {project_path} pull --rebase origin master',
stdin=None,
stderr=None,
shell=True,
timeout=Global.GIT_TIMEOUT
)
print(final_output)
except subprocess.TimeoutExpired:
final_output = 'Error: Harvey timed out during git pull operation.'
print(final_output)
Utils.kill(final_output, webhook)
except subprocess.CalledProcessError:
final_output = f'\nError: Harvey could not pull {Global.repo_full_name(webhook)}.'
print(final_output)
Utils.kill(final_output, webhook)

@classmethod
def pull_repo(cls, project_path, webhook):
"""Pull updates for a repo in the Harvey projects folder
"""
try:
final_output = subprocess.check_output(
f'git clone --depth=10 --branch=master {Global.repo_url(webhook)} {project_path}',
stdin=None,
stderr=None,
shell=True,
timeout=Global.GIT_TIMEOUT
)
print(final_output)
except subprocess.TimeoutExpired:
final_output = 'Error: Harvey timed out during git clone operation.'
print(final_output)
Utils.kill(final_output, webhook)
except subprocess.CalledProcessError:
final_output = f'\nError: Harvey could not clone {Global.repo_full_name(webhook)}.'
print(final_output)
Utils.kill(final_output, webhook)
2 changes: 1 addition & 1 deletion harvey/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def init(cls, webhook):
print(preamble)
git_message = (f'New commit by: {Global.repo_commit_author(webhook)}. \
\nCommit made on repo: {Global.repo_full_name(webhook)}.')
git = Git.pull(webhook)
git = Git.update_git_repo(webhook)

# Open the project's config file to assign pipeline variables
try:
Expand Down
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

setuptools.setup(
name='harvey-ci',
version='0.2.0',
version='0.3.0',
description='Your personal CI/CD and Docker orchestration platform.',
long_description=long_description,
long_description_content_type="text/markdown",
Expand All @@ -36,5 +36,10 @@
'mock >= 4.0.0',
]
},
entry_points={
'console_scripts': [
'harvey-ci=harvey.app:main'
]
},
python_requires='>=3.6',
)
89 changes: 89 additions & 0 deletions test/unit/test_git.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pytest
import mock
import subprocess
from harvey.git import Git


@pytest.fixture
def _mock_webhook():
webhook = {
"repository": {
"name": "TEST-repo-name",
"full_name": "TEST_user/TEST-repo-name",
"ssh_url": "https://test-url.com",
"owner": {
"name": "TEST_owner"
}
},
"commits": [
{
"id": 123456,
"author": {
"name": "test_user"
}
}
]
}
return webhook


@pytest.fixture
def _mock_project_path():
path = 'projects/test_user/test-repo-name'
return path


@mock.patch('os.path.exists', return_value=True)
@mock.patch('harvey.git.Git.pull_repo')
def test_update_git_repo_path_exists(mock_pull_repo, mock_path_exists, _mock_project_path, _mock_webhook): # noqa
Git.update_git_repo(_mock_webhook)
mock_pull_repo.assert_called_once_with(_mock_project_path, _mock_webhook)


@mock.patch('os.path.exists', return_value=False)
@mock.patch('harvey.git.Git.clone_repo')
def test_update_git_repo_path_does_not_exist(mock_clone_repo, mock_path_exists, _mock_project_path, _mock_webhook): # noqa
Git.update_git_repo(_mock_webhook)
mock_clone_repo.assert_called_once_with(_mock_project_path, _mock_webhook)


@mock.patch('subprocess.check_output')
def test_clone_repo(mock_subprocess, _mock_project_path, _mock_webhook):
# TODO: Mock the subprocess better to ensure it does what it's supposed to
Git.clone_repo(_mock_project_path, _mock_webhook)
mock_subprocess.assert_called_once()


@mock.patch('harvey.utils.Utils.kill')
@mock.patch('subprocess.check_output', side_effect=subprocess.TimeoutExpired(cmd=subprocess.check_output, timeout=0.1)) # noqa
def test_clone_repo_subprocess_timeout(mock_subprocess, mock_utils_kill, _mock_project_path, _mock_webhook): # noqa
Git.clone_repo(_mock_project_path, _mock_webhook)
mock_utils_kill.assert_called_once()


@mock.patch('harvey.utils.Utils.kill')
@mock.patch('subprocess.check_output', side_effect=subprocess.CalledProcessError(returncode=1, cmd=subprocess.check_output)) # noqa
def test_clone_repo_process_error(mock_subprocess, mock_utils_kill, _mock_project_path, _mock_webhook): # noqa
Git.clone_repo(_mock_project_path, _mock_webhook)
mock_utils_kill.assert_called_once()


@mock.patch('subprocess.check_output')
def test_pull_repo(mock_subprocess, _mock_project_path, _mock_webhook):
# TODO: Mock the subprocess better to ensure it does what it's supposed to
Git.pull_repo(_mock_project_path, _mock_webhook)
mock_subprocess.assert_called_once()


@mock.patch('harvey.utils.Utils.kill')
@mock.patch('subprocess.check_output', side_effect=subprocess.TimeoutExpired(cmd=subprocess.check_output, timeout=0.1)) # noqa
def test_pull_repo_subprocess_timeout(mock_subprocess, mock_utils_kill, _mock_project_path, _mock_webhook): # noqa
Git.pull_repo(_mock_project_path, _mock_webhook)
mock_utils_kill.assert_called_once()


@mock.patch('harvey.utils.Utils.kill')
@mock.patch('subprocess.check_output', side_effect=subprocess.CalledProcessError(returncode=1, cmd=subprocess.check_output)) # noqa
def test_pull_repo_process_error(mock_subprocess, mock_utils_kill, _mock_project_path, _mock_webhook): # noqa
Git.pull_repo(_mock_project_path, _mock_webhook)
mock_utils_kill.assert_called_once()

0 comments on commit 180c162

Please sign in to comment.