Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
171 commits
Select commit Hold shift + click to select a range
84476bf
Initial commit
msftgits Apr 26, 2017
0eebaad
Initial commit
microsoftopensource Apr 26, 2017
c1c35c5
Initial commit
microsoftopensource Apr 26, 2017
bb5a026
wip: initial work - incomplete
troydai May 11, 2017
e3dbb7a
Copy code from https://github.com/Azure/azure-cli/tree/master/src/azu…
May 12, 2017
74a489a
Add vcrpy version from azure-cli
valrus May 13, 2017
f622aea
Add VSCode ignores
valrus May 15, 2017
1da5dfb
Add Azure imports
valrus May 15, 2017
0c4e707
Start converting CLI commands to SDK calls
valrus May 15, 2017
c7c46f6
Minor proofreading
May 16, 2017
fe0f28a
Add required common and mock modules
May 16, 2017
43b349c
Add deps, update pkgs and url
May 16, 2017
42c93d7
Package structure
May 16, 2017
26aed99
Add more editor ignores
May 24, 2017
13aa766
Remove azure deps and nonexistent HISTORY ref
May 24, 2017
b85300b
Remove Azure mgmt dependencies
May 24, 2017
62920a7
Remove azure.common dependency
May 24, 2017
6a7e597
Add dependency on six
May 24, 2017
672cc04
Remove CLI-specific stuff
May 26, 2017
7d96614
Remove CLI exception
May 26, 2017
41c77e8
Remove most CLI-centric patches
May 26, 2017
e2d96cc
Remove older CLI-centric base class
May 26, 2017
27dfe7c
Add sleep patch back in
May 26, 2017
efc0759
First steps toward a unified config
May 26, 2017
d362aef
Config and no-record headers
May 26, 2017
b57c1e2
Move recording-deactivation header detection to right place
May 26, 2017
eba76c2
Add way to set default config file from scenario test
May 30, 2017
e6f703f
Remove 'CLI' from env vars
May 31, 2017
55597e3
Reinstate patch_long_run_operation_delay
May 31, 2017
32cc32f
New record disabling mechanism
May 31, 2017
4d9dae2
Modify config options for better backwards compatibility
May 31, 2017
8ee868e
Ignore more VS stuff
Jun 5, 2017
afaede1
Remove jmespath dependency, update vcrpy
Jun 5, 2017
b90edaf
Remove checkers
Jun 5, 2017
1014d33
Remove unuseds, make patches/processors kwargs
Jun 5, 2017
7fee146
Remove checkers, used only for CLI
Jun 5, 2017
009c6fb
Update import and instance var name
Jun 5, 2017
651c9af
Remove unused imports
Jun 5, 2017
fc32c79
line organization
Jun 5, 2017
1f74aed
Update version number and vcrpy version
Jun 5, 2017
2344faf
Add a little more to README, add setup.cfg
Jun 5, 2017
4a0d084
Add unit tests for TestConfig
Jun 5, 2017
6d95db8
Add travis.yml and update dependencies
Jun 5, 2017
12be549
Update author email
Jun 5, 2017
b9bdc9e
Dummy push to try to trigger a Travis build
Jun 5, 2017
c32e8b3
Another dummy commit for Travis
Jun 5, 2017
29ed04d
Switch to README.rst
Jun 5, 2017
548c59c
Update Travis tag pointer
Jun 5, 2017
475872a
Use io to get encoding arg when reading README
Jun 5, 2017
c67f4b1
Remove unnecessary import
Jun 6, 2017
7ccce1a
Expose most everything in top-level namespace
Jun 6, 2017
ac70d4f
ScenarioTest -> ReplayableTest; update version
Jun 6, 2017
e1ad6a4
Update version
Jun 6, 2017
29f1dc3
Remove unused os.path import
Jun 6, 2017
05862f4
Fix super call
Jun 6, 2017
d83e9f2
Fix super call in setUp
Jun 6, 2017
850f236
Update ReplayableTest import
Jun 6, 2017
6a37a03
Use earlier vcrpy, update version num
Jun 6, 2017
517e96e
Don't fix vcrpy version
Jun 7, 2017
510c2fb
Drop "cli" from default random name prefix
Jun 7, 2017
285a396
Add deployment to Travis
Jun 7, 2017
c699130
Specify universal wheel
Jun 7, 2017
19ad502
Bump version to 0.2.2 for testing CI release process
Jun 7, 2017
4533032
Let preparer subclasses override create_random_name
Jun 7, 2017
aeeebe6
De-"privatize" mock_in_unit_test and expose it
Jun 8, 2017
9f3d8ac
Bump version to 0.3.0
Jun 8, 2017
8d396bf
Set disable_recording from kwarg
Jun 8, 2017
b38e87e
Don't treat mock_in_unit_test as a unittest
Jun 8, 2017
741c521
Remove skip for mock_in_unit_test, specify test dir
Jun 14, 2017
5951319
Merge pull request #8 from Azure/dont-skip-patched-tests
troydai Jun 15, 2017
01655ba
Update version numbers
Jun 15, 2017
ab50387
Remove extraneous requirements
Jun 14, 2017
64d6479
Remove outdated README.md stuff
Jun 14, 2017
c33f0a9
Update install for new requirements.txt
Jun 14, 2017
17c6c68
Add 3.6-dev to pythons
Jun 14, 2017
d07dbc9
Update code style and run pylint in Travis
troydai Jun 17, 2017
0f4456e
Integrate with codecov.io
troydai Jun 17, 2017
b1a9c07
Add test cover IntegrationTestBase
troydai Jun 17, 2017
fd14899
Add test case for LiveTest constructor
troydai Jun 18, 2017
6cd35ae
Add tests cover create_random_name
troydai Jun 18, 2017
863f9ee
Add test covering get_sha1_hash
troydai Jun 18, 2017
579f803
Add test cover RecordingProcessor base class
troydai Jun 18, 2017
df25e5b
Add test covering SubscriptionRecordingProcessor
troydai Jun 18, 2017
aa64bee
Configure code coverage using configuration file
troydai Jun 18, 2017
c640434
Use unittest instead of nosetests to drive the automation
troydai Jun 19, 2017
b48db41
Fix a few code style issues
troydai Jun 19, 2017
3b896d7
Rename coveragerc file
troydai Jun 19, 2017
c9886bf
Add AccessTokenProcessor
Jun 22, 2017
66d4e3c
Merge pull request #20 from Azure/access-token-processor
Jun 23, 2017
cf97ac7
Split intro and overview into clauses
Jun 15, 2017
3809c29
Back to paragraphs
Jun 15, 2017
6ab0815
Update intro, add vcr link
Jun 15, 2017
6e848c6
Update intro paragraph to be more general
Jun 15, 2017
df97434
Remove reference to command modules
Jun 15, 2017
0714406
Update test and module refs; clarify VCR.py role
Jun 15, 2017
686c7a5
Remove outdated reference to class with builtin preparer
Jun 15, 2017
39cd932
Replace intro text
Jun 15, 2017
a585e0c
s/ScenarioTest/ReplayableTest/
Jun 15, 2017
0d8b639
Add note about semantic linefeeds
Jun 15, 2017
2ab07ea
Make semantic linefeeds note an HTML comment
Jun 16, 2017
3f306f0
Remove CLI examples and add links to consumers
Jun 16, 2017
c09c915
Remove doc for legacy test case class
Jun 16, 2017
a8f4d1a
Finish sentence
Jun 16, 2017
5b9b34e
Add subclass kwarg information
Jun 22, 2017
2158ee8
Fix subscription ID removal and test new cases
Jun 28, 2017
1396cf9
Fix no-self-use linter complaint
Jun 28, 2017
2ddd9c7
Downgrade the version of vcrpy dependency
troydai Jun 29, 2017
302ea45
Bump version to 0.5.0 to prepare for release
Jun 29, 2017
3e4f6e3
Fix import on Py3 not-TravisCI
lmazuel Aug 18, 2017
a6c1c1a
Fix recordmode
lmazuel Aug 18, 2017
9c3ceae
Tight config test
lmazuel Aug 18, 2017
43a30b7
url parsing fix
williexu Sep 5, 2017
b16236d
fixed pylint import order
williexu Sep 5, 2017
cbc6452
pylint import fix
williexu Sep 5, 2017
41fb8fd
added record processor for slashes
williexu Sep 6, 2017
16b92eb
changed processor name
williexu Sep 6, 2017
7ddeb1b
Bump version 0.5.0 => 0.5.1
troydai Sep 6, 2017
708c5ff
Fix SubscriptionIdReplacer
lmazuel Sep 19, 2017
141d9a2
Update version 0.5.1 => 0.5.2
troydai Sep 19, 2017
1692c9f
Update __init__.py
troydai Sep 19, 2017
bdaf7f7
Detect leaked LRO poller
lmazuel Oct 3, 2017
f547a97
PyLint happiness
lmazuel Oct 3, 2017
b90f321
Improve error message
lmazuel Oct 3, 2017
1bceef1
wip
yugangw-msft Feb 12, 2018
6b11374
fix error
yugangw-msft Feb 13, 2018
2357826
use base 64
yugangw-msft Feb 13, 2018
d83ad86
simplify
yugangw-msft Feb 13, 2018
733ecff
add comments
yugangw-msft Feb 13, 2018
ed535e1
fix tests
yugangw-msft Feb 13, 2018
0374015
fix lint error
yugangw-msft Feb 13, 2018
eeeac97
use newer pylint to avoid invalid line errors
yugangw-msft Feb 13, 2018
e63d229
add a new test
yugangw-msft Feb 13, 2018
db71ee5
address review feedback
yugangw-msft Feb 14, 2018
3a74ace
address review feedback
yugangw-msft Feb 14, 2018
1e467bf
support large response payload
yugangw-msft Feb 14, 2018
7a452e1
remove useless single quots from the error message
yugangw-msft Feb 14, 2018
9690738
Merge pull request #38 from yugangw-msft/bin2
tjprescott Feb 15, 2018
f037bef
setup: update version
yugangw-msft Feb 28, 2018
b737522
do not use preparer model for allowing large payload
yugangw-msft Mar 7, 2018
c4f01be
address review feedback
yugangw-msft Mar 7, 2018
23b537c
Support Autorest.Python 3.x LRO leak (#42)
lmazuel Apr 19, 2018
4aba710
Bump version to 0.5.5
troydai Apr 19, 2018
29150dc
Update the resource removal sequence
troydai May 2, 2018
8915c3f
remove x-ms-authorization-auxiliary header
yugangw-msft May 23, 2018
579cc3f
fix lint error
yugangw-msft May 23, 2018
38452be
update version
yugangw-msft May 24, 2018
f0c1dde
Add some CI tools to devtools
lmazuel May 8, 2018
328ae3e
Readme update
lmazuel May 9, 2018
ae76a2d
Some tests configuration
lmazuel May 9, 2018
5362764
Use Pytest as test launcher
lmazuel May 9, 2018
cbaf015
PyLint fixes
lmazuel May 9, 2018
3ceb13a
CI tools tests are Py3.6 only
lmazuel May 9, 2018
bddaf87
PyLint CI tools only if 3.6
lmazuel May 17, 2018
eaeea24
CI tools tests recording
lmazuel May 17, 2018
ee6b0b2
Dont't Pylint the tests
lmazuel May 17, 2018
5e9700e
Fix incorrect usage of os.environ
lmazuel May 17, 2018
410effb
Make tests X-platform valid
lmazuel May 17, 2018
d64bf9d
Fix test dependent of traceback
lmazuel May 17, 2018
6950ebc
Robust preparer testing
lmazuel May 17, 2018
3d9e0d9
PyGithub 1.40
lmazuel Jun 27, 2018
d4f6fc4
Release 1.1.0
lmazuel Jun 27, 2018
2bf87b1
Allow clone_to_path to have both PR and SHA1 (#49)
lmazuel Jul 6, 2018
d673282
Update github_tools.py (#55)
lmazuel May 9, 2019
a9cca7d
SingleValueReplacer.process_request properly decodes request.body if …
adewaleo Jun 4, 2019
a3e531c
Remove pointless files
lmazuel Jun 7, 2019
2282878
Move to tools
lmazuel Jun 7, 2019
fd9a377
Merge remote-tracking branch 'azure-python-devtools/transition' into …
lmazuel Jun 7, 2019
93dd51d
Update dev_requirement file
lmazuel Jun 7, 2019
862089b
Don't record requests to AAD OAuth2 v2.0 endpoint
chlowell Jun 7, 2019
7844a2f
Use devtools from repo, not from PyPI
lmazuel Jun 7, 2019
de523ec
Fix mock dep
lmazuel Jun 7, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions scripts/dev_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,20 @@ def pip_command(command, additional_dir=".", error_ok=False):
]
)

# Put azure-common in front
if "azure-common" in content_packages:
content_packages.remove("azure-common")
content_packages.insert(0, "azure-common")
# Install tests dep first
if "azure-devtools" in content_packages:
content_packages.remove("azure-devtools")
content_packages.insert(0, "azure-devtools")

if "azure-sdk-tools" in content_packages:
content_packages.remove("azure-sdk-tools")
content_packages.insert(1, "azure-sdk-tools")

# Put azure-common in front of content package
if "azure-common" in content_packages:
content_packages.remove("azure-common")
content_packages.insert(2, "azure-common")

print("Running dev setup...")
print("Root directory '{}'\n".format(root_dir))

Expand All @@ -100,8 +105,9 @@ def pip_command(command, additional_dir=".", error_ok=False):
pip_command("install {}/{}/".format(packages[package_name], package_name))

# install packages
print("Packages to install: {}".format(content_packages))
for package_name in content_packages:
print("Installing {}".format(package_name))
print("\nInstalling {}".format(package_name))
# if we are running dev_setup with no arguments. going after dev_requirements will be a pointless exercise
# and waste of cycles as all the dependencies will be installed regardless.
if os.path.isfile(
Expand Down
21 changes: 21 additions & 0 deletions tools/azure-devtools/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) Microsoft Corporation. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
32 changes: 32 additions & 0 deletions tools/azure-devtools/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.. image:: https://travis-ci.org/Azure/azure-python-devtools.svg?branch=master
:target: https://travis-ci.org/Azure/azure-python-devtools

Development tools for Python-based Azure tools
==============================================

This package contains tools to aid in developing Python-based Azure code.

This includes the following components:

scenario_tests
--------------

A testing framework to handle much of the busywork
associated with testing code that interacts with Azure.

ci_tools
--------

Some tooling to help developing CI tools. This includes some Git helpers,
Github RestAPI wrapper and a Bot framework for Github issues.

Contributing
============

This project has adopted the
`Microsoft Open Source Code of Conduct <https://opensource.microsoft.com/codeofconduct/>`__.
For more information see the
`Code of Conduct FAQ <https://opensource.microsoft.com/codeofconduct/faq/>`__
or contact
`[email protected] <mailto:[email protected]>`__
with any additional questions or comments.
4 changes: 4 additions & 0 deletions tools/azure-devtools/dev_requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-e .[ci_tools]

mock;python_version<="2.7"
pytest
101 changes: 101 additions & 0 deletions tools/azure-devtools/doc/scenario_base_tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# How to write ReplayableTest based VCR tests

The `scenario_tests` package uses the [VCR.py](https://pypi.python.org/pypi/vcrpy) library
to record the HTTP messages exchanged during a program run
and play them back at a later time,
making it useful for creating "scenario tests"
that interact with Azure (or other) services.
These tests can be replayed at a later time without any network activity,
allowing us to detect changes in the Python layers
between the code being tested and the underlying REST API.


## Overview

Tests all derive from the `ReplayableTest` class
found in `azure_devtools.scenario_tests.base`.
This class exposes the VCR tests using the standard Python `unittest` framework
and allows the tests to be discovered by and debugged in Visual Studio.

When you run a test,
the test driver will automatically detect the test is unrecorded
and record the HTTP requests and responses in a .yaml file
(referred to by VCR.py as a "cassette").
If the test succeeds, the cassette will be preserved
and future playthroughs of the test will come from the cassette
rather than using actual network communication.

If the tests are run on TravisCI,
any tests which cannot be replayed will automatically fail.

`ReplayableTest` itself derives from `IntegrationTestBase`,
which provides some helpful methods for use in more general unit tests
but no functionality pertaining to network communication.


## Configuring ReplayableTest

The only configuration of `ReplayableTest` that is "exposed"
(in the sense of being accessible other than through subclassing)
is whether tests should be run in "live" or "playback" mode.
This can be set in the following two ways,
of which the first takes precedence:
* Set the environment variable `AZURE_TEST_RUN_LIVE`.
Any value will cause the tests to run in live mode;
if the variable is unset the default of playback mode will be used.
* Specify a boolean value for `live-mode` in a configuration file,
the path to which must be specified by a `ReplayableTest` subclass as described below
(i.e. by default no config file will be read).
True values mean "live" mode; false ones mean "playback."

"Live" and "playback" mode are actually just shorthand for recording modes
in the underlying VCR.py package;
they correspond to "all" and "once"
as described in the [VCR.py documentation](http://vcrpy.readthedocs.io/en/latest/usage.html#record-modes).

### Subclassing ReplayableTest and features

Most customization of `ReplayableTest` is accessible only through subclassing.

The two main users of `ReplayableTest` are
[azure-cli](https://github.com/Azure/azure-cli)
and [azure-sdk-for-python](https://github.com/Azure/azure-sdk-for-python).
Each uses a subclass of `ReplayableTest` to add context-specific functionality
and preserve backward compatibility with test code
prior to the existence of `azure-devtools`.
For example, azure-cli's [compatibility layer](https://github.com/Azure/azure-cli/tree/master/src/azure-cli-testsdk)
adds methods for running CLI commands and evaluating their output.

Subclasses of `ReplayableTest` can configure its behavior
by passing the following keyword arguments when they call
its `__init__` method (probably using `super`):

* `config_file`: Path to a configuration file.
It should be in the format described in Python's
[ConfigParser](https://docs.python.org/3/library/configparser.html) docs
and currently allows only the boolean option `live-mode`.
* `recording_dir` and `recording_name`:
Directory path and file name, respectively,
for the recording that should be used for a given test case.
By default, the directory will be a `recordings` directory
in the same location as the file containing the test case,
and the file name will be the same as the test method name.
A `.yaml` extension will be appended to whatever is used for `recording_name`.
* `recording_processors` and `replay_processors`:
Lists of `RecordingProcessor` instances for making changes to requests and responses
during test recording and test playback, respectively.
See [recording_processors.py](src/azure_devtools/scenario_tests/recording_processors.py)
for some examples and how to implement them.
* `recording_patches` and `replay_patches`:
Lists of patches to apply to functions, methods, etc.
during test recording and playback, respectively.
See [patches.py](src/azure_devtools/scenario_tests/patches.py)
for some examples. Note the `mock_in_unit_test` function
which abstracts out some boilerplate for applying a patch.


<!--
Note: This document's source uses
[semantic linefeeds](http://rhodesmill.org/brandon/2012/one-sentence-per-line/)
to make diffs and updates clearer.
-->
4 changes: 4 additions & 0 deletions tools/azure-devtools/scripts/ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

pylint src/azure_devtools
pytest src/azure_devtools/scenario_tests/tests --cov=./
2 changes: 2 additions & 0 deletions tools/azure-devtools/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[bdist_wheel]
universal=1
63 changes: 63 additions & 0 deletions tools/azure-devtools/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python

# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

import io
from setuptools import setup


VERSION = "1.1.1"


CLASSIFIERS = [
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'License :: OSI Approved :: MIT License',
]


DEPENDENCIES = [
'ConfigArgParse>=0.12.0',
'six>=1.10.0',
'vcrpy>=1.11.0',
]

with io.open('README.rst', 'r', encoding='utf-8') as f:
README = f.read()

setup(
name='azure-devtools',
version=VERSION,
description='Microsoft Azure Development Tools for SDK',
long_description=README,
license='MIT',
author='Microsoft Corporation',
author_email='[email protected]',
url='https://github.com/Azure/azure-python-devtools',
zip_safe=False,
classifiers=CLASSIFIERS,
packages=[
'azure_devtools',
'azure_devtools.scenario_tests',
'azure_devtools.ci_tools',
],
extras_require={
'ci_tools':[
"PyGithub>=1.40", # Can Merge PR after 1.36, "requests" and tests after 1.40
"GitPython",
"requests>=2.0"
]
},
package_dir={'': 'src'},
install_requires=DEPENDENCIES,
)
Empty file.
4 changes: 4 additions & 0 deletions tools/azure-devtools/src/azure_devtools/ci_tools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
121 changes: 121 additions & 0 deletions tools/azure-devtools/src/azure_devtools/ci_tools/bot_framework.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from collections import namedtuple
from functools import lru_cache
import logging
import os
import re

from github import Github, GithubException, UnknownObjectException

from .github_tools import (
exception_to_github,
)

_LOGGER = logging.getLogger(__name__)


def order(function):
function.bot_order = True
return function

WebhookMetadata = namedtuple(
'WebhookMetadata',
['repo', 'issue', 'text', 'comment']
)

def build_from_issue_comment(gh_token, body):
"""Create a WebhookMetadata from a comment added to an issue.
"""
if body["action"] in ["created", "edited"]:
github_con = Github(gh_token)
repo = github_con.get_repo(body['repository']['full_name'])
issue = repo.get_issue(body['issue']['number'])
text = body['comment']['body']
try:
comment = issue.get_comment(body['comment']['id'])
except UnknownObjectException:
# If the comment has already disapeared, skip the command
return None
return WebhookMetadata(repo, issue, text, comment)
return None

def build_from_issues(gh_token, body):
"""Create a WebhookMetadata from an opening issue text.
"""
if body["action"] in ["opened", "edited"]:
github_con = Github(gh_token)
repo = github_con.get_repo(body['repository']['full_name'])
issue = repo.get_issue(body['issue']['number'])
text = body['issue']['body']
comment = issue # It's where we update the comment: in the issue itself
return WebhookMetadata(repo, issue, text, comment)
return None

@lru_cache()
def robot_name_from_env_variable():
github_con = Github(os.environ["GH_TOKEN"])
return github_con.get_user().login


class BotHandler:
def __init__(self, handler, robot_name=None, gh_token=None):
self.handler = handler
self.gh_token = gh_token or os.environ["GH_TOKEN"]
self.robot_name = robot_name or robot_name_from_env_variable()

def _is_myself(self, body):
return body['sender']['login'].lower() == self.robot_name.lower()

def issue_comment(self, body):
if self._is_myself(body):
return {'message': 'I don\'t talk to myself, I\'m not schizo'}
webhook_data = build_from_issue_comment(self.gh_token, body)
return self.manage_comment(webhook_data)

def issues(self, body):
if self._is_myself(body):
return {'message': 'I don\'t talk to myself, I\'m not schizo'}
webhook_data = build_from_issues(self.gh_token, body)
return self.manage_comment(webhook_data)

def orders(self):
"""Return method tagged "order" in the handler.
"""
return [order_cmd for order_cmd in dir(self.handler)
if getattr(getattr(self.handler, order_cmd), "bot_order", False)]

def manage_comment(self, webhook_data):
if webhook_data is None:
return {'message': 'Nothing for me'}
# Is someone talking to me:
message = re.search("@{} (.*)".format(self.robot_name), webhook_data.text, re.I)
response = None
if message:
command = message.group(1)
split_text = command.lower().split()
orderstr = split_text.pop(0)
if orderstr == "help":
response = self.help_order()
elif orderstr in self.orders():
try: # Reaction is fun, but it's preview not prod.
# Be careful, don't fail the command if we can't thumbs up...
webhook_data.comment.create_reaction("+1")
except GithubException:
pass
with exception_to_github(webhook_data.issue): # Just in case
response = getattr(self.handler, orderstr)(webhook_data.issue, *split_text)
else:
response = "I didn't understand your command:\n```bash\n{}\n```\nin this context, sorry :(\n".format(
command
)
response += self.help_order()
if response:
webhook_data.issue.create_comment(response)
return {'message': response}
return {'message': 'Nothing for me or exception'}

def help_order(self):
orders = ["This is what I can do:"]
for orderstr in self.orders():
orders.append("- `{}`".format(orderstr))
orders.append("- `help` : this help message")
return "\n".join(orders)
Loading