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

Some Goonstation improvements(?) #1

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
ARG BYOND_VERSION=513.1542
FROM beestation/byond:${BYOND_VERSION}
FROM beestation/byond

WORKDIR /app

Expand Down
4 changes: 0 additions & 4 deletions compile.sh

This file was deleted.

129 changes: 80 additions & 49 deletions listener.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import platform
import random
import re
import os
from flask import Flask, jsonify, request, abort
import shutil
import string
import subprocess
from pathlib import Path

import docker
from flask import Flask, abort, jsonify, request

app = Flask(__name__)
client = docker.from_env()
Expand All @@ -28,7 +29,11 @@ def startCompile():
if request.method == "POST":
posted_data = request.get_json()
if "code_to_compile" in posted_data:
return jsonify(compileTest(posted_data["code_to_compile"], posted_data["byond_version"]))
return jsonify(
compileTest(
posted_data["code_to_compile"], posted_data["byond_version"]
)
)
else:
abort(400)

Expand All @@ -53,12 +58,12 @@ def randomString(stringLength=24):

def checkVersions(version: str):
try:
image_list = client.images.list(name="test")
image_list = client.images.list(name="beestation/byond")
except IndexError:
return False

for image in image_list:
if f"test:{version}" in image.tags:
if f"beestation/byond:{version}" in image.tags:
return True

return False
Expand All @@ -72,24 +77,46 @@ def buildVersion(version: str):
try:
print(f"Attempting to build version: {version}")
return client.images.build(
path=f"{Path.cwd()}",
dockerfile="Dockerfile",
path=f"https://github.com/BeeStation/byond-docker.git",
platform="linux/386",
rm=True,
pull=True,
tag=f"test:{version}",
buildargs={"BYOND_VERSION": version},
tag=f"beestation/byond:{version}",
buildargs={
"buildtime_BYOND_MAJOR": version.split(".")[0],
"buildtime_BYOND_MINOR": version.split(".")[1],
},
)
except docker.errors.BuildError:
raise


def normalizeCode(codeText: str):
indent_step = float("inf")
lines = codeText.split("\n")
for line in lines:
indent_level = len(line) - len(line.lstrip())
if indent_level > 0:
indent_step = min(indent_step, indent_level)
if indent_step == float("inf"):
return codeText
result_lines = []
for line in lines:
stripped = line.lstrip()
indent_level = len(line) - len(stripped)
result_lines.append("\t" * (indent_level // indent_step) + stripped)
return "\n".join(result_lines)


def compileTest(codeText: str, version: str):
try:
buildVersion(version=version)
except docker.errors.BuildError as e:
results = {"build_error": True, "exception": str(e)}
return results

codeText = normalizeCode(codeText)

randomDir = Path.cwd().joinpath(randomString())
randomDir.mkdir()
shutil.copyfile(TEST_DME, randomDir.joinpath("test.dme"))
Expand All @@ -98,53 +125,47 @@ def compileTest(codeText: str, version: str):
fc.write(loadTemplate(codeText))
else:
fc.write(loadTemplate(codeText, False))
command = [
"bash",
"-c",
"cp code/* .; DreamMaker test.dme; DreamDaemon test.dmb -close -verbose -ultrasafe | cat",
]
pali6 marked this conversation as resolved.
Show resolved Hide resolved
docker_path = None
if HOST_OS == "Windows":
# To get cleaner outputs, we run docker as a subprocess rather than through the API
proc = subprocess.Popen(
[
"docker",
"run",
"--name",
f"{randomDir.name}",
"--rm",
"--network",
"none",
"-v",
f"{randomDir}:/app/code:ro",
f"test:{version}",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
docker_path = "docker"
else:
# Expects the linux user to be running docker locally, not as root
proc = subprocess.Popen(
[
f"{Path.home()}/bin/docker",
"run",
"--name",
f"{randomDir.name}",
"--rm",
"--network",
"none",
"-v",
f"{randomDir}:/app/code:ro",
f"test:{version}",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
docker_path = f"{Path.home()}/bin/docker"
if not os.path.isfile(docker_path):
docker_path = "/usr/bin/docker"
pali6 marked this conversation as resolved.
Show resolved Hide resolved
proc = subprocess.Popen(
[
docker_path,
"run",
"--name",
f"{randomDir.name}",
"--platform",
"linux/386",
"--rm",
"--network",
"none",
"-v",
f"{randomDir}:/app/code:ro",
"-w",
"/app",
f"beestation/byond:{version}",
]
+ command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
try:
compile_log, run_log = proc.communicate(
timeout=30
) # A bit hacky, but provides exceptionally clean results. The main output will be captured as the compile_log while the "error" output is captured as run_log
test_killed = False
except subprocess.TimeoutExpired:
proc.kill()
if HOST_OS == "Windows":
subprocess.run(["docker", "stop", f"{randomDir.name}"], capture_output=True)
else:
subprocess.run([f"{Path.home()}/bin/docker", "stop", f"{randomDir.name}"], capture_output=True)
subprocess.run([docker_path, "stop", f"{randomDir.name}"], capture_output=True)
compile_log, run_log = proc.communicate()
test_killed = True

Expand All @@ -153,15 +174,25 @@ def compileTest(codeText: str, version: str):
run_log = re.sub(
r"The BYOND hub reports that port \d* is not reachable.", "", run_log
) # remove the network error message
compile_log = (compile_log[:1200] + "...") if len(compile_log) > 1200 else compile_log
run_log = re.sub(r"\n---------------\n", "\n", run_log) # remove the dashes
run_log = re.sub(r"World opened on network port \d*\.\n", "", run_log)
compile_log = re.sub(r"loading test.dme\n", "", compile_log)
compile_log = re.sub(r"saving test.dmb\n", "", compile_log)
compile_log = (
(compile_log[:1200] + "...") if len(compile_log) > 1200 else compile_log
)
run_log = (run_log[:1200] + "...") if len(run_log) > 1200 else run_log

shutil.rmtree(randomDir)

if f"Unable to find image 'test:{version}' locally" in run_log:
if f"Unable to find image 'byond:{version}' locally" in run_log:
results = {"build_error": True, "exception": run_log}
else:
results = {"compile_log": compile_log, "run_log": run_log, "timeout": test_killed}
results = {
"compile_log": compile_log,
"run_log": run_log,
"timeout": test_killed,
}

return results

Expand Down