chore(demo): one-shot host bootstrap (never merged)
Some checks failed
Demo bootstrap (one-shot) / bootstrap (push) Failing after 5s
Some checks failed
Demo bootstrap (one-shot) / bootstrap (push) Failing after 5s
Runs once on the demo-site runner while the org lead is away: writes /opt/demo.runicgateway.com/.env from deploy/demo/.env.example with every secret generated on the host and the rig's web token read through the panel, and reports whether the host reaches the panel and the sidecar. Never overwrites an existing .env and never prints a value. This branch is deleted after the run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
94
.gitea/workflows/demo-bootstrap.yml
Normal file
94
.gitea/workflows/demo-bootstrap.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
# 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
|
||||
if test -f "$DEMO_DIR/.env"; then
|
||||
echo "$DEMO_DIR/.env already exists; left untouched."
|
||||
exit 0
|
||||
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'; }
|
||||
f="$DEMO_DIR/.env.new"
|
||||
cp deploy/demo/.env.example "$f"
|
||||
chmod 600 "$f"
|
||||
setkv() { K="$1" V="$2" awk 'index($0, ENVIRON["K"] "=") == 1 { $0 = ENVIRON["K"] "=" ENVIRON["V"] } { print }' "$f" > "$f.tmp"; mv "$f.tmp" "$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
|
||||
Reference in New Issue
Block a user