Okay Wish I could say I know exactly. Will do better next time promise lol
This commit is contained in:
81
scripts/mkdocs-build-trigger.py
Normal file
81
scripts/mkdocs-build-trigger.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
Tiny HTTP server that triggers `mkdocs build --clean` on POST /build.
|
||||
Runs inside the MkDocs container alongside `mkdocs serve`.
|
||||
Listens on port 8001 (internal only, not exposed to host).
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import time
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
|
||||
class BuildHandler(BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
if self.path != '/build':
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'{"error":"not found"}')
|
||||
return
|
||||
|
||||
start = time.time()
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['mkdocs', 'build', '--clean'],
|
||||
cwd='/docs',
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
)
|
||||
duration = int((time.time() - start) * 1000)
|
||||
body = json.dumps({
|
||||
'success': result.returncode == 0,
|
||||
'output': (result.stdout + result.stderr) or '(no output)',
|
||||
'duration': duration,
|
||||
})
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(body.encode())
|
||||
except subprocess.TimeoutExpired:
|
||||
duration = int((time.time() - start) * 1000)
|
||||
body = json.dumps({
|
||||
'success': False,
|
||||
'output': 'Build timed out after 120s',
|
||||
'duration': duration,
|
||||
})
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(body.encode())
|
||||
except Exception as e:
|
||||
duration = int((time.time() - start) * 1000)
|
||||
body = json.dumps({
|
||||
'success': False,
|
||||
'output': f'Build error: {str(e)}',
|
||||
'duration': duration,
|
||||
})
|
||||
self.send_response(500)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(body.encode())
|
||||
|
||||
def do_GET(self):
|
||||
if self.path == '/health':
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self.end_headers()
|
||||
self.wfile.write(b'{"status":"ok"}')
|
||||
return
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def log_message(self, format, *args):
|
||||
# Suppress default logging to keep container logs clean
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = HTTPServer(('0.0.0.0', 8001), BuildHandler)
|
||||
print('Build trigger server listening on :8001')
|
||||
server.serve_forever()
|
||||
13
scripts/mkdocs-entrypoint.sh
Executable file
13
scripts/mkdocs-entrypoint.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Entrypoint for MkDocs container: runs both the dev server and the build trigger.
|
||||
|
||||
# Start the build trigger server in the background
|
||||
python /scripts/mkdocs-build-trigger.py &
|
||||
BUILD_PID=$!
|
||||
|
||||
# Start MkDocs serve — restart it on crash so the build trigger stays alive
|
||||
while true; do
|
||||
mkdocs serve --dev-addr=0.0.0.0:8000 --watch-theme --livereload || true
|
||||
echo "MkDocs serve exited, restarting in 5s..."
|
||||
sleep 5
|
||||
done
|
||||
@@ -31,6 +31,7 @@ INFRA_CONTAINERS="v2-postgres redis"
|
||||
USER_PATHS=(
|
||||
"mkdocs/docs/"
|
||||
"mkdocs/mkdocs.yml"
|
||||
"mkdocs/site/"
|
||||
"configs/"
|
||||
"nginx/conf.d/services.conf"
|
||||
)
|
||||
@@ -143,7 +144,7 @@ on_failure() {
|
||||
local exit_code=$?
|
||||
release_lock
|
||||
if [[ $exit_code -ne 0 ]] && [[ "$DRY_RUN" != "true" ]]; then
|
||||
error "Upgrade failed at line $BASH_LINENO (exit code $exit_code)"
|
||||
error "Upgrade failed at line ${BASH_LINENO[0]} (exit code $exit_code)"
|
||||
print_rollback_help
|
||||
info "Log file: $LOG_FILE"
|
||||
fi
|
||||
@@ -266,7 +267,7 @@ if [[ "$ROLLBACK" == "true" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git checkout "$ROLLBACK_COMMIT"
|
||||
git checkout -B "$BRANCH" "$ROLLBACK_COMMIT"
|
||||
docker compose build $SOURCE_CONTAINERS
|
||||
docker compose up -d
|
||||
success "Rolled back to $ROLLBACK_COMMIT"
|
||||
@@ -458,18 +459,13 @@ if [[ "$DRY_RUN" == "true" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Step 1: Reset generated files (regenerated on container start)
|
||||
info "Resetting generated files..."
|
||||
git checkout -- mkdocs/site/ 2>/dev/null || true
|
||||
success "Reset mkdocs/site/ (regenerated on start)"
|
||||
|
||||
# Step 2: Stash user changes if any exist
|
||||
# Step 1: Stash user changes if any exist
|
||||
HAS_CHANGES=false
|
||||
if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then
|
||||
HAS_CHANGES=true
|
||||
STASH_NAME="upgrade-${TIMESTAMP}"
|
||||
info "Stashing local changes as '$STASH_NAME'..."
|
||||
git stash push -m "$STASH_NAME" --include-untracked
|
||||
git stash push -m "$STASH_NAME"
|
||||
success "Local changes stashed"
|
||||
fi
|
||||
|
||||
@@ -499,12 +495,12 @@ if [[ "$HAS_CHANGES" == "true" ]]; then
|
||||
for user_path in "${USER_PATHS[@]}"; do
|
||||
CONFLICTED="$(git diff --name-only --diff-filter=U -- "$user_path" 2>/dev/null || true)"
|
||||
if [[ -n "$CONFLICTED" ]]; then
|
||||
echo "$CONFLICTED" | while read -r cf; do
|
||||
while IFS= read -r cf; do
|
||||
info " Auto-resolving (keeping yours): $cf"
|
||||
git checkout --theirs "$cf" 2>/dev/null || true
|
||||
git add "$cf"
|
||||
done
|
||||
RESOLVED_COUNT=$((RESOLVED_COUNT + 1))
|
||||
RESOLVED_COUNT=$((RESOLVED_COUNT + 1))
|
||||
done < <(echo "$CONFLICTED")
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -536,7 +532,9 @@ if [[ -f "$PROJECT_DIR/.env.example" ]] && [[ -f "$PROJECT_DIR/.env" ]]; then
|
||||
key="$(echo "$key" | xargs)"
|
||||
[[ ! "$key" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] && continue
|
||||
if ! grep -q "^${key}=" "$PROJECT_DIR/.env" 2>/dev/null; then
|
||||
# Append with default value from .env.example
|
||||
# Strip inline comments and trim whitespace before appending
|
||||
value="${value%%#*}"
|
||||
value="$(echo "$value" | xargs)"
|
||||
echo "${key}=${value}" >> "$PROJECT_DIR/.env"
|
||||
NEW_VARS+=("$key")
|
||||
fi
|
||||
@@ -645,6 +643,12 @@ while true; do
|
||||
if docker compose exec -T api wget -q --spider http://localhost:4000/api/health 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
# Detect container crash early (don't wait full timeout)
|
||||
if ! docker compose ps api --format '{{.State}}' 2>/dev/null | grep -q "running"; then
|
||||
error "API container exited unexpectedly"
|
||||
docker compose logs api --tail 20
|
||||
exit 1
|
||||
fi
|
||||
sleep $HEALTH_INTERVAL
|
||||
API_WAIT=$((API_WAIT + HEALTH_INTERVAL))
|
||||
if [[ $API_WAIT -ge $HEALTH_TIMEOUT ]]; then
|
||||
@@ -655,11 +659,21 @@ while true; do
|
||||
done
|
||||
success "API healthy (${API_WAIT}s)"
|
||||
|
||||
# Start everything else
|
||||
# Start everything else (exclude one-shot init containers)
|
||||
info "Starting remaining services..."
|
||||
docker compose up -d
|
||||
docker compose up -d \
|
||||
--scale listmonk-init=0 \
|
||||
--scale gancio-init=0 \
|
||||
--scale vaultwarden-init=0
|
||||
success "All services started"
|
||||
|
||||
# Restart Pangolin tunnel connector if running (may hold stale state after nginx rebuild)
|
||||
if docker ps --format '{{.Names}}' | grep -q 'newt'; then
|
||||
info "Restarting Pangolin tunnel connector..."
|
||||
docker compose restart newt 2>/dev/null || true
|
||||
success "Newt tunnel restarted"
|
||||
fi
|
||||
|
||||
# Restart monitoring if it was running before
|
||||
if [[ "$MONITORING_WAS_RUNNING" == "true" ]]; then
|
||||
info "Restarting monitoring stack..."
|
||||
|
||||
Reference in New Issue
Block a user