Skip to content
This repository was archived by the owner on Mar 14, 2023. It is now read-only.

Commit 4b97a5a

Browse files
committed
load configuration from the environment
1 parent c6a8488 commit 4b97a5a

File tree

11 files changed

+112
-48
lines changed

11 files changed

+112
-48
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
/.env
2-
config
32
*.pyc
43
*~

Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ COPY setup.py .
1414
COPY highfive/*.py highfive/
1515
COPY highfive/configs/ highfive/configs/
1616
RUN pip install .
17-
RUN touch highfive/config
1817

1918
EXPOSE 80
2019
ENV HIGHFIVE_PORT 80

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,11 @@ your local instance. Here is one approach for running a local server:
162162
or domain name and port number (if necessary).
163163
- Obtain an OAuth token. In the account you are creating the token in,
164164
go to https://github.com/settings/tokens. Grant access to the repository scope.
165-
- Put the authorization information obtained in the previous step into
166-
a file named config in the top of the repository (i.e., the
167-
directory containing this file). Here's a template of what it should
168-
look like:
165+
- Put the authorization information obtained in the previous step into a file
166+
named `.env` in the top of the repository (i.e., the directory containing
167+
this file). Here is a template of what it should look like:
169168
```
170-
[github]
171-
user: OAUTH_TOKEN_USER
172-
token: OAUTH_TOKEN
169+
HIGHFIVE_GITHUB_TOKEN=your-token
173170
```
174171
_Do not check in this file or commit your OAuth token to a
175172
repository in any other way. It is a secret._
@@ -189,13 +186,17 @@ Docker
189186

190187
Alternatively, you can build a Docker image that runs Highfive.
191188

192-
$ docker build -t highfive .
189+
```
190+
$ docker build -t highfive .
191+
```
193192

194193
To run a container, you must mount a config file. Assuming you are
195194
launching a container from a directory containing a config file, you
196195
can do the following.
197196

198-
$ docker run -d --rm --name highfive -p 8080:80 -v $(pwd)/config:/highfive/highfive/config highfive
197+
```
198+
$ docker run -d --rm --name highfive -p 8000:80 -e HIGHFIVE_GITHUB_TOKEN=token highfive
199+
```
199200

200201
At this point, Highfive is accessible at http://localhost:8080.
201202

highfive/app.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import json
2+
import sys
23

4+
from .config import Config, InvalidTokenException
35
from .newpr import HighfiveHandler, UnsupportedRepoError
46
from .payload import Payload
57

@@ -9,7 +11,7 @@
911
import waitress
1012

1113

12-
def create_app():
14+
def create_app(config):
1315
app = flask.Flask(__name__)
1416

1517
# The canonical URL is /webhook, but other URLs are accepted for backward
@@ -23,7 +25,7 @@ def new_pr():
2325
except (KeyError, ValueError), _:
2426
return 'Error: missing or invalid payload\n', 400
2527
try:
26-
handler = HighfiveHandler(Payload(payload))
28+
handler = HighfiveHandler(Payload(payload), config)
2729
return handler.run(flask.request.headers['X-GitHub-Event'])
2830
except UnsupportedRepoError:
2931
return 'Error: this repository is not configured!\n', 400
@@ -37,8 +39,16 @@ def index():
3739

3840
@click.command()
3941
@click.option('--port', default=8000)
40-
def cli(port):
41-
app = create_app()
42+
@click.option('--github-token', required=True)
43+
def cli(port, github_token):
44+
try:
45+
config = Config(github_token)
46+
except InvalidTokenException:
47+
print 'error: invalid github token provided!'
48+
sys.exit(1)
49+
print 'Found a valid GitHub token for user @' + config.github_username
50+
51+
app = create_app(config)
4252
waitress.serve(app, port=port)
4353

4454

highfive/config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import requests
2+
3+
4+
class InvalidTokenException(Exception):
5+
pass
6+
7+
8+
class Config(object):
9+
def __init__(self, github_token):
10+
if not github_token:
11+
raise InvalidTokenException()
12+
self.github_token = github_token
13+
self.github_username = self.fetch_github_username()
14+
15+
def fetch_github_username(self):
16+
response = requests.get('https://api.github.com/user', headers={
17+
'Authorization': 'token ' + self.github_token
18+
})
19+
if response.status_code != 200:
20+
raise InvalidTokenException()
21+
return response.json()['login']

highfive/newpr.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,11 @@ class UnsupportedRepoError(IOError):
4747
pass
4848

4949
class HighfiveHandler(object):
50-
def __init__(self, payload):
50+
def __init__(self, payload, config):
5151
self.payload = payload
5252

53-
self.config = ConfigParser.RawConfigParser()
54-
self.config.read('./config')
55-
self.integration_user = self.config.get('github', 'user')
56-
self.integration_token = self.config.get('github', 'token')
53+
self.integration_user = config.github_username
54+
self.integration_token = config.github_token
5755

5856
self.repo_config = self.load_repo_config()
5957

highfive/tests/test_config.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import pytest
2+
import responses
3+
4+
from ..config import Config, InvalidTokenException
5+
6+
7+
@pytest.mark.unit
8+
@pytest.mark.hermetic
9+
class TestConfig(object):
10+
def test_empty_token(self):
11+
for token in ['', None]:
12+
with pytest.raises(InvalidTokenException):
13+
Config(token)
14+
15+
@responses.activate
16+
def test_valid_token(self):
17+
responses.add(
18+
responses.GET, 'https://api.github.com/user',
19+
json={'login': 'baz'},
20+
)
21+
22+
config = Config('foobar')
23+
assert config.github_token == 'foobar'
24+
assert config.github_username == 'baz'
25+
26+
@responses.activate
27+
def test_invalid_token(self):
28+
responses.add(responses.GET, 'https://api.github.com/user', status=403)
29+
30+
with pytest.raises(InvalidTokenException):
31+
Config('foobar')

highfive/tests/test_integration_tests.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from highfive import newpr, payload
2+
from highfive.config import Config
23
from highfive.tests import fakes
34
from highfive.tests.patcherize import patcherize
45
from highfive.tests.test_newpr import HighfiveHandlerMock
56
import mock
67
import pytest
8+
import responses
79

810
@pytest.mark.integration
911
class TestIsNewContributor(object):
@@ -41,6 +43,14 @@ def verify_calls(self):
4143

4244
assert self.mock.call_count == len(self.calls)
4345

46+
def dummy_config():
47+
with responses.RequestsMock() as resp:
48+
resp.add(
49+
responses.GET, 'https://api.github.com/user',
50+
json={'login': 'integration-user'},
51+
)
52+
return Config('integration_token')
53+
4454
@pytest.mark.integration
4555
@pytest.mark.hermetic
4656
class TestNewPr(object):
@@ -54,10 +64,6 @@ def make_mocks(cls, patcherize):
5464

5565
cls.mocks['get_irc_nick'].return_value = None
5666

57-
config_mock = mock.Mock()
58-
config_mock.get.side_effect = ('integration-user', 'integration-token')
59-
cls.mocks['ConfigParser'].RawConfigParser.return_value = config_mock
60-
6167
cls.mocks['load_json_file'].side_effect = (
6268
fakes.get_repo_configs()['individuals_no_dirs'],
6369
fakes.get_global_configs()['base'],
@@ -67,7 +73,7 @@ def test_new_pr_non_contributor(self):
6773
payload = fakes.Payload.new_pr(
6874
repo_owner='rust-lang', repo_name='rust', pr_author='pnkfelix'
6975
)
70-
handler = newpr.HighfiveHandler(payload)
76+
handler = newpr.HighfiveHandler(payload, dummy_config())
7177

7278
api_req_mock = ApiReqMocker([
7379
(
@@ -108,7 +114,7 @@ def test_new_pr_empty_body(self):
108114
repo_owner='rust-lang', repo_name='rust', pr_author='pnkfelix',
109115
pr_body=None,
110116
)
111-
handler = newpr.HighfiveHandler(payload)
117+
handler = newpr.HighfiveHandler(payload, dummy_config())
112118

113119
api_req_mock = ApiReqMocker([
114120
(
@@ -148,7 +154,7 @@ def test_new_pr_contributor(self):
148154
payload = fakes.Payload.new_pr(
149155
repo_owner='rust-lang', repo_name='rust', pr_author='pnkfelix'
150156
)
151-
handler = newpr.HighfiveHandler(payload)
157+
handler = newpr.HighfiveHandler(payload, dummy_config())
152158

153159
api_req_mock = ApiReqMocker([
154160
(
@@ -192,7 +198,7 @@ def test_new_pr_contributor_with_labels(self):
192198
payload = fakes.Payload.new_pr(
193199
repo_owner='rust-lang', repo_name='rust', pr_author='pnkfelix'
194200
)
195-
handler = newpr.HighfiveHandler(payload)
201+
handler = newpr.HighfiveHandler(payload, dummy_config())
196202

197203
api_req_mock = ApiReqMocker([
198204
(
@@ -253,7 +259,7 @@ def make_mocks(cls, patcherize):
253259

254260
def test_author_is_commenter(self):
255261
payload = fakes.Payload.new_comment()
256-
handler = newpr.HighfiveHandler(payload)
262+
handler = newpr.HighfiveHandler(payload, dummy_config())
257263
api_req_mock = ApiReqMocker([
258264
(
259265
(
@@ -270,7 +276,7 @@ def test_author_not_commenter_is_collaborator(self):
270276
payload = fakes.Payload.new_comment()
271277
payload._payload['issue']['user']['login'] = 'foouser'
272278

273-
handler = newpr.HighfiveHandler(payload)
279+
handler = newpr.HighfiveHandler(payload, dummy_config())
274280
api_req_mock = ApiReqMocker([
275281
(
276282
(

highfive/tests/test_newpr.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from copy import deepcopy
22
from highfive import newpr
3+
from highfive.config import Config
34
from highfive.payload import Payload
45
from highfive.tests import fakes
56
from highfive.tests.patcherize import patcherize
@@ -8,6 +9,7 @@
89
import mock
910
import os
1011
import pytest
12+
import responses
1113
from urllib2 import HTTPError
1214

1315
@pytest.mark.unit
@@ -24,27 +26,28 @@ def __init__(
2426
self.integration_user = integration_user
2527
self.integration_token = integration_token
2628

27-
def config_handler(section, key):
28-
if section == 'github':
29-
if key == 'user':
30-
return integration_user
31-
if key == 'token':
32-
return integration_token
33-
3429
self.patchers_stopped = False
30+
31+
with responses.RequestsMock() as resp:
32+
resp.add(
33+
responses.GET, 'https://api.github.com/user',
34+
json={'login': integration_user},
35+
)
36+
if integration_token:
37+
config = Config(integration_token)
38+
else:
39+
config = Config('dummy')
40+
config.github_token = ''
41+
3542
self.config_patcher = mock.patch('highfive.newpr.ConfigParser')
36-
self.mock_config_parser = self.config_patcher.start()
37-
self.mock_config = mock.Mock()
38-
self.mock_config.get.side_effect = config_handler
39-
self.mock_config_parser.RawConfigParser.return_value = self.mock_config
4043

4144
self.load_repo_config_patcher = mock.patch(
4245
'highfive.newpr.HighfiveHandler.load_repo_config'
4346
)
4447
self.mock_load_repo_config = self.load_repo_config_patcher.start()
4548
self.mock_load_repo_config.return_value = repo_config
4649

47-
self.handler = newpr.HighfiveHandler(payload)
50+
self.handler = newpr.HighfiveHandler(payload, config)
4851

4952
def __enter__(self):
5053
return self
@@ -58,7 +61,6 @@ def __del__(self):
5861
def stop_patchers(self):
5962
if not self.patchers_stopped:
6063
self.patchers_stopped = True
61-
self.config_patcher.stop()
6264
self.load_repo_config_patcher.stop()
6365

6466
class TestHighfiveHandler(TestNewPR):
@@ -67,11 +69,9 @@ def test_init(self, mock_load_repo_config):
6769
payload = Payload({'the': 'payload'})
6870
with HighfiveHandlerMock(payload, repo_config={'a': 'config!'}) as m:
6971
assert m.handler.payload == payload
70-
assert m.handler.config == m.mock_config
7172
assert m.handler.integration_user == 'integrationUser'
7273
assert m.handler.integration_token == 'integrationToken'
7374
assert m.handler.repo_config == {'a': 'config!'}
74-
m.mock_config.read.assert_called_once_with('./config')
7575

7676
@mock.patch('highfive.newpr.HighfiveHandler._load_json_file')
7777
def test_load_repo_config_supported(self, mock_load_json_file):
@@ -1255,22 +1255,19 @@ def test_newpr(self):
12551255
payload = Payload({'action': 'opened'})
12561256
m = self.handler_mock(payload)
12571257
assert m.handler.run('pull_request') == 'OK\n'
1258-
assert m.mock_config.get.call_count == 2
12591258
self.mocks['new_pr'].assert_called_once_with()
12601259
self.mocks['new_comment'].assert_not_called()
12611260

12621261
def test_new_comment(self):
12631262
payload = Payload({'action': 'created'})
12641263
m = self.handler_mock(payload)
12651264
assert m.handler.run('issue_comment') == 'OK\n'
1266-
assert m.mock_config.get.call_count == 2
12671265
self.mocks['new_pr'].assert_not_called()
12681266
self.mocks['new_comment'].assert_called_once_with()
12691267

12701268
def test_unsupported_payload(self):
12711269
payload = Payload({'action': 'something-not-supported'})
12721270
m = self.handler_mock(payload)
12731271
assert m.handler.run('issue_comment') != 'OK\n'
1274-
assert m.mock_config.get.call_count == 2
12751272
self.mocks['new_pr'].assert_not_called()
12761273
self.mocks['new_comment'].assert_not_called()

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
'click',
1717
'flask',
1818
'python-dotenv',
19+
'requests',
1920
'waitress',
2021
],
2122
entry_points = {

0 commit comments

Comments
 (0)