Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api #7

Merged
merged 16 commits into from
Aug 19, 2014
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ script:
- blumpkin coverage coverage.xml bob.builders:70
after_success:
- find thed -name "*.py" | xargs pep8 --ignore=E711 | tee pep8.out
notifications:
webhooks: http://requestb.in/sltsafsl
10 changes: 9 additions & 1 deletion bob/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ class BobError(Exception):
pass


import builders, notifiers, transports
import api
import builders
import notifiers
import transports


def main(global_config, **settings):
"""
This function returns a Pyramid WSGI application.
"""
return api.create_app()
32 changes: 32 additions & 0 deletions bob/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from __future__ import unicode_literals

import pyramid.threadlocal

from thed import api

from . import hooks


registry = pyramid.threadlocal.get_current_registry()
settings = registry.settings


def includeme(config):
hooks.HookController.scan(config)


def create(**overrides):
return api.Application.create(
{},
includes=['thed.api.resources', 'thed.api.controllers'],
**overrides
)


def create_app(**overrides):
def hook(config):
includeme(config)
config.add_view_predicate('resource', api.predicates.ResourcePredicate)
config.scan()

return create(hook=hook, **overrides)
33 changes: 33 additions & 0 deletions bob/api/forms/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from __future__ import unicode_literals

import github
import travis
from github import GithubForm
from travis import TravisForm

from bob.builders.ubuntu import UbuntuBuilder


def build(github_organization, github_repo, commit_hash_or_tag):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1, use venusian.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure i understand. can you expand or clarify?

from bob.api import settings

# HACK: until we figure out settings.ini
settings = settings or {}
working_dir = settings.get('working_dir', '~/work')
output_dir = settings.get('output_dir', '~/out')
builder = UbuntuBuilder(
github_repo, working_dir, output_dir
)
builder.prepare_workspace(
github_organization, github_repo, commit_hash_or_tag
)

builder.parse_options()
try:
builder.prepare_system()
builder.build()
package_name = builder.package(commit_hash_or_tag)
builder.upload(package_name)
builder.notify_success(commit_hash_or_tag)
except Exception as ex:
builder.notify_failure(commit_hash_or_tag, ex)
20 changes: 20 additions & 0 deletions bob/api/forms/github.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from __future__ import unicode_literals

import pilo


class GithubForm(pilo.Form):

commit = pilo.fields.String('ref')

@commit.filter
def commit(self, value):
if 'refs/tags' not in value:
return pilo.NONE
return value

name = pilo.fields.String('repository.name')

organization = pilo.fields.String('repository.organization')

build = pilo.fields.Boolean(default=False)
58 changes: 58 additions & 0 deletions bob/api/forms/travis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from __future__ import unicode_literals
from hashlib import sha256

import semantic_version
import pilo


user_tokens = {
'mjallday': 'irgy9pphBTydWzVYskRq'
}


class TravisForm(pilo.Form):

commit = pilo.fields.String()

success = pilo.fields.String('result_message')

name = pilo.fields.String('repository.name')

organization = pilo.fields.String('repository.owner_name')

branch = pilo.fields.String()

@success.munge
def success(self, value):
return value in ('Passed', 'Fixed')

build = pilo.fields.Boolean(default=False)

@build.compute
def build(self):
# branch is the tag on travis. since the tag should look like 1.0.0
# we can check if it is parseable as a semver to decide if this test
# run was for a build (tag) or a commit. on tag we can say build = True
# if the tests passed
commit = self['branch']
if commit and commit[0] == 'v':
commit = commit[1:]
try:
semantic_version.Version(commit)
except ValueError:
return False
else:
return self['success']


def compute_travis_security(headers, form):
auth_header = headers['Authorization']
organization = form['organization']
repo_name = form['name']
is_secure = any(
sha256(
'{}/{}{}'.format(organization, repo_name, token)
).hexdigest() == auth_header
for token in user_tokens.itervalues()
)
return is_secure
36 changes: 36 additions & 0 deletions bob/api/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import unicode_literals

from thed import api

from . import forms


@api.Resource.nest('hooks')
class HookResource(api.Resource):

pass


@api.RestController.register('hooks', context=HookResource)
class HookController(api.RestController):

@api.decorators.view_config(name='github', request_method='POST')
def github(self):
result = forms.GithubForm(self.request.json)
return api.Response('github.created')

@api.decorators.view_config(name='travis', request_method='POST')
def travis(self):
result = forms.TravisForm(self.request.json)
if result['build']:
forms.build(
result['organization'], result['name'], result['commit']
)

def iterate_response():
for c in 'travis.created':
import time
yield str(c)
time.sleep(0.1)

return api.Response(app_iter=iterate_response())
18 changes: 12 additions & 6 deletions bob/builders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,18 @@ class Builder(object):

notifications = None

def __init__(self, project_name, working_dir, output_dir, tmp_dir=None):
logger = None

def __init__(self, project_name, working_dir, output_dir, tmp_dir=None,
log_stream=None):
self.project_name = project_name
self.working_dir = working_dir
self.output_dir = output_dir
self.tmp_dir = os.path.join(
(tmp_dir or '/tmp'), project_name + '.build'
)
self.configured = False
self.logger = log_stream or logger

@property
def source(self):
Expand All @@ -73,21 +77,23 @@ def target(self):
project_name=self.project_name
)

@classmethod
def run_command(cls, command, **kwargs):
def log(self, msg, level='info', **kwargs):
getattr(self.logger, level)(msg, **kwargs)

def run_command(self, command, **kwargs):
if not isinstance(command, list):
command = [command]

logger.debug(command)
logger.debug(kwargs)
self.log(command, 'debug')
self.log(kwargs, 'debug')

result = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
shell=True, **kwargs
)

for line in result.stdout:
logger.info(line.decode('utf8').replace('\n', '', 1))
self.log(line.decode('utf8').replace('\n', '', 1), 'info')

if result.returncode:
raise Exception(
Expand Down
4 changes: 2 additions & 2 deletions bob/builders/ubuntu/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def _parse_options_v1(self):
result = forms.V1Settings(**settings)
for key, value in result['targets'][self.flavor].iteritems():
setattr(self, key, value)
logger.info(key)
logger.info(value)
self.log(key, 'info')
self.log(value, 'info')
self.configured = True

def _install_system_dependencies(self):
Expand Down
3 changes: 2 additions & 1 deletion bob/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import unicode_literals

from . import build
from . import build, serve


def add_commands(group):
group.add_command(build.group)
group.add_command(serve.serve)
31 changes: 31 additions & 0 deletions bob/commands/serve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from __future__ import unicode_literals
import logging
import wsgiref.simple_server

import click

from bob import api


logger = logging.getLogger(__name__)


class Dispatcher(object):

def __init__(self, app):
self.app = app

def __call__(self, environ, start_response):
return self.app(environ, start_response)


@click.command('serve')
@click.option('--port', default=6543)
@click.option('--host', default='0.0.0.0')
def serve(port, host):
dispatcher = Dispatcher(api.create_app())
server = wsgiref.simple_server.make_server(
host, port, dispatcher,
)
logger.info('serving on %s:%s ...', *server.server_address)
server.serve_forever()
2 changes: 1 addition & 1 deletion bob/transports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def upload(self, path_to_file, destination=None, **_):
conn = boto.connect_s3()
bucket = conn.get_bucket(destination)
file_name = os.path.basename(path_to_file)
key = bucket.get_key(file_name )
key = bucket.get_key(file_name)
if not key:
key = bucket.new_key(file_name)
key.set_contents_from_file(path_to_file)
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@
'hipchat',
'configobj',
'pilo>=0.3.8,<0.4',
'semantic_version>=2.3.0,<2.4'
]

extras_require = {
'tests': [
'blumpkin>=0.4.0,<0.5.0',
'ipdb',
'webtest',
]
}

Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__author__ = 'marshall'
from __future__ import unicode_literals
17 changes: 17 additions & 0 deletions tests/fixtures/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import unicode_literals
import json
import os


def get_for_reals_path(file_name):
return os.path.join(
os.path.dirname(
os.path.realpath(__file__)
),
file_name
)


def load_json(path):
with open(path) as f:
return json.load(f)
Loading