Compare commits

..
16 Commits
Author SHA1 Message Date
brb cd636cce20 updated logging to show host ip and port 2023-07-13 12:21:24 +02:00
brb 6dd7d3f465 updated packages 2023-07-13 12:14:07 +02:00
brb 4a15e9fee0 flake8 compliant 2023-07-13 12:13:55 +02:00
brb 7a965e5dfc mypy compliant 2023-07-13 12:11:49 +02:00
brb 2f69f49690 added mypy config 2023-07-13 12:11:43 +02:00
brb 1266204ecb flake8 compliant 2023-07-13 11:53:14 +02:00
brb 6492b22219 defined default values 2023-07-13 11:48:25 +02:00
brb 8631722116 added loading of defaults 2023-07-13 11:48:10 +02:00
brb 2f014deb05 added loading of defaults 2023-07-13 11:47:50 +02:00
brb 3d8b240d9b added logging 2023-07-13 11:38:20 +02:00
brb 5c2fc4022e changed layout of file 2023-07-13 11:37:34 +02:00
brb bf23ba3f38 added automatic parsing from file 2023-07-13 11:36:47 +02:00
brb 493dc874f0 added definition 2023-07-13 11:29:29 +02:00
brb 614a85c429 added definition 2023-07-13 11:29:14 +02:00
brb 9956d6a2c4 added definition 2023-07-13 11:23:52 +02:00
brb 25066d687e added definition 2023-07-13 11:22:42 +02:00
10 changed files with 274 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
import uvicorn
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import HTMLResponse
from dotenv import load_dotenv
import logging
import os
from utils import clone_repository, execute_runner_script
from setup_logging import setup_logging
from gitea_request import GiteaRequest
app = FastAPI()
@app.get('/', response_class=HTMLResponse)
async def root():
content_str = """
<html>
<head>
<title>Gitea Runner</title>
</head>
<body>
<h1>Welcome to Gitea Runner</h1>
<p>see /docs for more information</p>
</body>
</html>
"""
return HTMLResponse(content=content_str, status_code=200)
@app.post('/test')
async def test(request: Request):
""" Test connection to server. """
logging.debug('Content-Type: ', request.headers.get('content-type'))
raise HTTPException(
status_code=200,
detail='test completed'
)
@app.post('/event')
def handle_event(request: GiteaRequest):
""" Handle event from webhook. """
# extract information
commit_msg = request.head_commit['message']
repo_url = request.repository['clone_url']
# branch_name = request.ref.split('/')[-1]
# stop early if asked
if commit_msg.lower().startswith('[skip ci]'):
raise HTTPException(
status_code=200,
detail='skipping ci'
)
# clone repository
local_repo_dir = clone_repository(repo_url)
# locate runner script
runner_script_path = local_repo_dir / 'runner_script.yml'
if not runner_script_path.exists():
raise HTTPException(
status_code=200,
detail="no runner_script.yml in repository root"
)
# execute runner script
logging.info('rexecuting runner script')
execute_runner_script(path=runner_script_path)
# # TODO: create virtualenv?
# # install requirements
# assert pipeline.requirements is not None
# requirements_path = local_repo_dir / pipeline.requirements
# os.system(f'pip install -r {requirements_path}')
# for cmd in pipeline.script:
# try:
# os.system(cmd)
# except Exception as e:
# raise HTTPException(
# status_code=503,
# detail=f'failed running {cmd}: {e}'
# )
# send response
raise HTTPException(
status_code=202,
detail='received task'
)
# consider to include link to page that show progress
if __name__ == '__main__':
load_dotenv('dev.env')
setup_logging()
listen_ip = os.getenv('LISTEN_IP', default='0.0.0.0')
listen_port = int(os.getenv('LISTEN_PORT', default=8000))
logging.info(f'starting FastAPI server ({listen_ip}:{listen_port})')
uvicorn.run(
app=app,
host=listen_ip,
port=listen_port
)
+6
View File
@@ -0,0 +1,6 @@
[main]
logger_level=debug
[fastapi]
listen_ip=0.0.0.0
listen_port=8000
+15
View File
@@ -0,0 +1,15 @@
from pydantic import BaseModel
from typing import List
class GiteaRequest(BaseModel):
ref: str
before: str
after: str
compare_url: str
commits: List[dict]
total_commits: int
head_commit: dict
repository: dict
pusher: dict
sender: dict
+31
View File
@@ -0,0 +1,31 @@
import uvicorn
from setup_logging import setup_logging
from dotenv import load_dotenv
from app import app
import logging
import os
from configparser import ConfigParser
from pathlib import Path
import socket
# load default values
config = ConfigParser()
config.read(Path(__file__).parent / 'defaults.ini')
LISTEN_IP = config.get('fastapi', 'listen_ip')
LISTEN_PORT = int(config.get('fastapi', 'listen_port'))
if __name__ == '__main__':
# setup
load_dotenv()
setup_logging()
listen_ip = os.getenv('LISTEN_IP', default=LISTEN_IP)
listen_port = int(os.getenv('LISTEN_PORT', default=LISTEN_PORT))
own_ip = socket.gethostbyname(socket.gethostname())
logging.info(f'starting FastAPI server ({own_ip}:{listen_port})')
uvicorn.run(
app=app,
host=listen_ip,
port=listen_port
)
+29
View File
@@ -0,0 +1,29 @@
from pydantic import BaseModel
from typing import List
import yaml
from pathlib import Path
import logging
class RunnerScript(BaseModel):
script: List[str]
@staticmethod
def from_file(path: Path):
assert path.exists()
with open(path, 'r') as fh:
content_dict = yaml.safe_load(fh)
script_list = content_dict['script'].split('\n')
logging.debug('finished')
rs = RunnerScript.model_validate( # type: ignore
{
'script': script_list
}
)
return rs
if __name__ == '__main__':
path = Path(__file__).parent.parent / 'runner_script.yml'
rs = RunnerScript.from_file(path)
print(rs)
+31
View File
@@ -0,0 +1,31 @@
from dotenv import load_dotenv
import logging
from configparser import ConfigParser
from pathlib import Path
# load default values
config = ConfigParser()
config.read(Path(__file__).parent / 'defaults.ini')
LOGGER_LEVEL = config.get('main', 'logger_level')
def setup_logging(
logger_level: str = LOGGER_LEVEL
):
fmt = (
'%(asctime)s | '
'%(levelname)s | '
'%(filename)s | '
'%(funcName)s | '
'%(message)s'
)
datefmt = '%Y-%m-%d %H:%M:%S'
level = getattr(logging, logger_level.upper())
logging.basicConfig(format=fmt, datefmt=datefmt, level=level)
logging.debug('finished')
if __name__ == '__main__':
load_dotenv('dev.env')
setup_logging()
+30
View File
@@ -0,0 +1,30 @@
from pydantic import AnyHttpUrl
from pathlib import Path
from git import Repo
import logging
import shutil
import os
from runner_script import RunnerScript
def clone_repository(repo_url: AnyHttpUrl) -> Path:
# extract repo name
repo_name = repo_url.split('/')[-1].replace('.git', '')
repo_dir = os.getenv('REPO_DIR', default=None)
assert repo_dir is not None
dst_dir = Path(repo_dir) / repo_name
# prepare repo download dir
if dst_dir.exists():
shutil.rmtree(dst_dir)
dst_dir.mkdir()
# git clone repo
Repo.clone_from(url=repo_url, to_path=dst_dir)
logging.debug('finished')
return dst_dir
def execute_runner_script(path: Path) -> None:
script_list = RunnerScript.from_file(path)
for cmd in script_list:
os.system(cmd)
logging.debug('finished')
+13
View File
@@ -0,0 +1,13 @@
[mypy]
[mypy-uvicorn.*]
ignore_missing_imports = True
[mypy-dotenv.*]
ignore_missing_imports = True
[mypy-git.*]
ignore_missing_imports = True
[mypy-fastapi.*]
ignore_missing_imports = True
+18
View File
@@ -0,0 +1,18 @@
annotated-types==0.5.0
anyio==3.7.1
click==8.1.4
exceptiongroup==1.1.2
fastapi==0.100.0
gitdb==4.0.10
GitPython==3.1.32
h11==0.14.0
idna==3.4
pydantic==2.0.2
pydantic_core==2.1.2
python-dotenv==1.0.0
PyYAML==6.0
smmap==5.0.0
sniffio==1.3.0
starlette==0.27.0
typing_extensions==4.7.1
uvicorn==0.22.0
+3
View File
@@ -0,0 +1,3 @@
script: |
echo "hello world"
echo "hello back"