From 77c576ac539a40ca2c8726a38bf986fa504a77f5 Mon Sep 17 00:00:00 2001 From: cosmosquark Date: Sat, 21 Mar 2026 17:02:42 +0000 Subject: [PATCH 1/4] adding global instal instructions --- DEVELOPERS.md | 14 ++++++++ README.md | 7 ++++ docs/README.md | 2 ++ docs/install/local-user.md | 39 ++++++++++++++++++++ docs/install/system-wide.md | 47 ++++++++++++++++++++++++ scripts/gitalphagsm | 12 +++++++ scripts/install-local-user.sh | 34 ++++++++++++++++++ scripts/install-system-wide.sh | 65 ++++++++++++++++++++++++++++++++++ 8 files changed, 220 insertions(+) create mode 100644 docs/install/local-user.md create mode 100644 docs/install/system-wide.md create mode 100644 scripts/gitalphagsm create mode 100644 scripts/install-local-user.sh create mode 100644 scripts/install-system-wide.sh diff --git a/DEVELOPERS.md b/DEVELOPERS.md index 8ffa77a2..f289a348 100644 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -282,6 +282,11 @@ Main template: - [alphagsm.conf-template](alphagsm.conf-template) +Documented installation layouts: + +- [docs/install/local-user.md](docs/install/local-user.md) +- [docs/install/system-wide.md](docs/install/system-wide.md) + Important environment variables: - `ALPHAGSM_CONFIG_LOCATION` @@ -291,6 +296,15 @@ Important environment variables: The smoke tests rely on temporary config files and `ALPHAGSM_CONFIG_LOCATION` to isolate state per run. +For system-wide installs, the intended layout is: + +- `/etc/alphagsm.conf` +- `/home/alphagsm/` +- `/usr/local/lib/alphagsm` +- `/usr/local/sbin/gitalphagsm` +- `/usr/local/bin/alphagsm` +- `/etc/sudoers.d/gameservers` + ## Documentation Contract Documentation is now deliberately split by audience: diff --git a/README.md b/README.md index fe5b58f5..0adc363a 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ Some server types need one extra thing: ## Fast Start +If you want AlphaGSM to install itself for you, use one of these first: + +- local-user install: [docs/install/local-user.md](docs/install/local-user.md) +- system-wide install: [docs/install/system-wide.md](docs/install/system-wide.md) + ### 1. Install the Python packages ```bash @@ -140,6 +145,8 @@ These scripts are useful because they show a full real flow: ## Step-By-Step Server Guides - [Documentation Index](docs/README.md) +- [Local User Install](docs/install/local-user.md) +- [System-Wide Install](docs/install/system-wide.md) - [Minecraft Vanilla Guide](docs/servers/minecraft-vanilla.md) - [Team Fortress 2 Guide](docs/servers/team-fortress-2.md) - [CS:GO Guide](docs/servers/counter-strike-global-offensive.md) diff --git a/docs/README.md b/docs/README.md index e0e5df9b..465a2a06 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,8 @@ If you are not sure where to start, pick your server below and follow that guide ## Server Guides +- [Local User Install](install/local-user.md) +- [System-Wide Install](install/system-wide.md) - [Minecraft Vanilla](servers/minecraft-vanilla.md) - [Team Fortress 2](servers/team-fortress-2.md) - [Counter-Strike: Global Offensive](servers/counter-strike-global-offensive.md) diff --git a/docs/install/local-user.md b/docs/install/local-user.md new file mode 100644 index 00000000..b29400e2 --- /dev/null +++ b/docs/install/local-user.md @@ -0,0 +1,39 @@ +# Local User Install + +This install path is for one Linux user running AlphaGSM for their own account. + +## What It Does + +The local install script: + +- copies AlphaGSM into your home directory +- makes a config file for you +- creates a command link in `~/.local/bin` + +## Run It + +From the repository root: + +```bash +bash ./scripts/install-local-user.sh +``` + +## Default Paths + +- code: `~/.local/lib/alphagsm` +- command: `~/.local/bin/alphagsm` +- config: `~/.local/lib/alphagsm/alphagsm.conf` + +## After Install + +If `~/.local/bin` is not already in your `PATH`, add it in your shell profile: + +```bash +export PATH="$HOME/.local/bin:$PATH" +``` + +Then you can run: + +```bash +alphagsm --help +``` diff --git a/docs/install/system-wide.md b/docs/install/system-wide.md new file mode 100644 index 00000000..9c40f909 --- /dev/null +++ b/docs/install/system-wide.md @@ -0,0 +1,47 @@ +# System-Wide Install + +This install path is for a shared machine where AlphaGSM is installed once and used by multiple people. + +## Layout + +The system-wide install is designed around these paths: + +- overall config: `/etc/alphagsm.conf` +- shared download user home: `/home/alphagsm` +- code: `/usr/local/lib/alphagsm` +- shared command: `/usr/local/bin/alphagsm` +- root git helper: `/usr/local/sbin/gitalphagsm` +- sudoers file: `/etc/sudoers.d/gameservers` + +The shared download user can own common download data so that normal users are not all downloading and storing the same artifacts at the same time. + +## Run It + +Run as root: + +```bash +sudo bash ./scripts/install-system-wide.sh +``` + +## Shared Download User + +The installer creates or uses the `alphagsm` system user and prepares: + +- `/home/alphagsm/gitlive` +- `/home/alphagsm/downloads` + +## Sudoers Entry + +The installer writes: + +```text +%alphagsm ALL = (alphagsm) NOPASSWD:/usr/local/lib/alphagsm/alphagsm-downloads +``` + +That allows the right group to run the shared downloader helper as the `alphagsm` user without a password prompt. + +## Notes + +- This is the better fit for a shared host. +- The root helper `gitalphagsm` is installed to `/usr/local/sbin/gitalphagsm`. +- The `alphagsm` command is exposed through `/usr/local/bin/alphagsm`. diff --git a/scripts/gitalphagsm b/scripts/gitalphagsm new file mode 100644 index 00000000..ad9e9462 --- /dev/null +++ b/scripts/gitalphagsm @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -euo pipefail + +INSTALL_ROOT="${INSTALL_ROOT:-/usr/local/lib/alphagsm}" + +if [[ ! -d "$INSTALL_ROOT/.git" ]]; then + echo "No git repository found at $INSTALL_ROOT" >&2 + exit 1 +fi + +git -C "$INSTALL_ROOT" "$@" diff --git a/scripts/install-local-user.sh b/scripts/install-local-user.sh new file mode 100644 index 00000000..0aef459a --- /dev/null +++ b/scripts/install-local-user.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +INSTALL_ROOT="${INSTALL_ROOT:-$HOME/.local/lib/alphagsm}" +BIN_DIR="${BIN_DIR:-$HOME/.local/bin}" +CONFIG_PATH="$INSTALL_ROOT/alphagsm.conf" + +mkdir -p "$INSTALL_ROOT" "$BIN_DIR" + +cp -a "$REPO_ROOT"/. "$INSTALL_ROOT"/ + +if [[ ! -f "$CONFIG_PATH" ]]; then + cp "$INSTALL_ROOT/alphagsm.conf-template" "$CONFIG_PATH" +fi + +ln -sfn "$INSTALL_ROOT/alphagsm" "$BIN_DIR/alphagsm" + +cat <&2 + exit 1 +fi + +if ! id "$ALPHAGSM_USER" >/dev/null 2>&1; then + useradd --system --create-home --home-dir "$ALPHAGSM_HOME" --shell /usr/sbin/nologin "$ALPHAGSM_USER" +fi + +mkdir -p "$INSTALL_ROOT" "$BIN_DIR" "$SBIN_DIR" "$ALPHAGSM_HOME/gitlive" "$ALPHAGSM_HOME/downloads" + +cp -a "$REPO_ROOT"/. "$INSTALL_ROOT"/ + +if [[ ! -f "$CONFIG_PATH" ]]; then + cp "$INSTALL_ROOT/alphagsm.conf-template" "$CONFIG_PATH" +fi + +ln -sfn "$INSTALL_ROOT/alphagsm" "$BIN_DIR/alphagsm" +install -m 0755 "$INSTALL_ROOT/scripts/gitalphagsm" "$SBIN_DIR/gitalphagsm" + +cat >"$SUDOERS_PATH" < Date: Sat, 21 Mar 2026 17:05:47 +0000 Subject: [PATCH 2/4] add system install tests --- .github/workflows/unittest.yaml | 11 ++++++ DEVELOPERS.md | 17 ++++++++ README.md | 2 + .../run_system_install_in_container.sh | 39 +++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 system_install_tests/run_system_install_in_container.sh diff --git a/.github/workflows/unittest.yaml b/.github/workflows/unittest.yaml index 2ff98307..095cbe46 100644 --- a/.github/workflows/unittest.yaml +++ b/.github/workflows/unittest.yaml @@ -147,3 +147,14 @@ jobs: env: ALPHAGSM_RUN_INTEGRATION: "1" run: pytest ${{ matrix.test-file }} + + system-install-test: + runs-on: ubuntu-latest + needs: unit-test + name: system-install-test + steps: + - name: Checkout AlphaGSM code + uses: actions/checkout@v4 + - name: Run system-wide install test in container + run: | + bash ./system_install_tests/run_system_install_in_container.sh diff --git a/DEVELOPERS.md b/DEVELOPERS.md index f289a348..3239599b 100644 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -50,6 +50,8 @@ At runtime, the user-facing call path is: Pytest-driven end-to-end tests. - [smoke_tests](smoke_tests) Shell-driven streamed lifecycle runners used by CI and documentation. +- [system_install_tests](system_install_tests) + Container-based validation of the system-wide install layout. - [docs](docs) User-facing documentation. @@ -231,6 +233,20 @@ bash ./smoke_tests/run_minecraft_vanilla.sh bash ./smoke_tests/run_tf2.sh ``` +### System install tests + +Location: + +- [system_install_tests](system_install_tests) + +Command: + +```bash +bash ./system_install_tests/run_system_install_in_container.sh +``` + +This validates the system-wide installer in a fresh Linux container and checks the expected `/etc`, `/usr/local`, `/home/alphagsm`, and sudoers layout. + ## Linting Lint is driven by [lint.sh](lint.sh). @@ -259,6 +275,7 @@ Current job layout: 3. `unit-test` 4. matrix `smoke-test` 5. matrix `integration-test` +6. `system-install-test` Triggers: diff --git a/README.md b/README.md index 0adc363a..47ce7ae8 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ These scripts are useful because they show a full real flow: 4. check it 5. stop it cleanly +There is also a container-based system install check in `system_install_tests/` that verifies the shared Linux install layout without touching the host machine. + ## Step-By-Step Server Guides - [Documentation Index](docs/README.md) diff --git a/system_install_tests/run_system_install_in_container.sh b/system_install_tests/run_system_install_in_container.sh new file mode 100644 index 00000000..b8d4b4e6 --- /dev/null +++ b/system_install_tests/run_system_install_in_container.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail +set -x + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +CONTAINER_IMAGE="${CONTAINER_IMAGE:-ubuntu:24.04}" + +docker run --rm \ + -v "$REPO_ROOT:/src" \ + "$CONTAINER_IMAGE" \ + /bin/bash -lc ' +set -Eeuo pipefail +export DEBIAN_FRONTEND=noninteractive + +apt-get update +apt-get install -y python3 sudo git + +mkdir -p /etc/sudoers.d + +cd /src +bash ./scripts/install-system-wide.sh + +test -f /etc/alphagsm.conf +test -d /home/alphagsm +test -d /home/alphagsm/gitlive +test -d /home/alphagsm/downloads +test -d /usr/local/lib/alphagsm +test -f /usr/local/lib/alphagsm/alphagsm +test -f /usr/local/lib/alphagsm/alphagsm-downloads +test -L /usr/local/bin/alphagsm +test "$(readlink -f /usr/local/bin/alphagsm)" = "/usr/local/lib/alphagsm/alphagsm" +test -x /usr/local/sbin/gitalphagsm +test -f /etc/sudoers.d/gameservers +grep -Fx "%alphagsm ALL = (alphagsm) NOPASSWD:/usr/local/lib/alphagsm/alphagsm-downloads" /etc/sudoers.d/gameservers + +python3 /usr/local/bin/alphagsm --help >/tmp/alphagsm-help.txt +test -s /tmp/alphagsm-help.txt +' From 8ddab4117db238fca665ff7e5418a23697465a61 Mon Sep 17 00:00:00 2001 From: cosmosquark Date: Sat, 7 Nov 2026 22:03:16 +0000 Subject: [PATCH 3/4] Document the Linux install layouts and make the command link work. A system install writes the shared downloader settings AlphaGSM reads and leaves each operator's servers in that operator's home. The alphagsm link now resolves to the real program so Python can find src. --- .github/workflows/unittest.yaml | 9 ++ DEVELOPERS.md | 25 +++ README.md | 18 +++ alphagsm | 3 +- changelog.txt | 7 + docs/README.md | 2 + docs/install/local-user.md | 84 ++++++++-- docs/install/system-wide.md | 150 +++++++++++++++--- scripts/gitalphagsm | 0 scripts/install-local-user.sh | 26 ++- scripts/install-system-wide.sh | 92 +++++++++-- .../run_system_install_in_container.sh | 27 +++- 12 files changed, 384 insertions(+), 59 deletions(-) mode change 100644 => 100755 scripts/gitalphagsm mode change 100644 => 100755 scripts/install-local-user.sh mode change 100644 => 100755 system_install_tests/run_system_install_in_container.sh diff --git a/.github/workflows/unittest.yaml b/.github/workflows/unittest.yaml index bd7d8181..5949b2c0 100644 --- a/.github/workflows/unittest.yaml +++ b/.github/workflows/unittest.yaml @@ -63,6 +63,15 @@ jobs: run: | bash ./lint.sh + system-install-test: + runs-on: ubuntu-latest + name: system-install-test + steps: + - name: Checkout AlphaGSM code + uses: actions/checkout@v4 + - name: Run system-wide install test in container + run: bash ./system_install_tests/run_system_install_in_container.sh + unit-test: runs-on: ubuntu-latest needs: build diff --git a/DEVELOPERS.md b/DEVELOPERS.md index 073bc171..0de0668a 100644 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -946,6 +946,9 @@ merge groups, manual dispatch and reusable release validation. Its current gates - representative Minecraft backend integration on Windows and macOS - four native Windows unit cases, with exact passing JUnit evidence required by the final summary; Linux unit and coverage jobs deselect those cases +- `system-install-test`, which runs + [system_install_tests/run_system_install_in_container.sh](system_install_tests/run_system_install_in_container.sh) + and checks the Linux system install layout without changing the runner host Smoke, integration and isolated recheck commands run through `scripts/run_ci_workshop_tests.py`. It enables every public anonymous Workshop @@ -997,6 +1000,28 @@ Important environment variables: - `ALPHAGSM_DEBUG` The smoke tests rely on temporary config files and `ALPHAGSM_CONFIG_LOCATION` to isolate state per run. + +Linux install layouts: + +- [docs/install/local-user.md](docs/install/local-user.md) +- [docs/install/system-wide.md](docs/install/system-wide.md) + +`src/utils/settings/_settings.py` selects the default config from the program +path. A frozen binary reads `alphagsm.conf` beside the executable. A source +tree whose file path starts with `/usr/` reads `/etc/alphagsm.conf`. Every +other source tree reads `alphagsm.conf` four directories above that settings +module, which is the repository or install root. `ALPHAGSM_CONFIG_LOCATION` +replaces that choice. + +[scripts/install-system-wide.sh](scripts/install-system-wide.sh) copies the +tree under `/usr/local/lib/alphagsm`, so the program uses `/etc/alphagsm.conf`. +On a new config it sets `[downloader] user` to the `alphagsm` account and +points `db_path` and `target_path` at `/home/alphagsm/downloads`. Server data +still follows each operator's `~/.alphagsm` user settings. The download helper +re-executes through `sudo -Hu alphagsm`, and +`/etc/sudoers.d/gameservers` allows only `alphagsm-downloads` for the +`alphagsm` group. + GitHub Actions supplies `ALPHAGSM_GITHUB_TOKEN` to smoke and integration jobs using the workflow's read-only `GITHUB_TOKEN`; local runs can omit it unless they encounter GitHub's anonymous API rate limit. diff --git a/README.md b/README.md index f8168ec7..7710841d 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,19 @@ Linux, macOS, or Windows host when that guidance is known, and recommend using the Docker runtime instead when you would rather avoid host-side dependency setup. +## Install AlphaGSM on Linux + +Two scripts copy this repository into place and link the `alphagsm` command. +They do not install Python packages. Install `requirements-runtime.txt` first. +Both scripts are Linux only. + +- One account: [Local User Install](docs/install/local-user.md) +- A shared machine: [System-Wide Install](docs/install/system-wide.md) + +A system-wide install shares the program and the download cache. Each +person's servers stay in that person's home. The Fast Start below is the same +setup when you run `./alphagsm` from a checkout instead of an installed copy. + ## Fast Start If you want the quickest first run, start with the Docker-manager path above. @@ -510,9 +523,14 @@ These scripts are useful because they show a full real flow: 4. check it 5. stop it cleanly +`bash ./system_install_tests/run_system_install_in_container.sh` checks the +shared Linux install inside a container. It does not change the host. + ## Step-By-Step Server Guides - [Documentation Index](docs/README.md) +- [Local User Install](docs/install/local-user.md) +- [System-Wide Install](docs/install/system-wide.md) - [Everyday Commands](docs/commands.md) - [Installing Mods](docs/installing-mods.md) - [Updating Servers And AlphaGSM](docs/updating.md) diff --git a/alphagsm b/alphagsm index 71eb928e..089615f0 100755 --- a/alphagsm +++ b/alphagsm @@ -2,7 +2,8 @@ import os import sys -sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "src")) +_LAUNCHER = os.path.realpath(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(os.path.dirname(_LAUNCHER), "src")) if len(sys.argv) == 3 and sys.argv[1] == "--_supervise-process": from screen.supervisor import serve diff --git a/changelog.txt b/changelog.txt index 29094711..fe21d06a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -7,6 +7,13 @@ server support, CI, packaging, or workflow defaults. Historical detail lives in git history. Keep this file focused on release-facing notes and high-level milestones. +2026-11-07 +- Install: Linux local and system-wide scripts copy AlphaGSM into place and + link `alphagsm`. The command link resolves to the real program so Python + can find `src`. A system install reads `/etc/alphagsm.conf`, shares the + download cache as the `alphagsm` account, and leaves each operator's + servers in that operator's home. A container test checks that layout. + 2026-11-05 - ET: Legacy: setup finds the Linux x86_64 tar.gz on the current etlegacy.com download page. Release numbers are read from the version label. diff --git a/docs/README.md b/docs/README.md index d62d13eb..752be472 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,6 +22,8 @@ with `update`. | I want to… | Read | | --- | --- | | Get AlphaGSM installed | [Getting Started](../README.md) | +| Install AlphaGSM for one Linux account | [Local User Install](install/local-user.md) | +| Install AlphaGSM once on a shared Linux host | [System-Wide Install](install/system-wide.md) | | Take or skip the guided setup tour | [First-run setup](first-run-setup.md) | | Run or maintain AlphaGSM with an AI agent | [Agent Operations](agent-operations.md) | | See every command (`restore`, `kill`, `activate`, `send`, …) | [Everyday Commands](commands.md) | diff --git a/docs/install/local-user.md b/docs/install/local-user.md index b29400e2..1060b192 100644 --- a/docs/install/local-user.md +++ b/docs/install/local-user.md @@ -1,16 +1,33 @@ # Local User Install -This install path is for one Linux user running AlphaGSM for their own account. +Use this path on Linux when one person runs AlphaGSM from their own account. -## What It Does +The script copies the repository, writes a config file if you do not already +have one, and links `alphagsm` into `~/.local/bin`. It does not install Python +packages, system libraries, Java, or SteamCMD. It does not share downloads +with other accounts. -The local install script: +Windows and macOS are not covered here. On those systems, run AlphaGSM from a +checkout or use a standalone binary. See +[Getting Started](../../README.md). -- copies AlphaGSM into your home directory -- makes a config file for you -- creates a command link in `~/.local/bin` +## Before you run it -## Run It +Install Python 3 and the runtime packages from the repository: + +```bash +python3 -m pip install -r requirements-runtime.txt +``` + +`requirements.txt` is the wider set used for development and tests. A normal +server only needs `requirements-runtime.txt`. + +Game servers may also need Java, 32-bit libraries, or other host packages. +The [host install requirements](../../README.md#host-install-requirements) +list the common ones. AlphaGSM reports a missing host package when you start +a server that needs it. + +## Run it From the repository root: @@ -18,22 +35,61 @@ From the repository root: bash ./scripts/install-local-user.sh ``` -## Default Paths +The copy leaves out a local `alphagsm.conf`, `env`, `.venv`, and +`__pycache__`. It keeps `.git` when the checkout has one. Running the script +again refreshes the copied code and does not replace an existing +`~/.local/lib/alphagsm/alphagsm.conf`. + +## Paths + +| Path | Role | +| --- | --- | +| `~/.local/lib/alphagsm` | Copied program | +| `~/.local/bin/alphagsm` | Command link. It points at the copied program, and AlphaGSM follows that link to find `src`. | +| `~/.local/lib/alphagsm/alphagsm.conf` | Config read by this install | -- code: `~/.local/lib/alphagsm` -- command: `~/.local/bin/alphagsm` -- config: `~/.local/lib/alphagsm/alphagsm.conf` +AlphaGSM chooses that config because the code does not live under `/usr`. A +copy installed under `/usr`, including `/usr/local`, reads `/etc/alphagsm.conf` +instead. See [System-Wide Install](system-wide.md). -## After Install +`ALPHAGSM_CONFIG_LOCATION` still overrides the config file for one command. -If `~/.local/bin` is not already in your `PATH`, add it in your shell profile: +If `~/.local/bin` is not already on your `PATH`, add it to your shell profile: ```bash export PATH="$HOME/.local/bin:$PATH" ``` -Then you can run: +Open a new shell, then check the command: ```bash alphagsm --help ``` + +## First server + +Follow the [Fast Start](../../README.md#fast-start) commands. Use `alphagsm` +instead of `./alphagsm` once the link is on your `PATH`. For example: + +```bash +alphagsm mymc create minecraft.vanilla +alphagsm mymc setup +alphagsm mymc start +alphagsm mymc query +alphagsm mymc stop +``` + +Servers, logs, and backups for this account stay under `~/.alphagsm` unless +the config changes `alphagsm_path`. Downloads stay in this account too. There +is no shared download user. + +## Update the copy + +When the install still contains `.git`: + +```bash +git -C "$HOME/.local/lib/alphagsm" pull +``` + +Otherwise run `bash ./scripts/install-local-user.sh` again from a newer +checkout. Neither path edits your existing config file. diff --git a/docs/install/system-wide.md b/docs/install/system-wide.md index 9c40f909..c30e6953 100644 --- a/docs/install/system-wide.md +++ b/docs/install/system-wide.md @@ -1,21 +1,16 @@ # System-Wide Install -This install path is for a shared machine where AlphaGSM is installed once and used by multiple people. +Use this path on a shared Linux machine. It installs the AlphaGSM program +once. People on the machine then run that same `alphagsm` command. -## Layout +The shared part is the program and the download cache. Each person's servers, +logs, and backups stay in that person's home directory, normally +`~/.alphagsm`. This install does not put everyone's game servers into one +account. -The system-wide install is designed around these paths: +Windows and macOS are not covered here. -- overall config: `/etc/alphagsm.conf` -- shared download user home: `/home/alphagsm` -- code: `/usr/local/lib/alphagsm` -- shared command: `/usr/local/bin/alphagsm` -- root git helper: `/usr/local/sbin/gitalphagsm` -- sudoers file: `/etc/sudoers.d/gameservers` - -The shared download user can own common download data so that normal users are not all downloading and storing the same artifacts at the same time. - -## Run It +## What the installer prepares Run as root: @@ -23,25 +18,130 @@ Run as root: sudo bash ./scripts/install-system-wide.sh ``` -## Shared Download User +On a new machine it does all of the following. + +- Creates a system account named `alphagsm` when that account is missing. + The account gets a home directory and cannot log in. +- Copies the program to `/usr/local/lib/alphagsm`. +- Links `/usr/local/bin/alphagsm` at that program. +- Installs `/usr/local/sbin/gitalphagsm` for root. +- Creates `/etc/alphagsm.conf` from `alphagsm.conf-template` when that file + is missing. +- Turns on the shared download cache in a new config file. +- Writes `/etc/sudoers.d/gameservers` and checks it with `visudo` when + `visudo` is installed. +- Creates `/home/alphagsm/downloads` and `/home/alphagsm/gitlive`. + +The copy leaves out a local `alphagsm.conf`, `env`, `.venv`, and +`__pycache__`. It keeps `.git` when the checkout has one. Running the script +again refreshes the program and does not replace an existing +`/etc/alphagsm.conf`. -The installer creates or uses the `alphagsm` system user and prepares: +Install the Python packages before the first real command. The script does +not install them: -- `/home/alphagsm/gitlive` -- `/home/alphagsm/downloads` +```bash +sudo python3 -m pip install --break-system-packages -r requirements-runtime.txt +``` -## Sudoers Entry +Use the package names from +[host install requirements](../../README.md#host-install-requirements) when a +game asks for Java or system libraries. + +## Paths + +| Path | Role | +| --- | --- | +| `/usr/local/lib/alphagsm` | Copied program | +| `/usr/local/bin/alphagsm` | Command used by every operator. It points at the copied program, and AlphaGSM follows that link to find `src`. | +| `/etc/alphagsm.conf` | Config read by this install | +| `/home/alphagsm` | Home of the download account | +| `/home/alphagsm/downloads/downloads.txt` | Shared download database | +| `/home/alphagsm/downloads/downloads` | Shared download files | +| `/home/alphagsm/gitlive` | Empty directory for an operator checkout | +| `/usr/local/sbin/gitalphagsm` | Root helper that runs git in the program directory | +| `/etc/sudoers.d/gameservers` | Passwordless sudo for the shared downloader | + +AlphaGSM reads `/etc/alphagsm.conf` because the program file is under `/usr`. +The same build, copied anywhere else, reads `alphagsm.conf` beside the +program instead. `ALPHAGSM_CONFIG_LOCATION` overrides that choice for one +command. + +## Shared downloads + +A new `/etc/alphagsm.conf` contains: + +```ini +[downloader] +user = alphagsm +db_path = /home/alphagsm/downloads/downloads.txt +target_path = /home/alphagsm/downloads/downloads +``` -The installer writes: +With `user` set, a missing file is downloaded by the `alphagsm` account. +AlphaGSM runs: + +```bash +sudo -Hu alphagsm /usr/local/lib/alphagsm/alphagsm-downloads MODULE ARGS +``` + +The sudoers entry allows that one command with no password for members of +the `alphagsm` group: ```text -%alphagsm ALL = (alphagsm) NOPASSWD:/usr/local/lib/alphagsm/alphagsm-downloads +%alphagsm ALL = (alphagsm) NOPASSWD:/usr/local/lib/alphagsm/alphagsm-downloads +``` + +Add each person who will run servers: + +```bash +sudo usermod -aG alphagsm USER ``` -That allows the right group to run the shared downloader helper as the `alphagsm` user without a password prompt. +That person starts a new login before the group applies. People outside the +group can still run `alphagsm`, but the first download of a file asks for a +password through `sudo`. -## Notes +If `/etc/alphagsm.conf` already existed, the installer leaves it alone. Set +the three `downloader` keys yourself to use the shared cache. + +`/home/alphagsm/gitlive` is only a directory. AlphaGSM does not clone into it +or read it. Use it when you want a shared checkout that is separate from the +copy under `/usr/local/lib/alphagsm`. + +## Each person's servers + +Creating a server writes that server under the account that ran the command. +Two people on the same machine get two homes and two server lists. They share +downloaded archives through the `alphagsm` account, so the second setup can +reuse a file the first setup already fetched. + +Per-person settings still live in `~/.alphagsm`, including the server list. +Change `alphagsm_path` in the user settings only when an account should keep +servers somewhere else. + +## Update the program + +When the copy contains `.git`, root can update it: + +```bash +sudo gitalphagsm pull +``` + +`gitalphagsm` runs `git` in `/usr/local/lib/alphagsm` and then stops. It does +not restart game servers. If that directory has no `.git`, the helper exits +and tells you. Run `sudo bash ./scripts/install-system-wide.sh` from a newer +checkout instead. + +## Check the layout without touching the host + +From a machine with Docker: + +```bash +bash ./system_install_tests/run_system_install_in_container.sh +``` -- This is the better fit for a shared host. -- The root helper `gitalphagsm` is installed to `/usr/local/sbin/gitalphagsm`. -- The `alphagsm` command is exposed through `/usr/local/bin/alphagsm`. +That starts Ubuntu 24.04, installs Python and the runtime packages, runs the +system installer, checks the paths and sudoers file, runs the installer a +second time to prove the existing config is kept, and runs `alphagsm --help`. +The host's `/etc` and `/usr/local` are not changed. diff --git a/scripts/gitalphagsm b/scripts/gitalphagsm old mode 100644 new mode 100755 diff --git a/scripts/install-local-user.sh b/scripts/install-local-user.sh old mode 100644 new mode 100755 index 0aef459a..76a7984b --- a/scripts/install-local-user.sh +++ b/scripts/install-local-user.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# Copy AlphaGSM for one Linux account. The program reads alphagsm.conf from +# this install root because the code is not under /usr. + set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -7,9 +10,25 @@ INSTALL_ROOT="${INSTALL_ROOT:-$HOME/.local/lib/alphagsm}" BIN_DIR="${BIN_DIR:-$HOME/.local/bin}" CONFIG_PATH="$INSTALL_ROOT/alphagsm.conf" -mkdir -p "$INSTALL_ROOT" "$BIN_DIR" +case "$INSTALL_ROOT" in + "$REPO_ROOT"|"$REPO_ROOT"/*) + echo "INSTALL_ROOT must be outside the repository: $INSTALL_ROOT" >&2 + exit 1 + ;; +esac + +if [[ ! -f "$REPO_ROOT/alphagsm.conf-template" ]]; then + echo "Missing $REPO_ROOT/alphagsm.conf-template" >&2 + exit 1 +fi -cp -a "$REPO_ROOT"/. "$INSTALL_ROOT"/ +mkdir -p "$INSTALL_ROOT" "$BIN_DIR" +tar -C "$REPO_ROOT" \ + --exclude=alphagsm.conf \ + --exclude=env \ + --exclude=.venv \ + --exclude=__pycache__ \ + -cf - . | tar -C "$INSTALL_ROOT" -xf - if [[ ! -f "$CONFIG_PATH" ]]; then cp "$INSTALL_ROOT/alphagsm.conf-template" "$CONFIG_PATH" @@ -29,6 +48,9 @@ Command link: Config file: $CONFIG_PATH +Python packages are not installed by this script. From the repository, run: + python3 -m pip install -r requirements-runtime.txt + If '$BIN_DIR' is not in your PATH, add this to your shell profile: export PATH="$BIN_DIR:\$PATH" EOF diff --git a/scripts/install-system-wide.sh b/scripts/install-system-wide.sh index b18eb5ac..fcb636e2 100644 --- a/scripts/install-system-wide.sh +++ b/scripts/install-system-wide.sh @@ -1,5 +1,9 @@ #!/usr/bin/env bash +# Copy AlphaGSM under /usr/local and point it at /etc/alphagsm.conf. +# Code installed under /usr uses that system file. Each operator's servers +# stay in that operator's home. The download cache is shared. + set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -9,34 +13,96 @@ SBIN_DIR="${SBIN_DIR:-/usr/local/sbin}" CONFIG_PATH="${CONFIG_PATH:-/etc/alphagsm.conf}" SUDOERS_PATH="${SUDOERS_PATH:-/etc/sudoers.d/gameservers}" ALPHAGSM_USER="${ALPHAGSM_USER:-alphagsm}" -ALPHAGSM_HOME="${ALPHAGSM_HOME:-/home/$ALPHAGSM_USER}" if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then echo "This installer must be run as root." >&2 exit 1 fi +if ! command -v python3 >/dev/null 2>&1; then + echo "python3 is required to write /etc/alphagsm.conf." >&2 + exit 1 +fi + +case "$INSTALL_ROOT" in + "$REPO_ROOT"|"$REPO_ROOT"/*) + echo "INSTALL_ROOT must be outside the repository: $INSTALL_ROOT" >&2 + exit 1 + ;; +esac + +if [[ ! -f "$REPO_ROOT/alphagsm.conf-template" ]]; then + echo "Missing $REPO_ROOT/alphagsm.conf-template" >&2 + exit 1 +fi + if ! id "$ALPHAGSM_USER" >/dev/null 2>&1; then - useradd --system --create-home --home-dir "$ALPHAGSM_HOME" --shell /usr/sbin/nologin "$ALPHAGSM_USER" + useradd --system --create-home --home-dir "/home/$ALPHAGSM_USER" --shell /usr/sbin/nologin "$ALPHAGSM_USER" +fi + +ALPHAGSM_HOME="$(getent passwd "$ALPHAGSM_USER" | cut -d: -f6)" +ALPHAGSM_GROUP="$(id -gn "$ALPHAGSM_USER")" +if [[ -z "$ALPHAGSM_HOME" || "$ALPHAGSM_HOME" == "/" ]]; then + echo "Refusing empty or root home for $ALPHAGSM_USER" >&2 + exit 1 fi -mkdir -p "$INSTALL_ROOT" "$BIN_DIR" "$SBIN_DIR" "$ALPHAGSM_HOME/gitlive" "$ALPHAGSM_HOME/downloads" +mkdir -p \ + "$INSTALL_ROOT" \ + "$BIN_DIR" \ + "$SBIN_DIR" \ + "$ALPHAGSM_HOME/gitlive" \ + "$ALPHAGSM_HOME/downloads/downloads" -cp -a "$REPO_ROOT"/. "$INSTALL_ROOT"/ +tar -C "$REPO_ROOT" \ + --exclude=alphagsm.conf \ + --exclude=env \ + --exclude=.venv \ + --exclude=__pycache__ \ + -cf - . | tar -C "$INSTALL_ROOT" -xf - if [[ ! -f "$CONFIG_PATH" ]]; then cp "$INSTALL_ROOT/alphagsm.conf-template" "$CONFIG_PATH" + CONFIG_PATH="$CONFIG_PATH" ALPHAGSM_USER="$ALPHAGSM_USER" ALPHAGSM_HOME="$ALPHAGSM_HOME" \ + python3 - <<'PY' +import os +from pathlib import Path + +path = Path(os.environ["CONFIG_PATH"]) +user = os.environ["ALPHAGSM_USER"] +home = os.environ["ALPHAGSM_HOME"] +text = path.read_text(encoding="utf-8") +header = "[downloader]\n" +start = text.find(header) +if start < 0: + raise SystemExit("alphagsm.conf has no [downloader] section") +body_start = start + len(header) +rest = text[body_start:] +end = rest.find("\n[") +body = rest if end < 0 else rest[:end] +if any(line.startswith("user ") or line.startswith("user=") for line in body.splitlines()): + raise SystemExit(0) +block = ( + f"user = {user}\n" + f"db_path = {home}/downloads/downloads.txt\n" + f"target_path = {home}/downloads/downloads\n" +) +path.write_text(text[:body_start] + block + text[body_start:], encoding="utf-8") +PY fi ln -sfn "$INSTALL_ROOT/alphagsm" "$BIN_DIR/alphagsm" install -m 0755 "$INSTALL_ROOT/scripts/gitalphagsm" "$SBIN_DIR/gitalphagsm" cat >"$SUDOERS_PATH" </dev/null 2>&1; then + visudo -cf "$SUDOERS_PATH" +fi -chown -R "$ALPHAGSM_USER":"$ALPHAGSM_USER" "$ALPHAGSM_HOME" +chown -R "$ALPHAGSM_USER:$ALPHAGSM_GROUP" "$ALPHAGSM_HOME" cat <> /etc/alphagsm.conf +bash ./scripts/install-system-wide.sh +grep -q "install-test-marker" /etc/alphagsm.conf python3 /usr/local/bin/alphagsm --help >/tmp/alphagsm-help.txt test -s /tmp/alphagsm-help.txt From 735654fc1f876e5a0c7779c690f824de7e2f1744 Mon Sep 17 00:00:00 2001 From: cosmosquark Date: Sat, 7 Nov 2026 22:16:42 +0000 Subject: [PATCH 4/4] Point Dependabot at the real package manifests. The checked-in config left package-ecosystem blank, so GitHub rejected .github/dependabot.yml. Weekly updates now cover pip, GitHub Actions, and the runtime Dockerfiles, with one grouped pull request per ecosystem. --- .github/dependabot.yml | 40 +++++++++++++++++++++++++++++++++++----- changelog.txt | 4 ++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5990d9c6..50eb3ef0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,41 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: +# Weekly version updates for the manifests this repository actually ships. # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - - package-ecosystem: "" # See documentation for possible values - directory: "/" # Location of package manifests + - package-ecosystem: "pip" + directory: "/" schedule: interval: "weekly" + exclude-paths: + - "pyproject.toml" + groups: + python: + patterns: + - "*" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + actions: + patterns: + - "*" + + - package-ecosystem: "docker" + directories: + - "/docker/java" + - "/docker/manager" + - "/docker/quake-linux" + - "/docker/service-console" + - "/docker/simple-tcp" + - "/docker/steamcmd-linux" + - "/docker/wine-proton" + schedule: + interval: "weekly" + groups: + images: + patterns: + - "*" + group-by: "dependency-name" diff --git a/changelog.txt b/changelog.txt index fe21d06a..3b43882e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -13,6 +13,10 @@ Historical detail lives in git history. Keep this file focused on release-facing can find `src`. A system install reads `/etc/alphagsm.conf`, shares the download cache as the `alphagsm` account, and leaves each operator's servers in that operator's home. A container test checks that layout. +- Dependencies: Dependabot checks pip, GitHub Actions, and the runtime + Dockerfiles every week. Each ecosystem is grouped into one update. + `pyproject.toml` stays out of that scan because it only holds tool + settings. 2026-11-05 - ET: Legacy: setup finds the Linux x86_64 tar.gz on the current etlegacy.com