#!/usr/bin/env bash # BotLauncher entry point for DevControlBot. # # Thin wrapper: all build/run/cleanup logic lives in the `runBot` nimble task # of DevControlBot.nimble. This script only locates itself, moves next to the # .nimble so Nimble finds it, forwards caller arguments, calls the task and # propagates the real exit code. Signals are forwarded to Nimble so the task's # own EXIT/INT/TERM cleanup always runs. # # Usage: DevControlBot.sh [--debug] [] [extra args for the bot] # # (no args) RELEASE build + run with the bundled DevControlBot.json # (no flag) RELEASE build (nim c -d:release), quiet output # --debug classic DEBUG build (plain nim c): compiler hints + diagnostics # # --debug is a build-mode flag for this wrapper only: it is stripped here and # never reaches the bot binary or nimble's task arguments. It travels to the # runBot task as $DEVCONTROLBOT_DEBUG (exported), because an env var cannot be # confused with a bot argument, cannot collide with the status file mechanism # and keeps the task's argument parsing untouched. Repeated --debug is harmless. # # Configuration (SERVER_URL, SERVER_SECRET, ...) is NOT handled here at all: the # API's start() reads those from the environment, and the optional .env file is # loaded by the runBot task, so it also works with a direct `nimble runBot`. set -uo pipefail set -m # job control: the background job gets its own process group SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" usage() { cat <] [extra args for the bot] --debug classic debug build: shows compiler hints and diagnostics (default is a quiet RELEASE build, nim c -d:release) -h,--help show this help Examples: $(basename "$0") # release build + run, bundled DevControlBot.json $(basename "$0") my.json # release build + run with custom metadata $(basename "$0") -h # this help $(basename "$0") --debug # debug build $(basename "$0") --debug my.json # debug build with custom metadata EOF } # Split caller args: everything except --debug is forwarded verbatim. BUILD_MODE=release FORWARDED=() for a in "$@"; do case "$a" in --debug) BUILD_MODE=debug ;; *) FORWARDED+=("$a") ;; esac done set -- ${FORWARDED+"${FORWARDED[@]}"} # No arguments is the normal case: build and run with the bundled metadata. # Usage is printed ONLY for an explicit -h/--help. case "${1:-}" in -h|--help) usage; exit 0 ;; esac export DEVCONTROLBOT_DEBUG="$BUILD_MODE" # The runBot task's shell always exits 0 (otherwise Nimble raises a NimScript # exception with a stack trace and a dump of the whole script) and writes the # bot's real exit code here instead. We read it back and exit with it, so # BotLauncher still sees the faithful status: 1 = no game server on 7654, # 2 = a dependency is missing, 3 = compile failure, anything else = the bot's # own code, 0 = clean run. STATUS_FILE="$(mktemp "${TMPDIR:-/tmp}/devcontrolbot.status.XXXXXX")" export DEVCONTROLBOT_STATUS_FILE="$STATUS_FILE" trap 'rm -f "$STATUS_FILE"' EXIT # Forward to the whole job process group (nimble + the shell it spawned) so the # task's own EXIT/INT/TERM cleanup runs. No cleanup logic here. forward() { kill -s "$1" -- "-$NIMBLE_PID" 2>/dev/null; } trap 'forward TERM' TERM trap 'forward INT' INT nimble runBot ${1+"$@"} & NIMBLE_PID=$! wait "$NIMBLE_PID" rc=$? trap - TERM INT # Prefer the code the run actually produced; fall back to Nimble's own status if # the task died before it could report (e.g. Nimble itself failed). real_rc="$(head -n 1 "$STATUS_FILE" 2>/dev/null | tr -d '[:space:]')" [ -n "$real_rc" ] || real_rc=$rc exit "$real_rc"