mirror of
https://github.com/YunoHost/yunorunner.git
synced 2024-09-03 20:05:52 +02:00
init
This commit is contained in:
commit
eb6d7ac19e
4 changed files with 220 additions and 0 deletions
41
models.py
Normal file
41
models.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import peewee
|
||||
|
||||
db = peewee.SqliteDatabase('db.sqlite')
|
||||
|
||||
|
||||
class Repo(peewee.Model):
|
||||
name = peewee.CharField()
|
||||
url = peewee.CharField()
|
||||
revision = peewee.CharField()
|
||||
app_list = peewee.CharField()
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
|
||||
|
||||
class BuildTask(peewee.Model):
|
||||
repo = peewee.ForeignKeyField(Repo)
|
||||
target_revision = peewee.CharField()
|
||||
yunohost_version = peewee.CharField()
|
||||
|
||||
state = peewee.CharField(choices=(
|
||||
('scheduled', 'Scheduled'),
|
||||
('runnning', 'Running'),
|
||||
('done', 'Done'),
|
||||
('failure', 'Failure'),
|
||||
))
|
||||
|
||||
created_time = peewee.DateTimeField(constraints=[peewee.SQL("DEFAULT (datetime('now'))")])
|
||||
started_time = peewee.DateTimeField(null=True)
|
||||
end_time = peewee.DateTimeField(null=True)
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
|
||||
|
||||
# peewee is a bit stupid and will crash if the table already exists
|
||||
for i in [Repo, BuildTask]:
|
||||
try:
|
||||
i.create_table()
|
||||
except:
|
||||
pass
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
sanic
|
||||
aiothttp
|
||||
aiofiles
|
||||
peewee
|
137
run.py
Normal file
137
run.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
# encoding: utf-8
|
||||
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
|
||||
import random
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import aiohttp
|
||||
import aiofiles
|
||||
|
||||
from websockets.exceptions import ConnectionClosed
|
||||
|
||||
from sanic import Sanic, response
|
||||
|
||||
from models import Repo, BuildTask, db
|
||||
|
||||
app = Sanic()
|
||||
|
||||
OFFICAL_APPS_LIST = "https://app.yunohost.org/official.json"
|
||||
# TODO handle community list
|
||||
COMMUNITY_APPS_LIST = "https://app.yunohost.org/community.json"
|
||||
|
||||
APPS_LIST = [OFFICAL_APPS_LIST, COMMUNITY_APPS_LIST]
|
||||
|
||||
|
||||
async def initialize_app_list():
|
||||
if not os.path.exists("lists"):
|
||||
os.makedirs("lists")
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
app_list = "official"
|
||||
sys.stdout.write(f"Downloading {OFFICAL_APPS_LIST}...")
|
||||
sys.stdout.flush()
|
||||
async with session.get(OFFICAL_APPS_LIST) as resp:
|
||||
data = await resp.json()
|
||||
sys.stdout.write("done\n")
|
||||
|
||||
repos = {x.name: x for x in Repo.select()}
|
||||
|
||||
for app_id, app_data in data.items():
|
||||
if app_id in repos:
|
||||
# print(f"déjà là: {app_id}")
|
||||
pass
|
||||
else:
|
||||
print(f"New application detected: {app_id} in {app_list}")
|
||||
repo = Repo.create(
|
||||
name=app_id,
|
||||
url=app_data["git"]["url"],
|
||||
revision=app_data["git"]["revision"],
|
||||
app_list=app_list,
|
||||
)
|
||||
|
||||
print(f"Schedule a new build for {app_id}")
|
||||
BuildTask.create(
|
||||
repo=repo,
|
||||
target_revision=app_data["git"]["revision"],
|
||||
yunohost_version="stretch-stable",
|
||||
state="scheduled",
|
||||
)
|
||||
|
||||
|
||||
async def run_jobs():
|
||||
print("Run jobs...")
|
||||
while True:
|
||||
with db.atomic():
|
||||
build_task = BuildTask.select().where(BuildTask.state == "scheduled").limit(1)
|
||||
|
||||
if not build_task.count():
|
||||
await asyncio.sleep(3)
|
||||
continue
|
||||
|
||||
build_task = build_task[0]
|
||||
|
||||
build_task.state = "running"
|
||||
build_task.started_time = datetime.now()
|
||||
build_task.save()
|
||||
|
||||
# fake stupid command, whould run CI instead
|
||||
print(f"Starting job for {build_task.repo.name}...")
|
||||
command = await asyncio.create_subprocess_shell("/usr/bin/tail /var/log/auth.log",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE)
|
||||
|
||||
while not command.stdout.at_eof():
|
||||
data = await command.stdout.readline()
|
||||
line = data.decode().rstrip()
|
||||
print(f">> {line}")
|
||||
|
||||
# XXX stupid crap to stimulate long jobs
|
||||
# await asyncio.sleep(random.randint(30, 120))
|
||||
# await asyncio.sleep(5)
|
||||
print(f"Finished task for {build_task.repo.name}")
|
||||
|
||||
await command.wait()
|
||||
build_task.end_time = datetime.now()
|
||||
build_task.save()
|
||||
|
||||
await broadcast_to_ws(all_index_ws, f"done for {build_task.repo.name}")
|
||||
|
||||
await asyncio.sleep(3)
|
||||
|
||||
|
||||
async def broadcast_to_ws(ws_list, message):
|
||||
dead_ws = []
|
||||
|
||||
for ws in ws_list:
|
||||
try:
|
||||
await ws.send(message)
|
||||
except ConnectionClosed:
|
||||
dead_ws.append(ws)
|
||||
|
||||
for to_remove in dead_ws:
|
||||
ws_list.remove(to_remove)
|
||||
|
||||
@app.websocket('/index-ws')
|
||||
async def index_ws(request, websocket):
|
||||
all_index_ws.append(websocket)
|
||||
while True:
|
||||
data = await websocket.recv()
|
||||
print(f"websocket: {data}")
|
||||
await websocket.send(f"echo {data}")
|
||||
|
||||
|
||||
@app.route('/')
|
||||
async def index(request):
|
||||
return response.html(open("./templates/index.html", "r").read())
|
||||
# return await render_template("index.html", build_tasks=BuildTask.select().order_by("id"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
all_index_ws = []
|
||||
app.add_task(initialize_app_list())
|
||||
app.add_task(run_jobs())
|
||||
app.run('localhost', port=5000, debug=True)
|
38
templates/index.html
Normal file
38
templates/index.html
Normal file
|
@ -0,0 +1,38 @@
|
|||
<html>
|
||||
<body>
|
||||
<h1>Tasks</h1>
|
||||
<table border="0" cellspacing="5" cellpadding="5">
|
||||
<tr>
|
||||
<th>App</th>
|
||||
<th>State</th>
|
||||
<th>Revision</th>
|
||||
<th>Ynh Version</th>
|
||||
<th>Created time</th>
|
||||
<th>Started time</th>
|
||||
<th>End time</th>
|
||||
</tr>
|
||||
{% for build_task in build_tasks %}
|
||||
<tr id="build_task_{{ build_task.id }}">
|
||||
<td>{{build_task.repo.name}}</td>
|
||||
<td>{{build_task.state}}</td>
|
||||
<td>{{build_task.target_revision}}</td>
|
||||
<td>{{build_task.yunohost_version}}</td>
|
||||
<td>{{build_task.created_time}}</td>
|
||||
<td>{{build_task.started_time}}</td>
|
||||
<td>{{build_task.end_time}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<script>
|
||||
(function() {
|
||||
console.log('ws://' + document.domain + ':' + location.port + '/index-ws')
|
||||
ws = new WebSocket('ws://' + document.domain + ':' + location.port + '/index-ws');
|
||||
ws.onmessage = function (event) {
|
||||
console.log(event.data);
|
||||
};
|
||||
|
||||
ws.send('bob');
|
||||
})()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue