From 95394873f54d30a490412477a70f82239ca2574a Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Tue, 5 May 2026 19:42:49 -0700 Subject: [PATCH] feat: update CI for sentry deployment Signed-off-by: Michael Pollind --- .github/server-management/README.md | 145 ---------- .github/server-management/Warfork.sh | 262 ------------------ .github/server-management/configs/_base.cfg | 119 -------- .../server-management/configs/clan-arena.cfg | 7 - .github/server-management/configs/duel.cfg | 9 - .../server-management/configs/instagib.cfg | 9 - .github/server-management/configs/race.cfg | 7 - .github/server-management/configs/votable.cfg | 9 - .github/server-management/server-config.json | 70 ----- .github/workflows/deploy-servers.yml | 219 --------------- .github/workflows/deploy.yml | 30 +- 11 files changed, 29 insertions(+), 857 deletions(-) delete mode 100644 .github/server-management/README.md delete mode 100644 .github/server-management/Warfork.sh delete mode 100644 .github/server-management/configs/_base.cfg delete mode 100644 .github/server-management/configs/clan-arena.cfg delete mode 100644 .github/server-management/configs/duel.cfg delete mode 100644 .github/server-management/configs/instagib.cfg delete mode 100644 .github/server-management/configs/race.cfg delete mode 100644 .github/server-management/configs/votable.cfg delete mode 100644 .github/server-management/server-config.json delete mode 100644 .github/workflows/deploy-servers.yml diff --git a/.github/server-management/README.md b/.github/server-management/README.md deleted file mode 100644 index 0ab1552235..0000000000 --- a/.github/server-management/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# Warfork Server Management - -Automated deployment of Warfork dedicated servers via GitHub Actions. - -## Setup - -### Server prerequisites - -Each server needs to be a Linux box with SSH access. The `steam` user is assumed by default in `server-config.json`. - -The `steam` user needs passwordless sudo to run the initial setup: - -```shell -steam ALL=(ALL) NOPASSWD: /bin/bash /home/steam/scripts/Warfork.sh setup -``` - -After that, everything the deploy workflow does (install, update, start, stop) runs as `steam` without sudo. - -### GitHub secrets - -Go to **Settings → Secrets and variables → Actions** in the repository and add: - -| Secret | What it is | -| -------- | ------------ | -| `SERVER_RCON_PASSWORD` (optional) | Remote console password (`rcon_password`) | -| `SERVER_OPERATOR_PASSWORD` (optional) | In-game operator password (`g_operator_password`) | -| `DO_SSH_PRIVATE_KEY_US` | SSH private key for the US East server | -| `DO_SSH_PRIVATE_KEY_EU` | SSH private key for the EU server | - -Passwords are passed to the server via `+set` at deploy time. They are never written to disk on the runner or stored in this repo. - -### First deploy - -Run **Actions → Deploy Servers** with action `provision`. This installs system packages, downloads SteamCMD, installs the game, uploads configs, and starts all server processes in one go. - -After the first deploy you can check that sessions are running: - -```bash -tmux ls -``` - -Each server runs in a session named `wf-{port}`, e.g. `wf-44401`. Attach with `tmux attach -t wf-44401`. - -## Directory structure - -```shell -server-management/ - server-config.json Server registry — regions, ports, gametypes, hostnames - Warfork.sh Uploaded to ~/scripts/ on each deploy - configs/ - _base.cfg Shared settings loaded by every server type - clan-arena.cfg Type-specific settings (execs _base.cfg then adds overrides) - duel.cfg - votable.cfg - instagib.cfg - race.cfg -``` - -## Config layering - -When a server starts, the command line looks roughly like this: - -```cfg -+exec configs/clan-arena.cfg <- runs first, loads _base.cfg then type settings -+set net_port 44401 <- CLI overrides anything set in the cfg -+set sv_hostname "..." -+set rcon_password "..." <- injected from GitHub secret, runs last -``` - -Since `+set` on the command line always wins over `set` in a cfg file, the values in `server-config.json` and the GitHub secrets are always authoritative. - -| Setting | Where it lives | -| --------- | --------------- | -| `net_port`, `g_gametype`, `sv_hostname`, `sv_maxclients`, `dedicated` | `server-config.json` | -| `sv_defaultmap`, `g_votable_gametypes`, per-type vote overrides | `configs/{type}.cfg` | -| Logging, masterservers, HTTP, antilag, recording, vote defaults | `configs/_base.cfg` | -| `rcon_password`, `g_operator_password` | GitHub secrets | - -## Deploying - -Trigger **Actions → Deploy Servers** with: - -| Input | Options | Default | -| ------- | --------- | --------- | -| `steam_branch` | `beta`, `public` | `beta` | -| `regions` | `US`, `EU`, or `all` | `all` | -| `server_types` | `clan-arena`, `duel`, etc., or `all` | `all` | -| `action` | `provision`, `update-and-restart`, `update-only`, `restart-only`, `stop`, `status` | `update-and-restart` | - -| Action | What it does | -| -------- | -------------- | -| `provision` | Full first-time setup: installs packages, SteamCMD, game, then starts servers | -| `update-and-restart` | Stop → update via SteamCMD → start | -| `update-only` | Update via SteamCMD without restarting | -| `restart-only` | Restart without updating | -| `stop` | Stop all matching sessions | -| `status` | Report whether sessions are running | - -## Adding a server type - -Add an entry to `server_types` in `server-config.json`: - -```json -"my-type": { - "label": "My Type", - "port": 44408, - "cvars": { - "sv_hostname": "[ WF Beta - ${REGION_LABEL} ] My Type", - "g_gametype": "mygametype" - } -} -``` - -Then create `configs/my-type.cfg`: - -```cfg -exec configs/_base.cfg -set sv_defaultmap "wfmap1" -set g_votable_gametypes "mygametype" -``` - -## Adding a region - -Regions are per-server entries, so subregions like `US-E`, `EU-DE`, `EU-FN`, `SG` each get their own block in `servers`: - -```json -"EU-DE": { - "host": "eu-de.warfork.com", - "key_secret": "DO_SSH_PRIVATE_KEY_EU_DE", - "username": "steam", - "label": "EU Frankfurt" -} -``` - -Add the corresponding SSH private key as a GitHub secret. The same `configs/` directory deploys to all of them. - -## Manual server commands - -```bash -WF_PARAMS="..." STEAM_BRANCH=beta ~/scripts/Warfork.sh status -WF_PARAMS="..." ~/scripts/Warfork.sh stop -WF_PARAMS="..." ~/scripts/Warfork.sh restart -``` - -Sessions are named by port (`wf-44401`, `wf-44402`, etc.) so multiple server types can run on the same machine without conflicting. diff --git a/.github/server-management/Warfork.sh b/.github/server-management/Warfork.sh deleted file mode 100644 index f99c28f15b..0000000000 --- a/.github/server-management/Warfork.sh +++ /dev/null @@ -1,262 +0,0 @@ -#!/bin/bash - -# Combined Environment Setup and Server Management Script -# Run with sudo for initial setup, then use server functions - -set -e - -# Set default values for environment variables -DEBUG="${DEBUG:-false}" -VALIDATE_SERVER_FILES="${VALIDATE_SERVER_FILES:-false}" -WF_CUSTOM_CONFIGS_DIR="${WF_CUSTOM_CONFIGS_DIR:-/var/wf}" -WF_PARAMS="${WF_PARAMS:-+set dedicated 1 +set net_port 44400}" -STEAM_BRANCH="${STEAM_BRANCH:-public}" - -# Server configuration -steam_dir="/app/Steam" -server_dir="/app/server" -server_installed_lock_file="$server_dir/installed.lock" -wf_dir="$server_dir/basewf" -wf_custom_configs_dir="$WF_CUSTOM_CONFIGS_DIR" - - -get_app_update_args() { - if [ "${STEAM_BRANCH}" = "beta" ]; then - echo "1136510 -beta beta" - else - echo "1136510" - fi -} - -sync_custom_files() { - echo "> Checking for custom files at \"$wf_custom_configs_dir\" ..." - - if [ -d "$wf_custom_configs_dir" ]; then - echo "> Found custom files. Syncing with \"${wf_dir}\" ..." - - if [ "${DEBUG}" = "true" ]; then - set -x - fi - - cp -asf "$wf_custom_configs_dir"/* "$wf_dir" # Copy custom files as soft links - find "$wf_dir" -xtype l -delete # Find and delete broken soft links - - if [ "${DEBUG}" = "true" ]; then - set +x - fi - - echo '> Done' - else - echo '> No custom files found' - fi -} - -provision() { - echo "Starting environment setup..." - - # Check if running as root for system setup - if [[ $EUID -ne 0 ]]; then - echo "Environment setup needs to be run with sudo for system package installation" - echo "Usage: sudo $0 setup" - exit 1 - fi - - # Update package lists - echo "Updating package lists..." - apt-get update - - # Install required packages - echo "Installing required packages..." - apt-get install -y --no-install-recommends \ - lib32gcc-s1 \ - lib32stdc++6 \ - wget \ - ca-certificates \ - rsync \ - unzip \ - tmux \ - jq \ - bc \ - binutils \ - util-linux \ - python3 \ - curl \ - file \ - tar \ - bzip2 \ - gzip \ - bsdmainutils \ - libcurl4 \ - libcurl3-gnutls \ - locales - - - # Configure locales - echo "Configuring locales..." - sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen - dpkg-reconfigure --frontend=noninteractive locales - - # Clean up package cache - echo "Cleaning up package cache..." - rm -rf /var/lib/apt/lists/* - - # Create Steam directories - echo "Creating Steam directories..." - mkdir -p /app/Steam - mkdir -p /app/server - mkdir -p /app/.steam - echo "Downloading and installing SteamCMD..." - cd /app/Steam - wget -qO- https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz | tar zxf - - - echo "Running SteamCMD initial setup..." - /app/Steam/steamcmd.sh +quit - - - if [ "${DEBUG}" = "true" ]; then - set -x - fi - - local app_args - app_args=$(get_app_update_args) - - $steam_dir/steamcmd.sh \ - +force_install_dir $server_dir \ - +login anonymous \ - +app_update $app_args validate \ - +quit - - if [ "${DEBUG}" = "true" ]; then - set +x - fi - - sync_custom_files - - echo '> Done' - touch $server_installed_lock_file -} - -get_session_name() { - if [ -n "${WF_SESSION_NAME:-}" ]; then - echo "$WF_SESSION_NAME" - return - fi - local port - port=$(echo "$WF_PARAMS" | sed -n 's/.*sv_port \([0-9]*\).*/\1/p') - echo "wf-${port:-44400}" -} - -start() { - echo '> Starting server ...' - - local session_name - session_name=$(get_session_name) - local log_file="$HOME/${session_name}.log" - local run_script="$HOME/${session_name}.sh" - - if tmux has-session -t "$session_name" 2>/dev/null; then - echo "> Session '$session_name' is already running. Use restart to restart it." - exit 1 - fi - - echo "Creating Steam SDK symlinks..." - mkdir -p $HOME/.steam - ln -sf /app/Steam/linux64 $HOME/.steam/sdk64 - ln -sf /app/Steam/linux32 $HOME/.steam/sdk32 - - # Write a launcher script so WF_PARAMS is never subject to tmux shell re-quoting - printf '#!/bin/bash\ncd %q\nexec ./wf_server.x86_64 %s 2>&1 | tee -a %q\n' \ - "$wf_dir/.." "$WF_PARAMS" "$log_file" > "$run_script" - chmod +x "$run_script" - - if [ "${DEBUG}" = "true" ]; then - set -x - fi - - tmux new-session -d -s "$session_name" "$run_script" - - echo "> Server started in tmux session '$session_name'" - echo "> Log: $log_file" - echo "> WF_PARMS $WF_PARAMS" - echo "> Attach: tmux attach -t $session_name" -} - -run_server() { - if [ "${DEBUG}" = "true" ]; then - set -x - fi - start -} - -stop() { - echo '> Stopping server ...' - - local session_name - session_name=$(get_session_name) - - if tmux has-session -t "$session_name" 2>/dev/null; then - tmux kill-session -t "$session_name" - echo '> Done' - else - echo "> No session '$session_name' found" - fi -} - -status() { - local session_name - session_name=$(get_session_name) - if tmux has-session -t "$session_name" 2>/dev/null; then - echo "> Session '$session_name' is running" - return 0 - else - echo "> Session '$session_name' is not running" - return 1 - fi -} - -restart() { - stop || true - sleep 2 - start -} - -# Main execution logic -case "${1:-}" in - "provision") - provision - ;; - "start") - start - ;; - "restart") - restart - ;; - "status") - status - ;; - "stop") - stop - ;; - "run"|"") - run_server - ;; - *) - echo "Usage: $0 {provision|start|restart|stop|status|run}" - echo "" - echo "Commands:" - echo " provision - Install system packages, SteamCMD, and game server (requires sudo)" - echo " start - Start game server (detached tmux session)" - echo " restart - Restart game server" - echo " stop - Stop game server" - echo " status - Exit 0 if session running, 1 if not" - echo " run - Sync custom files and start server" - echo "" - echo "Environment variables:" - echo " DEBUG=true # Enable verbose output" - echo " WF_CUSTOM_CONFIGS_DIR=/path # Custom configs directory (default: $HOME)" - echo " WF_PARAMS='--param value' # Additional server parameters" - echo " STEAM_BRANCH=beta # Steam branch (default: public)" - echo " WF_SESSION_NAME=wf-custom # Override tmux session name" - exit 1 - ;; -esac diff --git a/.github/server-management/configs/_base.cfg b/.github/server-management/configs/_base.cfg deleted file mode 100644 index e6f592d59b..0000000000 --- a/.github/server-management/configs/_base.cfg +++ /dev/null @@ -1,119 +0,0 @@ -// ============================================================================= -// _base.cfg — Shared settings for all Warfork dedicated servers -// Loaded via: exec configs/_base.cfg at the top of each type config. -// Note: CLI +set params injected by deploy-servers.yml always win over values set here. -// ============================================================================= - -// --- Network ----------------------------------------------------------------- -set sv_ip "" // bind to all interfaces; set to a specific IP if needed -set sv_http "1" -set sv_http_ip "" -set sv_http_port "44444" -set sv_http_upstream_baseurl "" -set sv_http_upstream_realip_header "" -set sv_http_upstream_ip "" - -// --- Logging ----------------------------------------------------------------- -set logconsole "wfconsole.log" -set logconsole_append "1" // preserve log across restarts - -// --- Server visibility ------------------------------------------------------- -set sv_public "1" -set sv_log_heartbeats "1" -set sv_skilllevel "1" // 0 = easy, 1 = normal, 2 = hard -set sv_pure "1" // clients must use same game modules -set sv_showInfoQueries "0" - -// --- Master servers ---------------------------------------------------------- -set masterservers "master1.forbidden.gg master2.forbidden.gg:27777 master3.forbidden.gg:42863 master1.icy.gg master2.icy.gg:27777 master3.icy.gg:42863" -set masterservers_steam "hl1master.steampowered.com:27010 hl2master.steampowered.com:27011" - -// --- File serving ------------------------------------------------------------ -set sv_uploads "1" -set sv_uploads_from_server "1" -set sv_uploads_baseurl "" -set sv_autoupdate "0" - -// --- Connection limits ------------------------------------------------------- -set sv_iplimit "5" -set password "" // server join password (empty = public) - -// --- Gameplay ---------------------------------------------------------------- -set g_numbots "0" -set g_instagib "0" -set g_instajump "0" -set g_instashield "0" -set g_warmup_enabled "1" - -// --- Recording --------------------------------------------------------------- -set g_autorecord "1" -set g_autorecord_maxdemos "200" -set g_uploads_demos "1" - -// --- Antilag ----------------------------------------------------------------- -set g_antilag "1" -set g_antilag_timenudge "0" - -// --- Stats ------------------------------------------------------------------- -set g_asGC_stats "0" -set g_asGC_interval "10" - -// --- Voting ------------------------------------------------------------------ -// g_disable_vote_gametype defaults to 1 (locked); override to 0 in type configs that allow it -set g_vote_allowed "1" -set g_disable_vote_gametype "1" -set g_disable_vote_map "0" -set g_disable_vote_nextmap "0" -set g_disable_vote_restart "0" -set g_disable_vote_scorelimit "1" -set g_disable_vote_timelimit "1" -set g_disable_vote_numbots "0" -set g_disable_vote_warmup "0" -set g_disable_vote_warmup_timelimit "1" -set g_disable_vote_extended_time "1" -set g_disable_vote_maxteams "0" -set g_disable_vote_maxteamplayers "0" -set g_disable_vote_lock "1" -set g_disable_vote_unlock "1" -set g_disable_vote_allow_teamdamage "1" -set g_disable_vote_allow_selfdamage "1" -set g_disable_vote_allow_falldamage "1" -set g_disable_vote_allow_uneven "0" -set g_disable_vote_maxtimeouts "1" -set g_disable_vote_timeout "0" -set g_disable_vote_timein "0" -set g_disable_vote_challengers_queue "1" -set g_disable_vote_kick "0" -set g_disable_vote_kickban "1" -set g_disable_vote_instajump "0" -set g_disable_vote_instashield "0" -set g_disable_vote_shuffle "0" - -// --- Operator calls ---------------------------------------------------------- -set g_disable_opcall_gametype "0" -set g_disable_opcall_map "0" -set g_disable_opcall_nextmap "0" -set g_disable_opcall_restart "0" -set g_disable_opcall_scorelimit "0" -set g_disable_opcall_timelimit "0" -set g_disable_opcall_numbots "0" -set g_disable_opcall_warmup "0" -set g_disable_opcall_warmup_timelimit "0" -set g_disable_opcall_extended_time "0" -set g_disable_opcall_maxteams "0" -set g_disable_opcall_maxteamplayers "0" -set g_disable_opcall_lock "0" -set g_disable_opcall_unlock "0" -set g_disable_opcall_allow_teamdamage "0" -set g_disable_opcall_allow_selfdamage "0" -set g_disable_opcall_allow_falldamage "0" -set g_disable_opcall_allow_uneven "0" -set g_disable_opcall_maxtimeouts "0" -set g_disable_opcall_timeout "0" -set g_disable_opcall_timein "0" -set g_disable_opcall_challengers_queue "0" -set g_disable_opcall_kick "0" -set g_disable_opcall_kickban "0" -set g_disable_opcall_instajump "0" -set g_disable_opcall_instashield "0" -set g_disable_opcall_shuffle "0" diff --git a/.github/server-management/configs/clan-arena.cfg b/.github/server-management/configs/clan-arena.cfg deleted file mode 100644 index bb6c493b85..0000000000 --- a/.github/server-management/configs/clan-arena.cfg +++ /dev/null @@ -1,7 +0,0 @@ -// ============================================================================= -// clan-arena.cfg — Clan Arena server -// ============================================================================= -exec configs/_base.cfg - -set sv_defaultmap "return" -set g_votable_gametypes "ca" diff --git a/.github/server-management/configs/duel.cfg b/.github/server-management/configs/duel.cfg deleted file mode 100644 index ca4a833d34..0000000000 --- a/.github/server-management/configs/duel.cfg +++ /dev/null @@ -1,9 +0,0 @@ -// ============================================================================= -// duel.cfg — Duel server -// ============================================================================= -exec configs/_base.cfg - -set sv_defaultmap "wfdm1" // no default in duel.cfg; update as needed -set g_votable_gametypes "duel" -set g_disable_vote_shuffle "1" // no teams in duel -set g_disable_vote_allow_uneven "1" // 1v1 only diff --git a/.github/server-management/configs/instagib.cfg b/.github/server-management/configs/instagib.cfg deleted file mode 100644 index cd244bcc63..0000000000 --- a/.github/server-management/configs/instagib.cfg +++ /dev/null @@ -1,9 +0,0 @@ -// ============================================================================= -// instagib.cfg — Instagib server -// ============================================================================= -exec configs/_base.cfg - -set sv_defaultmap "wfdm1" -set g_instagib "1" -set g_instajump "1" -set g_votable_gametypes "irdm" diff --git a/.github/server-management/configs/race.cfg b/.github/server-management/configs/race.cfg deleted file mode 100644 index 8c960ee3f1..0000000000 --- a/.github/server-management/configs/race.cfg +++ /dev/null @@ -1,7 +0,0 @@ -// ============================================================================= -// race.cfg — Race server -// ============================================================================= -exec configs/_base.cfg - -set sv_defaultmap "wfrace1" -set g_votable_gametypes "race" diff --git a/.github/server-management/configs/votable.cfg b/.github/server-management/configs/votable.cfg deleted file mode 100644 index be96b0e311..0000000000 --- a/.github/server-management/configs/votable.cfg +++ /dev/null @@ -1,9 +0,0 @@ -// ============================================================================= -// votable.cfg — Votable (multi-gametype) server -// g_gametype, net_port, sv_hostname, sv_maxclients set via CLI -// ============================================================================= -exec configs/_base.cfg - -set sv_defaultmap "wfdm1" -set g_votable_gametypes "bomb ca ctf ctftactics da dm duel ffa headhunt race rekt tdm" -set g_disable_vote_gametype "0" // override base: allow gametype votes diff --git a/.github/server-management/server-config.json b/.github/server-management/server-config.json deleted file mode 100644 index 10bac3f00c..0000000000 --- a/.github/server-management/server-config.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "servers": { - "US": { - "host": "137.184.85.214", - "key_secret": "DO_SSH_PRIVATE_KEY", - "label": "US East" - }, - "EU": { - "host": "138.68.170.74", - "key_secret": "DO_SSH_PRIVATE_KEY", - "label": "EU" - } - }, - "server_defaults": { - "cvars": { - "dedicated": 1, - "sv_maxclients": 15 - } - }, - "server_types": { - "clan-arena": { - "label": "Clan Arena", - "port": 44401, - "cvars": { - "sv_hostname": "[ WF Beta - ${REGION_LABEL} ] Clan Arena", - "g_gametype": "ca" - } - }, - "duel": { - "label": "Duel", - "port": 44402, - "cvars": { - "sv_hostname": "[ WF Beta - ${REGION_LABEL} ] Duel", - "g_gametype": "duel" - } - }, - "votable": { - "label": "Votable", - "port": 44403, - "cvars": { - "sv_hostname": "[ WF Beta - ${REGION_LABEL} ] Votable", - "g_gametype": "dm" - } - }, - "instagib": { - "label": "Instagib", - "port": 44404, - "cvars": { - "sv_hostname": "[ WF Beta - ${REGION_LABEL} ] Instagib", - "g_gametype": "dm" - } - }, - "race": { - "label": "Race", - "port": 44407, - "cvars": { - "sv_hostname": "[ WF Beta - ${REGION_LABEL} ] Race", - "g_gametype": "race" - } - } - }, - "steam_branches": { - "public": { - "label": "Public" - }, - "beta": { - "label": "Beta" - } - } -} diff --git a/.github/workflows/deploy-servers.yml b/.github/workflows/deploy-servers.yml deleted file mode 100644 index d519dd4714..0000000000 --- a/.github/workflows/deploy-servers.yml +++ /dev/null @@ -1,219 +0,0 @@ -# ───────────────────────────────────────────────────────────────────────────── -# Warfork Server Deploy -# Deploys and updates game servers across regions using a matrix strategy. -# Uses appleboy/ssh-action to execute commands on Digital Ocean droplets. -# ───────────────────────────────────────────────────────────────────────────── -name: Deploy Servers - -on: - workflow_dispatch: - inputs: - steam_branch: - description: 'Steam branch to deploy' - required: true - type: choice - options: - - beta - - public - default: beta - regions: - description: 'Regions to deploy (comma-separated, or "all")' - required: true - type: string - default: 'all' - server_types: - description: 'Server types to deploy (comma-separated, or "all")' - required: true - type: string - default: 'all' - action: - description: 'Action to perform' - required: true - type: choice - options: - - provision - - update-and-restart - - update-only - - restart-only - - stop - - status - default: update-and-restart - - # Reusable workflow — called by update-servers, restart-servers, etc. - workflow_call: - inputs: - steam_branch: - required: true - type: string - regions: - required: true - type: string - server_types: - required: true - type: string - action: - required: true - type: string - -# Only allow one deploy at a time -concurrency: - group: server-deploy - cancel-in-progress: false - -jobs: - # ─── Build the matrix dynamically ──────────────────────────────────────── - prepare-matrix: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - server_matrix: ${{ steps.set-matrix.outputs.server_matrix }} - steps: - - uses: actions/checkout@v4 - - name: Build deployment matrix - id: set-matrix - env: - CONFIG: .github/server-management/server-config.json - REGIONS: ${{ inputs.regions }} - TYPES: ${{ inputs.server_types }} - run: bash .github/scripts/build-matrix.sh - - # ─── Setup server environment (runs once per region/host) ──────────────── - setup-server: - needs: prepare-matrix - # Only run setup if we are deploying/updating (skip for status-only if desired, - # but user request implies always separating it. Let's keep the logic consistent.) - if: inputs.action == 'update-and-restart' || inputs.action == 'provision' || inputs.action == 'update-only' - runs-on: ubuntu-latest - strategy: - matrix: ${{ fromJson(needs.prepare-matrix.outputs.server_matrix) }} - fail-fast: false - max-parallel: 4 - name: "Setup ${{ matrix.region_label }}" - steps: - - uses: actions/checkout@v4 - - - name: Upload Warfork.sh to server - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ matrix.host }} - username: root - key: ${{ secrets[matrix.key_secret] }} - port: 22 - source: ".github/server-management/Warfork.sh" - target: "/app/scripts/" - strip_components: 2 - - - name: Upload server configs - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ matrix.host }} - username: root - key: ${{ secrets[matrix.key_secret] }} - port: 22 - source: ".github/server-management/configs/" - target: "/app/server/basewf/" - strip_components: 2 - - - name: Make Warfork.sh executable - uses: appleboy/ssh-action@v1.2.0 - with: - host: ${{ matrix.host }} - username: root - key: ${{ secrets[matrix.key_secret] }} - port: 22 - script: | - chmod +x /app/scripts/Warfork.sh - /app/scripts/Warfork.sh provision - - # ─── Deploy to each server instance in the matrix ──────────────────────── - deploy: - needs: [prepare-matrix, setup-server] - # If setup-server was skipped (status action), deploy should still run if needed? - # Actually, if action is status, setup-server is skipped. Deploy should run. - # If action is NOT status, setup-server runs. Deploy must wait for it. - if: | - always() && - needs.prepare-matrix.result == 'success' && - (needs.setup-server.result == 'success' || needs.setup-server.result == 'skipped') - runs-on: ubuntu-latest - strategy: - matrix: ${{ fromJson(needs.prepare-matrix.outputs.matrix) }} - fail-fast: false - max-parallel: 4 - name: "${{ matrix.region_label }} - ${{ matrix.type_label }}" - steps: - - name: Execute server action - uses: appleboy/ssh-action@v1.2.0 - env: - STEAM_BRANCH: ${{ inputs.steam_branch }} - SERVER_TYPE: ${{ matrix.server_type }} - WF_PARAMS: ${{ matrix.wf_params }} - REGION_LABEL: ${{ matrix.region_label }} - ACTION: ${{ inputs.action }} - RCON_PASSWORD: ${{ secrets.SERVER_RCON_PASSWORD }} - OPERATOR_PASSWORD: ${{ secrets.SERVER_OPERATOR_PASSWORD }} - with: - host: ${{ matrix.host }} - username: root - key: ${{ secrets[matrix.key_secret] }} - port: 22 - envs: STEAM_BRANCH,SERVER_TYPE,WF_PARAMS,REGION_LABEL,ACTION,RCON_PASSWORD,OPERATOR_PASSWORD - command_timeout: 15m - script: | - useradd wf || echo "wf User already exists." - mkdir -p /home/wf - chown wf:wf /home/wf - - # Substitute region label into params (for hostname) - export WF_PARAMS=$(echo "${WF_PARAMS}" | sed "s/\${REGION_LABEL}/${REGION_LABEL}/g") - - # Inject credentials if set - [ -n "${RCON_PASSWORD}" ] && WF_PARAMS="${WF_PARAMS} +set rcon_password \"${RCON_PASSWORD}\"" - [ -n "${OPERATOR_PASSWORD}" ] && WF_PARAMS="${WF_PARAMS} +set g_operator_password \"${OPERATOR_PASSWORD}\"" - - export STEAM_BRANCH="${STEAM_BRANCH}" - export SERVER_TYPE="${SERVER_TYPE}" - - # Script handles tmux sessions internally (named wf-{port}) - SUDO_CMD="sudo -u wf STEAM_BRANCH='${STEAM_BRANCH}' SERVER_TYPE='${SERVER_TYPE}' WF_PARAMS='${WF_PARAMS}' REGION_LABEL='${REGION_LABEL}' ACTION='${ACTION}' RCON_PASSWORD='${RCON_PASSWORD}' OPERATOR_PASSWORD='${OPERATOR_PASSWORD}'" - - case "${ACTION}" in - provision) - eval $SUDO_CMD /app/scripts/Warfork.sh run - ;; - update-and-restart) - eval $SUDO_CMD /app/scripts/Warfork.sh stop - eval $SUDO_CMD /app/scripts/Warfork.sh start - ;; - update-only) - eval $SUDO_CMD /app/scripts/Warfork.sh restart - ;; - restart-only) - eval $SUDO_CMD /app/scripts/Warfork.sh restart - ;; - stop) - eval $SUDO_CMD /app/scripts/Warfork.sh stop - ;; - status) - eval $SUDO_CMD /app/scripts/Warfork.sh status || true - ;; - esac - - # ─── Summary ───────────────────────────────────────────────────────────── - summary: - needs: [prepare-matrix, deploy] - runs-on: ubuntu-latest - if: always() - steps: - - name: Deployment summary - run: | - echo "## Warfork Server Deployment Summary" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Branch | \`${{ inputs.steam_branch }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Regions | \`${{ inputs.regions }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Server Types | \`${{ inputs.server_types }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Action | \`${{ inputs.action }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Triggered by | @${{ github.actor }} |" >> $GITHUB_STEP_SUMMARY - echo "| Time | $(date -u '+%Y-%m-%d %H:%M:%S UTC') |" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index eceee047dd..0d03be52ab 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,7 +32,14 @@ jobs: prepare-steam-build: needs: [linux, macos, windows] runs-on: ubuntu-latest + outputs: + sentry_release: ${{ steps.sentry-release.outputs.version }} steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Download all artifacts uses: actions/download-artifact@v5 with: @@ -65,10 +72,15 @@ jobs: run: | # find then remove steam_appid.txt files from all depots find steam-build -type f -name steam_appid.txt -exec rm -v {} \; - - name: Sentry Upload + - name: Sentry release & debug files + id: sentry-release run: | curl -sL https://sentry.io/get-cli/ | bash sentry-cli --version + VERSION=$(sentry-cli releases propose-version) + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + sentry-cli releases new "$VERSION" + sentry-cli releases set-commits "$VERSION" --auto sentry-cli debug-files upload steam-build/ env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} @@ -130,3 +142,19 @@ jobs: depot8Path: ${{ matrix.steam_app.depot8 }} releaseBranch: ${{ inputs.release_branch }} debugBranch: true + + # Finalize the Sentry release once the Steam deploy succeeds + finalize-sentry: + needs: [prepare-steam-build, deploy] + runs-on: ubuntu-latest + steps: + - name: Finalize Sentry release + run: | + curl -sL https://sentry.io/get-cli/ | bash + sentry-cli releases finalize "$VERSION" + sentry-cli releases deploys "$VERSION" new -e "${{ inputs.release_branch }}" + env: + VERSION: ${{ needs.prepare-steam-build.outputs.sentry_release }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: team-forbidden-llc + SENTRY_PROJECT: warfork