feat(drill): the staging drill, the week before each forced wipe (stage 9c, D307-D309, D317-D319)
A Gitea job that checks each new Rust staging build in the seven days before the monthly forced wipe: - tools/drill/static.sh: staging's managed assemblies (DepotDownloader) and Oxide's staging build; whether Oxide has caught up (fieldlist --oxide-behind), the swap's field list compared, and the plugin compiled. - tools/drill/drill.js: window (D318), gate (one check per depot manifest), live (rust-staging runs rnt.run all; rust-carbon is stopped and restarted when both 6000-map rigs run, D309) and report (one issue per build, state on the drill branch; a build waits up to three days for Oxide, D317). - tools/drill/rig-start.sh: the rig's startup; installs the branch and Oxide's build for it, and refuses to start a mismatched pair. - RunicNpcTest 0.7.2: stage 5's five fights look for a field with room for their 280 m line (D319), and the spawn check names what did not spawn. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
149
.gitea/workflows/staging-drill.yml
Normal file
149
.gitea/workflows/staging-drill.yml
Normal file
@@ -0,0 +1,149 @@
|
||||
# The staging drill (docs/runicnpc/PLAN.md stage 9c, D307-D309, D317, D318).
|
||||
#
|
||||
# Rust changes under RunicNPC on every forced wipe, and what breaks first is the subclass swap and
|
||||
# the brain (README "The update surface"). Facepunch publishes each build on Steam's `staging`
|
||||
# branch days before it ships. The forced wipe is monthly, the first Thursday, so the job does
|
||||
# something only in the seven days before it, which leaves a week to fix what it finds (D318). It
|
||||
# is scheduled daily and stops at its first step outside that week. In the week, it checks every
|
||||
# new staging build:
|
||||
#
|
||||
# window (tools/drill/drill.js window): the week before a forced wipe? (A run by hand always is.)
|
||||
# static half (tools/drill/static.sh, about a minute, no server):
|
||||
# Rust's managed assemblies from Steam's staging branch and Oxide's staging build →
|
||||
# has Oxide caught up · the swap's field list regenerated and compared · RunicNPC compiled.
|
||||
# gate (tools/drill/drill.js gate): stop here when this build (the manifest of Rust's Linux
|
||||
# depot) was already checked.
|
||||
# live half (drill.js live): the `rust-staging` server installs the same pair at start, then
|
||||
# RunicNPC, Kits and the test harness are loaded and `rnt.run all` runs. When both 6000-map rigs
|
||||
# are running, `rust-carbon` is stopped for it and started again (D309). Skipped while Oxide is
|
||||
# behind Rust: an Oxide server on that pair dies at boot.
|
||||
# report (drill.js report): anything that fails or differs opens ONE issue for the build, labelled
|
||||
# `staging-drill` (a later run on the same build comments on it). The result is kept on the
|
||||
# `drill` branch (drill.json), like the installer's `bundles` branch.
|
||||
#
|
||||
# While Oxide's staging build is behind Rust's, a build waits up to three days, opening nothing;
|
||||
# then it is reported as "Oxide has not caught up" (D317).
|
||||
#
|
||||
# Run by hand with `branch: public` to rehearse it on a pair known to match: it reports in the job
|
||||
# summary only, and changes no state and opens no issue.
|
||||
#
|
||||
# The schedule runs only from the default branch (`main`), so it starts with the stage 9 cutover;
|
||||
# before then, run it by hand on `edge`.
|
||||
#
|
||||
# Secrets (Settings → Actions → Secrets, the organisation's):
|
||||
# PTERODACTYL_DRILL_KEY — the panel client key made for this job alone (D309). Never printed.
|
||||
# REGISTRY_USER / REGISTRY_TOKEN — as release.yml: push the `drill` branch and open issues.
|
||||
|
||||
name: Staging drill
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '23 10 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'Steam branch to drill (public = a rehearsal: job summary only)'
|
||||
default: staging
|
||||
force:
|
||||
description: 'Check the build again even if it was checked'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: staging-drill
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
GITEA_HOST: gitea.whitlocktech.com
|
||||
REPO: RunicGateway/runicnpc-rust
|
||||
|
||||
jobs:
|
||||
drill:
|
||||
runs-on: ubuntu-latest
|
||||
# Rust's update, a 3500 map's boot and navmesh (about 18 minutes when a new Rust build
|
||||
# regenerates the map), and the harness: under an hour.
|
||||
timeout-minutes: 180
|
||||
env:
|
||||
BRANCH: ${{ github.event.inputs.branch || 'staging' }}
|
||||
WORK: ${{ github.workspace }}/drill-work
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# 22 or later: drill.js talks to the panel's console over the global WebSocket.
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: The week before a forced wipe?
|
||||
id: window
|
||||
run: node tools/drill/drill.js window ${{ github.event_name == 'workflow_dispatch' && '--force' || '' }}
|
||||
|
||||
# tools/fieldlist and the compiler (Roslyn's csc.dll ships in the SDK).
|
||||
- name: .NET SDK
|
||||
if: ${{ steps.window.outputs.open == 'true' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl -sSL -o "$RUNNER_TEMP/dotnet-install.sh" https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh
|
||||
bash "$RUNNER_TEMP/dotnet-install.sh" --channel 9.0 --install-dir "$HOME/.dotnet" > /dev/null
|
||||
echo "$HOME/.dotnet" >> "$GITHUB_PATH"
|
||||
echo "DOTNET_ROOT=$HOME/.dotnet" >> "$GITHUB_ENV"
|
||||
|
||||
- name: The drill branch (the last build checked)
|
||||
if: ${{ steps.window.outputs.open == 'true' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "runicnpc-ci"
|
||||
git config user.email "ci@whitlocktech.com"
|
||||
rm -rf drill-state
|
||||
git worktree prune
|
||||
if git ls-remote --exit-code --heads origin drill > /dev/null 2>&1; then
|
||||
git fetch origin drill
|
||||
git worktree add -B drill drill-state origin/drill
|
||||
else
|
||||
# First run: a root commit with an empty tree, so the branch inherits no code history.
|
||||
ROOT="$(git commit-tree "$(git hash-object -t tree /dev/null)" -m 'chore(drill): start the drill branch')"
|
||||
git worktree add -B drill drill-state "$ROOT"
|
||||
fi
|
||||
[ -f drill-state/drill.json ] || echo '{}' > drill-state/drill.json
|
||||
cat drill-state/drill.json
|
||||
|
||||
- name: Static half
|
||||
if: ${{ steps.window.outputs.open == 'true' }}
|
||||
run: bash tools/drill/static.sh
|
||||
|
||||
- name: A build not checked yet?
|
||||
id: gate
|
||||
if: ${{ steps.window.outputs.open == 'true' }}
|
||||
run: node tools/drill/drill.js gate --state drill-state/drill.json --work "$WORK" ${{ github.event.inputs.force == 'true' && '--force' || '' }}
|
||||
|
||||
- name: Live half (rust-staging)
|
||||
id: live
|
||||
if: ${{ steps.gate.outputs.run == 'true' }}
|
||||
continue-on-error: true
|
||||
env:
|
||||
PANEL_KEY: ${{ secrets.PTERODACTYL_DRILL_KEY }}
|
||||
run: node tools/drill/drill.js live --work "$WORK" --branch "$BRANCH"
|
||||
|
||||
- name: Report
|
||||
id: report
|
||||
if: ${{ always() && steps.gate.outputs.run == 'true' }}
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
run: node tools/drill/drill.js report --state drill-state/drill.json --work "$WORK"
|
||||
|
||||
- name: Keep the result on the drill branch
|
||||
if: ${{ always() && steps.gate.outputs.run == 'true' && env.BRANCH == 'staging' }}
|
||||
env:
|
||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
CI_USER="$(printf '%s' "${REGISTRY_USER}" | tr -d '\r\n')"
|
||||
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"
|
||||
git remote set-url origin "https://${CI_USER}:${CI_TOKEN}@${GITEA_HOST}/${REPO}.git"
|
||||
cd drill-state
|
||||
git add -A
|
||||
if git diff --cached --quiet; then echo "no change"; exit 0; fi
|
||||
node -e "const s=require('./drill.json').lastRun||{};console.log('chore(drill): '+(s.manifest||'?')+' '+(s.outcome||'')+' [skip ci]')" > ../drill-msg.txt
|
||||
git commit -q -F ../drill-msg.txt
|
||||
git push origin drill
|
||||
Reference in New Issue
Block a user