Refactor deploy/push scripts and enhance game docs generator
- Add comprehensive headers to deploy_gsp.sh and push_to_github.sh - Make scripts configurable via environment variables - Update push_to_github.sh to support GITHUB_TOKEN env var - Enhance generate_game_docs.py with: * Command-line argument support (--games, --force, --todo-only) * Extraction of detailed startup parameters from XML * Prevention of overwriting existing files unless --force is used * Comprehensive parameter documentation with options and defaults Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
d3c83c33b1
commit
f8ae24e87b
4 changed files with 498 additions and 108 deletions
|
|
@ -1,4 +1,40 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# GSP Deployment Script
|
||||||
|
# =====================
|
||||||
|
# This script deploys the Game Server Panel (GSP) from GitHub to a web server.
|
||||||
|
#
|
||||||
|
# HOW IT WORKS:
|
||||||
|
# 1. Clones/updates the GSP repository to a staging directory
|
||||||
|
# 2. Syncs files to the web root using rsync (preserving configs)
|
||||||
|
# 3. Sets proper permissions for OGP panel operation
|
||||||
|
#
|
||||||
|
# CONFIGURATION:
|
||||||
|
# All settings can be configured via environment variables or by editing
|
||||||
|
# the defaults in the "Config" section below.
|
||||||
|
#
|
||||||
|
# ENVIRONMENT VARIABLES:
|
||||||
|
# - REPO_URL: Git repository URL (default: https://github.com/GameServerPanel/GSP.git)
|
||||||
|
# - STAGE_DIR: Staging directory for git clone (default: $HOME/gsp_stage)
|
||||||
|
# - WEB_ROOT: Live web server directory (default: /var/www/html/panel)
|
||||||
|
# - OWNER: File owner user (default: www-data)
|
||||||
|
# - GROUP: File owner group (default: www-data)
|
||||||
|
# - SUDO: Command prefix for privilege escalation (default: sudo, set empty to skip)
|
||||||
|
# - DRY_RUN: Set to 1 to test without making changes (default: 0)
|
||||||
|
#
|
||||||
|
# EXAMPLE USAGE:
|
||||||
|
# # Use defaults:
|
||||||
|
# ./deploy_gsp.sh
|
||||||
|
#
|
||||||
|
# # Custom web root:
|
||||||
|
# WEB_ROOT=/home/panel/public_html ./deploy_gsp.sh
|
||||||
|
#
|
||||||
|
# # Dry run to test:
|
||||||
|
# DRY_RUN=1 ./deploy_gsp.sh
|
||||||
|
#
|
||||||
|
# # Different user/group:
|
||||||
|
# OWNER=apache GROUP=apache ./deploy_gsp.sh
|
||||||
|
#
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
umask 022
|
umask 022
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
<h2 id="quick-info">Quick Info</h2>
|
<h2 id="quick-info">Quick Info</h2>
|
||||||
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
|
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
|
||||||
<ul style="color: #e5e7eb; line-height: 1.8; margin: 0;">
|
<ul style="color: #e5e7eb; line-height: 1.8; margin: 0;">
|
||||||
<li><strong style="color: #ffffff;">Default Port:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">28015</code></li>
|
<li><strong style="color: #ffffff;">Default Port:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">Varies (see configuration)</code></li>
|
||||||
<li><strong style="color: #ffffff;">Protocol:</strong> TCP/UDP</li>
|
<li><strong style="color: #ffffff;">Protocol:</strong> TCP/UDP</li>
|
||||||
<li><strong style="color: #ffffff;">Minimum RAM:</strong> 1GB</li>
|
<li><strong style="color: #ffffff;">Minimum RAM:</strong> 1GB</li>
|
||||||
<li><strong style="color: #ffffff;">Engine:</strong> Various</li>
|
<li><strong style="color: #ffffff;">Engine:</strong> Various</li>
|
||||||
|
|
@ -38,33 +38,7 @@
|
||||||
<h2 id="ports">🔌 Network Ports</h2>
|
<h2 id="ports">🔌 Network Ports</h2>
|
||||||
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
|
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
|
||||||
<h3 style="color: #ffffff; margin-top: 0;">Required Ports</h3>
|
<h3 style="color: #ffffff; margin-top: 0;">Required Ports</h3>
|
||||||
<p style="color: #e5e7eb;">The following ports are used by this game server:</p>
|
<p style="color: #e5e7eb;">The Rust server typically uses a configurable port. Check your server configuration files for the specific port settings.</p>
|
||||||
<table style="width: 100%; color: #e5e7eb; border-collapse: collapse;">
|
|
||||||
<thead>
|
|
||||||
<tr style="background: #0f172a;">
|
|
||||||
<th style="padding: 10px; text-align: left; color: #ffffff;">Port</th>
|
|
||||||
<th style="padding: 10px; text-align: left; color: #ffffff;">Protocol</th>
|
|
||||||
<th style="padding: 10px; text-align: left; color: #ffffff;">Purpose</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr style="border-bottom: 1px solid #374151;">
|
|
||||||
<td style="padding: 10px;"><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">28015</code></td>
|
|
||||||
<td style="padding: 10px;">UDP</td>
|
|
||||||
<td style="padding: 10px;">Game port</td>
|
|
||||||
</tr>
|
|
||||||
<tr style="border-bottom: 1px solid #374151;">
|
|
||||||
<td style="padding: 10px;"><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">28016</code></td>
|
|
||||||
<td style="padding: 10px;">TCP</td>
|
|
||||||
<td style="padding: 10px;">RCON</td>
|
|
||||||
</tr>
|
|
||||||
<tr style="border-bottom: 1px solid #374151;">
|
|
||||||
<td style="padding: 10px;"><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">28082</code></td>
|
|
||||||
<td style="padding: 10px;">TCP</td>
|
|
||||||
<td style="padding: 10px;">Web panel (if enabled)</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3 style="color: #ffffff; margin-top: 20px;">Firewall Configuration</h3>
|
<h3 style="color: #ffffff; margin-top: 20px;">Firewall Configuration</h3>
|
||||||
<p style="color: #e5e7eb;">Allow server ports through your firewall:</p>
|
<p style="color: #e5e7eb;">Allow server ports through your firewall:</p>
|
||||||
|
|
@ -194,68 +168,116 @@ setadminpassword [password]
|
||||||
|
|
||||||
<h2 id="parameters">⚙️ Startup Parameters</h2>
|
<h2 id="parameters">⚙️ Startup Parameters</h2>
|
||||||
|
|
||||||
<h3>Basic Startup</h3>
|
<h3>Command Line Template</h3>
|
||||||
<pre><code># Generic startup command structure
|
<p>The server uses the following command line template:</p>
|
||||||
./server_executable [parameters]
|
<pre><code>-batchmode -nographics +server.ip %IP% %PORT% %QUERY_PORT% %PLAYERS% %HOSTNAME% %IDENTITY% %DESCRIPTION% %WORLDSIZE% %SEED% %SALT% %TICKRATE% %MAP% %BCK% %SAVEINTERNAL% %SECURE% %RCONWEB% %CONTROL_PASSWORD% -logfile output.txt</code></pre>
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h3>Common Parameters</h3>
|
<h3>Available Startup Parameters</h3>
|
||||||
<ul>
|
<p>The following parameters can be configured when starting the server:</p>
|
||||||
<li><code>-port [number]</code> - Set the server port</li>
|
|
||||||
<li><code>-maxplayers [number]</code> - Maximum player slots</li>
|
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
|
||||||
<li><code>-map [name]</code> - Starting map/level</li>
|
|
||||||
<li><code>-console</code> - Enable console output</li>
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
<li><code>-nographics</code> - Run without graphics (headless mode)</li>
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.identity</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.identity</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">Changes path to your server data (e.g. rust/server/my_server_identity).</p>
|
||||||
|
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">server_identity</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.description</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.description</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">Server Description shown on server browser</p>
|
||||||
|
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">Plain old Rust Server</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.worldsize</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.worldsize</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">Defines the size of the map generated (min 1000, max 8000).</p>
|
||||||
|
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">1000</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.seed</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.seed</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">Defines the map generation seed.</p>
|
||||||
|
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">0</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.salt</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.salt</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">Defines the randomization to mining resources.</p>
|
||||||
|
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">0</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.tickrate</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.tickrate</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">Defines the server tickrate (going higher than 30 is not recommended).</p>
|
||||||
|
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">30</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.level</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.level</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">Defines the map of the server.</p>
|
||||||
|
<p style="color: #e5e7eb;"><strong>Options:</strong></p>
|
||||||
|
<ul style="color: #e5e7eb; margin-left: 20px;">
|
||||||
|
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">Barren</code> - Barren</li>
|
||||||
|
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">CraggyIsland</code> - Craggy Island</li>
|
||||||
|
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">HapisIsland</code> - Hapis Island</li>
|
||||||
|
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">Procedural Map</code> - Procedural Map</li>
|
||||||
|
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">SavasIsland</code> - Savas Island</li>
|
||||||
|
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">SavasIsland_koth</code> - Savas Island KoTH</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3>Creating a Start Script</h3>
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+backup</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +backup</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">Enable automatic backups.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p><strong>Linux (start.sh):</strong></p>
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
<pre><code>#!/bin/bash
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
cd /path/to/server
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+server.saveinterval</code>
|
||||||
./server_executable [parameters] 2>&1 | tee server.log
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +server.saveinterval</span>
|
||||||
</code></pre>
|
</h4>
|
||||||
<pre><code>chmod +x start.sh
|
<p style="color: #e5e7eb; margin: 10px 0;">Interval between the server saves the map.</p>
|
||||||
./start.sh
|
<p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">600</code></p>
|
||||||
</code></pre>
|
</div>
|
||||||
|
|
||||||
<p><strong>Windows (start.bat):</strong></p>
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
<pre><code>@echo off
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
cd /d "%~dp0"
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">+rcon.web</code>
|
||||||
server_executable.exe [parameters]
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - +rcon.web</span>
|
||||||
pause
|
</h4>
|
||||||
</code></pre>
|
<p style="color: #e5e7eb; margin: 10px 0;">If set to enabled, use websocket RCON. If set to disabled, use legacy source engine RCON.</p>
|
||||||
|
<p style="color: #e5e7eb;"><strong>Options:</strong></p>
|
||||||
<h3>Running as a Service</h3>
|
<ul style="color: #e5e7eb; margin-left: 20px;">
|
||||||
|
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">0</code> - Disabled</li>
|
||||||
<p><strong>Linux (systemd):</strong></p>
|
<li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">1</code> - Enabled</li>
|
||||||
<pre><code># Create service file: /etc/systemd/system/gameserver.service
|
</ul>
|
||||||
[Unit]
|
</div>
|
||||||
Description=Rust Server
|
</div>
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=gameserver
|
|
||||||
WorkingDirectory=/home/gameserver/server
|
|
||||||
ExecStart=/home/gameserver/server/start.sh
|
|
||||||
Restart=on-failure
|
|
||||||
RestartSec=10
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<pre><code># Enable and start service
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl enable gameserver
|
|
||||||
sudo systemctl start gameserver
|
|
||||||
sudo systemctl status gameserver
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
<h2 id="troubleshooting">🔧 Troubleshooting</h2>
|
|
||||||
|
|
||||||
<h3>Server Won't Start</h3>
|
|
||||||
|
|
||||||
<h4>Check Server Logs</h4>
|
<h4>Check Server Logs</h4>
|
||||||
<pre><code># View recent log entries
|
<pre><code># View recent log entries
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,82 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# GSP GitHub Push Script
|
||||||
|
# ======================
|
||||||
|
# This script commits local changes in your GSP panel directory and pushes
|
||||||
|
# them to GitHub via a Pull Request.
|
||||||
|
#
|
||||||
|
# HOW IT WORKS:
|
||||||
|
# 1. Initializes git in the panel directory (if needed)
|
||||||
|
# 2. Commits all changes (excluding sensitive files like config.inc.php)
|
||||||
|
# 3. Creates a timestamped branch
|
||||||
|
# 4. Attempts to push to upstream repo (requires write access)
|
||||||
|
# 5. If no write access, creates/uses a fork and submits PR from there
|
||||||
|
#
|
||||||
|
# CONFIGURATION:
|
||||||
|
# Configure settings via environment variables or by editing the defaults below.
|
||||||
|
#
|
||||||
|
# ENVIRONMENT VARIABLES:
|
||||||
|
# - PANEL_DIR: Path to your GSP panel installation (default: /var/www/html/panel)
|
||||||
|
# - TOKEN_FILE: File containing GitHub Personal Access Token (default: ~/.github-token)
|
||||||
|
# - GITHUB_TOKEN: GitHub token directly (used if TOKEN_FILE doesn't exist)
|
||||||
|
# - UPSTREAM_REPO: Target repository in "owner/repo" format (default: GameServerPanel/GSP)
|
||||||
|
# - BR_PREFIX: Branch name prefix for PRs (default: panel-sync)
|
||||||
|
# - GIT_USER_NAME: Git commit author name (default: Server Sync Bot)
|
||||||
|
# - GIT_USER_EMAIL: Git commit author email (default: server-sync@local)
|
||||||
|
#
|
||||||
|
# GITHUB TOKEN SETUP:
|
||||||
|
# You need a GitHub Personal Access Token with 'repo' scope.
|
||||||
|
# Create one at: https://github.com/settings/tokens
|
||||||
|
#
|
||||||
|
# Option 1: Store in file (recommended):
|
||||||
|
# echo "your_token_here" > ~/.github-token
|
||||||
|
# chmod 600 ~/.github-token
|
||||||
|
#
|
||||||
|
# Option 2: Use environment variable:
|
||||||
|
# export GITHUB_TOKEN="your_token_here"
|
||||||
|
#
|
||||||
|
# EXAMPLE USAGE:
|
||||||
|
# # Use defaults:
|
||||||
|
# ./push_to_github.sh
|
||||||
|
#
|
||||||
|
# # Custom panel directory:
|
||||||
|
# PANEL_DIR=/home/panel/public_html ./push_to_github.sh
|
||||||
|
#
|
||||||
|
# # Using environment token:
|
||||||
|
# GITHUB_TOKEN="ghp_xxxx" ./push_to_github.sh
|
||||||
|
#
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
umask 022
|
umask 022
|
||||||
|
|
||||||
# ---------- HARD-CODED PROJECT SETTINGS ----------
|
# ---------- CONFIGURABLE SETTINGS ----------
|
||||||
PANEL_DIR="/var/www/html/panel"
|
PANEL_DIR="${PANEL_DIR:-/var/www/html/panel}"
|
||||||
TOKEN_FILE="/home/gameserver/git.token"
|
TOKEN_FILE="${TOKEN_FILE:-$HOME/.github-token}"
|
||||||
|
|
||||||
UPSTREAM_REPO="GameServerPanel/GSP" # owner/repo
|
UPSTREAM_REPO="${UPSTREAM_REPO:-GameServerPanel/GSP}" # owner/repo
|
||||||
UPSTREAM_URL="https://github.com/${UPSTREAM_REPO}.git"
|
UPSTREAM_URL="https://github.com/${UPSTREAM_REPO}.git"
|
||||||
|
|
||||||
BR_PREFIX="panel-sync" # branch prefix for PRs
|
BR_PREFIX="${BR_PREFIX:-panel-sync}" # branch prefix for PRs
|
||||||
GIT_USER_NAME="Server Sync Bot"
|
GIT_USER_NAME="${GIT_USER_NAME:-Server Sync Bot}"
|
||||||
GIT_USER_EMAIL="server-sync@local"
|
GIT_USER_EMAIL="${GIT_USER_EMAIL:-server-sync@local}"
|
||||||
|
|
||||||
log(){ printf '[%s] %s\n' "$(date +'%F %T')" "$*"; }
|
log(){ printf '[%s] %s\n' "$(date +'%F %T')" "$*"; }
|
||||||
die(){ echo "ERROR: $*" >&2; exit 1; }
|
die(){ echo "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
# ---------- PRECHECKS ----------
|
# ---------- PRECHECKS ----------
|
||||||
[[ -d "$PANEL_DIR" ]] || die "Panel dir not found: $PANEL_DIR"
|
[[ -d "$PANEL_DIR" ]] || die "Panel dir not found: $PANEL_DIR"
|
||||||
[[ -s "$TOKEN_FILE" ]] || die "Token file missing/empty: $TOKEN_FILE"
|
|
||||||
|
# Try to get token from file or environment
|
||||||
|
TOKEN=""
|
||||||
|
if [[ -s "$TOKEN_FILE" ]]; then
|
||||||
TOKEN="$(<"$TOKEN_FILE")"
|
TOKEN="$(<"$TOKEN_FILE")"
|
||||||
[[ ${#TOKEN} -ge 10 ]] || die "Token in $TOKEN_FILE looks invalid"
|
elif [[ -n "${GITHUB_TOKEN:-}" ]]; then
|
||||||
|
TOKEN="$GITHUB_TOKEN"
|
||||||
|
else
|
||||||
|
die "GitHub token not found. Set GITHUB_TOKEN env var or create $TOKEN_FILE with your token.
|
||||||
|
Get a token at: https://github.com/settings/tokens (requires 'repo' scope)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ ${#TOKEN} -ge 10 ]] || die "Token looks invalid (too short)"
|
||||||
|
|
||||||
cd "$PANEL_DIR"
|
cd "$PANEL_DIR"
|
||||||
if [[ ! -d ".git" ]]; then
|
if [[ ! -d ".git" ]]; then
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,42 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Comprehensive Game Server Documentation Generator for GSP
|
Comprehensive Game Server Documentation Generator for GSP
|
||||||
Generates PHP documentation files for all games in the "todo" category
|
|
||||||
Based on the Minecraft template structure
|
This script generates PHP documentation files for game servers in the GSP billing module.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
# Generate docs for all incomplete games:
|
||||||
|
python3 generate_game_docs.py
|
||||||
|
|
||||||
|
# Generate docs for specific game(s):
|
||||||
|
python3 generate_game_docs.py --games minecraft csgo rust
|
||||||
|
|
||||||
|
# Regenerate docs (overwrite existing):
|
||||||
|
python3 generate_game_docs.py --games minecraft --force
|
||||||
|
|
||||||
|
# Process all "todo" category games:
|
||||||
|
python3 generate_game_docs.py --todo-only
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
--games GAME1 GAME2 ... Generate docs for specific game folder names
|
||||||
|
--force Overwrite existing documentation files
|
||||||
|
--todo-only Only process games with category="todo"
|
||||||
|
--help Show this help message
|
||||||
|
|
||||||
|
The generator extracts information from:
|
||||||
|
- XML configurations (modules/config_games/server_configs/*.xml)
|
||||||
|
- YAML knowledgepack (modules/billing/docs/gameserver_knowledgepack_v2.yaml)
|
||||||
|
- Existing metadata.json files
|
||||||
|
|
||||||
|
Generated documentation includes:
|
||||||
|
- Quick info (ports, RAM, Steam App ID)
|
||||||
|
- Network ports and firewall configuration
|
||||||
|
- Installation steps with SteamCMD commands
|
||||||
|
- Server configuration files and settings
|
||||||
|
- Detailed startup parameters from XML
|
||||||
|
- Comprehensive troubleshooting guide
|
||||||
|
- Performance optimization tips
|
||||||
|
- Security best practices
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
@ -10,6 +44,7 @@ import sys
|
||||||
import json
|
import json
|
||||||
import yaml
|
import yaml
|
||||||
import re
|
import re
|
||||||
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
@ -115,6 +150,68 @@ class GameDocGenerator:
|
||||||
|
|
||||||
return ports
|
return ports
|
||||||
|
|
||||||
|
def extract_startup_parameters_from_xml(self, xml_root):
|
||||||
|
"""Extract detailed startup parameters from XML server_params section"""
|
||||||
|
startup_params = []
|
||||||
|
|
||||||
|
# Get CLI template and params
|
||||||
|
cli_template = xml_root.find('cli_template')
|
||||||
|
cli_template_text = cli_template.text if cli_template is not None else ""
|
||||||
|
|
||||||
|
cli_params_elem = xml_root.find('cli_params')
|
||||||
|
cli_params_info = {}
|
||||||
|
if cli_params_elem is not None:
|
||||||
|
for param in cli_params_elem.findall('cli_param'):
|
||||||
|
param_id = param.get('id', '')
|
||||||
|
cli_string = param.get('cli_string', '')
|
||||||
|
options = param.get('options', '')
|
||||||
|
if param_id:
|
||||||
|
cli_params_info[param_id] = {
|
||||||
|
'cli_string': cli_string,
|
||||||
|
'options': options
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract server_params - these are the configurable startup parameters
|
||||||
|
server_params = xml_root.find('server_params')
|
||||||
|
if server_params is not None:
|
||||||
|
for param in server_params.findall('param'):
|
||||||
|
param_key = param.get('key', '')
|
||||||
|
param_id = param.get('id', '')
|
||||||
|
param_type = param.get('type', 'text')
|
||||||
|
|
||||||
|
# Get caption and description
|
||||||
|
caption_elem = param.find('caption')
|
||||||
|
desc_elem = param.find('desc')
|
||||||
|
default_elem = param.find('default')
|
||||||
|
|
||||||
|
caption = caption_elem.text if caption_elem is not None else param_key
|
||||||
|
description = desc_elem.text if desc_elem is not None else "No description available"
|
||||||
|
default_value = default_elem.text if default_elem is not None else None
|
||||||
|
|
||||||
|
# For select type, get options
|
||||||
|
options = []
|
||||||
|
if param_type == 'select':
|
||||||
|
for option in param.findall('option'):
|
||||||
|
opt_value = option.get('value', '')
|
||||||
|
opt_text = option.text if option.text else opt_value
|
||||||
|
options.append({'value': opt_value, 'text': opt_text})
|
||||||
|
|
||||||
|
startup_params.append({
|
||||||
|
'key': param_key,
|
||||||
|
'id': param_id,
|
||||||
|
'type': param_type,
|
||||||
|
'caption': caption,
|
||||||
|
'description': description,
|
||||||
|
'default': default_value,
|
||||||
|
'options': options
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'cli_template': cli_template_text,
|
||||||
|
'cli_params': cli_params_info,
|
||||||
|
'server_params': startup_params
|
||||||
|
}
|
||||||
|
|
||||||
def extract_config_files_from_xml(self, xml_root):
|
def extract_config_files_from_xml(self, xml_root):
|
||||||
"""Extract configuration file paths from XML"""
|
"""Extract configuration file paths from XML"""
|
||||||
config_files = []
|
config_files = []
|
||||||
|
|
@ -139,15 +236,17 @@ class GameDocGenerator:
|
||||||
kb_info = self.get_game_info_from_knowledgepack(game_name)
|
kb_info = self.get_game_info_from_knowledgepack(game_name)
|
||||||
xml_config = self.get_xml_config(folder_name)
|
xml_config = self.get_xml_config(folder_name)
|
||||||
|
|
||||||
# Extract ports and configs
|
# Extract ports, configs, and startup parameters
|
||||||
ports_info = []
|
ports_info = []
|
||||||
config_files = []
|
config_files = []
|
||||||
|
startup_params_data = {}
|
||||||
if xml_config is not None:
|
if xml_config is not None:
|
||||||
ports_info = self.extract_ports_from_xml(xml_config)
|
ports_info = self.extract_ports_from_xml(xml_config)
|
||||||
config_files = self.extract_config_files_from_xml(xml_config)
|
config_files = self.extract_config_files_from_xml(xml_config)
|
||||||
|
startup_params_data = self.extract_startup_parameters_from_xml(xml_config)
|
||||||
|
|
||||||
# Build the PHP document
|
# Build the PHP document
|
||||||
php_content = self.build_php_content(game_name, folder_name, kb_info, xml_config, ports_info, config_files)
|
php_content = self.build_php_content(game_name, folder_name, kb_info, xml_config, ports_info, config_files, startup_params_data)
|
||||||
|
|
||||||
return php_content
|
return php_content
|
||||||
|
|
||||||
|
|
@ -180,7 +279,7 @@ class GameDocGenerator:
|
||||||
|
|
||||||
return 'N/A'
|
return 'N/A'
|
||||||
|
|
||||||
def build_php_content(self, game_name, folder_name, kb_info, xml_config, ports_info, config_files):
|
def build_php_content(self, game_name, folder_name, kb_info, xml_config, ports_info, config_files, startup_params_data):
|
||||||
"""Build the complete PHP documentation content"""
|
"""Build the complete PHP documentation content"""
|
||||||
|
|
||||||
# Extract data from various sources
|
# Extract data from various sources
|
||||||
|
|
@ -495,7 +594,67 @@ setadminpassword [password]
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
|
||||||
<h2 id="parameters">⚙️ Startup Parameters</h2>
|
<h2 id="parameters">⚙️ Startup Parameters</h2>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Add detailed startup parameters from XML if available
|
||||||
|
if startup_params_data and startup_params_data.get('server_params'):
|
||||||
|
server_params = startup_params_data['server_params']
|
||||||
|
cli_template = startup_params_data.get('cli_template', '')
|
||||||
|
|
||||||
|
if cli_template:
|
||||||
|
php_doc += f'''
|
||||||
|
<h3>Command Line Template</h3>
|
||||||
|
<p>The server uses the following command line template:</p>
|
||||||
|
<pre><code>{cli_template}</code></pre>
|
||||||
|
'''
|
||||||
|
|
||||||
|
php_doc += '''
|
||||||
|
<h3>Available Startup Parameters</h3>
|
||||||
|
<p>The following parameters can be configured when starting the server:</p>
|
||||||
|
|
||||||
|
<div style="background: #1e3a5f; padding: 20px; border-left: 4px solid #3b82f6; margin: 20px 0; border-radius: 4px;">
|
||||||
|
'''
|
||||||
|
for param in server_params:
|
||||||
|
param_key = param['key']
|
||||||
|
caption = param['caption']
|
||||||
|
description = param['description']
|
||||||
|
param_type = param['type']
|
||||||
|
default = param.get('default')
|
||||||
|
options = param.get('options', [])
|
||||||
|
|
||||||
|
# Clean HTML from description
|
||||||
|
description_clean = re.sub(r'<[^>]+>', '', description) if description else "No description available"
|
||||||
|
|
||||||
|
php_doc += f'''
|
||||||
|
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #374151;">
|
||||||
|
<h4 style="color: #ffffff; margin-top: 0;">
|
||||||
|
<code style="background: #0f172a; padding: 4px 8px; border-radius: 3px; color: #a5b4fc;">{param_key}</code>
|
||||||
|
<span style="color: #e5e7eb; font-weight: normal; font-size: 0.9em;"> - {caption}</span>
|
||||||
|
</h4>
|
||||||
|
<p style="color: #e5e7eb; margin: 10px 0;">{description_clean}</p>
|
||||||
|
'''
|
||||||
|
|
||||||
|
if param_type == 'select' and options:
|
||||||
|
php_doc += ''' <p style="color: #e5e7eb;"><strong>Options:</strong></p>
|
||||||
|
<ul style="color: #e5e7eb; margin-left: 20px;">
|
||||||
|
'''
|
||||||
|
for opt in options:
|
||||||
|
php_doc += f''' <li><code style="background: #0f172a; padding: 2px 6px; border-radius: 3px; color: #a5b4fc;">{opt['value']}</code> - {opt['text']}</li>\n'''
|
||||||
|
php_doc += ''' </ul>
|
||||||
|
'''
|
||||||
|
|
||||||
|
if default:
|
||||||
|
php_doc += f''' <p style="color: #fbbf24;"><strong>Default:</strong> <code style="background: #0f172a; padding: 2px 6px; border-radius: 3px;">{default}</code></p>
|
||||||
|
'''
|
||||||
|
|
||||||
|
php_doc += ''' </div>
|
||||||
|
'''
|
||||||
|
|
||||||
|
php_doc += '''</div>
|
||||||
|
'''
|
||||||
|
else:
|
||||||
|
# Fallback to generic parameters if no XML data
|
||||||
|
php_doc += '''
|
||||||
<h3>Basic Startup</h3>
|
<h3>Basic Startup</h3>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
@ -893,7 +1052,102 @@ sudo ufw enable
|
||||||
|
|
||||||
return processed, errors
|
return processed, errors
|
||||||
|
|
||||||
|
def process_specific_games(self, game_names, force_overwrite=False):
|
||||||
|
"""Process specific game folders by name"""
|
||||||
|
processed = 0
|
||||||
|
skipped = 0
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for game_name in game_names:
|
||||||
|
folder = self.docs_dir / game_name
|
||||||
|
|
||||||
|
if not folder.is_dir():
|
||||||
|
error_msg = f"Game folder not found: {game_name}"
|
||||||
|
print(f" ✗ {error_msg}")
|
||||||
|
errors.append(error_msg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
metadata_file = folder / 'metadata.json'
|
||||||
|
index_file = folder / 'index.php'
|
||||||
|
|
||||||
|
if not metadata_file.exists():
|
||||||
|
error_msg = f"metadata.json not found for {game_name}"
|
||||||
|
print(f" ✗ {error_msg}")
|
||||||
|
errors.append(error_msg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Read metadata
|
||||||
|
with open(metadata_file, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
content = content.lstrip('\ufeff')
|
||||||
|
metadata = json.loads(content)
|
||||||
|
|
||||||
|
# Check if we should skip
|
||||||
|
if index_file.exists() and not force_overwrite:
|
||||||
|
print(f"Skipping {game_name} (file exists, use --force to overwrite)")
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"Processing: {game_name}")
|
||||||
|
|
||||||
|
# Generate new documentation
|
||||||
|
php_content = self.generate_php_doc(folder.name, metadata)
|
||||||
|
|
||||||
|
# Write the new index.php
|
||||||
|
with open(index_file, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(php_content)
|
||||||
|
|
||||||
|
# Update metadata if it was todo
|
||||||
|
if metadata.get('category', '').lower() == 'todo':
|
||||||
|
metadata['category'] = 'game'
|
||||||
|
|
||||||
|
# Mark as complete
|
||||||
|
metadata['complete'] = True
|
||||||
|
|
||||||
|
with open(metadata_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(metadata, f, indent=4, ensure_ascii=False)
|
||||||
|
|
||||||
|
processed += 1
|
||||||
|
print(f" ✓ Generated documentation for {game_name}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Error processing {game_name}: {e}"
|
||||||
|
print(f" ✗ {error_msg}")
|
||||||
|
errors.append(error_msg)
|
||||||
|
|
||||||
|
return processed, skipped, errors
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# Parse command line arguments
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Generate comprehensive game server documentation for GSP',
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
Examples:
|
||||||
|
# Generate docs for all incomplete games:
|
||||||
|
python3 generate_game_docs.py
|
||||||
|
|
||||||
|
# Generate docs for specific game(s):
|
||||||
|
python3 generate_game_docs.py --games minecraft csgo rust
|
||||||
|
|
||||||
|
# Regenerate docs (overwrite existing):
|
||||||
|
python3 generate_game_docs.py --games minecraft --force
|
||||||
|
|
||||||
|
# Process all "todo" category games:
|
||||||
|
python3 generate_game_docs.py --todo-only
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument('--games', nargs='+', metavar='GAME',
|
||||||
|
help='Generate docs for specific game folder names')
|
||||||
|
parser.add_argument('--force', action='store_true',
|
||||||
|
help='Overwrite existing documentation files')
|
||||||
|
parser.add_argument('--todo-only', action='store_true',
|
||||||
|
help='Only process games with category="todo"')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
docs_dir = "/home/runner/work/GSP/GSP/modules/billing/docs"
|
docs_dir = "/home/runner/work/GSP/GSP/modules/billing/docs"
|
||||||
config_dir = "/home/runner/work/GSP/GSP/modules/config_games/server_configs"
|
config_dir = "/home/runner/work/GSP/GSP/modules/config_games/server_configs"
|
||||||
knowledgepack = "/home/runner/work/GSP/GSP/modules/billing/docs/gameserver_knowledgepack_v2.yaml"
|
knowledgepack = "/home/runner/work/GSP/GSP/modules/billing/docs/gameserver_knowledgepack_v2.yaml"
|
||||||
|
|
@ -908,6 +1162,27 @@ def main():
|
||||||
generator.load_knowledgepack()
|
generator.load_knowledgepack()
|
||||||
generator.load_xml_configs()
|
generator.load_xml_configs()
|
||||||
|
|
||||||
|
processed = 0
|
||||||
|
skipped = 0
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
# Determine which mode to run
|
||||||
|
if args.games:
|
||||||
|
# Process specific games
|
||||||
|
print("\n" + "="*70)
|
||||||
|
print(f"Processing {len(args.games)} specified game(s)...")
|
||||||
|
if args.force:
|
||||||
|
print("FORCE mode: Will overwrite existing files")
|
||||||
|
print("="*70)
|
||||||
|
processed, skipped, errors = generator.process_specific_games(args.games, args.force)
|
||||||
|
elif args.todo_only:
|
||||||
|
# Process only todo category games
|
||||||
|
print("\n" + "="*70)
|
||||||
|
print("Processing TODO category games...")
|
||||||
|
print("="*70)
|
||||||
|
processed, errors = generator.process_todo_folders()
|
||||||
|
else:
|
||||||
|
# Process incomplete games (default)
|
||||||
print("\n" + "="*70)
|
print("\n" + "="*70)
|
||||||
print("Processing INCOMPLETE game documentation...")
|
print("Processing INCOMPLETE game documentation...")
|
||||||
print("="*70)
|
print("="*70)
|
||||||
|
|
@ -916,7 +1191,8 @@ def main():
|
||||||
print(f"\n{'='*70}")
|
print(f"\n{'='*70}")
|
||||||
print(f"Documentation generation complete!")
|
print(f"Documentation generation complete!")
|
||||||
print(f" ✓ Processed: {processed}")
|
print(f" ✓ Processed: {processed}")
|
||||||
print(f" → Skipped (already complete): {skipped}")
|
if skipped > 0:
|
||||||
|
print(f" → Skipped: {skipped}")
|
||||||
print(f" ✗ Errors: {len(errors)}")
|
print(f" ✗ Errors: {len(errors)}")
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
|
|
@ -926,12 +1202,12 @@ def main():
|
||||||
if len(errors) > 10:
|
if len(errors) > 10:
|
||||||
print(f" ... and {len(errors) - 10} more")
|
print(f" ... and {len(errors) - 10} more")
|
||||||
|
|
||||||
print(f"\nAll documentation has been enhanced with:")
|
print(f"\nGenerated documentation includes:")
|
||||||
print(f" • Actual port information (not 'Check server configuration')")
|
print(f" • Actual port information from XML configs")
|
||||||
print(f" • Steam App IDs and exact SteamCMD commands")
|
print(f" • Steam App IDs and exact SteamCMD commands")
|
||||||
print(f" • Configuration file details from XML configs")
|
print(f" • Configuration file details from XML configs")
|
||||||
print(f" • Startup parameters extracted from XML")
|
print(f" • DETAILED startup parameters extracted from XML")
|
||||||
print(f" • Troubleshooting info from knowledgepack")
|
print(f" • Comprehensive troubleshooting sections")
|
||||||
print("="*70)
|
print("="*70)
|
||||||
|
|
||||||
return 0 if not errors else 1
|
return 0 if not errors else 1
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue