Skip to content

Commit

Permalink
adding tox
Browse files Browse the repository at this point in the history
  • Loading branch information
ghostsquad committed Jul 22, 2016
1 parent 4e095cc commit fec2c26
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 62 deletions.
8 changes: 8 additions & 0 deletions Dockerfile
@@ -0,0 +1,8 @@
#swarmci
FROM python:3-alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY requirements.txt /usr/src/app/
RUN pip install --no-cache-dir -r requirements.txt
COPY . /usr/src/app
CMD [ "python", "-m", "swarmci" ]
3 changes: 3 additions & 0 deletions Dockerfile.test
@@ -0,0 +1,3 @@
FROM swarmci
RUN pip install --no-cache-dir -e ".[test]"
CMD [ "python", "-m", "pytest", "tests/" ]
73 changes: 26 additions & 47 deletions README.md
@@ -1,36 +1,12 @@
Swarm CI
========

## Architecture

![swarmci overview](docs/swarmci.png)

* Requires Docker 1.12 or greater
* Requires Redis for Task Queuing and Coordination

The basic idea of Swarm CI is to leverage Docker Swarm to run builds and tests within containers in an easy, distributed, parallel and fast way.

## Getting Started

### Composing a `.swarmci` file

#### Git Repository

First, you'll need to tell SwarmCI a bit about your repository. Repositories requiring authentication are currently not supported natively, though if you build a docker image with SSH private key baked in under the root account, then ssh will work without any extra configurations in the `.swarmci` file.

```yaml
git:
url: git+https://my.repo/project.git
```

##### Clone Depth
You can customize the clone depth (which defaults 50)

```yaml
git:
depth: 3
```

#### Build Layers

A `.swarmci` file consists of several layers.
Expand All @@ -42,7 +18,6 @@ A `.swarmci` file consists of several layers.
Each job consists of several pieces of information:

* `image(s)` **(required)**: the image to be used for all tasks within this job. This image should be on an available registry for the swarm to pull from (or be built using the `build` task). It should not have an entrypoint, as we'll want to execute an infinite sleep shell command so that it _does not exit_, because all tasks will run on this container, and SwarmCI expects to be able to launch the container, leave it running, and exec tasks on the running container. This can be either a string or a list. When in list form, this job will be converted to a [job matrix](#job-matrix).
* `clone` _(optional)_: not all jobs need to clone the repo, set this to `False` if you don't need to clone. Default `True`
* `env` _(optional)_: environment variables to be made available for `tasks`, `before_compose`, `after_failure`, and `finally`. This can be dictionary or a list of dictionaries. When in list form, this job will be converted to a [job matrix](#job-matrix).
* `build` _(optional)_: Similar to the [docker compose build](https://docs.docker.com/compose/compose-file/#build). The SwarmCI agent can build and run the docker image locally before running tasks. The name of the built image will be that of the `image` key within the job. If the `image` job data is a list, it will not be possible to determine what name should be used to build, therefore, you
* `task(s)` **(required)**: This can be either a string or a list. If any task in the list fails, subsequent tasks will not be run, however, `after_failure` and `finally` will run if defined.
Expand All @@ -52,29 +27,27 @@ Each job consists of several pieces of information:
Full Example:

```yaml
stages:
- foo-stage:
bar-job:
image: my-ci-python:3.5
build:
context: ./docker-dir
dockerfile: Dockerfile-alternate
args:
buildno: 1
env:
foo: bar
hello: world
clone: False
tasks:
- /bin/echo "this runs first"
- python -m pytest tests/
after_failure: /bin/echo "this runs if any script task fails"
finally: /bin/echo "this runs regardless of the result of the script tasks"
- bar-stage:
job1:
image: foo
task: /bin/echo "hello world"

stages:
- my_stage:
- my_job:
image: my-ci-python:3.5
build:
context: ./docker-dir
dockerfile: Dockerfile-alternate
args:
buildno: 1
env:
say_something: hello from
clone: False
tasks:
- /bin/bash -c 'echo "$say_something $HOSTNAME"'
after_failure: /bin/echo "this runs if any script task fails"
finally: /bin/echo "this runs regardless of the result of the script tasks"
- another_job:
image: ubuntu
tasks:
- /bin/bash -c 'echo "hello again from $HOSTNAME"'
```

#### <a name="job-matrix"></a>Job Matrix
Expand Down Expand Up @@ -103,4 +76,10 @@ vagrant ssh manager
pushd /vagrant
python setup.py install --force
python swarmci/agent/__init__.py
```

## Running Tests

```
python3.5 runtox.py -e linting,py35
```
Binary file removed docs/swarmci.png
Binary file not shown.
8 changes: 8 additions & 0 deletions runtox.py
@@ -0,0 +1,8 @@
#!/usr/bin/env python

if __name__ == "__main__":
import subprocess
import sys
subprocess.call([sys.executable, "-m", "tox",
"-i", "ALL=https://devpi.net/hpk/dev/",
"--develop"] + sys.argv[1:])
28 changes: 27 additions & 1 deletion setup.py
Expand Up @@ -7,16 +7,40 @@

# Always prefer setuptools over distutils
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand
# To use a consistent encoding
from codecs import open
from os import path
import sys

here = path.abspath(path.dirname(__file__))

# Get the long description from the README file
#with open(path.join(here, '../README.md'), encoding='utf-8') as f:
# long_description = f.read()


class Tox(TestCommand):
user_options = [('tox-args=', 'a', "Arguments to pass to tox")]

def initialize_options(self):
TestCommand.initialize_options(self)
self.tox_args = None

def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True

def run_tests(self):
#import here, cause outside the eggs aren't loaded
import tox
import shlex
args = self.tox_args
if args:
args = shlex.split(self.tox_args)
tox.cmdline(args=args)

setup(
name='swarmci',

Expand Down Expand Up @@ -84,5 +108,7 @@
'assertpy',
'mock',
'requests-mock==1.0.0']
}
},

cmdclass={'test': Tox},
)
Empty file added swarmci/__main__.py
Empty file.
31 changes: 17 additions & 14 deletions swarmci/agent/__init__.py
Expand Up @@ -25,34 +25,37 @@ def create_stages(yaml_path):

logger.debug('yaml file loaded')

_stages = data.get('stages', None)
if _stages is None:
stages_from_yaml = data.get('stages', None)
if stages_from_yaml is None:
raise BuildAgentException('[stages] key not found in yaml file!')

if type(_stages) is not list:
if type(stages_from_yaml) is not list:
raise BuildAgentException('[stages] should be a list in the yaml file!')

stages = []
for _stage in _stages:
stage_name = list(_stage)[0]
for _stage in stages_from_yaml:
stage_name = _stage.keys()[0]
# each stage should be a dictionary with 1 key (the name of the stage).
# the value should be a list of jobs.

jobs = []
for job_name, _job in _stage[stage_name].items():
f = _job.pop('finally', None)
for _job in _stage[stage_name]:
# each job should be a dictionary with 1 key (the name of the job).
# the value should be a dictionary containing the job details
job_name = _job.keys()[0]
job_details = _job[job_name]
finally_task = job_details.pop('finally', None)
if f is not None:
_job['finally_task'] = f
job_details['finally_task'] = finally_task

job = Job(
jobs.append(Job(
images=_job.pop('images', _job.pop('image', None)),
tasks=_job.pop('tasks', _job.pop('task', None)),
name=job_name,
**_job
)
))

jobs.append(job)

st = Stage(name=stage_name, jobs=jobs)
stages.append(st)
stages.append(Stage(name=stage_name, jobs=jobs))

return stages

Expand Down
46 changes: 46 additions & 0 deletions tox.ini
@@ -0,0 +1,46 @@
[tox]
envlist = linting,py35,doctesting

[testenv]
deps=pytest
commands=
pip install -e .[test]
py.test tests

[testenv:linting]
basepython = python3.5
deps = flake8
commands = flake8 swarmci tests

[testenv:doctest]
commands=py.test --doctest-modules swarmci
deps=

[testenv:regen]
changedir=doc/en
basepython = python3.5
deps=sphinx
PyYAML
regendoc>=0.6.1
whitelist_externals=
rm
make
commands=
rm -rf /tmp/doc-exec*
make regen

[testenv:coveralls]
passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN
usedevelop=True
basepython=python3.5
changedir=.
deps =
{[testenv]deps}
coveralls
commands=
coverage run --source=py.test -m swarmci tests
coverage report -m
coveralls

[flake8]
ignore =E401,E225,E261,E128,E124,E301,E302,E121,E303,W391,E501,E231,E126,E701,E265,E241,E251,E226,E101,W191,E131,E203,E122,E123,E271,E712,E222,E127,E125,E221,W292,E111,E113,E293,E262,W293,E129,E702,E201,E272,E202,E704,E731,E402

0 comments on commit fec2c26

Please sign in to comment.