Added .lugitorc configuration and hooked the config files into the connectors. Updates to HISTORY.rst, README.rst and AUTHORS.rst

pull/1/head
Ben Johnston 6 years ago
parent f327136c34
commit e445368ef9
No known key found for this signature in database
GPG Key ID: 63E9F49BEDEC573D

@ -1,27 +0,0 @@
{
"hosts": {
"http://phab.lubuntu.me/": {
"token": ""
}
},
"config": {
"default": "http://phab.lubunutu.me/api/"
},
"HMAC": i{
"irc": "",
},
"irc": {
"host": "irc.freenode.net",
"port": "6697",
"username": "",
"password": "",
"channel": "#lubuntu-devel"
},
"launchpad": {
"application": "lugito",
"staging": "production",
"version": "devel",
"supported_versions": ["Cosmic", "Bionic", "Xenial", "Trusty"]
}
}

5
.gitignore vendored

@ -1,6 +1,11 @@
# Created by .ignore support plugin (hsz.mobi)
# Config file
.arcconfig
.lugitorc
# launchpad files
devel/*
#
### Python template
# Byte-compiled / optimized / DLL files

@ -5,9 +5,9 @@ Credits
Development Lead
----------------
* Ben Johnston <bjohnston@neomailbox.net>
* Simon Quigley
Contributors
------------
-----------
None yet. Why not be the first?
* Ben Johnston (docEbrown) <bjohnston@neomailbox.net>

@ -2,7 +2,28 @@
History
=======
0.1.0 (2018-11-07)
------------------
0.1.0 (20th May 2018)
----------------------
* First release on PyPI.
* Initial Release
0.2.0 (21st November 2018)
---------------------------
* added functionality for reporting creation, comments and edits on comments of
diffs
* Refactored into Python package
* irc and launchpad connections written into separate Python modules
* corrected issues with reporting using links with anchors via IRC
* added some unittests
* **Still TODO**
* Documentation - read the docs
* Add decorators to Lugito class to check that a request has been validated before other tasks can be completed
* Improve test coverage
* Pypi upload
* Add exceptions in the event that connector send method calls are not executed correctly
* CI and automated docs and Pypi udpate
* in-situ testing

@ -1,5 +1,65 @@
# Lugito
lugito
======
This is Lubuntu's friendly IRC notifications bot, hooked up to our Phabricator instance at phab.lubuntu.me
[![image](https://img.shields.io/pypi/v/lugito.svg)](https://pypi.python.org/pypi/lugito)
The code is licensed under the 3-clause BSD license, and is copyrighted by the Lubuntu team. More info available in LICENSE.
[![image](https://img.shields.io/travis/doc-E-brown/lugito.svg)](https://travis-ci.org/doc-E-brown/lugito)
[![Documentation Status](https://readthedocs.org/projects/lugito/badge/?version=latest)](https://lugito.readthedocs.io/en/latest/?badge=latest)
Python Boilerplate contains all the boilerplate you need to create a
Python package.
- Free software: 3 Clause BSD license
- Documentation: <https://lugito.readthedocs.io>.
Temp - Example .lugitorc
------------------------
```
[phabricator]
host = http://127.0.0.1:9091/api/
token = api-nojs2ip33hmp4zn6u6cf72w7d6yh
[phabricator.hooks]
irc = cqg42zdcuqysff632kc6rnsu4m3hjg6c
commithook = znkyfflbcia5gviqx5ybad7s6uyfywxi
[connector.irc]
host = irc.freenode.net
port = 6697
username = someusername
password = somepassword
channel = #somechannel
[connector.launchpad]
application = lugito
staging = production
version = devel
supported_versions =
Cosmic
Bionic
Xenial
Trusty
[connector.launchpad.package_names]
rDEFAULTSETTINGS = lubuntu-default-settings
rART = lubuntu-artwork
rCALASETTINGS = calamares-settings-ubuntu
rQTERMINALPACKAGING = qterminal
rLXQTCONFIGPACKAGING = lxqt-config
rNMTRAYPACKAGING = nm-tray
```
Features
--------
- TODO
Credits
-------
This package was created with
[Cookiecutter](https://github.com/audreyr/cookiecutter) and the
[audreyr/cookiecutter-pypackage](https://github.com/audreyr/cookiecutter-pypackage)
project template.

@ -2,25 +2,55 @@
lugito
======
lugito is a Python package that provides a webserver for connecting updates from Phabricator to communication tools such as irc and other services such as launchpad.
.. image:: https://img.shields.io/pypi/v/lugito.svg
:target: https://pypi.python.org/pypi/lugito
Installation
-------------
.. image:: https://img.shields.io/travis/doc-E-brown/lugito.svg
:target: https://travis-ci.org/doc-E-brown/lugito
lugito can be installed via Pyp
.. image:: https://readthedocs.org/projects/lugito/badge/?version=latest
:target: https://lugito.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
* Free software: 3 Clause BSD license
Temp - Example .lugitorc
-------------------------
Python Boilerplate contains all the boilerplate you need to create a Python package.
.. code::
[phabricator]
host = http://127.0.0.1:9091/api/
token = api-nojs2ip33hmp4zn6u6cf72w7d6yh
[phabricator.hooks]
irc = cqg42zdcuqysff632kc6rnsu4m3hjg6c
commithook = znkyfflbcia5gviqx5ybad7s6uyfywxi
[connector.irc]
host = irc.freenode.net
port = 6697
username = someusername
password = somepassword
channel = #somechannel
[connector.launchpad]
application = lugito
staging = production
version = devel
supported_versions =
Cosmic
Bionic
Xenial
Trusty
[connector.launchpad.package_names]
rDEFAULTSETTINGS = lubuntu-default-settings
rART = lubuntu-artwork
rCALASETTINGS = calamares-settings-ubuntu
rQTERMINALPACKAGING = qterminal
rLXQTCONFIGPACKAGING = lxqt-config
rNMTRAYPACKAGING = nm-tray
* Free software: BSD license
* Documentation: https://lugito.readthedocs.io.
Features

@ -2,14 +2,7 @@ from lugito.lugito import (
Lugito,
)
from lugito.connectors.irc import (
IRCConnector,
)
from lugito.connectors.launchpad import (
LPConnectook,
)
import lugito.config
from ._version import get_versions
__version__ = get_versions()['version']

@ -13,7 +13,6 @@
# Imports
from lugito.webhooks import run
if __name__ == "__main__":
from lugito.webhooks import run
run()

@ -0,0 +1,135 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# S.D.G
"""
:mod:`lugito.config`
======================================
Module to manage lugito configuration
.. currentmodule:: lugito.config
"""
# Imports
import os
import logging
import configparser
DEFAULT_CONFIG_FILE = os.path.join(
os.getcwd(), '.lugitorc')
logger = logging.getLogger('lugito.config')
# Add log level
ch = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.setLevel(logging.DEBUG)
CONFIG = {}
def update_config(config_file=DEFAULT_CONFIG_FILE):
"""
Update the system config from a config file
Parameters
----------
config_file: str
The path of the lugito config file
Returns
-------
config: dictionary
A dictionary of config parameters
"""
config = configparser.ConfigParser()
config.read(config_file)
# Make some basic assertions for minimum functionality
if 'phabricator' not in config:
raise ValueError('phabricator section missing from config file: %s' %\
config_file)
if 'host' not in config['phabricator']:
raise ValueError('host value missing from phabricator section in'\
' config file: %s' % config_file)
if 'token' not in config['phabricator']:
raise ValueError('token value missing from phabricator section in'\
' config file: %s' % config_file)
CONFIG['phabricator'] = {}
CONFIG['phabricator']['host'] = config['phabricator']['host']
CONFIG['phabricator']['token'] = config['phabricator']['token']
CONFIG['phabricator']['hooks'] = {}
# Iterate through hooks for HMAC keys
if 'phabricator.hooks' in config:
for key, value in config['phabricator.hooks'].items():
CONFIG['phabricator']['hooks'][key] = value
CONFIG['connectors'] = {}
# Iterate through available connectors
for key in config.keys():
# Is a connector section
if ('connector.' in key) and (key.count('.') == 1) :
connector = key.split('.')[1]
if 'connectors' not in CONFIG:
CONFIG['connectors'] = {}
if connector not in CONFIG['connectors']:
CONFIG['connectors'][connector] = {}
for param, value in config[key].items():
# Check for multiple values for a parameter
if value.find('\n') >= 0:
value = value[1:].split('\n')
CONFIG['connectors'][connector][param] = value
# Is a connector sub-section
elif ('connector.' in key) and (key.count('.') > 1) :
sections = key.split('.')
connector = sections[1]
subsection = sections[-1]
if 'connectors' not in CONFIG:
CONFIG['connectors'] = {}
if connector not in CONFIG['connectors']:
CONFIG['connectors'][connector] = {}
CONFIG['connectors'][connector][subsection] = {}
for param, value in config[key].items():
# Check for multiple values for a parameter
if value.find('\n') >= 0:
value = value[1:].split('\n')
# configparser reads the parameters as lower case
# convert all but first character to upper case
param = 'r{}'.format(param[1:].upper())
CONFIG['connectors'][connector][subsection][param] = value
try:
update_config()
except ValueError:
# The config file is not present
logging.warning('Default config file: %s not found' % DEFAULT_CONFIG_FILE)

@ -1,3 +1,11 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# S.D.G
from lugito.connectors.irc import (
irc,
)
from lugito.connectors.launchpad import (
launchpad,
)

@ -18,25 +18,27 @@ import socket
import logging
import threading
import phabricator
import lugito
from time import sleep
class IRCConnector(object):
class irc(object):
def __init__(self, log_level=logging.DEBUG):
def __init__(self, log_level=logging.DEBUG, sleep_delay=5):
# IRC info
# Read the configuration out of the .arcconfig file
self.host = phabricator.ARCRC['irc']['host']
self.port = int(phabricator.ARCRC['irc']['port'])
self.username = phabricator.ARCRC['irc']['username']
self.password = phabricator.ARCRC['irc']['password']
self.channel = phabricator.ARCRC['irc']['channel']
self.host = lugito.config.CONFIG['connectors']['irc']['host']
self.port = int(lugito.config.CONFIG['connectors']['irc']['port'])
self.username = lugito.config.CONFIG['connectors']['irc']['username']
self.password = lugito.config.CONFIG['connectors']['irc']['password']
self.channel = lugito.config.CONFIG['connectors']['irc']['channel']
# Phabricator info
self.phab = phabricator.Phabricator()
self.phab_host = phabricator.ARCRC['config']['default'].replace(
'api/', '')
self.phab = phabricator.Phabricator(
host=lugito.config.CONFIG['phabricator']['host'],
token=lugito.config.CONFIG['phabricator']['token'],)
self.phab_host = self.phab.host.replace('api/', '')
self.logger = logging.getLogger('lugito.connector.IRCConnector')
@ -50,14 +52,17 @@ class IRCConnector(object):
self.logger.addHandler(ch)
self.logger.setLevel(log_level)
self.sleep_delay = sleep_delay
def _send_raw(self, message):
def _send_raw(self, message): # pragma: no cover
"""Low level send"""
self.conn.send(message.encode('utf-8'))
def _socket_conn(self):
def _setup_connection(self):
"""Setup connection"""
self.conn = ssl.wrap_socket(
socket.socket(socket.AF_INET, socket.SOCK_STREAM))
self.conn.connect((self.host, self.port))
@ -66,11 +71,11 @@ class IRCConnector(object):
def connect(self):
"""Connect"""
self._socket_conn()
self.logger.info("Connecting to IRC.")
self._setup_connection()
setup = False
usersuffix = 0
self.logger.info("Connecting to IRC.")
while not setup:
response = self.conn.recv(512).decode("utf-8")
@ -84,11 +89,11 @@ class IRCConnector(object):
self.username, self.password))
if "You are now identified" in response:
sleep(5)
sleep(self.sleep_delay)
self._send_raw("JOIN {}\r\n".format(self.channel))
if "477" in response:
sleep(5)
sleep(self.sleep_delay)
self._send_raw("JOIN {}\r\n".format(self.channel))
if "433" in response:
@ -106,20 +111,32 @@ class IRCConnector(object):
self.logger.info("Successfully connected to the IRC server.")
def send_notice(self, message):
def send_notice(self, message): # pragma: no cover
self._send_raw("NOTICE {} :{}\r\n".format(self.channel, message))
def send(self, objectstr, who, body, link):
def send(self, *args, **kwargs):
"""Send a formatted message"""
if len(args) == 4:
objectstr, who, body, link = args
elif len(kwargs) == 4:
objectstr = kwargs['objectstr']
who = kwargs['who']
body = kwargs['body']
link = kwargs['link']
# else
# raise exception
# e.g. [T31: Better IRC integration]
message = "\x033[\x03\x0313" + objectstr + "\x03\x033]\x03 "
# e.g. tsimonq2 (Simon Quigley)
message = message + "\x0315" + who + "\x03 "
message += "\x0315" + who + "\x03 "
# e.g. commented on the task:
message = message + body + ": "
message += body + ": "
# e.g. https://phab.lubuntu.me/T40#779
message = message + "\x032" + link + "\x03"
message += "\x032" + link + "\x03"
# Make sure we can debug this if it goes haywire
self.logger.debug(message)
# Sleep for a fifth of a second, so when we have a bunch of messages we have a buffer
@ -127,7 +144,7 @@ class IRCConnector(object):
# Aaaaand, send it off!
self.send_notice(message)
def gettaskinfo(self, task):
def get_task_info(self, task):
sendmessage = ""
@ -189,8 +206,14 @@ class IRCConnector(object):
# If someone wrote something like "Tblah", obviously that's not right.
except ValueError:
self.send_notice("\x034Error: " + task.strip() + "is an invalid task reference.\x03")
return None
if anchor is not None:
link = '{}#{}'.format(task.strip(), anchor)
else:
link = task.strip()
self.send_notice("\x034Error: " + link +\
" is an invalid task reference.\x03")
def bot(self, message, msgtype):
@ -200,7 +223,7 @@ class IRCConnector(object):
for item in message.split():
if item.startswith("T") or item.startwith("D"):
self.gettaskinfo(item.strip())
self.get_task_info(item.strip())
elif msgtype == "link":
@ -208,7 +231,7 @@ class IRCConnector(object):
if (item.split()[0].strip().startswith("T")) or \
(item.split()[0].strip().startswith("D")):
self.gettaskinfo(item.split()[0].strip())
self.get_task_info(item.split()[0].strip())
else:
self.sendnotice("\x034Error: unknown command.\x03")

@ -11,29 +11,53 @@ Define a launchpad connector class
"""
# Imports
import re
import logging
import phabricator
from launchpadlib.launchpad import Launchpad
import lugito
from string import Template
from launchpadlib.launchpad import Launchpad as lp
class LPConnector(object):
BUG_MESSAGE = Template(
"This bug has been marked as fixed in the Git repository: $link\n"
"The commit message is the following: $commit_message\n\n"
"(Note: I am only a bot. If this message was received in error, "
"please contact my owners on the Lubuntu Team.)")
RE_COMMIT_MSG = re.compile(r"lp:\s+\#\d+(?:,\s*\#\d+)*")
class launchpad(object):
def __init__(self, log_level=logging.DEBUG):
# Launchpad info
# Read the configuration out of the .arcconfig file
self.application = phabricator.ARCRC['launchpad']['application']
self.staging = phabricator.ARCRC['launchpad']['staging']
self.version = phabricator.ARCRC['launchpad']['version']
# Read the configuration out of the .lugitorc file
self.application = lugito.config.CONFIG['connectors']\
['launchpad']['application']
self.staging = lugito.config.CONFIG['connectors']\
['launchpad']['staging']
self.version = lugito.config.CONFIG['connectors']\
['launchpad']['version']
self.supported_vers =\
phabricator.ARCRC['launchpad']['supported_versions']
lugito.config.CONFIG['connectors']\
['launchpad']['supported_versions']
self.package_names =\
lugito.config.CONFIG['connectors']\
['launchpad']['package_names']
# Phabricator info
self.phab = phabricator.Phabricator()
self.phab_host = phabricator.ARCRC['config']['default'].replace(
self.phab = phabricator.Phabricator(
host=lugito.config.CONFIG['phabricator']['host'],
token=lugito.config.CONFIG['phabricator']['token'],
)
self.phab_host = lugito.config.CONFIG['phabricator']['host'].replace(
'api/', '')
self.logger = logging.getLogger('lugito.connector.LPConnector')
self.logger = logging.getLogger('lugito.connector.launchpad')
# Add log level
ch = logging.StreamHandler()
@ -50,17 +74,74 @@ class LPConnector(object):
"""Connect"""
self.logger.info("Connecting to Launchpad")
self.lp = lp.login_with(
self.application,
self.staging,
self.version)
def get_package_name(self, name):
"""Need to check"""
def send(self, objectstr, who, body, link):
pass
if name in self.package_names:
return self.package_names[name]
self.logger.debug('{} is an unsupported repository'.format(
name))
def listen(self):
pass
return None
def get_bugs_list(self, link):
"""Get bugs list using a link"""
regex_search = RE_COMMIT_MSG.search(link.lower())
if not regex_search:
self.logger.debug('{} not a commit message'.format(link))
return []
return regex_search.group(0).strip("lp: ").replace("#", "").split(", ")
if __name__ == "__main__":
def send(self, *args, **kwargs):
"""Send the commit message"""
obj = LPConnector()
if len(args) == 2:
package_name, commit_msg = args
elif len(kwargs) == 2:
commit_msg = kwargs['commit_msg']
package_name = kwargs['package_name']
# else
# raise exception
package_name = self.get_package_name(package_name)
bug_list = self.get_bugs_list(commit_msg)
if package_name and bug_list:
for bug in bug_list:
goodtask = None
bug = self.lp.load("/bugs/" + str(bug).strip())
for task in bug.bug_tasks:
for rel in self.supported_vers:
if package_name + " (Ubuntu " + rel + ")" in task.bug_target_display_name:
goodtask = task
break
if not goodtask:
if package_name + " (Ubuntu)" in task.bug_target_display_name:
goodtask = task
if goodtask:
message = BUG_MESSAGE.substitute(
link=self.phab_host + package_name,
commit_message=commit_msg,
)
bug.newMessage(content=message)
goodtask.status = "Fix Committed"
goodtask.lp_save()
def listen(self):
pass

@ -15,6 +15,7 @@ import hmac
import http
import logging
import phabricator
import lugito
from hashlib import sha256
PHAB_WEBHOOK_SIG = "X-Phabricator-Webhook-Signature"
@ -31,9 +32,12 @@ class Lugito(object):
Initialise
"""
self.phab = phabricator.Phabricator()
self.HMAC = phabricator.ARCRC['HMAC']
self.host = phabricator.ARCRC['config']['default']
self.phab = phabricator.Phabricator(
host=lugito.config.CONFIG['phabricator']['host'],
token=lugito.config.CONFIG['phabricator']['token'],
)
self.HMAC = lugito.config.CONFIG['phabricator']['hooks']
self.host = lugito.config.CONFIG['phabricator']['host']
self.logger = logging.getLogger('lugito.lugito')
@ -51,15 +55,7 @@ class Lugito(object):
self.HMAC[key] = bytes(u'%s' % val, 'utf-8')
def _transaction_search(self):
self.transaction = self.phab.transaction.search(objectIdentifier=
self.request_data["object"]["phid"])["data"]
def _request_data(self, request):
self.request_data = json.loads(request.data)
def validate_HMAC(self, hmac_key, request):
def validate_request(self, hmac_key, request):
"""
Check a request originated from Phabricator. This method must be called
first to validate a request is from Phabricator before any other method
@ -86,8 +82,13 @@ class Lugito(object):
# check if from phabricator
if hash_.hexdigest() == request.headers[PHAB_WEBHOOK_SIG]:
self._request_data(request)
self._transaction_search()
# Store the request and transaction
self.request_data = json.loads(request.data)
self.transaction = self.phab.transaction.search(objectIdentifier=
self.request_data["object"]["phid"])["data"]
self.logger.info('received phid: %s' %\
self.request_data["object"]["phid"])
return True

@ -17,7 +17,7 @@ import logging
import threading
from flask import Flask, request
from lugito import Lugito
from lugito.connectors.irc import IRCConnector
from lugito.connectors import irc, launchpad
# Constants
GLOBAL_LOG_LEVEL = logging.DEBUG
@ -26,7 +26,9 @@ GLOBAL_LOG_LEVEL = logging.DEBUG
lugito = Lugito(GLOBAL_LOG_LEVEL)
WEBSITE = lugito.host.replace('/api/', '')
irc_con = IRCConnector()
# Connectors
irc_con = irc()
launchpad_con = launchpad()
# Logging
logger = logging.getLogger('lugito.webhooks')
@ -45,11 +47,41 @@ logger.setLevel(GLOBAL_LOG_LEVEL)
app = Flask('lugito')
@app.route("/commithook", methods=["POST"])
def commithook():
"""Commit hook"""
if lugito.validate_request('commithook', request):
author = lugito.get_author_fullname()
# Without the author we can't continue
if author is None:
return 'Ok'
object_type = lugito.request_data["object"]["type"]
if object_type == "CMIT":
logger.debug("Object is a commit.")
commit_msg = lugito.get_object_string("fullName").replace(
lugito.get_object_string("name") + ": ", "")
pkg_name = lugito.get_object_string("name")
launchpad_con.send(pkg_name, commit_msg)
return 'Ok'
@app.route("/irc", methods=["POST"])
def _main():
"""Main route"""
if lugito.validate_HMAC('irc', request):
if lugito.validate_request('irc', request):
author = lugito.get_author_fullname()
@ -136,6 +168,7 @@ def _main():
def run():
irc_con.connect()
launchpad_con.connect()
t = threading.Thread(target=irc_con.listen)
t.daemon = True
t.start()

@ -1,4 +1,5 @@
[pytest]
timeout=100
python_files = tests.py test_*.py *_tests.py
pytest_plugins = "pytest_cov", "pep8"
addopts = --doctest-modules --cov-config=.coveragerc --cov=lugito --cov-report=term-missing

@ -17,7 +17,6 @@ keyring==16.0.2
launchpadlib==1.10.6
lazr.restfulclient==0.14.0
lazr.uri==1.0.3
-e git+ssh://git@phab.lubuntu.me:2222/source/lugito.git@165c866a5d5a184b0b36552334fa11aba95b5fb8#egg=lugito
MarkupSafe==1.1.0
more-itertools==4.3.0
oauthlib==2.1.0
@ -31,5 +30,7 @@ pytest-cov==2.6.0
SecretStorage==3.1.0
six==1.11.0
testresources==2.0.1
versioneer==0.18
wadllib==1.3.3
Werkzeug==0.14.1
twine==1.12.1

@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
"""The setup script."""
# import versioneer
import versioneer
from setuptools import setup, find_packages
with open('README.rst') as readme_file:
@ -29,8 +29,8 @@ test_requirements = [
]
setup(
author="",
author_email='',
author="Ben Johnston (docEbrown)",
author_email='bjohnston@neomailbox.net',
classifiers=[
'Development Status :: 2 - Pre-Alpha',
'Intended Audience :: Developers',
@ -42,11 +42,11 @@ setup(
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
description="Python Boilerplate contains all the boilerplate "\
"you need to create a Python package.",
description="Python package to connect services such as irc and launchpad"\
" to Phabricator and provide updates",
entry_points={
'console_scripts': [
'lugito=lugito.cli:run',
'lugito=lugito.webhooks:run',
],
},
install_requires=requirements,
@ -60,8 +60,7 @@ setup(
test_suite='tests',
tests_require=test_requirements,
url='',
version='0.1.0',
zip_safe=False,
# version=versioneer.get_version(),
# cmdclass=versioneer.get_cmdclass(),
version=versioneer.get_version(),
cmdclass=versioneer.get_cmdclass(),
)

@ -4,24 +4,4 @@
"token": "api-nojs2ip33hmp4zn6u6cf72w7d6yh"
}
},
"config": {
"default": "http://127.0.0.1:9091/api/"
},
"HMAC": {
"diffhook": "vglzi6t4gsumnilv27r27no7rs3vgs75",
"commithook": "znkyfflbcia5gviqx5ybad7s6uyfywxi"
},
"irc": {
"host": "irc.freenode.net",
"port": "6697",
"username": "someusername",
"password": "somepassword",
"channel": "#somechannel"
},
"launchpad": {
"application": "lugito",
"staging": "production",
"version": "devel",
"supported_versions": ["Cosmic", "Bionic", "Xenial", "Trusty"]
}
}

@ -0,0 +1,32 @@
[phabricator]
host = http://127.0.0.1:9091/api/
token = api-nojs2ip33hmp4zn6u6cf72w7d6yh
[phabricator.hooks]
diffhook = vglzi6t4gsumnilv27r27no7rs3vgs75
commithook = znkyfflbcia5gviqx5ybad7s6uyfywxi
[connector.irc]
host = irc.freenode.net
port = 6697
username = someusername
password = somepassword
channel = #somechannel
[connector.launchpad]
application = lugito
staging = production
version = devel
supported_versions =
Cosmic
Bionic
Xenial
Trusty
[connector.launchpad.package_names]
rDEFAULTSETTINGS = lubuntu-default-settings
rART = lubuntu-artwork
rCALASETTINGS = calamares-settings-ubuntu
rQTERMINALPACKAGING = qterminal
rLXQTCONFIGPACKAGING = lxqt-config
rNMTRAYPACKAGING = nm-tray

@ -0,0 +1,23 @@
[phabricator]
token = api-nojs2ip33hmp4zn6u6cf72w7d6yh
[phabricator.hooks]
diffhook = vglzi6t4gsumnilv27r27no7rs3vgs75
commithook = znkyfflbcia5gviqx5ybad7s6uyfywxi
[connector.irc]
host = irc.freenode.net
port = 6697
username = someusername
password = somepassword
channel = #somechannel
[connector.launchpad]
application = lugito
staging = production
version = devel
supported_versions =
Cosmic
Bionic
Xenial
Trusty

@ -0,0 +1,20 @@
[phabricator.hooks]
diffhook = vglzi6t4gsumnilv27r27no7rs3vgs75
commithook = znkyfflbcia5gviqx5ybad7s6uyfywxi
[connector.irc]
host = irc.freenode.net
port = 6697
username = someusername
password = somepassword
channel = #somechannel
[connector.launchpad]
application = lugito
staging = production
version = devel
supported_versions =
Cosmic
Bionic
Xenial
Trusty

@ -0,0 +1,23 @@
[phabricator]
host = http://127.0.0.1/api/
[phabricator.hooks]
diffhook = vglzi6t4gsumnilv27r27no7rs3vgs75
commithook = znkyfflbcia5gviqx5ybad7s6uyfywxi
[connector.irc]
host = irc.freenode.net
port = 6697
username = someusername
password = somepassword
channel = #somechannel
[connector.launchpad]
application = lugito
staging = production
version = devel
supported_versions =
Cosmic
Bionic
Xenial
Trusty

@ -0,0 +1 @@
[lugito]

@ -0,0 +1,114 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# S.D.G
"""Test config values
:author: Ben Johnston
"""
# Imports
import os
import pytest
import lugito.config
TEST_FILE = os.path.join(
os.path.dirname(__file__),
'.lugitorc')
TEST_FILE_NO_PHAB = os.path.join(
os.path.dirname(__file__),
'.lugitorc_no_phab')
TEST_FILE_NO_HOST = os.path.join(
os.path.dirname(__file__),
'.lugitorc_no_host')
TEST_FILE_NO_TOKEN = os.path.join(
os.path.dirname(__file__),
'.lugitorc_no_token')
def test_loading_config_hooks():
"""Test loading config"""
lugito.config.update_config(TEST_FILE)
CONFIG = lugito.config.CONFIG
assert(CONFIG['phabricator']['host'] == 'http://127.0.0.1:9091/api/')
assert(CONFIG['phabricator']['token'] == 'api-nojs2ip33hmp4zn6u6cf72w7d6yh')
# Hooks
assert(CONFIG['phabricator']['hooks']['diffhook'] ==\
'vglzi6t4gsumnilv27r27no7rs3vgs75')
assert(CONFIG['phabricator']['hooks']['commithook'] ==\
'znkyfflbcia5gviqx5ybad7s6uyfywxi')
def test_loading_config_connectors():
"""Test loading config connectors"""
lugito.config.update_config(TEST_FILE)
CONFIG = lugito.config.CONFIG
# Connectors
assert(CONFIG['connectors']['irc'] == {
'host': 'irc.freenode.net',
'port': '6697',
'username': 'someusername',
'password':'somepassword',
'channel': '#somechannel',
})
if not (CONFIG['connectors']['launchpad'] == {
'application': 'lugito',
'staging': 'production',
'version': 'devel',
'supported_versions': ['Cosmic', 'Bionic', 'Xenial', 'Trusty'],
'package_names': {
'rDEFAULTSETTINGS': 'lubuntu-default-settings',
'rART': 'lubuntu-artwork',
'rCALASETTINGS': 'calamares-settings-ubuntu',
'rQTERMINALPACKAGING': 'qterminal',
'rLXQTCONFIGPACKAGING': 'lxqt-config',
'rNMTRAYPACKAGING': 'nm-tray',
},
}):
import pdb;pdb.set_trace()
def test_load_config_no_phab():
"""Test loading config to load phabricator"""
with pytest.raises(ValueError) as err:
lugito.config.update_config(TEST_FILE_NO_PHAB)
assert('phabricator section missing from config file' in str(err))
def test_load_config_no_host():
"""Test loading config to load phabricator"""
with pytest.raises(ValueError) as err:
lugito.config.update_config(TEST_FILE_NO_HOST)
assert('host value missing from phabricator section config file' in str(err))
def test_load_config_no_token():
"""Test loading config to load phabricator"""
with pytest.raises(ValueError) as err:
lugito.config.update_config(TEST_FILE_NO_TOKEN)
assert('host value missing from phabricator conffig file' in str(err))

@ -10,25 +10,49 @@ Test IRC connector
import os
import json
import phabricator
from lugito import IRCConnector
from unittest.mock import MagicMock
import lugito
import pytest
from lugito.connectors import irc
# docEbrown - 20181120
# There is a bug in inspect.unwrap preventing the import of call directly
import unittest.mock
from unittest.mock import MagicMock, patch
# Setup ###############################################################
TEST_DIR = os.path.dirname(__file__)
lugito.config.CONFIG = {
'phabricator': {
'host': 'http://127.0.0.1:9091/api/',
'token': 'api-nojs2ip33hmp4zn6u6cf72w7d6yh',
'hooks': {
'diffhook': 'vglzi6t4gsumnilv27r27no7rs3vgs75',
'commithook': 'znkyfflbcia5gviqx5ybad7s6uyfywxi',
},
},
'connectors': {
'irc': {
'host': 'irc.freenode.net',
'port': '6697',
'username': 'someusername',
'password': 'somepassword',
'channel': '#somechannel',
},
'launchpad': {
'application': 'lugito',
'staging': 'production',
'version': 'devel',
'supported_versions': ['Cosmic', 'Bionic', 'Xenial', 'Trusty'],
},
},
}
# Force phabricator to use the ./tests/.arcconfig file
TEST_CONFIG = os.path.join(TEST_DIR, '.arcconfig')
with open(TEST_CONFIG, 'r') as f:
phabricator.ARCRC = json.load(f)
# Tests ###############################################################
def test_init():
"""Test initialise irc connector"""
obj = IRCConnector()
obj = irc()
assert('irc.freenode.net' == obj.host)
assert(6697 == obj.port)
@ -38,12 +62,179 @@ def test_init():
assert('http://127.0.0.1:9091/' == obj.phab_host)
def test_connect():
@patch('phabricator.Phabricator')
def test_connect(phab_mock):
"""Test initial connection"""
obj = IRCConnector()
obj = irc()
assert(phab_mock.is_called())
@patch('phabricator.Phabricator')
def test_send(phab_mock):
"""Test sending a message"""
obj = irc()
obj.send_notice = MagicMock()
objectstr = "objectstr"
who = "who"
body = "body"
link = "link"
obj.send(objectstr, who, body, link)
obj.send_notice.assert_called_with(
'\x033[\x03\x0313objectstr\x03\x033]\x03 \x0315who\x03 body: \x032link\x03')
@patch('phabricator.Phabricator')
def test_send_kwargs(phab_mock):
"""Test sending a message - kwargs"""
obj = irc()
obj.send_notice = MagicMock()
objectstr = "objectstr"
who = "who"
body = "body"
link = "link"
obj.send(objectstr=objectstr, who=who, body=body, link=link)
obj.send_notice.assert_called_with(
'\x033[\x03\x0313objectstr\x03\x033]\x03 \x0315who\x03 body: \x032link\x03')
def test_connect():
"""Test connect"""
obj = irc(sleep_delay=0)
obj._send_raw = MagicMock()
obj._setup_connection = MagicMock()
obj.conn = MagicMock()
obj.conn.recv.side_effect = [
b'No Ident response',
b'You are now identified',
b'477',
b'433',
b'PING: something',
b'366',
]
obj.connect()
# No Ident response results
assert(unittest.mock.call('NICK someusername1\r\n') in\
obj._send_raw.call_args_list)
assert(unittest.mock.call('USER someusername * * :someusername\r\n') in\
obj._send_raw.call_args_list)
assert(unittest.mock.call('PRIVMSG nickserv :identify someusername'\
' somepassword\r\n') in obj._send_raw.call_args_list)
# Now identified / 477
assert(unittest.mock.call('JOIN #somechannel\r\n') in\
obj._send_raw.call_args_list)
# 433
assert(unittest.mock.call('NICK someusername1\r\n') in\
obj._send_raw.call_args_list)
assert(unittest.mock.call('USER someusername1 * * :someusername1\r\n') in\
obj._send_raw.call_args_list)
# Ping
assert(unittest.mock.call('PONG : something\r\n') in\
obj._send_raw.call_args_list)
# docEbrown - 20181120
# Address including anchors in reference
# https://phab.lubuntu.me/T88#3230
def test_get_task_info_with_anchor():
"""Test getting task info with anchor"""
obj = irc()
obj.send_notice = MagicMock()
obj.phab = MagicMock()
obj.phab.maniphest.info = MagicMock(
return_value={
'priorityColor': 'pink',
'statusName': 'Open',
'title': 'Fix shortcuts related to Super key',
'uri': 'https://phab.lubuntu.me/T154'
}
)
link_with_anchor = 'https://phab.lubuntu.me/T154#3228'
obj.get_task_info(link_with_anchor)
assert(unittest.mock.call(task_id=154) in\
obj.phab.maniphest.info.call_args_list)
assert(obj.send_notice.call_args == \
unittest.mock.call('\x033[\x03\x035Unbreak Now!, Open\x03\x033]\x03 '
'Fix shortcuts related to Super key: '\
'\x032https://phab.lubuntu.me/T154#3228\x03'))
def test_get_diff_info_with_anchor():
"""Test getting diff info with anchor"""
obj = irc()
obj.send_notice = MagicMock()
obj.phab = MagicMock()
obj.phab.differential.query = MagicMock(
return_value=[{
'statusName': 'Closed',
'title': 'Some diff title',
'uri': 'https://phab.lubuntu.me/D24'
},]
)
link_with_anchor = 'https://phab.lubuntu.me/D24#123'
obj.get_task_info(link_with_anchor)
assert(unittest.mock.call(ids=[24]) in\
obj.phab.differential.query.call_args_list)
assert(obj.send_notice.call_args == \
unittest.mock.call('\x033[\x03Closed\x03\x033]\x03 '
'Some diff title: '\
'\x032https://phab.lubuntu.me/D24#123\x03'))
def test_get_task_info_with_error_anchor():
"""Test getting task info with anchor"""
obj = irc()
obj.send_notice = MagicMock()
obj.phab = MagicMock()
obj.phab.maniphest.info = MagicMock(side_effect=ValueError(''))
link_with_anchor = 'https://phab.lubuntu.me/T154#3228'
obj.get_task_info(link_with_anchor)
assert(obj.send_notice.call_args ==
unittest.mock.call('\x034Error: https://phab.lubuntu.me/T154#3228'\
' is an invalid task reference.\x03'))
def test_get_task_info_with_error_no_anchor():
"""Test getting task info with no anchor"""
obj = irc()
obj.send_notice = MagicMock()
obj.phab = MagicMock()
obj.phab.maniphest.info = MagicMock(side_effect=ValueError(''))
obj._socket_conn = MagicMock()
link_with_anchor = 'https://phab.lubuntu.me/T154'
# obj.conn.recv = MagicMock(side_effect=[
obj.get_task_info(link_with_anchor)
assert(obj.send_notice.call_args ==
unittest.mock.call('\x034Error: https://phab.lubuntu.me/T154'\
' is an invalid task reference.\x03'))

@ -9,19 +9,90 @@ Test launchpad connector
# Imports
import json
import phabricator
from lugito.connectors.launchpad import LPConnector
import lugito
from lugito.connectors import launchpad
# Setup ###############################################################
TEST_DIR = os.path.dirname(__file__)
lugito.config.CONFIG = {
'phabricator': {
'host': 'http://127.0.0.1:9091/api/',
'token': 'api-nojs2ip33hmp4zn6u6cf72w7d6yh',
'hooks': {
'diffhook': 'vglzi6t4gsumnilv27r27no7rs3vgs75',
'commithook': 'znkyfflbcia5gviqx5ybad7s6uyfywxi',
},
},
'connectors': {
'irc': {
'host': 'irc.freenode.net',
'port': '6697',
'username': 'someusername',
'password': 'somepassword',
'channel': '#somechannel',
},
'launchpad': {
'application': 'lugito',
'staging': 'production',
'version': 'devel',
'supported_versions': ['Cosmic', 'Bionic', 'Xenial', 'Trusty'],
'package_names': {
'rDEFAULTSETTINGS': 'lubuntu-default-settings',
'rART': 'lubuntu-artwork',
'rCALASETTINGS': 'calamares-settings-ubuntu',
'rQTERMINALPACKAGING': 'qterminal',
'rLXQTCONFIGPACKAGING': 'lxqt-config',
'rNMTRAYPACKAGING': 'nm-tray',
},
},
},
}
# Force phabricator to use the ./tests/.arcconfig file
TEST_CONFIG = os.path.join(TEST_DIR, '.arcconfig')
with open(TEST_CONFIG, 'r') as f:
phabricator.ARCRC = json.load(f)
# Tests ###############################################################
def test_init()
def test_init():
"""Test initialising LPConnector"""
obj = launchpad()
assert(obj.application == "lugito")
assert(obj.staging == "production")
assert(obj.version == "devel")
assert(obj.supported_vers == ["Cosmic", "Bionic", "Xenial", "Trusty"])
assert(obj.package_names == {
'rDEFAULTSETTINGS': 'lubuntu-default-settings',
'rART': 'lubuntu-artwork',
'rCALASETTINGS': 'calamares-settings-ubuntu',
'rQTERMINALPACKAGING': 'qterminal',
'rLXQTCONFIGPACKAGING': 'lxqt-config',
'rNMTRAYPACKAGING': 'nm-tray',
})
def test_get_package_name():
"""Test get package name"""
obj = launchpad()
assert(obj.get_package_name('rART') == 'lubuntu-artwork')
assert(obj.get_package_name('rT') is None)
def test_get_package_name():
"""Test getting package name"""
obj = launchpad()
assert(obj.get_package_name('rNMTRAYPACKAGING') == 'nm-tray')
assert(obj.get_package_name('rNMTRKAGING') is None)
def test_get_bugs_list():
"""Test getting buglist"""
obj = launchpad()
assert(obj.get_bugs_list("lp: #1234") == ['1234'])
assert(obj.get_bugs_list("#1234") == [])

@ -12,6 +12,7 @@ import pytest
import phabricator
import json
import http
import lugito
from lugito import Lugito
from unittest.mock import MagicMock
@ -19,11 +20,33 @@ from unittest.mock import MagicMock
TEST_DIR = os.path.dirname(__file__)
# Force phabricator to use the ./tests/.arcconfig file
TEST_CONFIG = os.path.join(TEST_DIR, '.arcconfig')
# Apply default values
lugito.config.CONFIG = {
'phabricator': {
'host': 'http://127.0.0.1:9091/api/',
'token': 'api-nojs2ip33hmp4zn6u6cf72w7d6yh',
'hooks': {
'diffhook': 'vglzi6t4gsumnilv27r27no7rs3vgs75',
'commithook': 'znkyfflbcia5gviqx5ybad7s6uyfywxi',
},
},
'connectors': {
'irc': {
'host': 'irc.freenode.net',
'port': '6697',
'username': 'someusername',
'password': 'somepassword',
'channel': '#somechannel',
},
'launchpad': {
'application': 'lugito',
'staging': 'production',
'version': 'devel',
'supported_versions': ['Cosmic', 'Bionic', 'Xenial', 'Trusty'],
},
},
}
with open(TEST_CONFIG, 'r') as f:
phabricator.ARCRC = json.load(f)
# Pre-prepared request
FAKE_REQUEST = os.path.join(TEST_DIR, 'request.json')
@ -56,10 +79,12 @@ def test_init():
'utf-8'))
def test_validate_HMAC():
def test_validate_request():
"""Test validating HMAC"""
obj = Lugito()
obj.phab = MagicMock()
obj.phab.transaction.search = MagicMock()
request_mock = MagicMock()
@ -71,7 +96,8 @@ def test_validate_HMAC():
"a8f636f03ed4464ddb398ea873ffab409d941f87396f28fa9d22bb58cfbedc9f"
}
assert(obj.validate_HMAC('diffhook', request_mock))
assert(obj.validate_request('diffhook', request_mock))
assert(obj.phab.transaction.search.is_called())
def test_invalid_HMAC():
@ -89,7 +115,7 @@ def test_invalid_HMAC():
"a8f6364464ddb398ea873ffab409d941f87396f28fa9d22bb58cfbedc9f"
}
assert(not obj.validate_HMAC('diffhook', request_mock))
assert(not obj.validate_request('diffhook', request_mock))
def test_author_fullname():
@ -141,8 +167,6 @@ def test_get_object_type():
with open(FAKE_REQ_DATA, 'r') as f:
obj.request_data = json.load(f)
obj._transaction_search()
assert(obj.get_object_type() == 'DREV')
def test_is_new_object_false():

Loading…
Cancel
Save