source2rosetta/.forgejo/workflows/derive.yml
Kamal Tufekcic 90db2e3ae2
All checks were successful
CI / lint (push) Successful in 16s
CI / fuzz (push) Successful in 1m38s
CI / test (push) Successful in 20s
add timeout
2026-07-27 11:03:06 +03:00

123 lines
5.6 KiB
YAML

name: derive
on:
workflow_dispatch:
inputs:
game:
description: "Game key"
required: true
default: cs2
type: choice
options:
- cs2
- dota2
concurrency:
group: derive-${{ github.event.inputs.game }}
cancel-in-progress: false
jobs:
derive:
runs-on: s2-runner
env:
GAME: ${{ github.event.inputs.game }}
STEAM_APPS: /home/cs2/.steam/SteamApps
STEAM_USER: source2rosetta
RELEASE_BASE: ${{ github.server_url }}/${{ github.repository }}/releases/download
BOOTSTRAP_DIR: /home/cs2/rosetta-bootstrap
steps:
- uses: actions/checkout@v6.0.2
- name: Update the install to the current build
run: |
case "$GAME" in
cs2) APPID=730 ;;
dota2) APPID=570 ;;
*) echo "unknown game '$GAME' (expected cs2 or dota2)"; exit 1 ;;
esac
echo "APPID=$APPID" >> "$GITHUB_ENV"
# No password here: the runner holds a cached refresh token from a one-time interactive login, so
# this is non-interactive. If it ever fails with a login error the token has lapsed — re-run
# `steamcmd +login $STEAM_USER` once on the runner, as the runner user.
steamcmd +login "$STEAM_USER" +app_update "$APPID" +quit
- name: Resolve the game paths + the new buildid
run: |
MANIFEST="$STEAM_APPS/appmanifest_$APPID.acf"
[ -f "$MANIFEST" ] || { echo "no manifest at $MANIFEST — check STEAM_APPS"; exit 1; }
INSTALLDIR=$(grep -oP '"installdir"\s+"\K[^"]+' "$MANIFEST")
BUILDID=$(grep -oP '"buildid"\s+"\K[0-9]+' "$MANIFEST")
GAME_DIR="$STEAM_APPS/common/$INSTALLDIR/game"
[ -d "$GAME_DIR" ] || { echo "expected a game tree at '$GAME_DIR' — check STEAM_APPS"; exit 1; }
{ echo "BUILDID=$BUILDID"; echo "GAME_DIR=$GAME_DIR"; } >> "$GITHUB_ENV"
- name: Build the deriver
run: cargo build --release
- name: Fetch the previous model + seed (the two non-user-facing release artifacts)
run: |
mkdir -p in dist work
code=$(curl -sSL -o in/model.gz -w '%{http_code}' "$RELEASE_BASE/$GAME-latest/model-$GAME.json.gz" || echo 000)
if [ "$code" = "200" ]; then
gunzip -c in/model.gz > "in/model-$GAME.json"
echo "model: from the $GAME-latest release"
elif [ "$code" = "404" ] && [ -f "${BOOTSTRAP_DIR:-}/model-$GAME.json" ]; then
# First run only. ONLY a genuine 404 falls back: on a transient failure we must NOT quietly
# re-derive from a stale on-disk model and then overwrite `latest` with the result.
cp "${BOOTSTRAP_DIR}/model-$GAME.json" "in/model-$GAME.json"
echo "model: BOOTSTRAP from $BOOTSTRAP_DIR — clear BOOTSTRAP_DIR once this run has published"
else
echo "model fetch failed (HTTP $code) and no bootstrap copy at '${BOOTSTRAP_DIR:-<unset>}/model-$GAME.json' — refusing to derive"
exit 1
fi
if curl -fsSL -o in/seed.gz "$RELEASE_BASE/$GAME-latest/seed-$GAME.json.gz"; then
gunzip -c in/seed.gz > "mappings/seed-$GAME.json"
echo "seed: from the $GAME-latest release"
else
echo "seed: none published yet — using the repo's mappings/seed-$GAME.json"
fi
- name: Produce — derive + validate-live + typed netvars + fold model N -> N+1
run: |
# --target is a buildid-named SYMLINK to the install, not the install path itself: the deriver labels
# a build by its directory NAME, and the forward-only guard demands each target sort strictly after
# the model's latest build. Passing ".../game" would label every build "game", so the SECOND run
# would be rejected as not-newer. Buildids are monotonic and sort after the corpus's date labels.
ln -sfn "$GAME_DIR" "work/$BUILDID"
./target/release/source2rosetta --game "$GAME" produce \
--seed "mappings/seed-$GAME.json" \
--corpus-model "in/model-$GAME.json" \
--target "work/$BUILDID" \
--game-dir "$GAME_DIR" \
--version "$GAME-$BUILDID-0" \
--wait 300 \
--out-dir dist
cp "mappings/seed-$GAME.json" "dist/seed-$GAME.json"
- name: Compress the internal artifacts for release
run: |
gzip -6 "dist/model-$GAME.json"
gzip -6 "dist/seed-$GAME.json"
- name: Publish the immutable per-build snapshot
uses: https://code.forgejo.org/actions/forgejo-release@v2
with:
direction: upload
url: ${{ github.server_url }}
repo: ${{ github.repository }}
tag: ${{ env.GAME }}-${{ env.BUILDID }}-0
release-dir: dist
token: ${{ secrets.GITHUB_TOKEN }}
override: true
release-notes: "${{ env.GAME }} gamedata for Steam build ${{ env.BUILDID }}, derived by source2rosetta. Patch 0 = the game-update build; later patches on this buildid come from contribution PRs."
- name: Move <game>-latest to this build
uses: https://code.forgejo.org/actions/forgejo-release@v2
with:
direction: upload
url: ${{ github.server_url }}
repo: ${{ github.repository }}
tag: ${{ env.GAME }}-latest
release-dir: dist
token: ${{ secrets.GITHUB_TOKEN }}
override: true
release-notes: "Rolling ${{ env.GAME }} gamedata — always the newest build (currently ${{ env.BUILDID }}). Stable URL; assets overwritten each update."