name: contribution on: pull_request: paths: - 'mappings/contributions/**' concurrency: group: contribution-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: contribution: runs-on: s2-runner env: STEAM_APPS: /home/cs2/.steam/SteamApps RELEASE_BASE: ${{ github.server_url }}/${{ github.repository }}/releases/download API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }} steps: - uses: actions/checkout@v6.0.2 with: fetch-depth: 0 - name: Resolve the affected game and its paths run: | changed=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" -- 'mappings/contributions/**') game="" if echo "$changed" | grep -q 'contributions/csgo/'; then game=cs2; fi if echo "$changed" | grep -q 'contributions/dota/'; then if [ -n "$game" ]; then echo "PR touches both games — split it into one PR per game"; exit 1; fi game=dota2 fi if [ -z "$game" ]; then echo "no contributions// files changed"; exit 1; fi case "$game" in cs2) key=csgo; appid=730 ;; dota2) key=dota; appid=570 ;; esac # The manifest carries the installed buildid + the install folder name (spaces and all). 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 "GAME=$game" echo "KEY=$key" echo "APPID=$appid" echo "GAME_DIR=$game_dir" echo "BUILDID=$buildid" } >> "$GITHUB_ENV" - name: Next patch number for this build run: | # The game-update run published --0; each contribution on that build bumps the patch. tags=$(curl -fsSL -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "$API/releases?limit=100" | jq -r '.[].tag_name') max=0 while read -r t; do case "$t" in "$GAME-$BUILDID-"*) p=${t##*-}; if [ "$p" -gt "$max" ] 2>/dev/null; then max=$p; fi ;; esac done <<< "$tags" echo "PATCH=$((max + 1))" >> "$GITHUB_ENV" - name: Fetch current model + seed # Stored gzipped on the release (see derive.yml); the deriver reads plain JSON. run: | mkdir -p in dist work curl -fsSL -o in/model.gz "$RELEASE_BASE/$GAME-latest/model-$GAME.json.gz" gunzip -c in/model.gz > "in/model-$GAME.json" curl -fsSL -o in/seed.gz "$RELEASE_BASE/$GAME-latest/seed-$GAME.json.gz" gunzip -c in/seed.gz > "seed-$GAME.json" - name: Fold the PR's contributions into the seed's snapshot run: | python3 - "$KEY" "seed-$GAME.json" "seed-$GAME.patched.json" <<'PY' import json, sys, glob, os key, seed_in, seed_out = sys.argv[1:4] seed = json.load(open(seed_in)) contribs = dict(seed.get("contributions") or {}) for f in glob.glob(f"mappings/contributions/{key}/*.json"): contribs[os.path.basename(f)] = json.load(open(f)) seed["contributions"] = contribs json.dump(seed, open(seed_out, "w")) PY - name: Build the deriver run: cargo build --release - name: Produce — validate the contribution live (no model fold; the build is unchanged) run: | # The model already contains this build (derive folded it), and the forward-only guard needs the # target to sort strictly after the model's latest. "-" does: it shares the buildid # prefix and is longer, and the next update's buildid still sorts above it. ln -sfn "$GAME_DIR" "work/$BUILDID-$PATCH" ./target/release/source2rosetta --game "$GAME" produce \ --seed "seed-$GAME.patched.json" \ --corpus-model "in/model-$GAME.json" \ --target "work/$BUILDID-$PATCH" \ --game-dir "$GAME_DIR" \ --version "$GAME-$BUILDID-$PATCH" \ --out-dir dist cp "seed-$GAME.patched.json" "dist/seed-$GAME.json" - name: Drop the re-folded model, compress the seed # A contribution does not change the binary, so the sidecar fold just re-folds a build the model # already has. Publishing that would append a duplicate row and grow the model on every PR — so the # model asset is left alone and `-latest` keeps the one the last derive published. run: | rm -f "dist/model-$GAME.json" gzip -6 "dist/seed-$GAME.json" - name: Publish the immutable patch 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 }}-${{ env.PATCH }} release-dir: dist token: ${{ secrets.GITHUB_TOKEN }} override: true release-notes: "${{ env.GAME }} gamedata — build ${{ env.BUILDID }}, patch ${{ env.PATCH }} (contribution PR #${{ github.event.pull_request.number }})." - name: Move -latest to this patch 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 — currently build ${{ env.BUILDID }} patch ${{ env.PATCH }}. Stable URL; assets overwritten on each update."