Skip to content

Commit

Permalink
Initial public release
Browse files Browse the repository at this point in the history
  • Loading branch information
oysols committed Mar 3, 2020
0 parents commit b9f0990
Show file tree
Hide file tree
Showing 22 changed files with 2,357 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM python:3.8@sha256:2b8823de22d5434cd9b7aff963e9299ef52605402bd0d4ef23e45f55333a5d4c

RUN apt-get update && apt-get install -y docker.io

COPY server/requirements.txt .
RUN pip3 install -r requirements.txt

WORKDIR /workdir
COPY . ./
RUN pip3 install .

WORKDIR /server
COPY server/ ./
CMD python3 -u ./server.py
674 changes: 674 additions & 0 deletions LICENSE.txt

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
SHA = $(shell git rev-parse HEAD)

build:
docker build . -t minimalci:${SHA}

test:
python3 tests/test_executors.py
python3 tests/test_taskrunner.py

check:
mypy minimalci --strict
mypy server --strict
mypy tests --strict
mypy . --strict

dev:
docker-compose build && docker-compose kill && docker-compose up -d && docker-compose logs -f

clean:
@rm **/.mypy_cache -r || true
@rm **/__pycache__ -r || true
@rm .mypy_cache -r || true
@rm __pycache__ -r || true
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# MinimalCI

Fast and simple continuous integration wiht Python as DSL.

- Server and frontend `server/`

http://35.227.107.72/

- Executors `executors.py`

```python
with LocalContainer("debian:buster") as e:
e.sh("echo Hello World > hello_world.txt")
my_stash = e.stash("hello_world.txt")

with Ssh("user@remotehost") as e:
e.unstash(my_stash)
e.sh("cat hello_world.txt")

with Local() as e:
e.unstash(my_stash)
```

- Tasks / Taskrunner `tasks.py` / `taskrunner.py`

```python
class Setup(Task):
def run(self):
with Local() as e:
e.sh("some stuff")

class Test(Task):
run_after = [Setup]
def run(self):
with LocalContainer("some_image") as e:
e.sh(f"echo Testing commit {self.state.commit}")
```

# Docs

TODO

## Server setup

- SSH setup

```
ssh-keygen -P '' -f /srv/minimalci/ssh/id_rsa
ssh-keyscan -t rsa github.com >> /srv/minimalci/ssh/known_hosts
```

# TODO

## Minimalci
- Fix print prefix for running executors locally
- Run taskrunner in docker
- Setup consistent executor queues without relying on local semaphore
- Test and verify remote python execution
- Only optionally write log/state

## Server
- Authentication
- Stop, Delete, Trigger from web UI
- Do not print task logs to server logs
- Write server logs to web view (git fetch/clone, import, ...)
- Show current git tags in overview
- Live update overview
- Evaluate polling vs inotify
- "Dependent task did not succeed" when raising Skipped. Handling by user instead?
- state as magic global import?
- Run taskrunner from webserver, not the other way around?
- Visualize dependent tasks by using indentation
- Detect crashed builds
- Timeouts

## Alternative syntax?

```python
import state

@task(run_after=setup)
def test():
with Local() as e:
e.sh(f"echo '{state.commit}'")

if state.tasks[setup].status == Status.success:
print("SUCCESS")
```

# License

Copyright (C) 2020 Øystein Olsen

Licensed under GPL-3.0-only
22 changes: 22 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: "3"
services:
minimalci:
build: .
volumes:
- /srv/minimalci/repo:/server/repo
- /srv/minimalci/logs:/server/logs
- /srv/minimalci/secrets:/server/secrets:ro
- /srv/minimalci/ssh:/root/.ssh:ro
- /var/run/docker.sock:/var/run/docker.sock
ports:
- 80:8000
environment:
- REPO_NAME=minimalci
- [email protected]:oysols/minimalci.git
- BASE_URL=http://35.227.107.72/
# Optional
- TASKS_FILE=tasks.py
- DOCKER_REGISTRY=""
- DOCKER_USER=""
- DOCKER_PASS=""
restart: always
51 changes: 51 additions & 0 deletions dssh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Forward remote docker.sock and set DOCKER_HOST in subshell"""
import os
import sys
import subprocess
import argparse
from typing import Dict

from minimalci import executors

# Prompt colors
RED = "\033[91m"
END_COLOR = "\033[0m"


def start_subshell(env: Dict[str, str], prompt_info: str) -> None:
"""Start an interactive subshell with custom env and prompt"""
cmd = os.environ.get("SHELL", "/bin/sh")
# PS1 sets the prompt for the subshell for visual display of remote docker host
env["PS1"] = "[{}]:\\w\\$ ".format(prompt_info)
process = subprocess.Popen([cmd, "--norc"], env=env)
process.wait()


def main() -> None:
"""Forward remote docker.sock and set DOCKER_HOST in subshell"""
parser = argparse.ArgumentParser(description="Forward remote docker.sock and set DOCKER_HOST in subshell")
parser.add_argument("remote_host", help="Remote host")
parser.add_argument("command", nargs="*", help="Run a single command")
args = parser.parse_args()

remote_host = args.remote_host
command = args.command

docker_host = os.environ.get("DOCKER_HOST")
if docker_host:
print("ERROR: DOCKER_HOST already set: {}".format(docker_host))
sys.exit(1)
with executors.LocalWithForwardedDockerSock(remote_host) as exe:
if command:
exe.sh(command)
else:
# prompt_info = "DOCKER_HOST -> {}{}{}".format(RED, remote_host, END_COLOR)
prompt_info = remote_host
env = os.environ.copy()
env["DOCKER_HOST"] = "unix://{}".format(exe.forwarded_socket)
start_subshell(env, prompt_info)


if __name__ == "__main__":
main()
Empty file added minimalci/__init__.py
Empty file.
Loading

0 comments on commit b9f0990

Please sign in to comment.