mirror of
https://github.com/lubuntu-team/lugito.git
synced 2025-04-18 06:31:10 +00:00
Added .lugitorc configuration and hooked the config files into the connectors. Updates to HISTORY.rst, README.rst and AUTHORS.rst
This commit is contained in:
parent
f327136c34
commit
e445368ef9
27
.arcconfig
27
.arcconfig
@ -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
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>
|
||||
|
27
HISTORY.rst
27
HISTORY.rst
@ -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
|
66
README.md
66
README.md
@ -1,5 +1,65 @@
|
||||
# Lugito
|
||||
lugito
|
||||
======
|
||||
|
||||
This is Lubuntu's friendly IRC notifications bot, hooked up to our Phabricator instance at phab.lubuntu.me
|
||||
[](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.
|
||||
[](https://travis-ci.org/doc-E-brown/lugito)
|
||||
|
||||
[](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.
|
||||
|
52
README.rst
52
README.rst
@ -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
|
||||
|
||||
.. image:: https://readthedocs.org/projects/lugito/badge/?version=latest
|
||||
:target: https://lugito.readthedocs.io/en/latest/?badge=latest
|
||||
:alt: Documentation Status
|
||||
lugito can be installed via Pyp
|
||||
|
||||
|
||||
* Free software: 3 Clause BSD license
|
||||
|
||||
|
||||
Python Boilerplate contains all the boilerplate you need to create a Python package.
|
||||
Temp - Example .lugitorc
|
||||
-------------------------
|
||||
|
||||
.. 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()
|
||||
|
135
lugito/config.py
Normal file
135
lugito/config.py
Normal file
@ -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 = ""
|
||||
|
||||
@ -170,7 +187,7 @@ class IRCConnector(object):
|
||||
if color is not None:
|
||||
sendmessage += ", "
|
||||
|
||||
sendmessage += taskinfo["statusName"] + "\x03\x033]\x03 "
|
||||
sendmessage += taskinfo["statusName"] + "\x03\x033]\x03 "
|
||||
|
||||
# Put the title in there as well.
|
||||
sendmessage += taskinfo["title"].strip() + ": "
|
||||
@ -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"""
|
||||
|
||||
if name in self.package_names:
|
||||
return self.package_names[name]
|
||||
|
||||
self.logger.debug('{} is an unsupported repository'.format(
|
||||
name))
|
||||
|
||||
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(", ")
|
||||
|
||||
|
||||
def send(self, objectstr, who, body, link):
|
||||
pass
|
||||
def send(self, *args, **kwargs):
|
||||
"""Send the commit message"""
|
||||
|
||||
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
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
obj = LPConnector()
|
||||
|
||||
|
@ -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
|
||||
|
17
setup.py
17
setup.py
@ -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"]
|
||||
}
|
||||
}
|
||||
|
32
tests/.lugitorc
Normal file
32
tests/.lugitorc
Normal file
@ -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
|
23
tests/.lugitorc_no_host
Normal file
23
tests/.lugitorc_no_host
Normal file
@ -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
|
20
tests/.lugitorc_no_phab
Normal file
20
tests/.lugitorc_no_phab
Normal file
@ -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
|
23
tests/.lugitorc_no_token
Normal file
23
tests/.lugitorc_no_token
Normal file
@ -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
|
1
tests/lugito.ini
Normal file
1
tests/lugito.ini
Normal file
@ -0,0 +1 @@
|
||||
[lugito]
|
114
tests/test_config.py
Normal file
114
tests/test_config.py
Normal file
@ -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()
|
||||
|
||||
obj._socket_conn = MagicMock()
|
||||
assert(phab_mock.is_called())
|
||||
|
||||
# obj.conn.recv = MagicMock(side_effect=[
|
||||
|
||||
@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(''))
|
||||
|
||||
link_with_anchor = 'https://phab.lubuntu.me/T154'
|
||||
|
||||
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…
x
Reference in New Issue
Block a user