mirror of
https://github.com/blw1138/cross-py-builder.git
synced 2026-09-07 21:41:09 -05:00
Serialize builds and add health endpoint to build agent
- Add a non-blocking build lock so only one job runs at a time; concurrent upload/checkout requests get a 409 response - Add /healthz returning 200 when idle and 503 while a build is running - Ignore .DS_Store artifacts
This commit is contained in:
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
known_hosts
|
known_hosts
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
|
.DS_Store
|
||||||
@@ -5,6 +5,7 @@ import signal
|
|||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from flask import Flask, request, jsonify, send_file
|
from flask import Flask, request, jsonify, send_file
|
||||||
@@ -30,6 +31,7 @@ LOCAL_DIR = os.path.dirname(__file__)
|
|||||||
BUILD_DIR = "pybuild-data"
|
BUILD_DIR = "pybuild-data"
|
||||||
TMP_DIR = tempfile.gettempdir()
|
TMP_DIR = tempfile.gettempdir()
|
||||||
system_status = {"status": "ready", "running_job": None}
|
system_status = {"status": "ready", "running_job": None}
|
||||||
|
build_lock = threading.Lock()
|
||||||
|
|
||||||
def is_windows():
|
def is_windows():
|
||||||
return platform.system().lower() == "windows"
|
return platform.system().lower() == "windows"
|
||||||
@@ -127,6 +129,12 @@ def status_page():
|
|||||||
return (f"{APP_NAME} - Build Agent {build_agent_version} - \n"
|
return (f"{APP_NAME} - Build Agent {build_agent_version} - \n"
|
||||||
f"{system_os()} | {cpu_arch()} | {version} | {hostname} | {ZeroconfServer.get_local_ip()}")
|
f"{system_os()} | {cpu_arch()} | {version} | {hostname} | {ZeroconfServer.get_local_ip()}")
|
||||||
|
|
||||||
|
@app.get("/healthz")
|
||||||
|
def healthz():
|
||||||
|
ok = system_status['status'] == "ready"
|
||||||
|
return jsonify({"status": "ok" if ok else "busy",
|
||||||
|
"agent_version": build_agent_version}), 200 if ok else 503
|
||||||
|
|
||||||
@app.get("/status")
|
@app.get("/status")
|
||||||
def status():
|
def status():
|
||||||
def get_directory_size(directory):
|
def get_directory_size(directory):
|
||||||
@@ -173,6 +181,9 @@ def checkout_project():
|
|||||||
if not request.is_json or not request.json.get('repo_url'):
|
if not request.is_json or not request.json.get('repo_url'):
|
||||||
return jsonify({'error': 'Repository URL is required'}), 400
|
return jsonify({'error': 'Repository URL is required'}), 400
|
||||||
|
|
||||||
|
if not build_lock.acquire(blocking=False):
|
||||||
|
return jsonify({'error': f"Another build is already running: {system_status['running_job']}"}), 409
|
||||||
|
|
||||||
repo_url = request.json['repo_url']
|
repo_url = request.json['repo_url']
|
||||||
print(f"\n========== Checking Out Git Project ==========")
|
print(f"\n========== Checking Out Git Project ==========")
|
||||||
|
|
||||||
@@ -194,6 +205,8 @@ def checkout_project():
|
|||||||
if isinstance(e, subprocess.CalledProcessError):
|
if isinstance(e, subprocess.CalledProcessError):
|
||||||
return jsonify({'error': 'Failed to clone repository'}), 500
|
return jsonify({'error': 'Failed to clone repository'}), 500
|
||||||
return jsonify({'error': f"Uncaught error processing checkout: {e}"}), 500
|
return jsonify({'error': f"Uncaught error processing checkout: {e}"}), 500
|
||||||
|
finally:
|
||||||
|
build_lock.release()
|
||||||
|
|
||||||
def safe_extract(zip_ref, dest_dir):
|
def safe_extract(zip_ref, dest_dir):
|
||||||
"""Extract a zipfile, rejecting members that would escape dest_dir."""
|
"""Extract a zipfile, rejecting members that would escape dest_dir."""
|
||||||
@@ -206,6 +219,9 @@ def safe_extract(zip_ref, dest_dir):
|
|||||||
|
|
||||||
@app.route('/upload', methods=['POST'])
|
@app.route('/upload', methods=['POST'])
|
||||||
def upload_project():
|
def upload_project():
|
||||||
|
if not build_lock.acquire(blocking=False):
|
||||||
|
return jsonify({'error': f"Another build is already running: {system_status['running_job']}"}), 409
|
||||||
|
|
||||||
try:
|
try:
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
if 'file' not in request.files:
|
if 'file' not in request.files:
|
||||||
@@ -234,6 +250,8 @@ def upload_project():
|
|||||||
system_status['status'] = "ready"
|
system_status['status'] = "ready"
|
||||||
shutil.rmtree(working_dir, ignore_errors=True)
|
shutil.rmtree(working_dir, ignore_errors=True)
|
||||||
return jsonify({"error": f"Uncaught error processing job: {e}"}), 500
|
return jsonify({"error": f"Uncaught error processing job: {e}"}), 500
|
||||||
|
finally:
|
||||||
|
build_lock.release()
|
||||||
|
|
||||||
def install_and_build(project_path, job_id, start_time):
|
def install_and_build(project_path, job_id, start_time):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user