diff --git a/.pylintrc b/.pylintrc index 8102c85d..b0ecd8da 100644 --- a/.pylintrc +++ b/.pylintrc @@ -3,9 +3,11 @@ disable = bad-continuation, bad-whitespace, + consider-using-sys-exit, cyclic-import, duplicate-code, global-statement, + import-outside-toplevel, missing-docstring, protected-access, unused-argument diff --git a/README.md b/README.md index d87bc374..8a67e4bf 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Chroma Feedback [![Build Status Travis](https://img.shields.io/travis/redaxmedia/chroma-feedback.svg)](https://travis-ci.org/redaxmedia/chroma-feedback) [![Build Status AppVeyor](https://img.shields.io/appveyor/ci/redaxmedia/chroma-feedback.svg)](https://ci.appveyor.com/project/redaxmedia/chroma-feedback) [![Build Status Circle](https://img.shields.io/circleci/project/github/redaxmedia/chroma-feedback.svg)](https://circleci.com/gh/redaxmedia/chroma-feedback) +[![Build Status Codeship](https://img.shields.io/codeship/chroma-feedback.svg)](https://app.codeship.com/projects/372431) [![Coverage Status](https://img.shields.io/coveralls/redaxmedia/chroma-feedback.svg)](https://coveralls.io/r/redaxmedia/chroma-feedback) [![PyPI](https://img.shields.io/pypi/v/chroma-feedback.svg)](https://pypi.org/project/chroma-feedback) [![License](https://img.shields.io/pypi/l/chroma-feedback.svg)](https://pypi.org/project/chroma-feedback) @@ -62,107 +63,6 @@ chroma-feedback [options] ``` -Consumers -========= - - -Lifx Light ----------- - -| Name | Mandatory | -|----------|-----------| -| Light | optional | -| Group | optional | - -Indicate status via lights: - -``` -chroma-feedback --consumer=lifx_light - ---lifx-light-light -``` - -Indicate status via groups: - -``` -chroma-feedback --consumer=lifx_light - ---lifx-light-group -``` - - -Philips Hue ------------ - -| Name | Mandatory | -|----------|-----------| -| IP | optional | -| Light | optional | -| Group | optional | - -Indicate status via lights: - -``` -chroma-feedback --consumer=philips_hue - ---philips-hue-light -``` - -Indicate status via groups: - -``` -chroma-feedback --consumer=philips_hue - ---philips-hue-group -``` - - -Razer Chroma ------------- - -| Name | Mandatory | -|--------|-----------| -| Device | optional | - -Indicate status via devices: - -``` -chroma-feedback --consumer=razer_chroma - ---razer-chrome-device -``` - - -ThingM Blink ------------- - -| Name | Mandatory | -|--------|-----------| -| Device | optional | - -Indicate status via devices: - -``` -chroma-feedback --consumer=thingm_blink - ---thingm-blink-device -``` - - -Xiaomi Yeelight ---------------- - -| Name | Mandatory | -|----------|-----------| -| IP | optional | - -Indicate status via lights: - -``` -chroma-feedback --consumer=xiaomi_yeelight -``` - - Providers ========= @@ -219,6 +119,41 @@ chroma-feedback --provider=circle ``` +Codeship +-------- + +| Name | Default | Mandatory | +|--------------|--------------------------|-----------| +| Host | https://api.codeship.com | optional | +| Organization | | required | +| Project | | required | +| Username | | required | +| Password | | required | + +Monitor a single project: + +``` +chroma-feedback --provider=codeship + +--codeship-oranization +--codeship-project +--codeship-username +--codeship-password +``` + +Monitor multiple projects: + +``` +chroma-feedback --provider=codeship + +--codeship-oranization +--codeship-project +--codeship-project +--codeship-username +--codeship-password +``` + + GitHub ------ @@ -361,3 +296,104 @@ chroma-feedback --provider=travis --travis-slug ``` + + +Consumers +========= + + +Lifx Light +---------- + +| Name | Mandatory | +|----------|-----------| +| Light | optional | +| Group | optional | + +Indicate status via lights: + +``` +chroma-feedback --consumer=lifx_light + +--lifx-light-light +``` + +Indicate status via groups: + +``` +chroma-feedback --consumer=lifx_light + +--lifx-light-group +``` + + +Philips Hue +----------- + +| Name | Mandatory | +|----------|-----------| +| IP | optional | +| Light | optional | +| Group | optional | + +Indicate status via lights: + +``` +chroma-feedback --consumer=philips_hue + +--philips-hue-light +``` + +Indicate status via groups: + +``` +chroma-feedback --consumer=philips_hue + +--philips-hue-group +``` + + +Razer Chroma +------------ + +| Name | Mandatory | +|--------|-----------| +| Device | optional | + +Indicate status via devices: + +``` +chroma-feedback --consumer=razer_chroma + +--razer-chrome-device +``` + + +ThingM Blink +------------ + +| Name | Mandatory | +|--------|-----------| +| Device | optional | + +Indicate status via devices: + +``` +chroma-feedback --consumer=thingm_blink + +--thingm-blink-device +``` + + +Xiaomi Yeelight +--------------- + +| Name | Mandatory | +|----------|-----------| +| IP | optional | + +Indicate status via lights: + +``` +chroma-feedback --consumer=xiaomi_yeelight +``` diff --git a/chroma_feedback/metadata.py b/chroma_feedback/metadata.py index c8c7ba6e..52079f67 100644 --- a/chroma_feedback/metadata.py +++ b/chroma_feedback/metadata.py @@ -2,7 +2,7 @@ { 'name': 'chroma-feedback', 'description': 'Turn your RGB powered hardware into an extreme feedback device for continuous integration', - 'version': '5.0.1', + 'version': '5.1.0', 'license': 'GPL-3.0', 'keywords': 'appveyor circle github gitlab jenkins teamcity travis ci notification indication', 'author': 'Henry Ruhs', diff --git a/chroma_feedback/provider/__init__.py b/chroma_feedback/provider/__init__.py index f30bf2b4..e4ee4ede 100644 --- a/chroma_feedback/provider/__init__.py +++ b/chroma_feedback/provider/__init__.py @@ -4,6 +4,7 @@ [ 'appveyor', 'circle', + 'codeship', 'github', 'gitlab', 'jenkins', diff --git a/chroma_feedback/provider/codeship/__init__.py b/chroma_feedback/provider/codeship/__init__.py new file mode 100644 index 00000000..a24bdfe6 --- /dev/null +++ b/chroma_feedback/provider/codeship/__init__.py @@ -0,0 +1 @@ +from .core import init, run diff --git a/chroma_feedback/provider/codeship/core.py b/chroma_feedback/provider/codeship/core.py new file mode 100644 index 00000000..d12f639e --- /dev/null +++ b/chroma_feedback/provider/codeship/core.py @@ -0,0 +1,67 @@ +import base64 +import requests +from chroma_feedback import helper +from .normalize import normalize_data + +ARGS = None + + +def init(program): + global ARGS + + if not ARGS: + program.add_argument('--codeship-host', default = 'https://api.codeship.com') + program.add_argument('--codeship-organization', required = True) + program.add_argument('--codeship-project', action = 'append', required = True) + program.add_argument('--codeship-username', required = True) + program.add_argument('--codeship-password', required = True) + ARGS = program.parse_known_args()[0] + + +def run(): + result = [] + token = fetch_token(ARGS.codeship_host, ARGS.codeship_username, ARGS.codeship_password) + + if token: + for project in ARGS.codeship_project: + result.extend(fetch(ARGS.codeship_host, ARGS.codeship_organization, project, token)) + return result + + +def fetch(host, organization, project, token): + response = None + + if host and organization and project and token: + response = requests.get(host + '/v2/organizations/' + organization + '/projects/' + project + '/builds', headers = + { + 'Authorization': 'Bearer ' + token + }) + + # process response + + if response and response.status_code == 200: + data = helper.parse_json(response) + + if 'builds' in data: + return normalize_data(data['builds'][0]) + return [] + + +def fetch_token(host, username, password): + response = None + + if host and username and password: + username_token = username + ':' + password + response = requests.post(host + '/v2/auth', headers = + { + 'Authorization': 'Basic ' + base64.b64encode(username_token.encode('utf-8')).decode('ascii') + }) + + # process response + + if response and response.status_code == 200: + data = helper.parse_json(response) + + if data['access_token']: + return data['access_token'] + return None diff --git a/chroma_feedback/provider/codeship/normalize.py b/chroma_feedback/provider/codeship/normalize.py new file mode 100644 index 00000000..1d952175 --- /dev/null +++ b/chroma_feedback/provider/codeship/normalize.py @@ -0,0 +1,20 @@ +def normalize_data(build): + return\ + [ + { + 'provider': 'codeship', + 'slug': build['project_id'], + 'active': True, + 'status': normalize_status(build['status']) + } + ] + + +def normalize_status(status): + if status in ['initiated', 'waiting']: + return 'process' + if status in ['error', 'blocked', 'ignored']: + return 'errored' + if status in ['failed', 'infrastructure_failure']: + return 'failed' + return 'passed' diff --git a/setup.py b/setup.py index 5b20d81c..42a22b29 100755 --- a/setup.py +++ b/setup.py @@ -25,6 +25,7 @@ 'chroma_feedback.provider', 'chroma_feedback.provider.appveyor', 'chroma_feedback.provider.circle', + 'chroma_feedback.provider.codeship', 'chroma_feedback.provider.github', 'chroma_feedback.provider.gitlab', 'chroma_feedback.provider.jenkins', @@ -49,8 +50,9 @@ 'requests', 'yeelight' ], - tests_require= + tests_require = [ + 'coveralls', 'pylint', 'pytest', 'pytest-cov', diff --git a/tests/consumer/lifx_light/test_light.py b/tests/consumer/lifx_light/test_light.py index eaa88493..22db72f8 100644 --- a/tests/consumer/lifx_light/test_light.py +++ b/tests/consumer/lifx_light/test_light.py @@ -1,5 +1,8 @@ import pytest -from mock import MagicMock +try: + from unittest.mock import MagicMock +except ImportError: + from mock import MagicMock from chroma_feedback.consumer.lifx_light.light import process_lights MOCK = MagicMock() diff --git a/tests/consumer/philips_hue/test_light.py b/tests/consumer/philips_hue/test_light.py index dd6fe2d2..2d194241 100644 --- a/tests/consumer/philips_hue/test_light.py +++ b/tests/consumer/philips_hue/test_light.py @@ -1,5 +1,8 @@ import pytest -from mock import MagicMock +try: + from unittest.mock import MagicMock +except ImportError: + from mock import MagicMock from chroma_feedback.consumer.philips_hue.light import process_lights MOCK = MagicMock() diff --git a/tests/consumer/razer_chroma/test_device.py b/tests/consumer/razer_chroma/test_device.py index 42c010b4..de524aed 100644 --- a/tests/consumer/razer_chroma/test_device.py +++ b/tests/consumer/razer_chroma/test_device.py @@ -1,5 +1,8 @@ import pytest -from mock import MagicMock +try: + from unittest.mock import MagicMock +except ImportError: + from mock import MagicMock from chroma_feedback.consumer.razer_chroma.device import process_devices MOCK = MagicMock() diff --git a/tests/consumer/thingm_blink/test_device.py b/tests/consumer/thingm_blink/test_device.py index f00d5415..856cc38e 100644 --- a/tests/consumer/thingm_blink/test_device.py +++ b/tests/consumer/thingm_blink/test_device.py @@ -1,5 +1,8 @@ import pytest -from mock import MagicMock +try: + from unittest.mock import MagicMock +except ImportError: + from mock import MagicMock from chroma_feedback.consumer.thingm_blink.device import process_devices MOCK = MagicMock() diff --git a/tests/consumer/xiaomi_yeelight/test_light.py b/tests/consumer/xiaomi_yeelight/test_light.py index 398955dc..bca761b8 100644 --- a/tests/consumer/xiaomi_yeelight/test_light.py +++ b/tests/consumer/xiaomi_yeelight/test_light.py @@ -1,5 +1,8 @@ import pytest -from mock import MagicMock +try: + from unittest.mock import MagicMock +except ImportError: + from mock import MagicMock from chroma_feedback.consumer.xiaomi_yeelight.light import process_lights MOCK = MagicMock() diff --git a/tests/provider/appveyor/test_core.py b/tests/provider/appveyor/test_core.py index 9737ff7c..c22574ab 100755 --- a/tests/provider/appveyor/test_core.py +++ b/tests/provider/appveyor/test_core.py @@ -20,7 +20,7 @@ def test_fetch_user(): assert result[0]['active'] is True assert result[0]['status'] else: - pytest.skip('APPVEYOR_TOKEN not defined') + pytest.skip('APPVEYOR_TOKEN is not defined') def test_fetch_invalid(): diff --git a/tests/provider/circle/test_core.py b/tests/provider/circle/test_core.py index e0838ddd..e13292dc 100755 --- a/tests/provider/circle/test_core.py +++ b/tests/provider/circle/test_core.py @@ -20,7 +20,7 @@ def test_fetch_user(): assert result[0]['active'] is True assert result[0]['status'] else: - pytest.skip('CIRCLE_TOKEN not defined') + pytest.skip('CIRCLE_TOKEN is not defined') def test_fetch_invalid(): diff --git a/tests/provider/codeship/__init__.py b/tests/provider/codeship/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/provider/codeship/test_core.py b/tests/provider/codeship/test_core.py new file mode 100755 index 00000000..03e95c36 --- /dev/null +++ b/tests/provider/codeship/test_core.py @@ -0,0 +1,21 @@ +import os +import pytest +from chroma_feedback.provider.codeship.core import fetch, fetch_token + + +def test_fetch_project(): + if 'CODESHIP_USERNAME' in os.environ and 'CODESHIP_PASSWORD' in os.environ and 'CODESHIP_ORGANIZATION' in os.environ and 'CODESHIP_PROJECT' in os.environ: + token = fetch_token('https://api.codeship.com', os.environ['CODESHIP_USERNAME'], os.environ['CODESHIP_PASSWORD']) + result = fetch('https://api.codeship.com', os.environ['CODESHIP_ORGANIZATION'], os.environ['CODESHIP_PROJECT'], token) + + assert result[0]['provider'] == 'codeship' + assert result[0]['active'] is True + assert result[0]['status'] + else: + pytest.skip('CODESHIP_USERNAME or CODESHIP_PASSWORD or CODESHIP_ORGANIZATION or CODESHIP_PROJECT is not defined') + + +def test_fetch_invalid(): + result = fetch(None, None, None, None) + + assert result == [] diff --git a/tests/provider/github/test_core.py b/tests/provider/github/test_core.py index a208be1f..e614801f 100755 --- a/tests/provider/github/test_core.py +++ b/tests/provider/github/test_core.py @@ -11,7 +11,7 @@ def test_fetch_slug(): assert result[0]['active'] is True assert result[0]['status'] else: - pytest.skip('GITHUB_TOKEN not defined') + pytest.skip('GITHUB_TOKEN is not defined') def test_fetch_invalid(): diff --git a/tests/provider/gitlab/test_core.py b/tests/provider/gitlab/test_core.py index cf40538b..cd27669d 100755 --- a/tests/provider/gitlab/test_core.py +++ b/tests/provider/gitlab/test_core.py @@ -11,7 +11,7 @@ def test_fetch_slug(): assert result[0]['active'] is True assert result[0]['status'] else: - pytest.skip('GITLAB_TOKEN not defined') + pytest.skip('GITLAB_TOKEN is not defined') def test_fetch_invalid(): diff --git a/tests/provider/test_core.py b/tests/provider/test_core.py index 043ecca3..1c45d8f4 100644 --- a/tests/provider/test_core.py +++ b/tests/provider/test_core.py @@ -2,6 +2,7 @@ import sys from chroma_feedback import provider + def test_process(mocker): program = ArgumentParser() program.add_argument('-P', '--provider', action = 'append', required = True)