feat: first-boot records what a server does with no settings
Validate game templates / validate (push) Successful in 9m34s
Validate game templates / boot-test (push) Skipped
Validate game templates / publish (push) Skipped

`gbrgames first-boot -game <id>` (or -image for a game with no template)
starts the server once like the node-agent does (same volumes, user,
SteamCMD install and startup prefix) but with none of the template's
settings and no restart policy, in the isolated gbr-boottest network.
It watches the console until the server exits, goes quiet or hits -max,
stops it gracefully (many servers write their settings on shutdown) and
records:
- the console with timings, plus ready and error lines; the last strong
  ready line before the stop becomes a readyPattern / readyExample pair
- every file created; config files are copied out and parsed into
  settings with their defaults (INI, properties, JSON, YAML, Unreal
  OptionSettings=(...) lists); log files are scanned for ready lines too
- files written outside the volumes, which a recreated container loses

Minecraft: ready line `Done \([0-9.]+s\)! For help, type "help"` and 76
settings (69 from server.properties) found with no template input. ARK:
showed its image retries nothing when SteamCMD fails with "Missing
configuration" and starts a server that was never installed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
gregandClaude Opus 5.5 committed 2026-10-25 02:18:21 -04:00
1 parent 40e7372dab
commit c9d9cd6ff5
4 files changed
+912 -1

No files matched your search

+4 -1
View File
@@ -5,6 +5,7 @@
// go run ./cmd/gbrgames export -out p.json # build the games_published payload CI sends to the panel
// go run ./cmd/gbrgames boottest-payload ID # build the deploy message the node-agent's BootTest runs
// go run ./cmd/gbrgames inspect-image -game ID # read the image's facts and check the template against them
// go run ./cmd/gbrgames first-boot -game ID # start the server once, untouched; record its log and files
package main
import (
@@ -28,6 +29,8 @@ func main() {
err = runBootTestPayload(os.Args[2:])
case "inspect-image":
err = runInspectImage(os.Args[2:])
case "first-boot":
err = runFirstBoot(os.Args[2:])
default:
usage()
}
@@ -38,6 +41,6 @@ func main() {
}
func usage() {
fmt.Fprintln(os.Stderr, "usage: gbrgames validate [-games DIR] | import [-games DIR] [-all] EXPORT.json | export [-repo R] [-commit SHA] [-out FILE] | boottest-payload [-out FILE] ID | inspect-image [-game ID] [-json] [-no-pull] [IMAGE]")
fmt.Fprintln(os.Stderr, "usage: gbrgames validate [-games DIR] | import [-games DIR] [-all] EXPORT.json | export [-repo R] [-commit SHA] [-out FILE] | boottest-payload [-out FILE] ID | inspect-image [-game ID] [-json] [-no-pull] [IMAGE] | first-boot [-game ID | -image IMAGE] [-env K=V] [-max D] [-quiet D] [-out DIR]")
os.Exit(2)
}