-
Notifications
You must be signed in to change notification settings - Fork 1
184 lines (168 loc) · 8.48 KB
/
Copy pathredist-verify.yaml
File metadata and controls
184 lines (168 loc) · 8.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
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