Files
website/.gitea/workflows/demo-bootstrap.yml
wtclaude fe40aaf834
All checks were successful
Demo bootstrap (one-shot) / bootstrap (push) Successful in 6s
chore(demo): bootstrap fills an empty rig token in an existing .env
The first run wrote the .env but could not read RUSTLINK_WEB_TOKEN (the
demo subuser had no startup.read; it has now). On an existing .env the
bootstrap now fills DEMO_RIG_SIDECAR_TOKEN when it is empty and leaves
every other value alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-27 12:16:58 -05:00

106 lines
5.3 KiB
YAML

# ONE-SHOT, NEVER MERGED. Lives only on the chore/demo-bootstrap branch, which is
# deleted after it runs. It does the demo host's one-time setup while the org lead
# is away (deploy/demo/README.md): writes /opt/demo.runicgateway.com/.env from
# deploy/demo/.env.example with every secret generated ON THE HOST, and reports
# whether the host reaches the panel and the rig's sidecar.
#
# It never overwrites an existing .env, and it never prints a value: the log holds
# only yes/no facts and HTTP status codes.
name: Demo bootstrap (one-shot)
on:
push:
branches: [chore/demo-bootstrap]
jobs:
bootstrap:
runs-on: demo-site
env:
DEMO_DIR: /opt/demo.runicgateway.com
PTERODACTYL_URL: http://192.168.0.12
PTERODACTYL_SERVER: 6eb9d621
SIDECAR_URL: http://192.168.0.12:21004
steps:
- name: Check out the deploy files
uses: actions/checkout@v4
- name: Host facts
run: |
echo "host: $(hostname) user: $(id -un) ($(id -u))"
echo "docker: $(docker version --format '{{.Server.Version}}' 2>&1 | head -1)"
echo "compose: $(docker compose version --short 2>&1 | head -1)"
echo "node: $(node --version 2>&1 | head -1)"
echo "curl: $(command -v curl || echo missing)"
echo "sudo -n: $(sudo -n true 2>/dev/null && echo yes || echo no)"
echo "$DEMO_DIR exists: $(test -d "$DEMO_DIR" && echo yes || echo no)"
echo "$DEMO_DIR/.env exists: $(test -f "$DEMO_DIR/.env" && echo yes || echo no)"
- name: Reach the panel and the sidecar
run: |
code() { curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$@" || true; }
echo "panel $PTERODACTYL_URL/ -> $(code "$PTERODACTYL_URL/")"
echo "sidecar $SIDECAR_URL/health -> $(code "$SIDECAR_URL/health")"
- name: Write the .env
env:
PTERODACTYL_CLIENT_KEY: ${{ secrets.PTERODACTYL_CLIENT_KEY }}
run: |
set -euo pipefail
SUDO=""
if ! mkdir -p "$DEMO_DIR" 2>/dev/null || ! test -w "$DEMO_DIR"; then
sudo -n mkdir -p "$DEMO_DIR"
sudo -n chown "$(id -u):$(id -g)" "$DEMO_DIR"
fi
echo "PTERODACTYL_CLIENT_KEY secret present: $(test -n "$PTERODACTYL_CLIENT_KEY" && echo yes || echo no)"
# The rig's web token: the startup variable first, then sidecar.toml.
api() { curl -s --max-time 15 -H "Authorization: Bearer $PTERODACTYL_CLIENT_KEY" -H 'Accept: application/json' "$PTERODACTYL_URL/api/client/servers/$PTERODACTYL_SERVER/$1"; }
RIG_TOKEN=$(api startup | node -e "let s='';process.stdin.on('data',c=>s+=c).on('end',()=>{try{const v=JSON.parse(s).data.find(x=>x.attributes.env_variable==='RUSTLINK_WEB_TOKEN');process.stdout.write((v&&v.attributes.server_value)||'')}catch{}})") || true
echo "rig token from the startup variables: $(test -n "$RIG_TOKEN" && echo yes || echo no)"
if test -z "$RIG_TOKEN"; then
RIG_TOKEN=$(api 'files/contents?file=%2Frust-link%2Fsidecar.toml' | sed -n 's/^[[:space:]]*auth_token[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' | head -1) || true
echo "rig token from rust-link/sidecar.toml: $(test -n "$RIG_TOKEN" && echo yes || echo no)"
fi
if test -n "$RIG_TOKEN"; then
echo "sidecar /server with that token -> $(curl -s -o /dev/null -w '%{http_code}' --max-time 10 -H "Authorization: Bearer $RIG_TOKEN" "$SIDECAR_URL/server" || true)"
fi
hex() { head -c "$1" /dev/urandom | od -An -tx1 | tr -d ' \n'; }
setkv() { K="$1" V="$2" awk 'index($0, ENVIRON["K"] "=") == 1 { $0 = ENVIRON["K"] "=" ENVIRON["V"] } { print }' "$f" > "$f.tmp"; cat "$f.tmp" > "$f"; rm -f "$f.tmp"; }
# An existing .env keeps every value; only an empty rig token is filled.
if test -f "$DEMO_DIR/.env"; then
f="$DEMO_DIR/.env"
if grep -q '^DEMO_RIG_SIDECAR_TOKEN=$' "$f" && test -n "$RIG_TOKEN"; then
setkv DEMO_RIG_SIDECAR_TOKEN "$RIG_TOKEN"
echo "$f exists; filled DEMO_RIG_SIDECAR_TOKEN only."
else
echo "$f exists; left untouched."
fi
echo "Empty keys left:"
grep -E '^[A-Z_]+=$' "$f" | cut -d= -f1 || true
exit 0
fi
f="$DEMO_DIR/.env.new"
cp deploy/demo/.env.example "$f"
chmod 600 "$f"
setkv DB_PASSWORD "$(hex 24)"
setkv DB_ROOT_PASSWORD "$(hex 24)"
setkv JWT_SECRET "$(hex 32)"
setkv SECRET_ENC_KEY "$(hex 32)"
setkv BOT_INTERNAL_KEY "$(hex 32)"
# The three published logins are shown on the site, so short is fine.
setkv DEMO_ADMIN_PASSWORD "demo-$(hex 5)"
setkv DEMO_MODERATOR_PASSWORD "demo-$(hex 5)"
setkv DEMO_PLAYER_PASSWORD "demo-$(hex 5)"
setkv PLAY_REVIEW_PASSWORD "$(hex 16)"
# The walks' stand-in id (76561199910000001); the default-set step gives it play data.
setkv DEMO_PLAYER_STEAM_ID 76561199910000001
setkv DEMO_RIG_SIDECAR_TOKEN "$RIG_TOKEN"
mv "$f" "$DEMO_DIR/.env"
chmod 600 "$DEMO_DIR/.env"
echo "wrote $DEMO_DIR/.env (mode 600). Empty keys left:"
grep -E '^[A-Z_]+=$' "$DEMO_DIR/.env" | cut -d= -f1 || true