Skip to content

🤖 Auto-update server-preview-publicized redist files #20

🤖 Auto-update server-preview-publicized redist files

🤖 Auto-update server-preview-publicized redist files #20

Workflow file for this run

name: "Redist Verify"
# Uses `pull_request` (NOT `pull_request_target`): the validation steps below run
# only PR-supplied data (file checks), never untrusted PR code, so they have no
# access to repo secrets from a fork — which is what we want. The auto-merge /
# auto-approve steps are additionally gated on the PR author being the trusted
# bot (`rocketmodfixadmin`), a value GitHub sets and a fork cannot spoof.
on:
pull_request:
branches:
- master
paths:
- 'redist/**'
types:
- opened
- synchronize
- reopened
- ready_for_review
jobs:
verify:
name: "Verify Redist Update"
runs-on: ubuntu-latest
env:
ALLOW_AUTO_MERGE_REDIST_PR: ${{ vars.ALLOW_AUTO_MERGE_REDIST_PR }}
permissions:
pull-requests: write
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Resolve variant and redist directory
run: |
set -euo pipefail
# Automated redist PRs use the branch name redist-update/<variant>.
if [[ "${GITHUB_HEAD_REF}" != redist-update/* ]]; then
echo "::error::This validator only handles automated 'redist-update/<variant>' branches (got '${GITHUB_HEAD_REF}')."
exit 1
fi
VARIANT="${GITHUB_HEAD_REF##*/}"
echo "Detected variant: $VARIANT"
# Single source of truth: map variant -> dir via .github/variants.json.
REDIST_DIR=$(jq -r --arg v "$VARIANT" '.[] | select(.variant == $v) | .dir' .github/variants.json)
IS_PREVIEW=$(jq -r --arg v "$VARIANT" '.[] | select(.variant == $v) | .preview' .github/variants.json)
if [ -z "$REDIST_DIR" ] || [ "$REDIST_DIR" = "null" ]; then
echo "::error::Unknown variant '$VARIANT' (not found in .github/variants.json)."
exit 1
fi
if [ ! -d "$REDIST_DIR" ]; then
echo "::error::Redist directory does not exist: $REDIST_DIR"
exit 1
fi
echo "✅ Variant=$VARIANT Dir=$REDIST_DIR Preview=$IS_PREVIEW"
echo "VARIANT=$VARIANT" >> "$GITHUB_ENV"
echo "REDIST_DIR=$REDIST_DIR" >> "$GITHUB_ENV"
# Preview variants track version.preview.json; everyone else version.json.
if [ "$IS_PREVIEW" = "true" ]; then
echo "VERSION_FILE=$REDIST_DIR/version.preview.json" >> "$GITHUB_ENV"
else
echo "VERSION_FILE=$REDIST_DIR/version.json" >> "$GITHUB_ENV"
fi
- name: Validate required DLL and XML files
run: |
set -euo pipefail
echo "📋 Validating required DLL and XML documentation files in $REDIST_DIR..."
REQUIRED_DLLS=(
"Assembly-CSharp.dll"
"SDG.NetPak.Runtime.dll"
"com.rlabrecque.steamworks.net.dll"
"SDG.Glazier.Runtime.dll"
"SDG.HostBans.Runtime.dll"
"SDG.NetTransport.dll"
"SystemEx.dll"
"UnityEx.dll"
"UnturnedDat.dll"
)
# DLLs that must ship with a matching XML doc.
XML_FOR=("Assembly-CSharp.dll" "SDG.NetPak.Runtime.dll")
missing=()
for dll in "${REQUIRED_DLLS[@]}"; do
if [ -f "$REDIST_DIR/$dll" ]; then echo "✅ $dll"; else echo "❌ $dll"; missing+=("$dll"); fi
done
for dll in "${XML_FOR[@]}"; do
xml="${dll%.dll}.xml"
if [ -f "$REDIST_DIR/$xml" ]; then echo "✅ $xml"; else echo "❌ $xml"; missing+=("$xml"); fi
done
if [ ${#missing[@]} -ne 0 ]; then
echo "::error::Missing required files: ${missing[*]}"
exit 1
fi
echo "✅ All required files present."
- name: Validate file hashes against manifest.sha256.json
run: |
set -euo pipefail
HASH_FILE="$REDIST_DIR/manifest.sha256.json"
if [ ! -f "$HASH_FILE" ]; then
echo "::error::Missing $HASH_FILE — cannot verify file integrity."
exit 1
fi
echo "🔒 Verifying SHA-256 of each tracked file against manifest.sha256.json..."
# manifest.sha256.json: { "<filename>": "<sha256>" }. sha256sum -c wants
# "<hash> <path>"; run it from inside the redist dir so names resolve.
(
cd "$REDIST_DIR"
jq -r 'to_entries[] | "\(.value) \(.key)"' manifest.sha256.json | sha256sum -c -
)
echo "✅ All file hashes match the manifest."
- name: Validate this is a newer upstream build
run: |
set -euo pipefail
if [ ! -f "$VERSION_FILE" ]; then
echo "::error::Missing $VERSION_FILE."
exit 1
fi
NEW_VER=$(jq -r '.NuGetVersion' "$VERSION_FILE")
NEW_BUILD=$(jq -r '.BuildId' "$VERSION_FILE")
echo "PR: version=$NEW_VER buildId=$NEW_BUILD ($VERSION_FILE)"
# Compare against the same file on the base branch (self-contained; no
# network dependency on nuget.org).
git fetch --no-tags --depth=1 origin "${{ github.event.pull_request.base.ref }}" >/dev/null 2>&1 || true
BASE_JSON=$(git show "origin/${{ github.event.pull_request.base.ref }}:$VERSION_FILE" 2>/dev/null || echo "")
if [ -z "$BASE_JSON" ]; then
echo "No baseline on '${{ github.event.pull_request.base.ref }}' (new file) — skipping the check."
exit 0
fi
OLD_VER=$(printf '%s' "$BASE_JSON" | jq -r '.NuGetVersion')
OLD_BUILD=$(printf '%s' "$BASE_JSON" | jq -r '.BuildId')
echo "Base: version=$OLD_VER buildId=$OLD_BUILD"
# Authoritative gate: the Steam build id. Build ids are monotonic per
# app+branch and increase even when a release reverts game content (a
# rollback is a NEW, higher build). A LOWER build id therefore means we
# processed an OLDER upstream build — a real regression — so fail.
# The game VERSION itself can legitimately dip on a preview rollback
# while the build id still rises, so we do NOT hard-fail on version.
# See ARCHITECTURE.md ("Why the build id, not the version").
if [[ "$NEW_BUILD" =~ ^[0-9]+$ ]] && [[ "$OLD_BUILD" =~ ^[0-9]+$ ]]; then
if [ "$NEW_BUILD" -lt "$OLD_BUILD" ]; then
echo "::error::Build id went backwards ($OLD_BUILD -> $NEW_BUILD): this is an older upstream build than what is already published."
exit 1
fi
else
echo "::warning::Non-numeric build id ('$OLD_BUILD' -> '$NEW_BUILD'); skipping build-id gate."
fi
# Informational only: surface a NuGet-version dip (e.g. an upstream
# rollback) without blocking — the build id already confirmed it is the
# newer build.
result=$(python3 .github/scripts/compare_nuget_version.py "$NEW_VER" "$OLD_VER" 2>/dev/null || echo "unknown")
case "$result" in
lt) echo "::warning::NuGet version decreased ($OLD_VER -> $NEW_VER) but the build id advanced — likely an upstream rollback. Allowing (newer build)." ;;
eq) echo "::warning::NuGet version unchanged ($NEW_VER); the publish step will fail on a duplicate version by design." ;;
gt) echo "✅ Newer build and higher version: $OLD_VER ($OLD_BUILD) -> $NEW_VER ($NEW_BUILD)." ;;
*) echo "Build id advanced; version comparison skipped." ;;
esac
# --- Auto-merge: only for the trusted bot's PRs, when opted in via the
# repo variable. GITHUB_TOKEN approves (so the review shows as the bot
# reviewing); the PAT enables auto-merge (it can merge to a protected base).
- name: Auto-approve PR
if: success() && env.ALLOW_AUTO_MERGE_REDIST_PR == 'true' && github.event.pull_request.user.login == 'rocketmodfixadmin'
uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363 # v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ github.event.pull_request.number }}
review-message: "All checks passed (files, hashes, version). Auto-approved automated PR."
- name: Enable PR automerge
if: success() && env.ALLOW_AUTO_MERGE_REDIST_PR == 'true' && github.event.pull_request.user.login == 'rocketmodfixadmin'
uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3
with:
token: ${{ secrets.PAT }}
pull-request-number: ${{ github.event.pull_request.number }}
merge-method: squash